iFileProxyAdmin/src/views/UserProfile.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>