512 lines
12 KiB
Vue
512 lines
12 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="profile-container">
|
|||
|
|
<!-- 深空背景 -->
|
|||
|
|
<div class="space-background">
|
|||
|
|
<div class="stars" ref="stars"></div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 顶部导航 -->
|
|||
|
|
<div class="header glass-card">
|
|||
|
|
<van-icon name="arrow-left" size="24" @click="goBack" />
|
|||
|
|
<h2>个人中心</h2>
|
|||
|
|
<van-icon name="setting-o" size="24" @click="goToSettings" />
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 用户信息卡片 -->
|
|||
|
|
<div class="user-card glass-card p-20">
|
|||
|
|
<div class="user-avatar">
|
|||
|
|
<img :src="userAvatar" alt="用户头像" />
|
|||
|
|
</div>
|
|||
|
|
<div class="user-info">
|
|||
|
|
<h3>{{ userName }}</h3>
|
|||
|
|
<p>{{ userEmail }}</p>
|
|||
|
|
<p class="user-motto">{{ userMotto }}</p>
|
|||
|
|
</div>
|
|||
|
|
<van-icon name="edit" size="20" @click="editProfile" />
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 核心数据卡片 -->
|
|||
|
|
<div class="stats-container">
|
|||
|
|
<div class="stats-card glass-card">
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="stat-number">{{ sentCount }}</div>
|
|||
|
|
<div class="stat-label">寄出的信</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-divider"></div>
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="stat-number">{{ receivedCount }}</div>
|
|||
|
|
<div class="stat-label">收到的信</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-divider"></div>
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="stat-number">{{ totalDays }}</div>
|
|||
|
|
<div class="stat-label">时间旅行(天)</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 功能列表 -->
|
|||
|
|
<div class="function-list">
|
|||
|
|
<van-cell-group class="glass-card">
|
|||
|
|
<van-cell
|
|||
|
|
title="我的时间线"
|
|||
|
|
icon="chart-trending-o"
|
|||
|
|
is-link
|
|||
|
|
@click="goToTimeline"
|
|||
|
|
/>
|
|||
|
|
<van-cell
|
|||
|
|
title="数据统计"
|
|||
|
|
icon="bar-chart-o"
|
|||
|
|
is-link
|
|||
|
|
@click="goToStats"
|
|||
|
|
/>
|
|||
|
|
<van-cell
|
|||
|
|
title="AI助手"
|
|||
|
|
icon="bulb-o"
|
|||
|
|
is-link
|
|||
|
|
@click="goToAIAssistant"
|
|||
|
|
/>
|
|||
|
|
</van-cell-group>
|
|||
|
|
|
|||
|
|
<van-cell-group class="glass-card mt-20">
|
|||
|
|
<van-cell
|
|||
|
|
title="数据备份"
|
|||
|
|
icon="folder-o"
|
|||
|
|
is-link
|
|||
|
|
@click="goToBackup"
|
|||
|
|
/>
|
|||
|
|
<van-cell
|
|||
|
|
title="隐私设置"
|
|||
|
|
icon="shield-o"
|
|||
|
|
is-link
|
|||
|
|
@click="goToPrivacy"
|
|||
|
|
/>
|
|||
|
|
<van-cell
|
|||
|
|
title="通知管理"
|
|||
|
|
icon="bell"
|
|||
|
|
is-link
|
|||
|
|
@click="goToNotifications"
|
|||
|
|
/>
|
|||
|
|
</van-cell-group>
|
|||
|
|
|
|||
|
|
<van-cell-group class="glass-card mt-20">
|
|||
|
|
<van-cell
|
|||
|
|
title="帮助与反馈"
|
|||
|
|
icon="question-o"
|
|||
|
|
is-link
|
|||
|
|
@click="goToHelp"
|
|||
|
|
/>
|
|||
|
|
<van-cell
|
|||
|
|
title="关于ChronoMail"
|
|||
|
|
icon="info-o"
|
|||
|
|
is-link
|
|||
|
|
@click="showAbout"
|
|||
|
|
/>
|
|||
|
|
</van-cell-group>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 退出登录按钮 -->
|
|||
|
|
<div class="logout-container">
|
|||
|
|
<van-button round block class="logout-button" @click="logout">
|
|||
|
|
退出登录
|
|||
|
|
</van-button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 底部导航 -->
|
|||
|
|
<van-tabbar v-model="active" class="custom-tabbar">
|
|||
|
|
<van-tabbar-item icon="home-o" to="/home">时光胶囊</van-tabbar-item>
|
|||
|
|
<van-tabbar-item icon="envelop-o" to="/inbox">收件箱</van-tabbar-item>
|
|||
|
|
<van-tabbar-item icon="send-o" to="/sent">发件箱</van-tabbar-item>
|
|||
|
|
<van-tabbar-item icon="user-o" to="/profile">个人中心</van-tabbar-item>
|
|||
|
|
</van-tabbar>
|
|||
|
|
|
|||
|
|
<!-- 关于弹窗 -->
|
|||
|
|
<van-popup v-model:show="showAboutPopup" position="bottom" :style="{ height: '40%' }">
|
|||
|
|
<div class="about-popup">
|
|||
|
|
<div class="about-logo">
|
|||
|
|
<div class="time-capsule"></div>
|
|||
|
|
<h3>ChronoMail</h3>
|
|||
|
|
<p>版本 1.0.0</p>
|
|||
|
|
</div>
|
|||
|
|
<div class="about-content">
|
|||
|
|
<p>ChronoMail 是一款可以发送邮件到未来的应用,帮助您与未来的自己进行对话,记录成长与蜕变。</p>
|
|||
|
|
<p class="about-slogan">"写给未来,不负当下"</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</van-popup>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import { ref, onMounted, computed } from 'vue'
|
|||
|
|
import { useRouter } from 'vue-router'
|
|||
|
|
import { showFailToast, showSuccessToast, Dialog } from 'vant'
|
|||
|
|
import { userActions, mailActions, userState } from '../store'
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
name: 'Profile',
|
|||
|
|
setup() {
|
|||
|
|
const router = useRouter()
|
|||
|
|
const active = ref(3)
|
|||
|
|
const stars = ref(null)
|
|||
|
|
const showAboutPopup = ref(false)
|
|||
|
|
|
|||
|
|
// 使用直接导入的状态和操作
|
|||
|
|
const user = computed(() => userState.userInfo)
|
|||
|
|
|
|||
|
|
// 用户信息
|
|||
|
|
const userName = ref(user.value?.username || '时光旅人')
|
|||
|
|
const userEmail = ref(user.value?.email || 'traveler@chronomail.com')
|
|||
|
|
const userMotto = ref('穿越时空,与未来的自己对话')
|
|||
|
|
const userAvatar = ref(user.value?.avatar || 'https://picsum.photos/seed/user123/100/100.jpg')
|
|||
|
|
|
|||
|
|
// 统计数据
|
|||
|
|
const sentCount = ref(0)
|
|||
|
|
const receivedCount = ref(0)
|
|||
|
|
const totalDays = ref(0)
|
|||
|
|
|
|||
|
|
// 获取用户统计数据
|
|||
|
|
const fetchStatistics = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await mailActions.getStatistics()
|
|||
|
|
const data = response.data
|
|||
|
|
|
|||
|
|
sentCount.value = data.totalSent || 0
|
|||
|
|
receivedCount.value = data.totalReceived || 0
|
|||
|
|
totalDays.value = data.timeTravelDuration || 0
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取统计数据失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取用户信息
|
|||
|
|
const fetchUserProfile = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await userActions.getProfile()
|
|||
|
|
const userData = response.data
|
|||
|
|
|
|||
|
|
userName.value = userData.username || '时光旅人'
|
|||
|
|
userEmail.value = userData.email || 'traveler@chronomail.com'
|
|||
|
|
userAvatar.value = userData.avatar || 'https://picsum.photos/seed/user123/100/100.jpg'
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取用户信息失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成星空背景
|
|||
|
|
const generateStars = () => {
|
|||
|
|
if (!stars.value) return
|
|||
|
|
|
|||
|
|
const starsContainer = stars.value
|
|||
|
|
const starCount = 100
|
|||
|
|
|
|||
|
|
for (let i = 0; i < starCount; i++) {
|
|||
|
|
const star = document.createElement('div')
|
|||
|
|
star.className = 'star'
|
|||
|
|
|
|||
|
|
// 随机位置
|
|||
|
|
const left = Math.random() * 100
|
|||
|
|
const top = Math.random() * 100
|
|||
|
|
|
|||
|
|
// 随机大小
|
|||
|
|
const size = Math.random() * 3 + 1
|
|||
|
|
|
|||
|
|
// 随机动画延迟
|
|||
|
|
const delay = Math.random() * 4
|
|||
|
|
|
|||
|
|
star.style.left = `${left}%`
|
|||
|
|
star.style.top = `${top}%`
|
|||
|
|
star.style.width = `${size}px`
|
|||
|
|
star.style.height = `${size}px`
|
|||
|
|
star.style.animationDelay = `${delay}s`
|
|||
|
|
|
|||
|
|
starsContainer.appendChild(star)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 返回上一页
|
|||
|
|
const goBack = () => {
|
|||
|
|
router.back()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到设置页面
|
|||
|
|
const goToSettings = () => {
|
|||
|
|
showFailToast('设置页面开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 编辑个人资料
|
|||
|
|
const editProfile = () => {
|
|||
|
|
showFailToast('编辑个人资料功能开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到时间线
|
|||
|
|
const goToTimeline = () => {
|
|||
|
|
router.push('/timeline')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到数据统计
|
|||
|
|
const goToStats = () => {
|
|||
|
|
showFailToast('数据统计功能开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到AI助手
|
|||
|
|
const goToAIAssistant = () => {
|
|||
|
|
showFailToast('AI助手功能开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到数据备份
|
|||
|
|
const goToBackup = () => {
|
|||
|
|
showFailToast('数据备份功能开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到隐私设置
|
|||
|
|
const goToPrivacy = () => {
|
|||
|
|
showFailToast('隐私设置功能开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到通知管理
|
|||
|
|
const goToNotifications = () => {
|
|||
|
|
showFailToast('通知管理功能开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到帮助与反馈
|
|||
|
|
const goToHelp = () => {
|
|||
|
|
showFailToast('帮助与反馈功能开发中')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示关于弹窗
|
|||
|
|
const showAbout = () => {
|
|||
|
|
showAboutPopup.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 退出登录
|
|||
|
|
const logout = () => {
|
|||
|
|
Dialog.confirm({
|
|||
|
|
title: '确认退出',
|
|||
|
|
message: '确定要退出登录吗?',
|
|||
|
|
})
|
|||
|
|
.then(() => {
|
|||
|
|
// 清除登录状态
|
|||
|
|
localStorage.removeItem('token')
|
|||
|
|
localStorage.removeItem('user')
|
|||
|
|
|
|||
|
|
showSuccessToast('已退出登录')
|
|||
|
|
|
|||
|
|
// 跳转到登录页
|
|||
|
|
router.push('/login')
|
|||
|
|
})
|
|||
|
|
.catch(() => {
|
|||
|
|
// 取消操作
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
generateStars()
|
|||
|
|
fetchUserProfile()
|
|||
|
|
fetchStatistics()
|
|||
|
|
|
|||
|
|
// 获取用户信息
|
|||
|
|
const userStr = localStorage.getItem('user')
|
|||
|
|
if (userStr) {
|
|||
|
|
const user = JSON.parse(userStr)
|
|||
|
|
userName.value = user.name || '时光旅人'
|
|||
|
|
userEmail.value = user.email || 'traveler@chronomail.com'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
active,
|
|||
|
|
stars,
|
|||
|
|
userName,
|
|||
|
|
userEmail,
|
|||
|
|
userMotto,
|
|||
|
|
userAvatar,
|
|||
|
|
sentCount,
|
|||
|
|
receivedCount,
|
|||
|
|
totalDays,
|
|||
|
|
showAboutPopup,
|
|||
|
|
goBack,
|
|||
|
|
goToSettings,
|
|||
|
|
editProfile,
|
|||
|
|
goToTimeline,
|
|||
|
|
goToStats,
|
|||
|
|
goToAIAssistant,
|
|||
|
|
goToBackup,
|
|||
|
|
goToPrivacy,
|
|||
|
|
goToNotifications,
|
|||
|
|
goToHelp,
|
|||
|
|
showAbout,
|
|||
|
|
logout
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.profile-container {
|
|||
|
|
height: 100vh;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
position: relative;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-card {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin: 0 15px 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-avatar {
|
|||
|
|
width: 60px;
|
|||
|
|
height: 60px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
overflow: hidden;
|
|||
|
|
margin-right: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-avatar img {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
object-fit: cover;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-info {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-info h3 {
|
|||
|
|
margin: 0 0 5px;
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-info p {
|
|||
|
|
margin: 0 0 5px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-motto {
|
|||
|
|
font-style: italic;
|
|||
|
|
color: var(--accent-color) !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-container {
|
|||
|
|
margin: 0 15px 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-card {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-around;
|
|||
|
|
padding: 20px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-item {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-number {
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: var(--accent-color);
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-label {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-divider {
|
|||
|
|
width: 1px;
|
|||
|
|
height: 40px;
|
|||
|
|
background-color: rgba(255, 255, 255, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.function-list {
|
|||
|
|
margin: 0 15px 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.function-list .van-cell-group {
|
|||
|
|
border-radius: 12px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.function-list .van-cell {
|
|||
|
|
background-color: transparent;
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.function-list .van-cell::after {
|
|||
|
|
border-color: rgba(255, 255, 255, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.logout-container {
|
|||
|
|
margin: 0 15px 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.logout-button {
|
|||
|
|
background: linear-gradient(135deg, #ff4d4f, #c41e3a);
|
|||
|
|
border: none;
|
|||
|
|
height: 50px;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-top: 20px;
|
|||
|
|
box-shadow: 0 8px 20px rgba(255, 77, 79, 0.3);
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.logout-button:hover {
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
box-shadow: 0 12px 25px rgba(255, 77, 79, 0.4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.logout-button:active {
|
|||
|
|
transform: translateY(0);
|
|||
|
|
box-shadow: 0 5px 15px rgba(255, 77, 79, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.about-popup {
|
|||
|
|
padding: 30px 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.about-logo {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.about-logo h3 {
|
|||
|
|
margin: 10px 0 5px;
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
background: linear-gradient(135deg, #00D4FF, #ffffff);
|
|||
|
|
-webkit-background-clip: text;
|
|||
|
|
-webkit-text-fill-color: transparent;
|
|||
|
|
background-clip: text;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.about-logo p {
|
|||
|
|
margin: 0;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.about-content {
|
|||
|
|
text-align: left;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.about-content p {
|
|||
|
|
margin: 10px 0;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.about-slogan {
|
|||
|
|
text-align: center;
|
|||
|
|
font-style: italic;
|
|||
|
|
color: var(--accent-color);
|
|||
|
|
margin-top: 20px;
|
|||
|
|
}
|
|||
|
|
</style>
|