feat(user): 新增用户名更新功能,优化用户资料编辑体验

- 在后端用户资料更新接口中添加用户名验证和唯一性检查
- 前端用户信息组件支持编辑用户名,增加输入验证
- 更新用户状态管理,确保用户名和手机号的正确存储与展示
This commit is contained in:
Wenjie Zhang 2025-10-06 21:24:02 +08:00
parent e6a36164a5
commit 1487520e4e
3 changed files with 67 additions and 9 deletions

View File

@ -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:
# 如果手机号不为空,验证格式

View File

@ -78,11 +78,26 @@
<div class="info-section">
<div class="info-item">
<div class="info-label">用户名</div>
<div class="info-value">{{ userStore.username || '未设置' }}</div>
<div class="info-value" v-if="!profileEditing">{{ userStore.username || '未设置' }}</div>
<div class="info-value" v-else>
<a-input
v-model:value="editedProfile.username"
placeholder="请输入用户名2-20个字符"
:max-length="20"
style="width: 240px;"
/>
</div>
</div>
<div class="info-item">
<div class="info-label">用户ID</div>
<div class="info-value user-id">{{ userStore.userIdLogin || '未设置' }}</div>
<div class="info-value user-id" v-if="!profileEditing">{{ userStore.userIdLogin || '未设置' }}</div>
<div class="info-value" v-else>
<a-input
:value="userStore.userIdLogin || ''"
disabled
style="width: 240px;"
/>
</div>
</div>
<div class="info-item">
<div class="info-label">手机号</div>
@ -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;
}

View File

@ -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) {