210 lines
5.0 KiB
Vue
210 lines
5.0 KiB
Vue
<template>
|
||
<div class="ucenter">
|
||
<a-card>
|
||
<a-row :gutter="24">
|
||
<a-col :span="6">
|
||
<ProfileInfo :user-info="userInfo" @avatar-click="showAvatarModal = true" />
|
||
<a-menu v-model:selectedKeys="selectedKeys" mode="inline" class="menu">
|
||
<a-menu-item key="basic">
|
||
<UserOutlined />
|
||
基本信息
|
||
</a-menu-item>
|
||
<a-menu-item key="password">
|
||
<LockOutlined />
|
||
修改密码
|
||
</a-menu-item>
|
||
<a-menu-item key="security">
|
||
<SafetyOutlined />
|
||
账号安全
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</a-col>
|
||
<a-col :span="18">
|
||
<div class="content-wrapper">
|
||
<BasicInfo v-if="selectedKeys[0] === 'basic'" :user-info="userInfo"
|
||
@update="handleUpdateUserInfo" />
|
||
<Password v-else-if="selectedKeys[0] === 'password'" @success="handlePasswordSuccess" />
|
||
<Security v-else-if="selectedKeys[0] === 'security'" @change-password="handleChangePassword" />
|
||
</div>
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
|
||
<!-- 头像上传弹窗 -->
|
||
<a-modal v-model:open="showAvatarModal" title="更换头像" :confirm-loading="loading" @ok="handleAvatarUpload"
|
||
@cancel="showAvatarModal = false">
|
||
<div class="avatar-upload">
|
||
<a-upload list-type="picture-card" :max-count="1" :before-upload="beforeUpload"
|
||
@change="handleAvatarChange" :file-list="avatarFileList">
|
||
<div v-if="avatarFileList.length === 0">
|
||
<PlusOutlined />
|
||
<div class="ant-upload-text">上传头像</div>
|
||
</div>
|
||
</a-upload>
|
||
<div class="upload-tip">
|
||
<a-typography-text type="secondary"> 支持 JPG、PNG 格式,文件大小不超过 2MB </a-typography-text>
|
||
</div>
|
||
</div>
|
||
</a-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted } from 'vue'
|
||
import { message } from 'ant-design-vue'
|
||
import { PlusOutlined, UserOutlined, LockOutlined, SafetyOutlined } from '@ant-design/icons-vue'
|
||
import dayjs from 'dayjs'
|
||
import ProfileInfo from './components/ProfileInfo.vue'
|
||
import BasicInfo from './components/BasicInfo.vue'
|
||
import Password from './components/Password.vue'
|
||
import Security from './components/Security.vue'
|
||
import { useUserStore } from '@/stores/modules/user'
|
||
import api from '@/api/auth'
|
||
|
||
defineOptions({
|
||
name: 'UserCenter',
|
||
})
|
||
|
||
const userStore = useUserStore()
|
||
|
||
// 用户信息
|
||
const userInfo = ref({})
|
||
|
||
// 选中的菜单
|
||
const selectedKeys = ref(['basic'])
|
||
|
||
// 头像上传
|
||
const showAvatarModal = ref(false)
|
||
const avatarFileList = ref([])
|
||
const loading = ref(false)
|
||
|
||
// 初始化用户信息
|
||
const initUserInfo = async () => {
|
||
try {
|
||
// 从 store 获取用户信息
|
||
const storeUserInfo = userStore.userInfo
|
||
if (storeUserInfo) {
|
||
userInfo.value = storeUserInfo
|
||
} else {
|
||
// 如果 store 中没有用户信息,则从接口获取
|
||
const response = await api.user.get()
|
||
if (response && response.data) {
|
||
userStore.setUserInfo(response.data)
|
||
userInfo.value = response.data
|
||
}
|
||
}
|
||
} catch (err) {
|
||
message.error(err.message || '获取用户信息失败')
|
||
}
|
||
}
|
||
|
||
// 更新用户信息
|
||
const handleUpdateUserInfo = (data) => {
|
||
// 更新本地用户信息
|
||
Object.assign(userInfo.value, data)
|
||
|
||
// 如果 birthday 有值,转换为 dayjs 对象
|
||
if (data.birthday && typeof data.birthday === 'string') {
|
||
userInfo.value.birthday = dayjs(data.birthday)
|
||
}
|
||
}
|
||
|
||
// 密码修改成功
|
||
const handlePasswordSuccess = () => {
|
||
// 密码修改成功后的处理
|
||
}
|
||
|
||
// 切换到密码修改页面
|
||
const handleChangePassword = () => {
|
||
selectedKeys.value = ['password']
|
||
}
|
||
|
||
// 头像上传前校验
|
||
const beforeUpload = (file) => {
|
||
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
|
||
if (!isJpgOrPng) {
|
||
message.error('只能上传 JPG/PNG 格式的文件!')
|
||
return false
|
||
}
|
||
const isLt2M = file.size / 1024 / 1024 < 2
|
||
if (!isLt2M) {
|
||
message.error('图片大小不能超过 2MB!')
|
||
return false
|
||
}
|
||
return false // 阻止自动上传
|
||
}
|
||
|
||
// 头像文件变化
|
||
const handleAvatarChange = ({ fileList }) => {
|
||
avatarFileList.value = fileList
|
||
}
|
||
|
||
// 上传头像
|
||
const handleAvatarUpload = () => {
|
||
if (avatarFileList.value.length === 0) {
|
||
message.warning('请先选择头像')
|
||
return
|
||
}
|
||
loading.value = true
|
||
// 模拟上传
|
||
setTimeout(() => {
|
||
const file = avatarFileList.value[0]
|
||
userInfo.value.avatar = URL.createObjectURL(file.originFileObj)
|
||
message.success('头像更新成功')
|
||
showAvatarModal.value = false
|
||
avatarFileList.value = []
|
||
loading.value = false
|
||
}, 1000)
|
||
}
|
||
|
||
onMounted(() => {
|
||
initUserInfo()
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.ucenter {
|
||
.content-wrapper {
|
||
padding: 24px;
|
||
background: #fafafa;
|
||
border-radius: 8px;
|
||
min-height: 400px;
|
||
}
|
||
|
||
.menu {
|
||
margin-top: 16px;
|
||
background: transparent;
|
||
border: none;
|
||
|
||
.ant-menu-item {
|
||
border-radius: 6px;
|
||
margin: 4px 0;
|
||
|
||
&:hover {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
&.ant-menu-item-selected {
|
||
background: rgba(255, 255, 255, 0.4);
|
||
|
||
&::after {
|
||
border-right-width: 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.avatar-upload {
|
||
.upload-tip {
|
||
margin-top: 16px;
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
:deep(.ant-card-head-title) {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
</style>
|