From 1487520e4ef3e7c9e6f426cc27b7ddc7c79a3dde Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Mon, 6 Oct 2025 21:24:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E6=96=B0=E5=A2=9E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=90=8D=E6=9B=B4=E6=96=B0=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=94=A8=E6=88=B7=E8=B5=84=E6=96=99=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在后端用户资料更新接口中添加用户名验证和唯一性检查 - 前端用户信息组件支持编辑用户名,增加输入验证 - 更新用户状态管理,确保用户名和手机号的正确存储与展示 --- server/routers/auth_router.py | 26 ++++++++++++++++ web/src/components/UserInfoComponent.vue | 38 ++++++++++++++++++++---- web/src/stores/user.js | 12 +++++--- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/server/routers/auth_router.py b/server/routers/auth_router.py index 7fbd18cb..0944eee8 100644 --- a/server/routers/auth_router.py +++ b/server/routers/auth_router.py @@ -46,6 +46,7 @@ class UserUpdate(BaseModel): class UserProfileUpdate(BaseModel): + username: str | None = None phone_number: str | None = None @@ -259,6 +260,31 @@ async def update_profile( """更新当前用户的个人资料""" update_details = [] + # 更新用户名(仅允许修改显示名,不修改 user_id) + if profile_data.username is not None: + # 验证用户名格式 + is_valid, error_msg = validate_username(profile_data.username) + if not is_valid: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=error_msg, + ) + + # 检查用户名是否已被其他用户使用 + existing_user = ( + db.query(User) + .filter(User.username == profile_data.username, User.id != current_user.id) + .first() + ) + if existing_user: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="用户名已存在", + ) + + current_user.username = profile_data.username + update_details.append(f"用户名: {profile_data.username}") + # 更新手机号 if profile_data.phone_number is not None: # 如果手机号不为空,验证格式 diff --git a/web/src/components/UserInfoComponent.vue b/web/src/components/UserInfoComponent.vue index b188ea33..9e2ffc47 100644 --- a/web/src/components/UserInfoComponent.vue +++ b/web/src/components/UserInfoComponent.vue @@ -78,11 +78,26 @@
用户名
-
{{ userStore.username || '未设置' }}
+
{{ userStore.username || '未设置' }}
+
+ +
用户ID
-
{{ userStore.userIdLogin || '未设置' }}
+
{{ userStore.userIdLogin || '未设置' }}
+
+ +
手机号
@@ -150,6 +165,7 @@ const profileModalVisible = ref(false); const avatarUploading = ref(false); const profileEditing = ref(false); const editedProfile = ref({ + username: '', phone_number: '' }); @@ -215,6 +231,7 @@ const openProfile = async () => { try { await userStore.getCurrentUser(); editedProfile.value = { + username: userStore.username || '', phone_number: userStore.phoneNumber || '' }; } catch (error) { @@ -236,6 +253,7 @@ const getRoleColor = (role) => { const startEdit = () => { profileEditing.value = true; editedProfile.value = { + username: userStore.username || '', phone_number: userStore.phoneNumber || '' }; }; @@ -244,6 +262,7 @@ const startEdit = () => { const cancelEdit = () => { profileEditing.value = false; editedProfile.value = { + username: userStore.username || '', phone_number: userStore.phoneNumber || '' }; }; @@ -251,13 +270,22 @@ const cancelEdit = () => { // 保存个人资料 const saveProfile = async () => { try { + // 验证用户名 + if (editedProfile.value.username && (editedProfile.value.username.trim().length < 2 || editedProfile.value.username.trim().length > 20)) { + message.error('用户名长度必须在 2-20 个字符之间'); + return; + } + // 验证手机号格式 if (editedProfile.value.phone_number && !validatePhoneNumber(editedProfile.value.phone_number)) { message.error('请输入正确的手机号格式'); return; } - await userStore.updateProfile(editedProfile.value); + await userStore.updateProfile({ + username: editedProfile.value.username?.trim() || undefined, + phone_number: editedProfile.value.phone_number || undefined, + }); message.success('个人资料更新成功!'); profileEditing.value = false; } catch (error) { @@ -513,8 +541,8 @@ const handleAvatarChange = async (info) => { &.user-id { font-family: 'Monaco', 'Consolas', monospace; - background: #f5f5f5; - padding: 4px 8px; + // background: #f5f5f5; + // padding: 4px 8px; border-radius: 4px; display: inline-block; } diff --git a/web/src/stores/user.js b/web/src/stores/user.js index b09ef01c..4f91e249 100644 --- a/web/src/stores/user.js +++ b/web/src/stores/user.js @@ -349,10 +349,14 @@ export const useUserStore = defineStore('user', () => { const userData = await response.json() // 更新本地状态 - phoneNumber.value = userData.phone_number || '' - - // 更新本地存储 - localStorage.setItem('phone_number', userData.phone_number || '') + if (typeof userData.username === 'string') { + username.value = userData.username + localStorage.setItem('username', userData.username) + } + if (typeof userData.phone_number !== 'undefined') { + phoneNumber.value = userData.phone_number || '' + localStorage.setItem('phone_number', userData.phone_number || '') + } return userData } catch (error) {