Files
Yi.Framework/Yi.Ai.Vue3/src/components/userPersonalCenter/components/UserManagement.vue
2026-01-02 22:47:09 +08:00

524 lines
12 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.
<script lang="ts" setup>
import { Camera, Edit, Postcard, Promotion, SuccessFilled, User } from '@element-plus/icons-vue';
import { format } from 'date-fns';
import { computed, onMounted, ref } from 'vue';
import { getUserInfo } from '@/api';
import QrCodeLogin from '@/components/LoginDialog/components/QrCodeLogin/index.vue';
import { useUserStore } from '@/stores';
import { getUserProfilePicture, isUserVip, WECHAT_QRCODE_TYPE } from '@/utils/user.ts';
const userStore = useUserStore();
onMounted(async () => {
const resUserInfo = await getUserInfo();
userStore.setUserInfo(resUserInfo.data);
});
const user = computed(() => userStore.userInfo.user || {});
const wechatDialogVisible = ref(false);
// 计算属性
const userIcon = computed(() => {
return getUserProfilePicture() || `https://your-cdn.com/${user.value.icon}`;
});
const userNick = computed(() => {
return user.value.nick || user.value.userName || '未知用户';
});
// 是否绑定了微信
const isWechatBound = computed(() => {
return userStore.userInfo.isBindFuwuhao || false;
});
// 用户VIP状态
const userVipStatus = computed(() => {
return isUserVip();
});
// 格式化日期
function formatDate(dateString: string | null) {
if (!dateString)
return '-';
try {
return format(new Date(dateString), 'yyyy-MM-dd HH:mm:ss');
}
catch {
return dateString;
}
}
// 性别显示
function getSexText(sex: string | null) {
const sexMap: Record<string, string> = {
Male: '男',
Female: '女',
Unknown: '未知',
};
return sexMap[sex || 'Unknown'] || '未知';
}
function getSexTagType(sex: string | null) {
const typeMap: Record<string, string> = {
Male: 'primary',
Female: 'danger',
Unknown: 'info',
};
return typeMap[sex || 'Unknown'] || 'info';
}
// 敏感信息脱敏
function maskEmail(email: string) {
if (!email)
return '';
const [name, domain] = email.split('@');
if (name.length <= 2)
return email;
return `${name.substring(0, 2)}****@${domain}`;
}
function maskPhone(phone: number) {
if (!phone)
return '';
return phone.toString().replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}
// 操作处理
function handleEdit() {
ElMessage.info('编辑功能开发中');
}
function changeAvatar() {
ElMessage.info('更换头像功能开发中');
}
function handleWechatBind() {
wechatDialogVisible.value = true;
}
// 微信绑定成功
function bindWechat() {
wechatDialogVisible.value = false;
ElMessage.success('微信绑定成功!');
}
</script>
<template>
<div class="user-profile">
<!-- 用户卡片 -->
<el-card class="profile-card" shadow="hover">
<!-- 顶部标题 -->
<div class="header">
<h2>
<el-icon><User /></el-icon>
个人信息
</h2>
</div>
<!-- 头像和基本信息区域 -->
<div class="user-header-section">
<!-- 头像区域 -->
<div class="avatar-section">
<div class="avatar-wrapper">
<el-avatar :size="120" :src="userIcon" class="user-avatar">
{{ userNick.charAt(0) }}
</el-avatar>
<div v-if="userVipStatus" class="vip-badge">
<el-icon><Promotion /></el-icon>
VIP
</div>
</div>
<div v-if="false" class="avatar-actions">
<el-button size="small" type="primary" @click="changeAvatar">
<el-icon><Camera /></el-icon>
更换头像
</el-button>
</div>
</div>
<!-- 用户名称和状态 -->
<div class="user-info-quick">
<h3 class="user-name">
{{ userNick }}
</h3>
<div class="user-tags">
<el-tag v-if="userVipStatus" type="warning" effect="dark" size="large">
<el-icon><Promotion /></el-icon>
尊享VIP会员
</el-tag>
<el-tag v-else type="info" size="large">
普通用户
</el-tag>
<el-tag :type="getSexTagType(user.sex)" size="large">
{{ getSexText(user.sex) }}
</el-tag>
</div>
<div class="user-stats">
<div class="stat-item">
<div class="stat-value">
{{ formatDate(user.creationTime)?.split(' ')[0] || '-' }}
</div>
<div class="stat-label">
注册时间
</div>
</div>
</div>
</div>
</div>
<el-divider />
<!-- 详细信息区域 -->
<div class="info-section">
<div class="info-grid">
<!-- 用户名 -->
<div class="info-item">
<div class="info-label">
<el-icon><User /></el-icon>
用户名
</div>
<div class="info-value">
{{ user.userName || '-' }}
</div>
</div>
<!-- 昵称 -->
<div class="info-item">
<div class="info-label">
<el-icon><Postcard /></el-icon>
昵称
</div>
<div class="info-value">
{{ userNick }}
</div>
</div>
<!-- 邮箱 -->
<div class="info-item">
<div class="info-label">
<el-icon><Message /></el-icon>
邮箱
</div>
<div class="info-value">
<span v-if="user.email">
{{ maskEmail(user.email) }}
<el-tooltip content="已验证" placement="top">
<el-icon color="#67C23A" style="margin-left: 5px;"><SuccessFilled /></el-icon>
</el-tooltip>
</span>
<span v-else class="unset-text">未设置</span>
</div>
</div>
<!-- 手机号 -->
<div class="info-item">
<div class="info-label">
<el-icon><Phone /></el-icon>
手机号
</div>
<div class="info-value">
<span v-if="user.phone">{{ maskPhone(user.phone) }}</span>
<span v-else class="unset-text">未设置</span>
</div>
</div>
<!-- 微信绑定 -->
<div class="info-item full-width">
<div class="info-label">
<el-icon color="#07C160">
<ChatDotRound />
</el-icon>
微信绑定
</div>
<div class="info-value wechat-binding">
<span v-if="isWechatBound" class="wechat-status">
<el-icon color="#07C160"><SuccessFilled /></el-icon>
已绑定
</span>
<span v-else class="wechat-status unset-text">
未绑定
</span>
<el-button
v-if="!isWechatBound"
type="success"
size="small"
@click="handleWechatBind"
>
立即绑定
</el-button>
</div>
</div>
<!-- 个人简介 -->
<div class="info-item full-width">
<div class="info-label">
<el-icon><Document /></el-icon>
个人简介
</div>
<div class="info-value">
<span v-if="user.introduction">{{ user.introduction }}</span>
<span v-else class="unset-text">暂无简介</span>
</div>
</div>
</div>
</div>
<!-- 操作按钮预留 -->
<div v-if="false" class="action-section">
<el-button type="primary" @click="handleEdit">
<el-icon><Edit /></el-icon>
编辑资料
</el-button>
</div>
</el-card>
<!-- 微信绑定对话框 -->
<el-dialog
v-model="wechatDialogVisible"
title="微信绑定"
width="450px"
:close-on-click-modal="false"
>
<div class="wechat-dialog">
<div class="wechat-tip">
<el-alert
type="info"
:closable="false"
show-icon
>
<template #title>
<div>请使用微信扫描下方二维码完成绑定</div>
</template>
</el-alert>
</div>
<QrCodeLogin :type="WECHAT_QRCODE_TYPE.Bind" @bind-wechat="bindWechat()" />
</div>
<template #footer>
<el-button @click="wechatDialogVisible = false">
取消
</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped>
.user-profile {
height: 100%;
overflow-y: auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.header h2 {
display: flex;
align-items: center;
margin: 0;
font-size: 20px;
color: #333;
}
.header .el-icon {
margin-right: 8px;
color: #409eff;
}
.profile-card {
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
/* 用户头部区域 */
.user-header-section {
display: flex;
gap: 30px;
align-items: center;
padding: 20px 0;
}
.avatar-section {
text-align: center;
flex-shrink: 0;
}
.avatar-wrapper {
position: relative;
display: inline-block;
}
.user-avatar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-size: 48px;
font-weight: bold;
border: 4px solid #fff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.vip-badge {
position: absolute;
bottom: 0;
right: -10px;
background: linear-gradient(135deg, #f5af19 0%, #f12711 100%);
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
display: flex;
align-items: center;
gap: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.avatar-actions {
margin-top: 15px;
}
.user-info-quick {
flex: 1;
}
.user-name {
font-size: 28px;
font-weight: 600;
color: #333;
margin: 0 0 15px 0;
}
.user-tags {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.user-stats {
display: flex;
gap: 30px;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 16px;
font-weight: 600;
color: #409eff;
margin-bottom: 4px;
}
.stat-label {
font-size: 13px;
color: #909399;
}
/* 信息区域 */
.info-section {
padding: 10px 0;
}
.info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.info-item {
display: flex;
flex-direction: column;
gap: 8px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
transition: all 0.3s;
}
.info-item:hover {
background: #e9ecef;
transform: translateY(-2px);
}
.info-item.full-width {
grid-column: 1 / -1;
}
.info-label {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
color: #666;
font-weight: 500;
}
.info-value {
font-size: 15px;
color: #333;
font-weight: 500;
}
.unset-text {
color: #999;
font-style: italic;
font-weight: normal;
}
.wechat-binding {
display: flex;
align-items: center;
justify-content: space-between;
}
.wechat-status {
display: flex;
align-items: center;
gap: 6px;
}
/* 操作区域 */
.action-section {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #f0f0f0;
text-align: center;
}
/* 微信绑定对话框 */
.wechat-dialog {
text-align: center;
}
.wechat-tip {
margin-bottom: 20px;
}
/* 响应式 */
@media (max-width: 768px) {
.user-header-section {
flex-direction: column;
text-align: center;
}
.user-info-quick {
width: 100%;
}
.user-stats {
justify-content: center;
}
.info-grid {
grid-template-columns: 1fr;
}
.info-item.full-width {
grid-column: 1;
}
}
</style>