|
|
@ -3,6 +3,7 @@ |
|
|
<!-- 引入侧边栏组件 --> |
|
|
<!-- 引入侧边栏组件 --> |
|
|
<Menu |
|
|
<Menu |
|
|
@menu-click="handleSidebarClick" |
|
|
@menu-click="handleSidebarClick" |
|
|
|
|
|
:avatar="userProfile.avatar" |
|
|
/> |
|
|
/> |
|
|
|
|
|
|
|
|
<!-- 主内容区域 --> |
|
|
<!-- 主内容区域 --> |
|
|
@ -33,11 +34,6 @@ |
|
|
> |
|
|
> |
|
|
</div> |
|
|
</div> |
|
|
<div class="username-display">{{ userProfile.username }}</div> |
|
|
<div class="username-display">{{ userProfile.username }}</div> |
|
|
<div class="avatar-status"> |
|
|
|
|
|
<p v-if="avatarUploading" class="uploading-text">上传中...</p> |
|
|
|
|
|
<p v-if="avatarUploadSuccess" class="success-text">头像更新成功</p> |
|
|
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
<!-- 个人信息卡片 --> |
|
|
<!-- 个人信息卡片 --> |
|
|
<div class="profile-card"> |
|
|
<div class="profile-card"> |
|
|
@ -73,6 +69,9 @@ |
|
|
required style=" width: 97%;" |
|
|
required style=" width: 97%;" |
|
|
> |
|
|
> |
|
|
<div class="form-label" style="text-align: left;margin-top: 5px;font-size: 13px;font-weight: 500">密码要求:8-16位,包含字母和数字,不允许空格</div> |
|
|
<div class="form-label" style="text-align: left;margin-top: 5px;font-size: 13px;font-weight: 500">密码要求:8-16位,包含字母和数字,不允许空格</div> |
|
|
|
|
|
<p v-if="passwordForm.newPassword && !isPasswordValid" class="error-text"> |
|
|
|
|
|
密码格式不符合要求 |
|
|
|
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<div class="form-group"> |
|
|
<label for="confirmPassword" class="form-label">确认新密码</label> |
|
|
<label for="confirmPassword" class="form-label">确认新密码</label> |
|
|
@ -92,23 +91,16 @@ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-actions"> |
|
|
<div class="form-actions"> |
|
|
<button type="submit" class="save-button" :disabled="passwordSaving || passwordMismatch"> |
|
|
<button type="submit" class="save-button" :disabled="passwordSaving"> |
|
|
保存新密码 |
|
|
保存新密码 |
|
|
</button> |
|
|
</button> |
|
|
<button type="submit" class="reset" :disabled="passwordSaving || passwordMismatch"> |
|
|
<button type="button" class="reset" :disabled="passwordSaving" @click="resetPasswordForm" > |
|
|
重置表单 |
|
|
重置表单 |
|
|
</button> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</form> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<!-- 操作反馈 --> |
|
|
|
|
|
<div v-if="successMessage" class="success-message"> |
|
|
|
|
|
{{ successMessage }} |
|
|
|
|
|
</div> |
|
|
|
|
|
<div v-if="errorMessage" class="error-message"> |
|
|
|
|
|
{{ errorMessage }} |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
@ -118,6 +110,7 @@ |
|
|
import { ref, computed, onMounted } from 'vue'; |
|
|
import { ref, computed, onMounted } from 'vue'; |
|
|
import Menu from '../components/Menu.vue'; |
|
|
import Menu from '../components/Menu.vue'; |
|
|
import { getUserProfile, updateAvatar, updatePassword } from '../api/profile'; |
|
|
import { getUserProfile, updateAvatar, updatePassword } from '../api/profile'; |
|
|
|
|
|
import {ElMessage} from "element-plus"; |
|
|
|
|
|
|
|
|
// 处理侧边栏菜单点击 |
|
|
// 处理侧边栏菜单点击 |
|
|
const handleSidebarClick = (menuItem) => { |
|
|
const handleSidebarClick = (menuItem) => { |
|
|
@ -158,6 +151,11 @@ const passwordMismatch = computed(() => { |
|
|
const triggerFileInput = () => { |
|
|
const triggerFileInput = () => { |
|
|
fileInput.value.click(); |
|
|
fileInput.value.click(); |
|
|
}; |
|
|
}; |
|
|
|
|
|
const resetPasswordForm = () => { |
|
|
|
|
|
passwordForm.value.currentPassword = ''; |
|
|
|
|
|
passwordForm.value.newPassword = ''; |
|
|
|
|
|
passwordForm.value.confirmPassword = ''; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// 处理头像加载错误 |
|
|
// 处理头像加载错误 |
|
|
const handleAvatarError = (event) => { |
|
|
const handleAvatarError = (event) => { |
|
|
@ -172,13 +170,13 @@ const handleAvatarChange = async (event) => { |
|
|
if (file) { |
|
|
if (file) { |
|
|
// 检查文件类型 |
|
|
// 检查文件类型 |
|
|
if (!file.type.startsWith('image/')) { |
|
|
if (!file.type.startsWith('image/')) { |
|
|
errorMessage.value = '请选择图片文件'; |
|
|
ElMessage.error("请选择图片文件") |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 检查文件大小 (限制为2MB) |
|
|
// 检查文件大小 (限制为2MB) |
|
|
if (file.size > 2 * 1024 * 1024) { |
|
|
if (file.size > 2 * 1024 * 1024) { |
|
|
errorMessage.value = '图片大小不能超过2MB'; |
|
|
ElMessage.error("图片大小不能超过2MB") |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -188,23 +186,10 @@ const handleAvatarChange = async (event) => { |
|
|
errorMessage.value = ''; |
|
|
errorMessage.value = ''; |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 从localStorage获取token |
|
|
|
|
|
const token = localStorage.getItem('token'); |
|
|
|
|
|
|
|
|
|
|
|
if (!token) { |
|
|
|
|
|
errorMessage.value = '用户未登录,请先登录'; |
|
|
|
|
|
avatarUploading.value = false; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 创建FormData对象 |
|
|
// 创建FormData对象 |
|
|
const formData = new FormData(); |
|
|
const formData = new FormData(); |
|
|
formData.append('avatar', file); |
|
|
formData.append('avatar', file); |
|
|
formData.append('token', token); |
|
|
|
|
|
|
|
|
|
|
|
console.log('准备上传头像文件:', file.name); |
|
|
console.log('准备上传头像文件:', file.name); |
|
|
console.log('Token:', token); |
|
|
|
|
|
|
|
|
|
|
|
// 调用API上传头像 |
|
|
// 调用API上传头像 |
|
|
const response = await updateAvatar(formData,{ |
|
|
const response = await updateAvatar(formData,{ |
|
|
headers: { |
|
|
headers: { |
|
|
@ -226,15 +211,10 @@ const handleAvatarChange = async (event) => { |
|
|
|
|
|
|
|
|
// 显示成功消息 |
|
|
// 显示成功消息 |
|
|
avatarUploadSuccess.value = true; |
|
|
avatarUploadSuccess.value = true; |
|
|
successMessage.value = '头像更新成功'; |
|
|
ElMessage.success("头像更新成功") |
|
|
|
|
|
|
|
|
// 3秒后隐藏成功提示 |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
avatarUploadSuccess.value = false; |
|
|
|
|
|
successMessage.value = ''; |
|
|
|
|
|
}, 3000); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
errorMessage.value = response.message || '头像更新失败'; |
|
|
ElMessage.error("头像更新失败") |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('头像上传失败:', error); |
|
|
console.error('头像上传失败:', error); |
|
|
@ -247,15 +227,11 @@ const handleAvatarChange = async (event) => { |
|
|
// 提供更详细的错误信息 |
|
|
// 提供更详细的错误信息 |
|
|
if (error.response) { |
|
|
if (error.response) { |
|
|
// 服务器响应了错误状态码 |
|
|
// 服务器响应了错误状态码 |
|
|
console.error('服务器响应:', error.response.status, error.response.data); |
|
|
ElMessage.error(`服务器错误 ${error.response.status}: ${JSON.stringify(error.response.data)}`) |
|
|
errorMessage.value = `服务器错误 ${error.response.status}: ${JSON.stringify(error.response.data)}`; |
|
|
|
|
|
} else if (error.request) { |
|
|
} else if (error.request) { |
|
|
// 请求已发出但没有收到响应 |
|
|
ElMessage.error("网络错误,请检查网络连接和服务器状态") |
|
|
console.error('网络错误,未收到响应:', error.request); |
|
|
|
|
|
errorMessage.value = '网络错误,请检查网络连接和服务器状态'; |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
// 其他错误 |
|
|
ElMessage.error(`请求配置错误: ${error.message}`) |
|
|
errorMessage.value = `请求配置错误: ${error.message}`; |
|
|
|
|
|
} |
|
|
} |
|
|
} finally { |
|
|
} finally { |
|
|
avatarUploading.value = false; |
|
|
avatarUploading.value = false; |
|
|
@ -263,54 +239,29 @@ const handleAvatarChange = async (event) => { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 更新个人信息 |
|
|
|
|
|
const updateProfile = () => { |
|
|
|
|
|
profileSaving.value = true; |
|
|
|
|
|
errorMessage.value = ''; |
|
|
|
|
|
successMessage.value = ''; |
|
|
|
|
|
|
|
|
|
|
|
// 模拟API请求 |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
profileSaving.value = false; |
|
|
|
|
|
successMessage.value = '个人信息更新成功'; |
|
|
|
|
|
|
|
|
|
|
|
// 3秒后隐藏成功提示 |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
successMessage.value = ''; |
|
|
|
|
|
}, 3000); |
|
|
|
|
|
}, 1000); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 修改密码 |
|
|
// 修改密码 |
|
|
const changePassword = async () => { |
|
|
const changePassword = async () => { |
|
|
if (passwordMismatch.value) { |
|
|
if (passwordMismatch.value) { |
|
|
|
|
|
ElMessage.error("两次输入的密码不一致") |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (!isPasswordValid.value) { |
|
|
|
|
|
ElMessage.error("新密码格式不符合要求:8-16位,必须包含字母和数字,且不能有空格") |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
passwordSaving.value = true; |
|
|
passwordSaving.value = true; |
|
|
errorMessage.value = ''; |
|
|
errorMessage.value = ''; |
|
|
successMessage.value = ''; |
|
|
successMessage.value = ''; |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 从localStorage获取token |
|
|
|
|
|
const token = localStorage.getItem('token'); |
|
|
|
|
|
|
|
|
|
|
|
if (!token) { |
|
|
|
|
|
errorMessage.value = '用户未登录,请先登录'; |
|
|
|
|
|
passwordSaving.value = false; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 调用API修改密码 |
|
|
// 调用API修改密码 |
|
|
const response = await updatePassword({ |
|
|
const response = await updatePassword({ |
|
|
token, |
|
|
|
|
|
currentPassword: passwordForm.value.currentPassword, |
|
|
currentPassword: passwordForm.value.currentPassword, |
|
|
newPassword: passwordForm.value.newPassword |
|
|
newPassword: passwordForm.value.newPassword |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
if (response.success) { |
|
|
if (response.success) { |
|
|
successMessage.value = '密码修改成功'; |
|
|
ElMessage.success("密码修改成功") |
|
|
|
|
|
|
|
|
// 清空表单 |
|
|
// 清空表单 |
|
|
passwordForm.value = { |
|
|
passwordForm.value = { |
|
|
currentPassword: '', |
|
|
currentPassword: '', |
|
|
@ -318,21 +269,22 @@ const changePassword = async () => { |
|
|
confirmPassword: '' |
|
|
confirmPassword: '' |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 3秒后隐藏成功提示 |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
successMessage.value = ''; |
|
|
|
|
|
}, 3000); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
errorMessage.value = response.message || '密码修改失败'; |
|
|
ElMessage.error("密码修改失败") |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('密码修改失败:', error); |
|
|
ElMessage.error("密码修改失败") |
|
|
errorMessage.value = '密码修改时发生错误'; |
|
|
|
|
|
} finally { |
|
|
} finally { |
|
|
passwordSaving.value = false; |
|
|
passwordSaving.value = false; |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
const isPasswordValid = computed(() => { |
|
|
|
|
|
const pwd = passwordForm.value.newPassword; |
|
|
|
|
|
if (!pwd) return false; |
|
|
|
|
|
if (pwd.length < 8 || pwd.length > 16) return false; |
|
|
|
|
|
if (/\s/.test(pwd)) return false; |
|
|
|
|
|
return /[a-zA-Z]/.test(pwd) && /\d/.test(pwd); |
|
|
|
|
|
}); |
|
|
// 组件挂载时获取用户信息 |
|
|
// 组件挂载时获取用户信息 |
|
|
onMounted(async () => { |
|
|
onMounted(async () => { |
|
|
try { |
|
|
try { |
|
|
@ -362,9 +314,7 @@ onMounted(async () => { |
|
|
avatar: avatarUrl |
|
|
avatar: avatarUrl |
|
|
}; |
|
|
}; |
|
|
} else { |
|
|
} else { |
|
|
console.error('获取用户信息失败:', response.message); |
|
|
ElMessage.error( '获取用户信息失败') |
|
|
errorMessage.value = response.message || '获取用户信息失败'; |
|
|
|
|
|
|
|
|
|
|
|
// 如果是token过期或无效,清除token并跳转到登录页 |
|
|
// 如果是token过期或无效,清除token并跳转到登录页 |
|
|
if (response.message && response.message.includes('登录')) { |
|
|
if (response.message && response.message.includes('登录')) { |
|
|
localStorage.removeItem('token'); |
|
|
localStorage.removeItem('token'); |
|
|
@ -372,12 +322,10 @@ onMounted(async () => { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
console.log('用户未登录'); |
|
|
ElMessage.error( '用户未登录,请先登录') |
|
|
errorMessage.value = '用户未登录,请先登录'; |
|
|
|
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('获取用户信息失败:', error); |
|
|
ElMessage.error( '获取用户信息时发生错误') |
|
|
errorMessage.value = '获取用户信息时发生错误'; |
|
|
|
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
</script> |
|
|
@ -495,6 +443,7 @@ onMounted(async () => { |
|
|
|
|
|
|
|
|
.avatar-overlay { |
|
|
.avatar-overlay { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
|
|
|
border: 3px solid #e5e7eb; |
|
|
top: 0; |
|
|
top: 0; |
|
|
left: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
width: 100%; |
|
|
|