committed by
GitHub
64 changed files with 2044 additions and 29 deletions
@ -0,0 +1,65 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { BasicOption } from '@vben/types'; |
||||
|
|
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfileBaseSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
import { getUserInfoApi } from '#/api'; |
||||
|
|
||||
|
const profileBaseSettingRef = ref(); |
||||
|
|
||||
|
const MOCK_ROLES_OPTIONS: BasicOption[] = [ |
||||
|
{ |
||||
|
label: '管理员', |
||||
|
value: 'super', |
||||
|
}, |
||||
|
{ |
||||
|
label: '用户', |
||||
|
value: 'user', |
||||
|
}, |
||||
|
{ |
||||
|
label: '测试', |
||||
|
value: 'test', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'realName', |
||||
|
component: 'Input', |
||||
|
label: '姓名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'username', |
||||
|
component: 'Input', |
||||
|
label: '用户名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'roles', |
||||
|
component: 'Select', |
||||
|
componentProps: { |
||||
|
mode: 'tags', |
||||
|
options: MOCK_ROLES_OPTIONS, |
||||
|
}, |
||||
|
label: '角色', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'introduction', |
||||
|
component: 'Textarea', |
||||
|
label: '个人简介', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
const data = await getUserInfoApi(); |
||||
|
profileBaseSettingRef.value.getFormApi().setValues(data); |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,49 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { Profile } from '@vben/common-ui'; |
||||
|
import { useUserStore } from '@vben/stores'; |
||||
|
|
||||
|
import ProfileBase from './base-setting.vue'; |
||||
|
import ProfileNotificationSetting from './notification-setting.vue'; |
||||
|
import ProfilePasswordSetting from './password-setting.vue'; |
||||
|
import ProfileSecuritySetting from './security-setting.vue'; |
||||
|
|
||||
|
const userStore = useUserStore(); |
||||
|
|
||||
|
const tabsValue = ref<string>('basic'); |
||||
|
|
||||
|
const tabs = ref([ |
||||
|
{ |
||||
|
label: '基本设置', |
||||
|
value: 'basic', |
||||
|
}, |
||||
|
{ |
||||
|
label: '安全设置', |
||||
|
value: 'security', |
||||
|
}, |
||||
|
{ |
||||
|
label: '修改密码', |
||||
|
value: 'password', |
||||
|
}, |
||||
|
{ |
||||
|
label: '新消息提醒', |
||||
|
value: 'notice', |
||||
|
}, |
||||
|
]); |
||||
|
</script> |
||||
|
<template> |
||||
|
<Profile |
||||
|
v-model:model-value="tabsValue" |
||||
|
title="个人中心" |
||||
|
:user-info="userStore.userInfo" |
||||
|
:tabs="tabs" |
||||
|
> |
||||
|
<template #content> |
||||
|
<ProfileBase v-if="tabsValue === 'basic'" /> |
||||
|
<ProfileSecuritySetting v-if="tabsValue === 'security'" /> |
||||
|
<ProfilePasswordSetting v-if="tabsValue === 'password'" /> |
||||
|
<ProfileNotificationSetting v-if="tabsValue === 'notice'" /> |
||||
|
</template> |
||||
|
</Profile> |
||||
|
</template> |
||||
@ -0,0 +1,31 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileNotificationSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '其他用户的消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'systemMessage', |
||||
|
label: '系统消息', |
||||
|
description: '系统消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'todoTask', |
||||
|
label: '待办任务', |
||||
|
description: '待办任务将以站内信的形式通知', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileNotificationSetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,66 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfilePasswordSetting, z } from '@vben/common-ui'; |
||||
|
|
||||
|
import { message } from 'ant-design-vue'; |
||||
|
|
||||
|
const profilePasswordSettingRef = ref(); |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'oldPassword', |
||||
|
label: '旧密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
placeholder: '请输入旧密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'newPassword', |
||||
|
label: '新密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请输入新密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'confirmPassword', |
||||
|
label: '确认密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请再次输入新密码', |
||||
|
}, |
||||
|
dependencies: { |
||||
|
rules(values) { |
||||
|
const { newPassword } = values; |
||||
|
return z |
||||
|
.string({ required_error: '请再次输入新密码' }) |
||||
|
.min(1, { message: '请再次输入新密码' }) |
||||
|
.refine((value) => value === newPassword, { |
||||
|
message: '两次输入的密码不一致', |
||||
|
}); |
||||
|
}, |
||||
|
triggerFields: ['newPassword'], |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
message.success('密码修改成功'); |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfilePasswordSetting |
||||
|
ref="profilePasswordSettingRef" |
||||
|
class="w-1/3" |
||||
|
:form-schema="formSchema" |
||||
|
@submit="handleSubmit" |
||||
|
/> |
||||
|
</template> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileSecuritySetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '当前密码强度:强', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityPhone', |
||||
|
label: '密保手机', |
||||
|
description: '已绑定手机:138****8293', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityQuestion', |
||||
|
label: '密保问题', |
||||
|
description: '未设置密保问题,密保问题可有效保护账户安全', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityEmail', |
||||
|
label: '备用邮箱', |
||||
|
description: '已绑定邮箱:ant***sign.com', |
||||
|
}, |
||||
|
{ |
||||
|
value: false, |
||||
|
fieldName: 'securityMfa', |
||||
|
label: 'MFA 设备', |
||||
|
description: '未绑定 MFA 设备,绑定后,可以进行二次确认', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileSecuritySetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,65 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { BasicOption } from '@vben/types'; |
||||
|
|
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfileBaseSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
import { getUserInfoApi } from '#/api'; |
||||
|
|
||||
|
const profileBaseSettingRef = ref(); |
||||
|
|
||||
|
const MOCK_ROLES_OPTIONS: BasicOption[] = [ |
||||
|
{ |
||||
|
label: '管理员', |
||||
|
value: 'super', |
||||
|
}, |
||||
|
{ |
||||
|
label: '用户', |
||||
|
value: 'user', |
||||
|
}, |
||||
|
{ |
||||
|
label: '测试', |
||||
|
value: 'test', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'realName', |
||||
|
component: 'Input', |
||||
|
label: '姓名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'username', |
||||
|
component: 'Input', |
||||
|
label: '用户名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'roles', |
||||
|
component: 'Select', |
||||
|
componentProps: { |
||||
|
mode: 'tags', |
||||
|
options: MOCK_ROLES_OPTIONS, |
||||
|
}, |
||||
|
label: '角色', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'introduction', |
||||
|
component: 'Textarea', |
||||
|
label: '个人简介', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
const data = await getUserInfoApi(); |
||||
|
profileBaseSettingRef.value.getFormApi().setValues(data); |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,49 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { Profile } from '@vben/common-ui'; |
||||
|
import { useUserStore } from '@vben/stores'; |
||||
|
|
||||
|
import ProfileBase from './base-setting.vue'; |
||||
|
import ProfileNotificationSetting from './notification-setting.vue'; |
||||
|
import ProfilePasswordSetting from './password-setting.vue'; |
||||
|
import ProfileSecuritySetting from './security-setting.vue'; |
||||
|
|
||||
|
const userStore = useUserStore(); |
||||
|
|
||||
|
const tabsValue = ref<string>('basic'); |
||||
|
|
||||
|
const tabs = ref([ |
||||
|
{ |
||||
|
label: '基本设置', |
||||
|
value: 'basic', |
||||
|
}, |
||||
|
{ |
||||
|
label: '安全设置', |
||||
|
value: 'security', |
||||
|
}, |
||||
|
{ |
||||
|
label: '修改密码', |
||||
|
value: 'password', |
||||
|
}, |
||||
|
{ |
||||
|
label: '新消息提醒', |
||||
|
value: 'notice', |
||||
|
}, |
||||
|
]); |
||||
|
</script> |
||||
|
<template> |
||||
|
<Profile |
||||
|
v-model:model-value="tabsValue" |
||||
|
title="个人中心" |
||||
|
:user-info="userStore.userInfo" |
||||
|
:tabs="tabs" |
||||
|
> |
||||
|
<template #content> |
||||
|
<ProfileBase v-if="tabsValue === 'basic'" /> |
||||
|
<ProfileSecuritySetting v-if="tabsValue === 'security'" /> |
||||
|
<ProfilePasswordSetting v-if="tabsValue === 'password'" /> |
||||
|
<ProfileNotificationSetting v-if="tabsValue === 'notice'" /> |
||||
|
</template> |
||||
|
</Profile> |
||||
|
</template> |
||||
@ -0,0 +1,31 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileNotificationSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '其他用户的消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'systemMessage', |
||||
|
label: '系统消息', |
||||
|
description: '系统消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'todoTask', |
||||
|
label: '待办任务', |
||||
|
description: '待办任务将以站内信的形式通知', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileNotificationSetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,66 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfilePasswordSetting, z } from '@vben/common-ui'; |
||||
|
|
||||
|
import { ElMessage } from 'element-plus'; |
||||
|
|
||||
|
const profilePasswordSettingRef = ref(); |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'oldPassword', |
||||
|
label: '旧密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
placeholder: '请输入旧密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'newPassword', |
||||
|
label: '新密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请输入新密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'confirmPassword', |
||||
|
label: '确认密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请再次输入新密码', |
||||
|
}, |
||||
|
dependencies: { |
||||
|
rules(values) { |
||||
|
const { newPassword } = values; |
||||
|
return z |
||||
|
.string({ required_error: '请再次输入新密码' }) |
||||
|
.min(1, { message: '请再次输入新密码' }) |
||||
|
.refine((value) => value === newPassword, { |
||||
|
message: '两次输入的密码不一致', |
||||
|
}); |
||||
|
}, |
||||
|
triggerFields: ['newPassword'], |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
ElMessage.success('密码修改成功'); |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfilePasswordSetting |
||||
|
ref="profilePasswordSettingRef" |
||||
|
class="w-1/3" |
||||
|
:form-schema="formSchema" |
||||
|
@submit="handleSubmit" |
||||
|
/> |
||||
|
</template> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileSecuritySetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '当前密码强度:强', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityPhone', |
||||
|
label: '密保手机', |
||||
|
description: '已绑定手机:138****8293', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityQuestion', |
||||
|
label: '密保问题', |
||||
|
description: '未设置密保问题,密保问题可有效保护账户安全', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityEmail', |
||||
|
label: '备用邮箱', |
||||
|
description: '已绑定邮箱:ant***sign.com', |
||||
|
}, |
||||
|
{ |
||||
|
value: false, |
||||
|
fieldName: 'securityMfa', |
||||
|
label: 'MFA 设备', |
||||
|
description: '未绑定 MFA 设备,绑定后,可以进行二次确认', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileSecuritySetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,65 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { BasicOption } from '@vben/types'; |
||||
|
|
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfileBaseSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
import { getUserInfoApi } from '#/api'; |
||||
|
|
||||
|
const profileBaseSettingRef = ref(); |
||||
|
|
||||
|
const MOCK_ROLES_OPTIONS: BasicOption[] = [ |
||||
|
{ |
||||
|
label: '管理员', |
||||
|
value: 'super', |
||||
|
}, |
||||
|
{ |
||||
|
label: '用户', |
||||
|
value: 'user', |
||||
|
}, |
||||
|
{ |
||||
|
label: '测试', |
||||
|
value: 'test', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'realName', |
||||
|
component: 'Input', |
||||
|
label: '姓名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'username', |
||||
|
component: 'Input', |
||||
|
label: '用户名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'roles', |
||||
|
component: 'Select', |
||||
|
componentProps: { |
||||
|
mode: 'tags', |
||||
|
options: MOCK_ROLES_OPTIONS, |
||||
|
}, |
||||
|
label: '角色', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'introduction', |
||||
|
component: 'Textarea', |
||||
|
label: '个人简介', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
const data = await getUserInfoApi(); |
||||
|
profileBaseSettingRef.value.getFormApi().setValues(data); |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,49 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { Profile } from '@vben/common-ui'; |
||||
|
import { useUserStore } from '@vben/stores'; |
||||
|
|
||||
|
import ProfileBase from './base-setting.vue'; |
||||
|
import ProfileNotificationSetting from './notification-setting.vue'; |
||||
|
import ProfilePasswordSetting from './password-setting.vue'; |
||||
|
import ProfileSecuritySetting from './security-setting.vue'; |
||||
|
|
||||
|
const userStore = useUserStore(); |
||||
|
|
||||
|
const tabsValue = ref<string>('basic'); |
||||
|
|
||||
|
const tabs = ref([ |
||||
|
{ |
||||
|
label: '基本设置', |
||||
|
value: 'basic', |
||||
|
}, |
||||
|
{ |
||||
|
label: '安全设置', |
||||
|
value: 'security', |
||||
|
}, |
||||
|
{ |
||||
|
label: '修改密码', |
||||
|
value: 'password', |
||||
|
}, |
||||
|
{ |
||||
|
label: '新消息提醒', |
||||
|
value: 'notice', |
||||
|
}, |
||||
|
]); |
||||
|
</script> |
||||
|
<template> |
||||
|
<Profile |
||||
|
v-model:model-value="tabsValue" |
||||
|
title="个人中心" |
||||
|
:user-info="userStore.userInfo" |
||||
|
:tabs="tabs" |
||||
|
> |
||||
|
<template #content> |
||||
|
<ProfileBase v-if="tabsValue === 'basic'" /> |
||||
|
<ProfileSecuritySetting v-if="tabsValue === 'security'" /> |
||||
|
<ProfilePasswordSetting v-if="tabsValue === 'password'" /> |
||||
|
<ProfileNotificationSetting v-if="tabsValue === 'notice'" /> |
||||
|
</template> |
||||
|
</Profile> |
||||
|
</template> |
||||
@ -0,0 +1,31 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileNotificationSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '其他用户的消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'systemMessage', |
||||
|
label: '系统消息', |
||||
|
description: '系统消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'todoTask', |
||||
|
label: '待办任务', |
||||
|
description: '待办任务将以站内信的形式通知', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileNotificationSetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,66 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfilePasswordSetting, z } from '@vben/common-ui'; |
||||
|
|
||||
|
import { message } from '#/adapter/naive'; |
||||
|
|
||||
|
const profilePasswordSettingRef = ref(); |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'oldPassword', |
||||
|
label: '旧密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
placeholder: '请输入旧密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'newPassword', |
||||
|
label: '新密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请输入新密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'confirmPassword', |
||||
|
label: '确认密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请再次输入新密码', |
||||
|
}, |
||||
|
dependencies: { |
||||
|
rules(values) { |
||||
|
const { newPassword } = values; |
||||
|
return z |
||||
|
.string({ required_error: '请再次输入新密码' }) |
||||
|
.min(1, { message: '请再次输入新密码' }) |
||||
|
.refine((value) => value === newPassword, { |
||||
|
message: '两次输入的密码不一致', |
||||
|
}); |
||||
|
}, |
||||
|
triggerFields: ['newPassword'], |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
message.success('密码修改成功'); |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfilePasswordSetting |
||||
|
ref="profilePasswordSettingRef" |
||||
|
class="w-1/3" |
||||
|
:form-schema="formSchema" |
||||
|
@submit="handleSubmit" |
||||
|
/> |
||||
|
</template> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileSecuritySetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '当前密码强度:强', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityPhone', |
||||
|
label: '密保手机', |
||||
|
description: '已绑定手机:138****8293', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityQuestion', |
||||
|
label: '密保问题', |
||||
|
description: '未设置密保问题,密保问题可有效保护账户安全', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityEmail', |
||||
|
label: '备用邮箱', |
||||
|
description: '已绑定邮箱:ant***sign.com', |
||||
|
}, |
||||
|
{ |
||||
|
value: false, |
||||
|
fieldName: 'securityMfa', |
||||
|
label: 'MFA 设备', |
||||
|
description: '未绑定 MFA 设备,绑定后,可以进行二次确认', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileSecuritySetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,65 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { BasicOption } from '@vben/types'; |
||||
|
|
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfileBaseSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
import { getUserInfoApi } from '#/api'; |
||||
|
|
||||
|
const profileBaseSettingRef = ref(); |
||||
|
|
||||
|
const MOCK_ROLES_OPTIONS: BasicOption[] = [ |
||||
|
{ |
||||
|
label: '管理员', |
||||
|
value: 'super', |
||||
|
}, |
||||
|
{ |
||||
|
label: '用户', |
||||
|
value: 'user', |
||||
|
}, |
||||
|
{ |
||||
|
label: '测试', |
||||
|
value: 'test', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'realName', |
||||
|
component: 'Input', |
||||
|
label: '姓名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'username', |
||||
|
component: 'Input', |
||||
|
label: '用户名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'roles', |
||||
|
component: 'Select', |
||||
|
componentProps: { |
||||
|
mode: 'tags', |
||||
|
options: MOCK_ROLES_OPTIONS, |
||||
|
}, |
||||
|
label: '角色', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'introduction', |
||||
|
component: 'Textarea', |
||||
|
label: '个人简介', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
const data = await getUserInfoApi(); |
||||
|
profileBaseSettingRef.value.getFormApi().setValues(data); |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,49 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { Profile } from '@vben/common-ui'; |
||||
|
import { useUserStore } from '@vben/stores'; |
||||
|
|
||||
|
import ProfileBase from './base-setting.vue'; |
||||
|
import ProfileNotificationSetting from './notification-setting.vue'; |
||||
|
import ProfilePasswordSetting from './password-setting.vue'; |
||||
|
import ProfileSecuritySetting from './security-setting.vue'; |
||||
|
|
||||
|
const userStore = useUserStore(); |
||||
|
|
||||
|
const tabsValue = ref<string>('basic'); |
||||
|
|
||||
|
const tabs = ref([ |
||||
|
{ |
||||
|
label: '基本设置', |
||||
|
value: 'basic', |
||||
|
}, |
||||
|
{ |
||||
|
label: '安全设置', |
||||
|
value: 'security', |
||||
|
}, |
||||
|
{ |
||||
|
label: '修改密码', |
||||
|
value: 'password', |
||||
|
}, |
||||
|
{ |
||||
|
label: '新消息提醒', |
||||
|
value: 'notice', |
||||
|
}, |
||||
|
]); |
||||
|
</script> |
||||
|
<template> |
||||
|
<Profile |
||||
|
v-model:model-value="tabsValue" |
||||
|
title="个人中心" |
||||
|
:user-info="userStore.userInfo" |
||||
|
:tabs="tabs" |
||||
|
> |
||||
|
<template #content> |
||||
|
<ProfileBase v-if="tabsValue === 'basic'" /> |
||||
|
<ProfileSecuritySetting v-if="tabsValue === 'security'" /> |
||||
|
<ProfilePasswordSetting v-if="tabsValue === 'password'" /> |
||||
|
<ProfileNotificationSetting v-if="tabsValue === 'notice'" /> |
||||
|
</template> |
||||
|
</Profile> |
||||
|
</template> |
||||
@ -0,0 +1,31 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileNotificationSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '其他用户的消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'systemMessage', |
||||
|
label: '系统消息', |
||||
|
description: '系统消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'todoTask', |
||||
|
label: '待办任务', |
||||
|
description: '待办任务将以站内信的形式通知', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileNotificationSetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,66 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfilePasswordSetting, z } from '@vben/common-ui'; |
||||
|
|
||||
|
import { message } from '#/adapter/tdesign'; |
||||
|
|
||||
|
const profilePasswordSettingRef = ref(); |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'oldPassword', |
||||
|
label: '旧密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
placeholder: '请输入旧密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'newPassword', |
||||
|
label: '新密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请输入新密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'confirmPassword', |
||||
|
label: '确认密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请再次输入新密码', |
||||
|
}, |
||||
|
dependencies: { |
||||
|
rules(values) { |
||||
|
const { newPassword } = values; |
||||
|
return z |
||||
|
.string({ required_error: '请再次输入新密码' }) |
||||
|
.min(1, { message: '请再次输入新密码' }) |
||||
|
.refine((value) => value === newPassword, { |
||||
|
message: '两次输入的密码不一致', |
||||
|
}); |
||||
|
}, |
||||
|
triggerFields: ['newPassword'], |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
message.success('密码修改成功'); |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfilePasswordSetting |
||||
|
ref="profilePasswordSettingRef" |
||||
|
class="w-1/3" |
||||
|
:form-schema="formSchema" |
||||
|
@submit="handleSubmit" |
||||
|
/> |
||||
|
</template> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileSecuritySetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '当前密码强度:强', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityPhone', |
||||
|
label: '密保手机', |
||||
|
description: '已绑定手机:138****8293', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityQuestion', |
||||
|
label: '密保问题', |
||||
|
description: '未设置密保问题,密保问题可有效保护账户安全', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityEmail', |
||||
|
label: '备用邮箱', |
||||
|
description: '已绑定邮箱:ant***sign.com', |
||||
|
}, |
||||
|
{ |
||||
|
value: false, |
||||
|
fieldName: 'securityMfa', |
||||
|
label: 'MFA 设备', |
||||
|
description: '未绑定 MFA 设备,绑定后,可以进行二次确认', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileSecuritySetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -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> |
||||
@ -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'; |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -0,0 +1,21 @@ |
|||||
|
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; |
||||
|
value: boolean; |
||||
|
} |
||||
|
|
||||
|
export interface SettingProps { |
||||
|
formSchema: FormSchemaItem[]; |
||||
|
} |
||||
@ -1,9 +1,17 @@ |
|||||
interface NotificationItem { |
interface NotificationItem { |
||||
|
id: number | string; |
||||
avatar: string; |
avatar: string; |
||||
date: string; |
date: string; |
||||
isRead?: boolean; |
isRead?: boolean; |
||||
message: string; |
message: string; |
||||
title: string; |
title: string; |
||||
|
/** |
||||
|
* 跳转链接,可以是路由路径或完整 URL |
||||
|
* @example '/dashboard' 或 'https://example.com' |
||||
|
*/ |
||||
|
link?: string; |
||||
|
query?: Record<string, any>; |
||||
|
state?: Record<string, any>; |
||||
} |
} |
||||
|
|
||||
export type { NotificationItem }; |
export type { NotificationItem }; |
||||
|
|||||
@ -0,0 +1,65 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { BasicOption } from '@vben/types'; |
||||
|
|
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfileBaseSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
import { getUserInfoApi } from '#/api'; |
||||
|
|
||||
|
const profileBaseSettingRef = ref(); |
||||
|
|
||||
|
const MOCK_ROLES_OPTIONS: BasicOption[] = [ |
||||
|
{ |
||||
|
label: '管理员', |
||||
|
value: 'super', |
||||
|
}, |
||||
|
{ |
||||
|
label: '用户', |
||||
|
value: 'user', |
||||
|
}, |
||||
|
{ |
||||
|
label: '测试', |
||||
|
value: 'test', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'realName', |
||||
|
component: 'Input', |
||||
|
label: '姓名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'username', |
||||
|
component: 'Input', |
||||
|
label: '用户名', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'roles', |
||||
|
component: 'Select', |
||||
|
componentProps: { |
||||
|
mode: 'tags', |
||||
|
options: MOCK_ROLES_OPTIONS, |
||||
|
}, |
||||
|
label: '角色', |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'introduction', |
||||
|
component: 'Textarea', |
||||
|
label: '个人简介', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
const data = await getUserInfoApi(); |
||||
|
profileBaseSettingRef.value.getFormApi().setValues(data); |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,49 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { Profile } from '@vben/common-ui'; |
||||
|
import { useUserStore } from '@vben/stores'; |
||||
|
|
||||
|
import ProfileBase from './base-setting.vue'; |
||||
|
import ProfileNotificationSetting from './notification-setting.vue'; |
||||
|
import ProfilePasswordSetting from './password-setting.vue'; |
||||
|
import ProfileSecuritySetting from './security-setting.vue'; |
||||
|
|
||||
|
const userStore = useUserStore(); |
||||
|
|
||||
|
const tabsValue = ref<string>('basic'); |
||||
|
|
||||
|
const tabs = ref([ |
||||
|
{ |
||||
|
label: '基本设置', |
||||
|
value: 'basic', |
||||
|
}, |
||||
|
{ |
||||
|
label: '安全设置', |
||||
|
value: 'security', |
||||
|
}, |
||||
|
{ |
||||
|
label: '修改密码', |
||||
|
value: 'password', |
||||
|
}, |
||||
|
{ |
||||
|
label: '新消息提醒', |
||||
|
value: 'notice', |
||||
|
}, |
||||
|
]); |
||||
|
</script> |
||||
|
<template> |
||||
|
<Profile |
||||
|
v-model:model-value="tabsValue" |
||||
|
title="个人中心" |
||||
|
:user-info="userStore.userInfo" |
||||
|
:tabs="tabs" |
||||
|
> |
||||
|
<template #content> |
||||
|
<ProfileBase v-if="tabsValue === 'basic'" /> |
||||
|
<ProfileSecuritySetting v-if="tabsValue === 'security'" /> |
||||
|
<ProfilePasswordSetting v-if="tabsValue === 'password'" /> |
||||
|
<ProfileNotificationSetting v-if="tabsValue === 'notice'" /> |
||||
|
</template> |
||||
|
</Profile> |
||||
|
</template> |
||||
@ -0,0 +1,31 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileNotificationSetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '其他用户的消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'systemMessage', |
||||
|
label: '系统消息', |
||||
|
description: '系统消息将以站内信的形式通知', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'todoTask', |
||||
|
label: '待办任务', |
||||
|
description: '待办任务将以站内信的形式通知', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileNotificationSetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
@ -0,0 +1,66 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
import { computed, ref } from 'vue'; |
||||
|
|
||||
|
import { ProfilePasswordSetting, z } from '@vben/common-ui'; |
||||
|
|
||||
|
import { message } from 'ant-design-vue'; |
||||
|
|
||||
|
const profilePasswordSettingRef = ref(); |
||||
|
|
||||
|
const formSchema = computed((): VbenFormSchema[] => { |
||||
|
return [ |
||||
|
{ |
||||
|
fieldName: 'oldPassword', |
||||
|
label: '旧密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
placeholder: '请输入旧密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'newPassword', |
||||
|
label: '新密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请输入新密码', |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
fieldName: 'confirmPassword', |
||||
|
label: '确认密码', |
||||
|
component: 'VbenInputPassword', |
||||
|
componentProps: { |
||||
|
passwordStrength: true, |
||||
|
placeholder: '请再次输入新密码', |
||||
|
}, |
||||
|
dependencies: { |
||||
|
rules(values) { |
||||
|
const { newPassword } = values; |
||||
|
return z |
||||
|
.string({ required_error: '请再次输入新密码' }) |
||||
|
.min(1, { message: '请再次输入新密码' }) |
||||
|
.refine((value) => value === newPassword, { |
||||
|
message: '两次输入的密码不一致', |
||||
|
}); |
||||
|
}, |
||||
|
triggerFields: ['newPassword'], |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
function handleSubmit() { |
||||
|
message.success('密码修改成功'); |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfilePasswordSetting |
||||
|
ref="profilePasswordSettingRef" |
||||
|
class="w-1/3" |
||||
|
:form-schema="formSchema" |
||||
|
@submit="handleSubmit" |
||||
|
/> |
||||
|
</template> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { computed } from 'vue'; |
||||
|
|
||||
|
import { ProfileSecuritySetting } from '@vben/common-ui'; |
||||
|
|
||||
|
const formSchema = computed(() => { |
||||
|
return [ |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'accountPassword', |
||||
|
label: '账户密码', |
||||
|
description: '当前密码强度:强', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityPhone', |
||||
|
label: '密保手机', |
||||
|
description: '已绑定手机:138****8293', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityQuestion', |
||||
|
label: '密保问题', |
||||
|
description: '未设置密保问题,密保问题可有效保护账户安全', |
||||
|
}, |
||||
|
{ |
||||
|
value: true, |
||||
|
fieldName: 'securityEmail', |
||||
|
label: '备用邮箱', |
||||
|
description: '已绑定邮箱:ant***sign.com', |
||||
|
}, |
||||
|
{ |
||||
|
value: false, |
||||
|
fieldName: 'securityMfa', |
||||
|
label: 'MFA 设备', |
||||
|
description: '未绑定 MFA 设备,绑定后,可以进行二次确认', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<ProfileSecuritySetting :form-schema="formSchema" /> |
||||
|
</template> |
||||
Loading…
Reference in new issue