397 lines
No EOL
9.8 KiB
Vue
397 lines
No EOL
9.8 KiB
Vue
<template>
|
|
<div class="profile-container">
|
|
<el-card class="profile-card">
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span class="title">个人信息</span>
|
|
</div>
|
|
</template>
|
|
|
|
<el-descriptions
|
|
:column="isMobile ? 1 : 2"
|
|
border
|
|
class="profile-descriptions">
|
|
<el-descriptions-item label="用户ID">
|
|
{{ userInfo.userId }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="用户名">
|
|
{{ userInfo.username }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="昵称">
|
|
{{ userInfo.nickname }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="权限级别">
|
|
<el-tag :type="getMaskType(userInfo.mask)">
|
|
{{ getMaskLabel(userInfo.mask) }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="账号状态">
|
|
<el-tag :type="userInfo.state === 0 ? 'success' : 'danger'">
|
|
{{ userInfo.state === 0 ? '正常' : '禁用' }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="注册时间">
|
|
{{ formatDateTime(userInfo.createTime) }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="最后登录时间">
|
|
{{ formatDateTime(userInfo.lastLoginTime) }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="最后登录IP">
|
|
{{ userInfo.lastLoginIP }}
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
|
|
<div class="profile-actions">
|
|
<el-button type="primary" @click="showNicknameDialog" class="action-button">
|
|
<el-icon><Edit /></el-icon>修改昵称
|
|
</el-button>
|
|
<el-button type="warning" @click="showPasswordDialog" class="action-button">
|
|
<el-icon><Lock /></el-icon>修改密码
|
|
</el-button>
|
|
</div>
|
|
</el-card>
|
|
|
|
<!-- 修改昵称对话框 -->
|
|
<el-dialog
|
|
v-model="nicknameDialogVisible"
|
|
title="修改昵称"
|
|
width="400px">
|
|
<el-form
|
|
ref="nicknameFormRef"
|
|
:model="nicknameForm"
|
|
:rules="nicknameRules"
|
|
label-width="80px">
|
|
<el-form-item label="新昵称" prop="newNickname">
|
|
<el-input
|
|
v-model="nicknameForm.newNickname"
|
|
placeholder="请输入新昵称" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="nicknameDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="handleUpdateNickname" :loading="updating">
|
|
确定
|
|
</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<!-- 修改密码对话框 -->
|
|
<el-dialog
|
|
v-model="passwordDialogVisible"
|
|
title="修改密码"
|
|
width="400px">
|
|
<el-form
|
|
ref="passwordFormRef"
|
|
:model="passwordForm"
|
|
:rules="passwordRules"
|
|
label-width="80px">
|
|
<el-form-item label="原密码" prop="oldPassword">
|
|
<el-input
|
|
v-model="passwordForm.oldPassword"
|
|
type="password"
|
|
show-password
|
|
placeholder="请输入原密码" />
|
|
</el-form-item>
|
|
<el-form-item label="新密码" prop="newPassword">
|
|
<el-input
|
|
v-model="passwordForm.newPassword"
|
|
type="password"
|
|
show-password
|
|
placeholder="请输入新密码" />
|
|
</el-form-item>
|
|
<el-form-item label="确认密码" prop="confirmPassword">
|
|
<el-input
|
|
v-model="passwordForm.confirmPassword"
|
|
type="password"
|
|
show-password
|
|
placeholder="请再次输入新密码" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="passwordDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="handleUpdatePassword" :loading="updating">
|
|
确定
|
|
</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import { Edit, Lock } from '@element-plus/icons-vue'
|
|
import { UserAPI } from '../api/user'
|
|
|
|
// 添加一个触发更新的标记
|
|
const updateTrigger = ref(0)
|
|
|
|
// 修改 userInfo 的计算方式,依赖 updateTrigger
|
|
const userInfo = computed(() => {
|
|
// updateTrigger 的值改变会触发重新计算
|
|
updateTrigger.value
|
|
return JSON.parse(localStorage.getItem('userInfo'))
|
|
})
|
|
|
|
// 格式化时间
|
|
const formatDateTime = (dateStr) => {
|
|
if (!dateStr) return '-'
|
|
return new Date(dateStr).toLocaleString()
|
|
}
|
|
|
|
// 获取权限标签类型
|
|
const getMaskType = (mask) => {
|
|
switch (mask) {
|
|
case 2:
|
|
return 'danger'
|
|
case 1:
|
|
return 'warning'
|
|
default:
|
|
return 'info'
|
|
}
|
|
}
|
|
|
|
// 获取权限标签文本
|
|
const getMaskLabel = (mask) => {
|
|
switch (mask) {
|
|
case 2:
|
|
return '超级管理员'
|
|
case 1:
|
|
return '管理员'
|
|
default:
|
|
return '普通用户'
|
|
}
|
|
}
|
|
|
|
const updating = ref(false)
|
|
const nicknameDialogVisible = ref(false)
|
|
const passwordDialogVisible = ref(false)
|
|
const nicknameFormRef = ref(null)
|
|
const passwordFormRef = ref(null)
|
|
|
|
const nicknameForm = ref({
|
|
newNickname: ''
|
|
})
|
|
|
|
const passwordForm = ref({
|
|
oldPassword: '',
|
|
newPassword: '',
|
|
confirmPassword: ''
|
|
})
|
|
|
|
const nicknameRules = {
|
|
newNickname: [
|
|
{ required: true, message: '请输入新昵称', trigger: 'blur' },
|
|
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
|
]
|
|
}
|
|
|
|
const validatePass = (rule, value, callback) => {
|
|
if (value === '') {
|
|
callback(new Error('请输入新密码'))
|
|
} else {
|
|
if (passwordForm.value.confirmPassword !== '') {
|
|
passwordFormRef.value.validateField('confirmPassword')
|
|
}
|
|
callback()
|
|
}
|
|
}
|
|
|
|
const validatePass2 = (rule, value, callback) => {
|
|
if (value === '') {
|
|
callback(new Error('请再次输入新密码'))
|
|
} else if (value !== passwordForm.value.newPassword) {
|
|
callback(new Error('两次输入密码不一致!'))
|
|
} else {
|
|
callback()
|
|
}
|
|
}
|
|
|
|
const passwordRules = {
|
|
oldPassword: [
|
|
{ required: true, message: '请输入原密码', trigger: 'blur' }
|
|
],
|
|
newPassword: [
|
|
{ validator: validatePass, trigger: 'blur' },
|
|
{ min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
|
|
],
|
|
confirmPassword: [
|
|
{ validator: validatePass2, trigger: 'blur' }
|
|
]
|
|
}
|
|
|
|
const showNicknameDialog = () => {
|
|
nicknameForm.value.newNickname = userInfo.value.nickname
|
|
nicknameDialogVisible.value = true
|
|
}
|
|
|
|
const showPasswordDialog = () => {
|
|
passwordForm.value = {
|
|
oldPassword: '',
|
|
newPassword: '',
|
|
confirmPassword: ''
|
|
}
|
|
passwordDialogVisible.value = true
|
|
}
|
|
|
|
const handleUpdateNickname = async () => {
|
|
if (!nicknameFormRef.value) return
|
|
|
|
await nicknameFormRef.value.validate(async (valid) => {
|
|
if (valid) {
|
|
updating.value = true
|
|
try {
|
|
const response = await UserAPI.updateNickname(nicknameForm.value.newNickname)
|
|
if (response.retcode === 0) {
|
|
// 重新获取用户信息
|
|
const userInfoResponse = await UserAPI.getUserInfo()
|
|
if (userInfoResponse.retcode === 0) {
|
|
// 更新本地存储的用户信息
|
|
localStorage.setItem('userInfo', JSON.stringify(userInfoResponse.data))
|
|
// 触发更新
|
|
updateTrigger.value++
|
|
ElMessage.success('昵称修改成功')
|
|
nicknameDialogVisible.value = false
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('修改昵称失败:', error)
|
|
} finally {
|
|
updating.value = false
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
const handleUpdatePassword = async () => {
|
|
if (!passwordFormRef.value) return
|
|
|
|
await passwordFormRef.value.validate(async (valid) => {
|
|
if (valid) {
|
|
updating.value = true
|
|
try {
|
|
const response = await UserAPI.updatePassword(
|
|
passwordForm.value.oldPassword,
|
|
passwordForm.value.newPassword
|
|
)
|
|
if (response.retcode === 0) {
|
|
ElMessage.success('密码修改成功,请重新登录')
|
|
// 清除登录状态,跳转到登录页
|
|
localStorage.clear()
|
|
router.push('/login')
|
|
}
|
|
} catch (error) {
|
|
console.error('修改密码失败:', error)
|
|
} finally {
|
|
updating.value = false
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 添加移动端检测
|
|
const isMobile = computed(() => {
|
|
return window.innerWidth <= 768
|
|
})
|
|
|
|
// 监听窗口大小变化
|
|
const resizeHandler = () => {
|
|
isMobile.value = window.innerWidth <= 768
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('resize', resizeHandler)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('resize', resizeHandler)
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.profile-container {
|
|
padding: 20px;
|
|
}
|
|
|
|
.profile-card {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
background: #fff;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.title {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
:deep(.el-descriptions__label) {
|
|
width: 120px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
:deep(.el-descriptions__cell) {
|
|
padding: 16px 24px;
|
|
}
|
|
|
|
.profile-actions {
|
|
margin-top: 24px;
|
|
display: flex;
|
|
gap: 16px;
|
|
justify-content: center;
|
|
}
|
|
|
|
.action-button {
|
|
min-width: 120px;
|
|
}
|
|
|
|
/* 添加响应式样式 */
|
|
@media screen and (max-width: 768px) {
|
|
.profile-container {
|
|
padding: 10px;
|
|
}
|
|
|
|
.profile-card {
|
|
max-width: 100%;
|
|
}
|
|
|
|
/* 调整对话框样式 */
|
|
:deep(.el-dialog) {
|
|
width: 90% !important;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
:deep(.el-dialog__body) {
|
|
padding: 15px;
|
|
}
|
|
|
|
.profile-actions {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.action-button {
|
|
width: 100%;
|
|
margin: 0;
|
|
}
|
|
|
|
:deep(.el-descriptions__cell) {
|
|
padding: 12px;
|
|
}
|
|
|
|
:deep(.el-descriptions__label) {
|
|
width: 90px;
|
|
min-width: 90px;
|
|
}
|
|
}
|
|
</style> |