重构: 拆Dashboard.vue仪表盘(strategy前端·Dashboard<500达标)
- 新建 components/dashboard/(StatCardRow 126 + ActiveProjectsPanel 174 + IdeasPanel 82): 统计卡+活跃项目+灵感池 - Dashboard.vue 563→238(<500达标): 头部+refresh+loadAll编排+共享样式 - store共享(useProjectStore单例, 子组件纯读, 无props/emit) strategy: 前端views, 子组件三拆; Dashboard<500达标(第四个继Ideas/Knowledge); scoped .df-panel父子各存(隔离必需)
This commit is contained in:
126
src/components/dashboard/StatCardRow.vue
Normal file
126
src/components/dashboard/StatCardRow.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div class="stat-row">
|
||||
<div
|
||||
v-for="(stat, i) in stats" :key="stat.key"
|
||||
class="stat-card"
|
||||
:style="{ animationDelay: `${i * 60}ms` }"
|
||||
>
|
||||
<div class="stat-card-bg" :class="'stat-bg--' + stat.key"></div>
|
||||
<div class="stat-card-inner">
|
||||
<div class="stat-top">
|
||||
<div class="stat-icon-wrap" :style="{ background: stat.iconBg }">
|
||||
<span class="stat-icon">{{ stat.icon }}</span>
|
||||
</div>
|
||||
<span class="stat-trend" :class="stat.trend > 0 ? 'up' : stat.trend < 0 ? 'down' : 'flat'">
|
||||
<template v-if="stat.trend > 0">↑</template><template v-else-if="stat.trend < 0">↓</template>
|
||||
<template v-else>—</template>
|
||||
{{ stat.trend > 0 ? '+' : '' }}{{ stat.trend }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="stat-value">{{ stat.value }}</div>
|
||||
<div class="stat-label">{{ stat.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 统计卡片行 — 从 Dashboard.vue 抽出(4 张统计卡: ideas/projects/tasks/drafts)。
|
||||
// 共享 project store(全局单例),stats computed 直读 store.stats,无需父传 props。
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useProjectStore } from '@/stores/project'
|
||||
|
||||
const store = useProjectStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
const stats = computed(() => [
|
||||
{ key: 'ideas', icon: '\u{1F4A1}', label: t('dashboard.stats.ideas'), value: store.stats.ideas, trend: 0,
|
||||
iconBg: 'rgba(123,111,240,0.12)' },
|
||||
{ key: 'projects', icon: '\u{1F4C2}', label: t('dashboard.stats.projects'), value: store.stats.projects, trend: 0,
|
||||
iconBg: 'rgba(94,175,240,0.10)' },
|
||||
{ key: 'tasks', icon: '⚡', label: t('dashboard.stats.activeTasks'), value: store.stats.activeTasks, trend: 0,
|
||||
iconBg: 'rgba(61,219,160,0.10)' },
|
||||
{ key: 'drafts', icon: '\u{1F4DD}', label: t('dashboard.stats.drafts'), value: store.stats.drafts, trend: 0,
|
||||
iconBg: 'rgba(240,199,94,0.10)' },
|
||||
])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 仅迁入统计卡行自身样式;页面级布局(dash-grid/responsive 全局栅格)留 Dashboard.vue */
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.stat-card {
|
||||
position: relative;
|
||||
border-radius: var(--df-radius-md);
|
||||
overflow: hidden;
|
||||
border: 0.5px solid var(--df-border);
|
||||
animation: fadeInUp 0.5s var(--df-ease) both;
|
||||
}
|
||||
.stat-card-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.stat-bg--ideas { background: var(--df-accent-bg); }
|
||||
.stat-bg--projects { background: var(--df-info-bg); }
|
||||
.stat-bg--tasks { background: var(--df-success-bg); }
|
||||
.stat-bg--drafts { background: var(--df-warning-bg); }
|
||||
.stat-card-inner {
|
||||
position: relative;
|
||||
padding: 12px 14px 10px;
|
||||
}
|
||||
.stat-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
|
||||
.stat-icon-wrap {
|
||||
width: 28px; height: 28px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border-radius: var(--df-radius-sm);
|
||||
}
|
||||
.stat-icon { font-size: 14px; }
|
||||
|
||||
.stat-trend {
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
padding: 1px 6px;
|
||||
border-radius: var(--df-radius-sm);
|
||||
}
|
||||
.stat-trend.up { color: var(--df-success); background: rgba(61,219,160,0.10); }
|
||||
.stat-trend.down { color: var(--df-danger); background: rgba(240,101,101,0.10); }
|
||||
.stat-trend.flat { color: var(--df-text-dim); background: rgba(255,255,255,0.04); }
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.8px;
|
||||
color: var(--df-text);
|
||||
line-height: 1;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 11px;
|
||||
color: var(--df-text-dim);
|
||||
font-weight: 450;
|
||||
}
|
||||
|
||||
/* 中等窗口:缩窄统计卡 */
|
||||
@media (min-width: 901px) and (max-width: 1200px) {
|
||||
.stat-row { grid-template-columns: repeat(4, 1fr); gap: 8px; }
|
||||
.stat-card-inner { padding: 10px 12px 8px; }
|
||||
.stat-value { font-size: 22px; }
|
||||
.stat-icon-wrap { width: 24px; height: 24px; }
|
||||
.stat-icon { font-size: 12px; }
|
||||
}
|
||||
|
||||
/* 超宽窗口:展开统计卡 */
|
||||
@media (min-width: 1600px) {
|
||||
.stat-row { gap: 14px; }
|
||||
.stat-card-inner { padding: 14px 18px 12px; }
|
||||
.stat-value { font-size: 28px; }
|
||||
.stat-icon-wrap { width: 30px; height: 30px; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user