feat(user): 新增用户名更新功能,优化用户资料编辑体验
- 在后端用户资料更新接口中添加用户名验证和唯一性检查 - 前端用户信息组件支持编辑用户名,增加输入验证 - 更新用户状态管理,确保用户名和手机号的正确存储与展示
This commit is contained in:
parent
e6a36164a5
commit
1487520e4e
@ -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:
|
||||
# 如果手机号不为空,验证格式
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user