<template> | |
<div class="profile-page"> | |
<Navbar /> | |
<div class="page-container"> | |
<!-- 个人信息卡片 --> | |
<div class="profile-header"> | |
<div class="user-avatar-section"> | |
<div class="avatar-container"> | |
<el-avatar :size="120" :src="userProfile.avatar"> | |
{{ userProfile.username.charAt(0).toUpperCase() }} | |
</el-avatar> | |
<el-button | |
type="primary" | |
size="small" | |
class="change-avatar-btn" | |
@click="showAvatarDialog = true" | |
> | |
更换头像 | |
</el-button> | |
</div> | |
<div class="user-basic-info"> | |
<h1 class="username">{{ userProfile.username }}</h1> | |
<div class="user-title"> | |
<el-tag :type="getUserTitleType(userProfile.userLevel)" size="large"> | |
{{ userProfile.userTitle }} | |
</el-tag> | |
</div> | |
<div class="join-info"> | |
<el-icon><Calendar /></el-icon> | |
<span>加入时间:{{ formatDate(userProfile.joinDate) }}</span> | |
</div> | |
<div class="last-login"> | |
<el-icon><Clock /></el-icon> | |
<span>最后登录:{{ formatTime(userProfile.lastLogin) }}</span> | |
</div> | |
</div> | |
</div> | |
<div class="user-stats-overview"> | |
<div class="stats-grid"> | |
<div class="stat-card"> | |
<div class="stat-icon upload"> | |
<el-icon size="32"><Upload /></el-icon> | |
</div> | |
<div class="stat-info"> | |
<h3>{{ userProfile.stats.uploaded }}</h3> | |
<p>上传量</p> | |
</div> | |
</div> | |
<div class="stat-card"> | |
<div class="stat-icon download"> | |
<el-icon size="32"><Download /></el-icon> | |
</div> | |
<div class="stat-info"> | |
<h3>{{ userProfile.stats.downloaded }}</h3> | |
<p>下载量</p> | |
</div> | |
</div> | |
<div class="stat-card"> | |
<div class="stat-icon ratio" :class="getRatioClass(userProfile.stats.ratio)"> | |
<el-icon size="32"><TrendCharts /></el-icon> | |
</div> | |
<div class="stat-info"> | |
<h3>{{ userProfile.stats.ratio }}</h3> | |
<p>分享率</p> | |
</div> | |
</div> | |
<div class="stat-card"> | |
<div class="stat-icon points"> | |
<el-icon size="32"><Star /></el-icon> | |
</div> | |
<div class="stat-info"> | |
<h3>{{ userProfile.stats.points }}</h3> | |
<p>积分</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- 详细信息选项卡 --> | |
<div class="profile-content"> | |
<el-tabs v-model="activeTab" type="border-card"> | |
<!-- 个人信息 --> | |
<el-tab-pane label="个人信息" name="info"> | |
<div class="info-section"> | |
<el-form | |
ref="profileFormRef" | |
:model="editProfile" | |
:rules="profileRules" | |
label-width="120px" | |
size="large" | |
> | |
<div class="form-section"> | |
<h3>基本信息</h3> | |
<el-form-item label="用户名"> | |
<el-input v-model="editProfile.username" disabled> | |
<template #suffix> | |
<el-tooltip content="用户名不可修改"> | |
<el-icon><QuestionFilled /></el-icon> | |
</el-tooltip> | |
</template> | |
</el-input> | |
</el-form-item> | |
<el-form-item label="邮箱地址" prop="email"> | |
<el-input v-model="editProfile.email" type="email" /> | |
</el-form-item> | |
<el-form-item label="真实姓名" prop="realName"> | |
<el-input v-model="editProfile.realName" placeholder="可选填写" /> | |
</el-form-item> | |
<el-form-item label="所在地区"> | |
<el-cascader | |
v-model="editProfile.location" | |
:options="locationOptions" | |
placeholder="请选择地区" | |
clearable | |
/> | |
</el-form-item> | |
</div> | |
</el-form> | |
</div> | |
</el-tab-pane> | |
</el-tabs> | |
</div> | |
</div> | |
<!-- 更换头像对话框 --> | |
<el-dialog v-model="showAvatarDialog" title="更换头像" width="400px"> | |
<div class="avatar-upload"> | |
<el-upload | |
ref="avatarUploadRef" | |
:auto-upload="false" | |
:limit="1" | |
accept="image/*" | |
:on-change="handleAvatarChange" | |
list-type="picture-card" | |
class="avatar-uploader" | |
> | |
<el-icon><Plus /></el-icon> | |
</el-upload> | |
<div class="upload-tips"> | |
<p>支持 JPG、PNG 格式</p> | |
<p>建议尺寸 200x200 像素</p> | |
<p>文件大小不超过 2MB</p> | |
</div> | |
</div> | |
<template #footer> | |
<el-button @click="showAvatarDialog = false">取消</el-button> | |
<el-button type="primary" @click="uploadAvatar" :loading="uploadingAvatar"> | |
上传头像 | |
</el-button> | |
</template> | |
</el-dialog> | |
</div> | |
</template> | |
<script> | |
import { ref, reactive, computed, onMounted, nextTick } from 'vue' | |
import { ElMessage } from 'element-plus' | |
import { | |
Calendar, | |
Clock, | |
Upload, | |
Download, | |
TrendCharts, | |
Star, | |
QuestionFilled, | |
Plus | |
} from '@element-plus/icons-vue' | |
import Navbar from '@/components/Navbar.vue' | |
export default { | |
name: 'ProfileView', | |
components: { | |
Navbar | |
}, | |
setup() { | |
const activeTab = ref('info') | |
const showAvatarDialog = ref(false) | |
const saving = ref(false) | |
const uploadingAvatar = ref(false) | |
const userProfile = ref({ | |
username: 'MovieExpert', | |
email: 'movieexpert@example.com', | |
realName: '', | |
avatar: '', | |
userLevel: 5, | |
userTitle: '资深会员', | |
joinDate: '2023-01-15T10:00:00', | |
lastLogin: '2025-06-03T14:30:00', | |
location: ['北京市', '朝阳区'], | |
signature: '热爱电影,分享快乐!', | |
website: 'https://movieblog.com', | |
interests: ['电影', '音乐', '科技', '摄影'], | |
emailPublic: false, | |
statsPublic: true, | |
activityPublic: true, | |
stats: { | |
uploaded: '256.8 GB', | |
downloaded: '89.6 GB', | |
ratio: '2.87', | |
points: '15,680' | |
}, | |
detailedStats: { | |
totalUploaded: '256.8 GB', | |
uploadedTorrents: 45, | |
avgUploadSize: '5.7 GB', | |
totalDownloaded: '89.6 GB', | |
downloadedTorrents: 123, | |
completedTorrents: 118, | |
seeding: 32, | |
seedingTime: '1,245 小时', | |
seedingRank: 86, | |
totalEarnedPoints: '28,940', | |
totalSpentPoints: '13,260' | |
} | |
}) | |
const editProfile = reactive({ | |
username: '', | |
email: '', | |
realName: '', | |
location: [], | |
signature: '', | |
website: '', | |
interests: [], | |
emailPublic: false, | |
statsPublic: true, | |
activityPublic: true | |
}) | |
const profileRules = { | |
email: [ | |
{ required: true, message: '请输入邮箱地址', trigger: 'blur' }, | |
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' } | |
] | |
} | |
const locationOptions = [ | |
{ | |
value: '北京市', | |
label: '北京市', | |
children: [ | |
{ value: '朝阳区', label: '朝阳区' }, | |
{ value: '海淀区', label: '海淀区' }, | |
{ value: '丰台区', label: '丰台区' } | |
] | |
}, | |
{ | |
value: '上海市', | |
label: '上海市', | |
children: [ | |
{ value: '浦东新区', label: '浦东新区' }, | |
{ value: '黄浦区', label: '黄浦区' }, | |
{ value: '静安区', label: '静安区' } | |
] | |
} | |
] | |
onMounted(() => { | |
loadUserProfile() | |
}) | |
const loadUserProfile = () => { | |
// 加载用户资料到编辑表单 | |
Object.assign(editProfile, { | |
username: userProfile.value.username, | |
email: userProfile.value.email, | |
realName: userProfile.value.realName, | |
location: userProfile.value.location, | |
signature: userProfile.value.signature, | |
website: userProfile.value.website, | |
interests: [...userProfile.value.interests], | |
emailPublic: userProfile.value.emailPublic, | |
statsPublic: userProfile.value.statsPublic, | |
activityPublic: userProfile.value.activityPublic | |
}) | |
} | |
const formatDate = (dateString) => { | |
const date = new Date(dateString) | |
return date.toLocaleDateString('zh-CN') | |
} | |
const formatTime = (timeString) => { | |
const date = new Date(timeString) | |
const now = new Date() | |
const diff = now - date | |
const hours = Math.floor(diff / (1000 * 60 * 60)) | |
if (hours < 1) return '刚刚' | |
if (hours < 24) return `${hours}小时前` | |
const days = Math.floor(hours / 24) | |
if (days < 7) return `${days}天前` | |
return date.toLocaleDateString('zh-CN') | |
} | |
const getUserTitleType = (level) => { | |
if (level >= 8) return 'danger' // 管理员 | |
if (level >= 6) return 'warning' // 资深会员 | |
if (level >= 4) return 'success' // 正式会员 | |
if (level >= 2) return 'info' // 初级会员 | |
return 'default' // 新手 | |
} | |
const getRatioClass = (ratio) => { | |
const r = parseFloat(ratio) | |
if (r >= 2) return 'excellent' | |
if (r >= 1) return 'good' | |
return 'warning' | |
} | |
const saveProfile = async () => { | |
try { | |
await profileFormRef.value?.validate() | |
saving.value = true | |
// 模拟保存过程 | |
await new Promise(resolve => setTimeout(resolve, 1500)) | |
// 更新用户资料 | |
Object.assign(userProfile.value, editProfile) | |
ElMessage.success('个人资料保存成功') | |
} catch (error) { | |
console.error('表单验证失败:', error) | |
} finally { | |
saving.value = false | |
} | |
} | |
return { | |
activeTab, | |
showAvatarDialog, | |
saving, | |
userProfile, | |
editProfile, | |
profileRules, | |
locationOptions, | |
formatDate, | |
formatTime, | |
getUserTitleType, | |
getRatioClass, | |
saveProfile | |
} | |
} | |
} | |
</script> | |
<style scoped> | |
/* 添加相关样式 */ | |
</style> |