This commit is contained in:
2026-01-20 09:43:58 +08:00
parent 9312d47430
commit 8c2d943dba
10 changed files with 892 additions and 72 deletions

202
src/pages/ucenter/index.vue Normal file
View File

@@ -0,0 +1,202 @@
<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>