Files
vueadmin/src/pages/ucenter/index.vue
2026-01-20 09:43:58 +08:00

203 lines
4.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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"> 支持 JPGPNG 格式文件大小不超过 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'
// 用户信息
const userInfo = ref({
username: '',
nickname: '',
phone: '',
email: '',
avatar: '',
status: 1,
gender: 0,
birthday: null,
bio: '',
})
// 选中的菜单
const selectedKeys = ref(['basic'])
// 头像上传
const showAvatarModal = ref(false)
const avatarFileList = ref([])
const loading = ref(false)
// 初始化用户信息
const initUserInfo = () => {
// 模拟用户数据
const mockUserInfo = {
username: 'admin',
nickname: '管理员',
phone: '13800138000',
email: 'admin@example.com',
avatar: '',
status: 1,
gender: 1,
birthday: dayjs('1990-01-01'),
bio: '热爱编程,专注于前端开发技术。',
}
userInfo.value = { ...mockUserInfo }
}
// 更新用户信息
const handleUpdateUserInfo = (data) => {
Object.assign(userInfo.value, data)
}
// 密码修改成功
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>