feat: add profile comps

This commit is contained in:
xingyu4j 2025-11-10 17:49:58 +08:00
parent a4aa133db5
commit 7dc68ed368
8 changed files with 309 additions and 0 deletions

View File

@ -2,3 +2,4 @@ export * from './about';
export * from './authentication'; export * from './authentication';
export * from './dashboard'; export * from './dashboard';
export * from './fallback'; export * from './fallback';
export * from './profile';

View File

@ -0,0 +1,56 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import type { VbenFormSchema } from '@vben-core/form-ui';
import { computed, reactive } from 'vue';
import { useVbenForm } from '@vben-core/form-ui';
import { VbenButton } from '@vben-core/shadcn-ui';
interface Props {
formSchema?: VbenFormSchema[];
}
const props = withDefaults(defineProps<Props>(), {
formSchema: () => [],
});
const emit = defineEmits<{
submit: [Recordable<any>];
}>();
const [Form, formApi] = useVbenForm(
reactive({
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
layout: 'horizontal',
schema: computed(() => props.formSchema),
showDefaultActions: false,
}),
);
async function handleSubmit() {
const { valid } = await formApi.validate();
const values = await formApi.getValues();
if (valid) {
emit('submit', values);
}
}
defineExpose({
getFormApi: () => formApi,
});
</script>
<template>
<div @keydown.enter.prevent="handleSubmit">
<Form />
<VbenButton type="submit" class="mt-4" @click="handleSubmit">
更新基本信息
</VbenButton>
</div>
</template>

View File

@ -0,0 +1,6 @@
export { default as ProfileBaseSetting } from './base-setting.vue';
export { default as ProfileNotificationSetting } from './notification-setting.vue';
export { default as ProfilePasswordSetting } from './password-setting.vue';
export { default as Profile } from './profile.vue';
export { default as ProfileSecuritySetting } from './security-setting.vue';
export type * from './types';

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import type { SettingProps } from './types';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
Switch,
} from '@vben-core/shadcn-ui';
withDefaults(defineProps<SettingProps>(), {
formSchema: () => [],
});
const emit = defineEmits<{
change: [Recordable<any>];
}>();
function handleChange(fieldName: string, value: boolean) {
emit('change', { fieldName, value });
}
</script>
<template>
<Form class="space-y-8">
<div class="space-y-4">
<template v-for="item in formSchema" :key="item.fieldName">
<FormField type="checkbox" :name="item.fieldName">
<FormItem
class="flex flex-row items-center justify-between rounded-lg border p-4"
>
<div class="space-y-0.5">
<FormLabel class="text-base"> {{ item.label }} </FormLabel>
<FormDescription>
{{ item.description }}
</FormDescription>
</div>
<FormControl>
<Switch
:model-value="item.value"
@update:model-value="handleChange(item.fieldName, $event)"
/>
</FormControl>
</FormItem>
</FormField>
</template>
</div>
</Form>
</template>

View File

@ -0,0 +1,56 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import type { VbenFormSchema } from '@vben-core/form-ui';
import { computed, reactive } from 'vue';
import { useVbenForm } from '@vben-core/form-ui';
import { VbenButton } from '@vben-core/shadcn-ui';
interface Props {
formSchema?: VbenFormSchema[];
}
const props = withDefaults(defineProps<Props>(), {
formSchema: () => [],
});
const emit = defineEmits<{
submit: [Recordable<any>];
}>();
const [Form, formApi] = useVbenForm(
reactive({
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
layout: 'horizontal',
schema: computed(() => props.formSchema),
showDefaultActions: false,
}),
);
async function handleSubmit() {
const { valid } = await formApi.validate();
const values = await formApi.getValues();
if (valid) {
emit('submit', values);
}
}
defineExpose({
getFormApi: () => formApi,
});
</script>
<template>
<div>
<Form />
<VbenButton type="submit" class="mt-4" @click="handleSubmit">
更新密码
</VbenButton>
</div>
</template>

View File

@ -0,0 +1,62 @@
<script setup lang="ts">
import type { Props } from './types';
import { preferences } from '@vben-core/preferences';
import {
Card,
Separator,
Tabs,
TabsList,
TabsTrigger,
VbenAvatar,
} from '@vben-core/shadcn-ui';
import { Page } from '../../components';
defineOptions({
name: 'ProfileUI',
});
withDefaults(defineProps<Props>(), {
title: '关于项目',
tabs: () => [],
});
const tabsValue = defineModel<string>('modelValue');
</script>
<template>
<Page auto-content-height>
<div class="flex h-full w-full">
<Card class="w-1/6 flex-none">
<div class="mt-4 flex h-40 flex-col items-center justify-center gap-4">
<VbenAvatar
:src="userInfo?.avatar ?? preferences.app.defaultAvatar"
class="size-20"
/>
<span class="text-lg font-semibold">
{{ userInfo?.realName ?? '' }}
</span>
<span class="text-foreground/80 text-sm">
{{ userInfo?.username ?? '' }}
</span>
</div>
<Separator class="my-4" />
<Tabs v-model="tabsValue" orientation="vertical" class="m-4">
<TabsList class="bg-card grid w-full grid-cols-1">
<TabsTrigger
v-for="tab in tabs"
:key="tab.value"
:value="tab.value"
class="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground h-12 justify-start"
>
{{ tab.label }}
</TabsTrigger>
</TabsList>
</Tabs>
</Card>
<Card class="ml-4 w-5/6 flex-auto p-8">
<slot name="content"></slot>
</Card>
</div>
</Page>
</template>

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import type { SettingProps } from './types';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
Switch,
} from '@vben-core/shadcn-ui';
withDefaults(defineProps<SettingProps>(), {
formSchema: () => [],
});
const emit = defineEmits<{
change: [Recordable<any>];
}>();
function handleChange(fieldName: string, value: boolean) {
emit('change', { fieldName, value });
}
</script>
<template>
<Form class="space-y-8">
<div class="space-y-4">
<template v-for="item in formSchema" :key="item.fieldName">
<FormField type="checkbox" :name="item.fieldName">
<FormItem
class="flex flex-row items-center justify-between rounded-lg border p-4"
>
<div class="space-y-0.5">
<FormLabel class="text-base"> {{ item.label }} </FormLabel>
<FormDescription>
{{ item.description }}
</FormDescription>
</div>
<FormControl>
<Switch
:model-value="item.value"
@update:model-value="handleChange(item.fieldName, $event)"
/>
</FormControl>
</FormItem>
</FormField>
</template>
</div>
</Form>
</template>

View File

@ -0,0 +1,22 @@
import type { BasicUserInfo } from '@vben/types';
export interface Props {
title?: string;
userInfo: BasicUserInfo | null;
tabs: {
label: string;
value: string;
}[];
}
export interface FormSchemaItem {
description: string;
fieldName: string;
label: string;
name: string;
value: boolean;
}
export interface SettingProps {
formSchema: FormSchemaItem[];
}