-
Notifications
You must be signed in to change notification settings - Fork 8.3k
feat: add profile setting #6897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7dc68ed
4347fba
e311cfb
48b3d30
226d9bd
cbf2a02
acf99f2
0319604
d811af3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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); | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+58
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling and null check for the API call. The
Apply this diff: onMounted(async () => {
- const data = await getUserInfoApi();
- profileBaseSettingRef.value.getFormApi().setValues(data);
+ try {
+ const data = await getUserInfoApi();
+ if (profileBaseSettingRef.value) {
+ profileBaseSettingRef.value.getFormApi().setValues(data);
+ }
+ } catch (error) {
+ console.error('Failed to load user profile:', error);
+ // Consider showing user-facing error notification
+ }
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| </script> | ||||||||||||||||||||||||||||||||
| <template> | ||||||||||||||||||||||||||||||||
| <ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" /> | ||||||||||||||||||||||||||||||||
| </template> | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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', | ||
| }, | ||
| ]); | ||
|
Comment on lines
+16
to
+33
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Internationalize hardcoded Chinese labels. Tab labels are hardcoded in Chinese. Use the i18n system for multi-language support. 🤖 Prompt for AI Agents |
||
| </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> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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: '待办任务将以站内信的形式通知', | ||
| }, | ||
|
Comment on lines
+8
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Extract hardcoded Chinese text to i18n resource files. All labels and descriptions are hardcoded in Chinese, preventing internationalization. Extract to locale files and use i18n keys like 🤖 Prompt for AI Agents |
||
| ]; | ||
| }); | ||
| </script> | ||
| <template> | ||
| <ProfileNotificationSetting :form-schema="formSchema" /> | ||
| </template> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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('密码修改成功'); | ||
| } | ||
|
Comment on lines
+55
to
+57
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainMissing API implementation for password change. The submit handler only displays a client-side success message without making an actual API call to change the password on the server. Implement the password change API call before the success message: -function handleSubmit() {
+async function handleSubmit(values: Record<string, any>) {
+ try {
+ // Example: await changePasswordApi(values);
+ // TODO: Implement actual API call
- message.success('密码修改成功');
+ message.success('密码修改成功');
+ } catch (error) {
+ message.error('密码修改失败');
+ }
}Run the following script to verify if a password change API already exists in the codebase: 🏁 Script executed: #!/bin/bash
# Search for password change API implementations
rg -nP --type=ts 'password|Password' -g '**/api/**' -C2Length of output: 2854 🏁 Script executed: #!/bin/bash
# Search for password change/update API with various naming patterns
rg -nP '(change|update|modify|set).*[Pp]assword' --type=ts -C2Length of output: 0 🏁 Script executed: #!/bin/bash
# Look at the password-setting.vue component to understand context
cat apps/web-antd/src/views/_core/profile/password-setting.vueLength of output: 1516 🏁 Script executed: #!/bin/bash
# Search for ProfilePasswordSetting component definition
fd -type f -name "*.ts" -o -name "*.vue" | xargs grep -l "ProfilePasswordSetting" | head -20Length of output: 170 🏁 Script executed: #!/bin/bash
# Search for any password endpoint in backend (beyond login)
find . -type f \( -name "*.ts" -o -name "*.js" \) -path "*/api/*" -o -path "*/backend*" | xargs grep -l "password" 2>/dev/null | grep -v node_modulesLength of output: 305 🏁 Script executed: #!/bin/bash
# Check if handleSubmit receives parameters from the form component
rg -nP "handleSubmit|@submit" apps/web-antd/src/views/_core/profile/password-setting.vue -A5 -B5Length of output: 308 🏁 Script executed: #!/bin/bash
# Fix the fd command - search for ProfilePasswordSetting
rg -l "ProfilePasswordSetting" --type ts --type vue | head -20Length of output: 27 🏁 Script executed: #!/bin/bash
# Check backend-mock structure to understand API patterns
find apps/backend-mock/api -type f -name "*.ts" | head -20Length of output: 830 🏁 Script executed: #!/bin/bash
# Search for other submit handlers to understand parameter passing pattern
rg -nP "function\s+handle\w+\s*\([^)]*\)\s*\{" apps/web-antd/src/views/ -C2 | head -50Length of output: 1754 Missing API implementation AND handler parameter for password change. The submit handler doesn't accept form values and has no API call to change the password on the server. Other similar forms in the codebase (register, forget-password) correctly receive form values and handle them. Two issues to fix:
-function handleSubmit() {
+async function handleSubmit(values: Record<string, any>) {
+ try {
+ // Example: await changePasswordApi(values);
+ // TODO: Implement actual API call and create backend endpoint
- message.success('密码修改成功');
+ message.success('密码修改成功');
+ } catch (error) {
+ message.error('密码修改失败');
+ }
}
🤖 Prompt for AI Agents |
||
| </script> | ||
| <template> | ||
| <ProfilePasswordSetting | ||
| ref="profilePasswordSettingRef" | ||
| class="w-1/3" | ||
| :form-schema="formSchema" | ||
| @submit="handleSubmit" | ||
| /> | ||
| </template> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use i18n for labels instead of hardcoded Chinese text.
Both
MOCK_ROLES_OPTIONSandformSchemacontain hardcoded Chinese labels. For proper internationalization support, these should use translation keys via$t()or similar i18n utilities.Example refactor:
const MOCK_ROLES_OPTIONS: BasicOption[] = [ { - label: '管理员', + label: $t('profile.roles.admin'), value: 'super', }, // ... other options ];🤖 Prompt for AI Agents