# Stats Dashboard Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 重新设计 StatsView.vue 数据统计页面,采用赛博朋克风格的迷你图表布局 **Architecture:** 单页垂直滚动布局,6个模块垂直排列,每个模块包含汇总数字卡片和迷你趋势图。复用现有 CSS 变量和 ChatView.vue 的设计模式。使用纯 CSS 实现迷你图表(条形图/折线图),避免引入新的图表依赖。 **Tech Stack:** Vue 3 + TypeScript + Pinia + lucide-vue-next(已有)+ echarts(已有,但优先用CSS实现) --- ## File Structure ``` frontend/src/ ├── views/ │ └── StatsView.vue # 完全重写 ├── components/stats/ │ ├── MetricCard.vue # 新建 - 指标卡片组件 │ ├── MiniLineChart.vue # 新建 - 迷你折线图 │ ├── MiniBarChart.vue # 新建 - 迷你柱状图 │ ├── SectionHeader.vue # 新建 - 区块标题 │ └── SummaryRow.vue # 新建 - 汇总行 └── style.css # 可能需要新增CSS变量 ``` --- ## Task 1: 创建 SectionHeader 组件 **Files:** - Create: `frontend/src/components/stats/SectionHeader.vue` - Modify: `frontend/src/views/StatsView.vue`(引入组件) - [ ] **Step 1: 创建 SectionHeader.vue** ```vue // {{ title }} {{ title.split(' ')[0] }} ``` - [ ] **Step 2: 在 StatsView.vue 中引入并使用 SectionHeader** 在 ` {{ value }} {{ label }} ``` --- ## Task 3: 创建 SummaryRow 组件 **Files:** - Create: `frontend/src/components/stats/SummaryRow.vue` - [ ] **Step 1: 创建 SummaryRow.vue** ```vue {{ item.value }} {{ item.label }} ``` --- ## Task 4: 创建 MiniBarChart 组件(CSS实现) **Files:** - Create: `frontend/src/components/stats/MiniBarChart.vue` - [ ] **Step 1: 创建 MiniBarChart.vue** ```vue ``` --- ## Task 5: 创建 MiniLineChart 组件(CSS实现) **Files:** - Create: `frontend/src/components/stats/MiniLineChart.vue` - [ ] **Step 1: 创建 MiniLineChart.vue** ```vue ``` --- ## Task 6: 重写 StatsView.vue **Files:** - Modify: `frontend/src/views/StatsView.vue` - [ ] **Step 1: 重写 script setup 部分** ```typescript import { ref, onMounted, computed } from 'vue' import * as statsApi from '@/api/stats' import { Cpu, HardDrive, MemoryStick, Clock, MessageSquare, BookOpen, CheckSquare, TrendingUp, Tag } from 'lucide-vue-next' import SectionHeader from '@/components/stats/SectionHeader.vue' import MetricCard from '@/components/stats/MetricCard.vue' import SummaryRow from '@/components/stats/SummaryRow.vue' import MiniLineChart from '@/components/stats/MiniLineChart.vue' import MiniBarChart from '@/components/stats/MiniBarChart.vue' const isLoading = ref(true) const hasError = ref(false) // 数据状态 const systemHealth = ref(null) const conversationStats = ref(null) const knowledgeStats = ref(null) const kanbanStats = ref(null) const communityStats = ref(null) const personalInsights = ref(null) function formatUptime(seconds: number) { const days = Math.floor(seconds / 86400) const hours = Math.floor((seconds % 86400) / 3600) const mins = Math.floor((seconds % 3600) / 60) if (days > 0) return `${days}d ${hours}h` if (hours > 0) return `${hours}h ${mins}m` return `${mins}m` } function formatNumber(num: number): string { if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M' if (num >= 1000) return (num / 1000).toFixed(1) + 'K' return num.toString() } onMounted(async () => { try { const promises = [ statsApi.getSystemHealth().catch(() => null), ] const [sys] = await Promise.all(promises) systemHealth.value = sys?.data || null // 尝试加载用户相关数据(需要认证) const userPromises = [ statsApi.getConversationStats().catch(() => null), statsApi.getKnowledgeStats().catch(() => null), statsApi.getKanbanStats().catch(() => null), statsApi.getCommunityStats().catch(() => null), statsApi.getPersonalInsights().catch(() => null), ] const [conv, know, kanban, community, insights] = await Promise.all(userPromises) conversationStats.value = conv?.data || null knowledgeStats.value = know?.data || null kanbanStats.value = kanban?.data || null communityStats.value = community?.data || null personalInsights.value = insights?.data || null } catch (e) { hasError.value = true console.error('Failed to load stats:', e) } finally { isLoading.value = false } }) // 图表数据转换 const convChartData = computed(() => conversationStats.value?.daily_conversations?.map((d: any) => ({ date: d.date, value: d.count })) || [] ) const msgChartData = computed(() => conversationStats.value?.daily_messages?.map((d: any) => ({ date: d.date, value: d.count })) || [] ) const inputTokenData = computed(() => conversationStats.value?.daily_input_tokens?.map((d: any) => ({ date: d.date, value: d.input_tokens })) || [] ) const outputTokenData = computed(() => conversationStats.value?.daily_output_tokens?.map((d: any) => ({ date: d.date, value: d.output_tokens })) || [] ) const knowChartData = computed(() => knowledgeStats.value?.daily_new_tags?.map((d: any) => ({ date: d.date, value: d.count })) || [] ) const kanbanNewData = computed(() => kanbanStats.value?.daily_new_tasks?.map((d: any) => d.count) || [] ) const kanbanDoneData = computed(() => kanbanStats.value?.daily_completed_tasks?.map((d: any) => d.count) || [] ) const communityChartData = computed(() => communityStats.value?.daily_posts?.map((d: any) => ({ date: d.date, value: d.count })) || [] ) const hourlyActivityData = computed(() => personalInsights.value?.hourly_activity?.map((h: any) => h.count) || [] ) ``` - [ ] **Step 2: 重写 template 部分** ```vue // DATA METRICS Loading metrics... Failed to load stats window.location.reload()">Refresh 30-Day Trend No conversation data yet Tag Growth No knowledge data yet Tasks: New vs Completed No kanban data yet Activity Trend No community data yet Hourly Activity No activity data Top Tags {{ tag.tag_path }} {{ tag.usage_count }} No tags yet Token Trend {{ personalInsights.token_trend_percent }}% vs last month Login to see personal insights ``` - [ ] **Step 3: 重写 style 部分** ```vue ``` --- ## Task 7: 添加缺少的 CSS 变量(如需要) **Files:** - Modify: `frontend/src/style.css` - [ ] **Step 1: 检查并添加缺失的 CSS 变量** 如果 `--accent-purple` 不存在,添加: ```css --accent-purple: #a855f7; --accent-purple-dim: rgba(123, 44, 191, 0.15); ``` --- ## Task 8: 验证与测试 **Files:** - Test: `frontend/src/views/StatsView.vue` - Test: `frontend/src/components/stats/*.vue` - [ ] **Step 1: 运行 TypeScript 检查** ```bash cd frontend && npx vue-tsc --noEmit ``` - [ ] **Step 2: 运行开发服务器测试** ```bash cd frontend && npm run dev ``` - [ ] **Step 3: 验证页面渲染** - 打开 http://localhost:5173/stats - 确认无 console errors - 确认页面布局正确 --- ## 执行选项 **1. Subagent-Driven (推荐)** - 我为每个任务派遣独立的子代理,任务间进行审查,快速迭代 **2. Inline Execution** - 在当前会话中按批次执行任务 选择哪种方式?