Files
happy-life-star/web-new/src/views/dashboard/PersonalDashboard.vue
T

441 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.
<template>
<div class="dashboard-page">
<!-- 欢迎区域 -->
<div class="welcome-section bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg p-6 mb-6 text-white">
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold mb-2">
{{ getGreeting() }}{{ userNickname }}
</h1>
<p class="text-blue-100">
今天是您使用情绪博物馆的第 {{ totalDays }}
</p>
</div>
<div class="text-right">
<div class="text-3xl font-bold">{{ todayMood }}/10</div>
<div class="text-blue-100 text-sm">今日心情指数</div>
</div>
</div>
</div>
<!-- 统计卡片 -->
<div class="stats-grid grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
<div
v-for="stat in statsData"
:key="stat.title"
class="stat-card bg-white rounded-lg shadow-sm p-6 hover:shadow-md transition-shadow"
>
<div class="flex items-center justify-between">
<div>
<p class="text-gray-600 text-sm mb-1">{{ stat.title }}</p>
<p class="text-2xl font-bold text-gray-900">{{ stat.value }}</p>
<div v-if="stat.trend" class="flex items-center mt-2">
<el-icon
:class="stat.trend === 'up' ? 'text-green-500' : 'text-red-500'"
size="16"
>
<ArrowUp v-if="stat.trend === 'up'" />
<ArrowDown v-else />
</el-icon>
<span
:class="stat.trend === 'up' ? 'text-green-500' : 'text-red-500'"
class="text-sm ml-1"
>
{{ stat.trendValue }}%
</span>
</div>
</div>
<div
class="w-12 h-12 rounded-lg flex items-center justify-center"
:style="{ backgroundColor: stat.color + '20' }"
>
<el-icon :size="24" :style="{ color: stat.color }">
<component :is="stat.icon" />
</el-icon>
</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="charts-section grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<!-- 情绪趋势图 -->
<div class="chart-card bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">情绪趋势</h3>
<el-select v-model="emotionPeriod" size="small">
<el-option label="最近7天" value="7d" />
<el-option label="最近30天" value="30d" />
<el-option label="最近90天" value="90d" />
</el-select>
</div>
<div ref="emotionChartRef" class="h-64"></div>
</div>
<!-- 情绪分布图 -->
<div class="chart-card bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">情绪分布</h3>
<el-button size="small" @click="exportChart">
<el-icon class="mr-1"><Download /></el-icon>
导出
</el-button>
</div>
<div ref="emotionPieChartRef" class="h-64"></div>
</div>
</div>
<!-- 活动和成就 -->
<div class="activity-section grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- 最近活动 -->
<div class="activity-card bg-white rounded-lg shadow-sm p-6 lg:col-span-2">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">最近活动</h3>
<el-link type="primary" @click="viewAllActivities">查看全部</el-link>
</div>
<div class="activity-list space-y-4">
<div
v-for="activity in recentActivities"
:key="activity.id"
class="activity-item flex items-start space-x-3 p-3 hover:bg-gray-50 rounded-lg transition-colors"
>
<div
class="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0"
:style="{ backgroundColor: activity.color + '20' }"
>
<el-icon :size="16" :style="{ color: activity.color }">
<component :is="activity.icon" />
</el-icon>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm text-gray-900">{{ activity.title }}</p>
<p class="text-xs text-gray-500">{{ formatTime(activity.time) }}</p>
</div>
</div>
</div>
</div>
<!-- 成就徽章 -->
<div class="achievements-card bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">成就徽章</h3>
<el-link type="primary" @click="viewAllAchievements">查看全部</el-link>
</div>
<div class="achievements-grid grid grid-cols-3 gap-3">
<div
v-for="achievement in recentAchievements"
:key="achievement.id"
class="achievement-item text-center p-3 hover:bg-gray-50 rounded-lg transition-colors cursor-pointer"
@click="viewAchievement(achievement)"
>
<div class="w-12 h-12 mx-auto mb-2 text-2xl">
{{ achievement.icon }}
</div>
<p class="text-xs text-gray-600 truncate">{{ achievement.name }}</p>
</div>
</div>
<div class="mt-4 text-center">
<p class="text-sm text-gray-500">
已获得 {{ totalAchievements }} 个成就
</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import {
ArrowUp,
ArrowDown,
Download,
ChatDotRound,
EditPen,
TrendCharts,
Calendar
} from '@element-plus/icons-vue'
import * as echarts from 'echarts'
import { useAuthStore } from '@/stores/auth'
import { EMOTION_COLORS } from '@/config/constants'
import { formatRelativeTime } from '@/utils/format'
const authStore = useAuthStore()
// 响应式数据
const emotionPeriod = ref('7d')
const emotionChartRef = ref<HTMLElement>()
const emotionPieChartRef = ref<HTMLElement>()
let emotionChart: echarts.ECharts | null = null
let emotionPieChart: echarts.ECharts | null = null
// 计算属性
const userNickname = computed(() => authStore.nickname)
const totalDays = ref(45)
const todayMood = ref(7)
const totalAchievements = ref(12)
// 统计数据
const statsData = [
{
title: '总对话数',
value: '156',
trend: 'up',
trendValue: 12,
icon: ChatDotRound,
color: '#3b82f6'
},
{
title: '日记篇数',
value: '23',
trend: 'up',
trendValue: 8,
icon: EditPen,
color: '#10b981'
},
{
title: '平均心情',
value: '7.2',
trend: 'up',
trendValue: 5,
icon: TrendCharts,
color: '#f59e0b'
},
{
title: '连续天数',
value: '12',
trend: 'stable',
trendValue: 0,
icon: Calendar,
color: '#8b5cf6'
}
]
// 最近活动
const recentActivities = [
{
id: '1',
title: '发布了新的情绪日记《今天的心情》',
time: Date.now() - 1000 * 60 * 30,
icon: EditPen,
color: '#10b981'
},
{
id: '2',
title: '与AI助手进行了深度对话',
time: Date.now() - 1000 * 60 * 60 * 2,
icon: ChatDotRound,
color: '#3b82f6'
},
{
id: '3',
title: '获得了"连续记录7天"成就',
time: Date.now() - 1000 * 60 * 60 * 24,
icon: TrendCharts,
color: '#f59e0b'
}
]
// 最近成就
const recentAchievements = [
{ id: '1', name: '初次记录', icon: '🎉' },
{ id: '2', name: '连续7天', icon: '🔥' },
{ id: '3', name: '情绪专家', icon: '🎯' },
{ id: '4', name: '分享达人', icon: '📢' },
{ id: '5', name: '深度思考', icon: '🤔' },
{ id: '6', name: '积极向上', icon: '☀️' }
]
// 方法
const getGreeting = () => {
const hour = new Date().getHours()
if (hour < 12) return '早上好'
if (hour < 18) return '下午好'
return '晚上好'
}
const formatTime = (timestamp: number) => {
return formatRelativeTime(timestamp)
}
const initEmotionChart = () => {
if (!emotionChartRef.value) return
emotionChart = echarts.init(emotionChartRef.value)
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
},
yAxis: {
type: 'value',
min: 0,
max: 10
},
series: [
{
name: '心情指数',
type: 'line',
smooth: true,
data: [6, 7, 8, 6, 9, 7, 8],
itemStyle: {
color: '#3b82f6'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(59, 130, 246, 0.3)' },
{ offset: 1, color: 'rgba(59, 130, 246, 0.1)' }
])
}
}
]
}
emotionChart.setOption(option)
}
const initEmotionPieChart = () => {
if (!emotionPieChartRef.value) return
emotionPieChart = echarts.init(emotionPieChartRef.value)
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '情绪分布',
type: 'pie',
radius: '50%',
data: [
{ value: 35, name: '开心', itemStyle: { color: EMOTION_COLORS.happy } },
{ value: 25, name: '平静', itemStyle: { color: EMOTION_COLORS.calm } },
{ value: 20, name: '兴奋', itemStyle: { color: EMOTION_COLORS.excited } },
{ value: 15, name: '焦虑', itemStyle: { color: EMOTION_COLORS.anxious } },
{ value: 5, name: '难过', itemStyle: { color: EMOTION_COLORS.sad } }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
emotionPieChart.setOption(option)
}
const exportChart = () => {
ElMessage.info('图表导出功能开发中...')
}
const viewAllActivities = () => {
ElMessage.info('查看全部活动功能开发中...')
}
const viewAllAchievements = () => {
ElMessage.info('查看全部成就功能开发中...')
}
const viewAchievement = (achievement: any) => {
ElMessage.success(`查看成就:${achievement.name}`)
}
// 监听图表周期变化
watch(emotionPeriod, () => {
// 重新加载图表数据
if (emotionChart) {
// 这里可以根据周期加载不同的数据
emotionChart.setOption({
xAxis: {
data: emotionPeriod.value === '7d'
? ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
: ['第1周', '第2周', '第3周', '第4周']
},
series: [{
data: emotionPeriod.value === '7d'
? [6, 7, 8, 6, 9, 7, 8]
: [7.2, 6.8, 7.5, 8.1]
}]
})
}
})
// 生命周期
onMounted(() => {
nextTick(() => {
initEmotionChart()
initEmotionPieChart()
})
})
// 响应式处理
onUnmounted(() => {
if (emotionChart) {
emotionChart.dispose()
}
if (emotionPieChart) {
emotionPieChart.dispose()
}
})
// 窗口大小变化时重新调整图表
window.addEventListener('resize', () => {
if (emotionChart) {
emotionChart.resize()
}
if (emotionPieChart) {
emotionPieChart.resize()
}
})
</script>
<style scoped>
.stat-card {
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-2px);
}
.activity-item:hover {
cursor: pointer;
}
.achievement-item:hover {
transform: scale(1.05);
}
</style>