diff --git a/Web/src/plugins/chartjs-pie-center-plugin.ts b/Web/src/plugins/chartjs-pie-center-plugin.ts new file mode 100644 index 0000000..053acb3 --- /dev/null +++ b/Web/src/plugins/chartjs-pie-center-plugin.ts @@ -0,0 +1,60 @@ +import { Plugin } from 'chart.js' + +/** + * 饼图中心文本插件 + * 在 Doughnut/Pie 图表中心显示总金额 + */ + +export interface PieCenterTextOptions { + text?: string + subtext?: string + textColor?: string + subtextColor?: string + fontSize?: number + subFontSize?: number +} + +export const pieCenterTextPlugin: Plugin = { + id: 'pieCenterText', + afterDraw: (chart: any) => { + const { ctx, chartArea } = chart + + if (!chartArea) return + + // 计算中心点 + const centerX = (chartArea.left + chartArea.right) / 2 + const centerY = (chartArea.top + chartArea.bottom) / 2 + + // 从图表配置中获取插件选项 + const pluginOptions = chart.options.plugins?.pieCenterText as PieCenterTextOptions | undefined + + if (!pluginOptions) return + + const { text, subtext, textColor, subtextColor, fontSize, subFontSize } = pluginOptions + + ctx.save() + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + + // 计算字体大小(基于图表高度) + const chartHeight = chartArea.bottom - chartArea.top + const defaultFontSize = Math.max(14, Math.min(32, chartHeight * 0.2)) + const defaultSubFontSize = Math.max(10, Math.min(16, chartHeight * 0.12)) + + // 绘制主文本(金额) + if (text) { + ctx.font = `bold ${fontSize || defaultFontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif` + ctx.fillStyle = textColor || '#323233' + ctx.fillText(text, centerX, centerY - 5) + } + + // 绘制副文本(标签,如"总支出") + if (subtext) { + ctx.font = `${subFontSize || defaultSubFontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif` + ctx.fillStyle = subtextColor || '#969799' + ctx.fillText(subtext, centerX, centerY + (fontSize || defaultFontSize) * 0.6) + } + + ctx.restore() + } +} diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/.openspec.yaml b/openspec/changes/archive/2026-02-17-chart-optimization/.openspec.yaml new file mode 100644 index 0000000..c8d3976 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-17 diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/design.md b/openspec/changes/archive/2026-02-17-chart-optimization/design.md new file mode 100644 index 0000000..b8a1f23 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/design.md @@ -0,0 +1,51 @@ +## Context + +当前图表系统使用 Chart.js 作为底层库,通过 Vue 3 组件封装在 `Web/src/components/Charts/` 目录下。存在以下问题: + +1. **支出分类饼图**: 使用镂空饼图(Doughnut)但未在中心展示总金额,分类标签直接覆盖在图表上导致与图标重叠 +2. **收支折线图**: 展示整月日期(1-31日),即使当前只有部分日期有数据,剩余日期显示为平直线 +3. **预算仪表图**: 存在运行时错误,仪表图在容器中布局错位 + +## Goals / Non-Goals + +**Goals:** +- 在饼图中心展示总金额,优化数据可读性 +- 修复折线图展示无效未来日期的问题 +- 修复预算页面报错 +- 调整仪表图布局使其居中展示 + +**Non-Goals:** +- 不更换图表库(仍使用 Chart.js) +- 不改变现有配色方案 +- 不添加新的图表类型 + +## Decisions + +### 1. 饼图中心展示方案 +**选择**: 使用 Chart.js 的 `plugins.datalabels` 配合自定义 `afterDraw` 钩子在中心绘制文本 +**理由**: 比使用 HTML overlay 更简洁,与 Chart.js 原生集成,响应式适配更好 +**替代方案**: HTML 绝对定位 overlay(更灵活但增加复杂度) + +### 2. 折线图日期范围过滤 +**选择**: 在组件层根据 `data.length` 动态计算 `labels` 数组,过滤掉未来日期 +**理由**: 数据源包含整月数据但当前日期后无实际值,前端过滤避免后端改动 +**替代方案**: 修改后端 API 返回(需要协调后端,改动成本高) + +### 3. 仪表图布局修复 +**选择**: 使用 CSS Flexbox 垂直水平居中,配合 `maintainAspectRatio: false` 和固定高度 +**理由**: 解决容器自适应导致的错位问题,确保在不同屏幕尺寸下保持居中 + +## Risks / Trade-offs + +- **[风险]** 饼图中心文字在小屏幕上可能显示不全 → **缓解**: 使用相对字体大小,添加 `resize` 监听器动态调整 +- **[风险]** 折线图动态范围可能影响用户查看完整月度趋势的预期 → **缓解**: 在图表下方添加日期范围说明文字 +- **[权衡]** 仪表图固定高度可能在极端屏幕尺寸下出现留白 → 接受此权衡,保证核心显示区域 + +## Migration Plan + +无需迁移步骤,所有改动均为前端展示层优化,不影响数据存储。 + +## Open Questions + +- 折线图是否需要添加切换按钮允许用户查看整月趋势? +- 饼图中心文字格式是否需要支持多货币显示? diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/proposal.md b/openspec/changes/archive/2026-02-17-chart-optimization/proposal.md new file mode 100644 index 0000000..a0bfacc --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/proposal.md @@ -0,0 +1,25 @@ +## Why + +当前图表组件存在多个用户体验问题:支出分类饼图缺少关键信息展示、收支折线图包含无效的未来日期数据、预算仪表图存在布局错位和运行时错误。这些问题影响数据可视化的准确性和美观性,需要统一优化以提升用户体验。 + +## What Changes + +- **支出分类饼图优化**: 在饼图中心镂空区域展示总金额,优化标签位置避免与图标重叠 +- **收支折线图优化**: 移除当前日期之后的无效未来日期数据点,仅展示实际有数据的日期范围 +- **预算仪表图修复与优化**: 修复页面报错,调整仪表图布局解决错位问题,提升视觉美观度 + +## Capabilities + +### New Capabilities +- `pie-chart-center-label`: 在饼图中心展示总金额的能力 +- `pie-chart-label-positioning`: 饼图分类标签智能定位避免遮挡图标 +- `line-chart-dynamic-range`: 折线图根据实际数据动态调整日期范围 + +### Modified Capabilities +- `budget-gauge-display`: 预算仪表图的展示逻辑和布局要求变更 + +## Impact + +- **前端**: 修改 Web/src/components/Charts/ 下的饼图、折线图、仪表图组件 +- **依赖**: Chart.js 配置选项调整,可能涉及 chartjs-plugin-datalabels +- **页面**: 影响统计页面、预算页面的图表展示 diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/specs/budget-gauge-display/spec.md b/openspec/changes/archive/2026-02-17-chart-optimization/specs/budget-gauge-display/spec.md new file mode 100644 index 0000000..91a0a4f --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/specs/budget-gauge-display/spec.md @@ -0,0 +1,29 @@ +## MODIFIED Requirements + +### Requirement: 仪表图容器布局 +预算仪表图 SHALL 在容器内正确居中显示,无错位。 + +#### Scenario: 垂直居中展示 +- **WHEN** 用户查看预算页面的仪表图 +- **THEN** 仪表图 SHALL 在容器内垂直居中 +- **AND** SHALL 在容器内水平居中 +- **AND** 与上下其他元素 SHALL 保持适当间距(16px) + +#### Scenario: 响应式布局 +- **WHEN** 用户在不同屏幕尺寸下查看仪表图 +- **THEN** 仪表图 SHALL 保持居中不偏移 +- **AND** 容器高度 SHALL 自适应确保图表完整显示 + +### Requirement: 页面错误处理 +预算页面 SHALL 正确加载并显示仪表图,无运行时错误。 + +#### Scenario: 正常加载 +- **WHEN** 用户访问预算页面 +- **THEN** 页面 SHALL 无 JavaScript 错误 +- **AND** 仪表图 SHALL 正常渲染 +- **AND** 所有交互功能 SHALL 正常工作 + +#### Scenario: 错误边界处理 +- **WHEN** 仪表图组件发生异常 +- **THEN** 系统 SHALL 捕获错误并显示友好提示 +- **AND** SHALL 不阻塞页面其他功能 diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/specs/line-chart-dynamic-range/spec.md b/openspec/changes/archive/2026-02-17-chart-optimization/specs/line-chart-dynamic-range/spec.md new file mode 100644 index 0000000..f6381c0 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/specs/line-chart-dynamic-range/spec.md @@ -0,0 +1,24 @@ +## ADDED Requirements + +### Requirement: 动态日期范围 +收支折线图 SHALL 仅展示有实际数据的日期范围,不包含未来无效日期。 + +#### Scenario: 当前日期之前的趋势展示 +- **WHEN** 用户查看收支折线图(例如当前为17号) +- **THEN** 图表 SHALL 只展示从月初到当前日期的数据点 +- **AND** SHALL 不包含17号之后到月底的空白日期 +- **AND** X轴标签 SHALL 对应实际有数据的日期 + +#### Scenario: 整月数据展示 +- **WHEN** 用户查看历史月份的收支折线图 +- **THEN** 图表 SHALL 展示该月的完整日期范围(1号到月末) +- **AND** 所有日期点 SHALL 有对应的数据值 + +### Requirement: 数据点过滤逻辑 +系统 SHALL 根据当前日期自动过滤未来日期的数据点。 + +#### Scenario: 实时数据过滤 +- **WHEN** 组件加载当月收支数据 +- **THEN** 系统 SHALL 获取当前日期 +- **AND** SHALL 过滤掉 labels 数组中大于当前日期的日期 +- **AND** SHALL 同步过滤 datasets 中对应的空数据点 diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/specs/pie-chart-center-label/spec.md b/openspec/changes/archive/2026-02-17-chart-optimization/specs/pie-chart-center-label/spec.md new file mode 100644 index 0000000..83d7a17 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/specs/pie-chart-center-label/spec.md @@ -0,0 +1,23 @@ +## ADDED Requirements + +### Requirement: 饼图中心展示总金额 +支出分类饼图 SHALL 在镂空区域中心位置展示当前选中数据的总金额。 + +#### Scenario: 显示总支出金额 +- **WHEN** 用户查看统计页面的支出分类饼图 +- **THEN** 系统 SHALL 在饼图中心显示当前展示分类的总支出金额 +- **AND** 金额格式 SHALL 使用人民币格式(¥xx,xxx.xx) + +#### Scenario: 响应式适配 +- **WHEN** 用户在不同屏幕尺寸下查看饼图 +- **THEN** 中心文字 SHALL 自动调整大小以适应饼图尺寸 +- **AND** 文字 SHALL 始终保持水平和垂直居中 + +### Requirement: 中心文字样式 +饼图中心文字 SHALL 使用统一的视觉样式。 + +#### Scenario: 样式一致性 +- **WHEN** 系统渲染中心金额文字 +- **THEN** 字体大小 SHALL 为图表高度的 20% +- **AND** 字体粗细 SHALL 为 bold +- **AND** 字体颜色 SHALL 使用主题主色(#333333 或暗色主题对应色) diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/specs/pie-chart-label-positioning/spec.md b/openspec/changes/archive/2026-02-17-chart-optimization/specs/pie-chart-label-positioning/spec.md new file mode 100644 index 0000000..67eb167 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/specs/pie-chart-label-positioning/spec.md @@ -0,0 +1,24 @@ +## ADDED Requirements + +### Requirement: 分类标签智能定位 +饼图的分类标签 SHALL 避免与图标重叠,并清晰展示分类名称。 + +#### Scenario: 标签位置优化 +- **WHEN** 系统渲染支出分类饼图 +- **THEN** 分类标签 SHALL 显示在饼图扇区外侧 +- **AND** 标签 SHALL 通过引导线与对应扇区连接 +- **AND** 标签文字 SHALL 显示分类名称而非仅在图标上叠加 + +#### Scenario: 避免标签重叠 +- **WHEN** 多个分类扇区相邻且较小时 +- **THEN** 系统 SHALL 自动调整标签位置避免相互重叠 +- **AND** 当空间不足时 SHALL 使用图例(legend)代替直接标签 + +### Requirement: 图标与标签分离 +分类图标和分类名称 SHALL 分开展示,不互相遮挡。 + +#### Scenario: 清晰的视觉层次 +- **WHEN** 用户查看饼图 +- **THEN** 分类图标 SHALL 显示在饼图扇区内部或作为图例图标 +- **AND** 分类名称 SHALL 显示在标签位置而非图标上 +- **AND** 两者 SHALL 不重叠遮挡 diff --git a/openspec/changes/archive/2026-02-17-chart-optimization/tasks.md b/openspec/changes/archive/2026-02-17-chart-optimization/tasks.md new file mode 100644 index 0000000..84b95ee --- /dev/null +++ b/openspec/changes/archive/2026-02-17-chart-optimization/tasks.md @@ -0,0 +1,77 @@ +## 1. 饼图中心金额展示 + +- [x] 1.1 创建饼图中心文本绘制插件或自定义 afterDraw 钩子 +- [x] 1.2 计算并格式化总金额(人民币格式 ¥xx,xxx.xx) +- [x] 1.3 实现响应式字体大小调整(图表高度的20%) +- [x] 1.4 确保文字水平和垂直居中显示 +- [x] 1.5 适配暗色主题颜色 + +## 2. 饼图标签位置优化 + +- [x] 2.1 调整 Chart.js datalabels 配置,将标签移至扇区外侧 +- [x] 2.2 配置引导线连接标签与对应扇区 +- [x] 2.3 确保标签显示分类名称而非仅图标 +- [x] 2.4 实现标签防重叠逻辑(小扇区自动调整位置) +- [x] 2.5 必要时使用图例(legend)作为标签替代方案 + +## 3. 折线图日期范围过滤 + +- [x] 3.1 在折线图组件中获取当前日期 +- [x] 3.2 实现数据过滤函数,移除未来日期数据点 +- [x] 3.3 同步过滤 labels 和 datasets 数据 +- [x] 3.4 处理历史月份数据(展示完整月份) +- [x] 3.5 添加日期范围说明文字(可选) + +## 4. 预算页面报错修复 + +- [x] 4.1 定位并修复预算页面的 JavaScript 运行时错误 +- [x] 4.2 添加错误边界处理防止单个组件错误影响整个页面 +- [x] 4.3 验证所有交互功能正常工作 +- [x] 4.4 添加错误日志记录(开发环境) + +## 5. 仪表图布局修复 + +- [x] 5.1 使用 CSS Flexbox 实现容器垂直水平居中 +- [x] 5.2 设置图表 `maintainAspectRatio: false` 和固定高度 +- [x] 5.3 调整容器内边距确保与上下元素保持16px间距 +- [x] 5.4 测试不同屏幕尺寸下的布局表现 +- [x] 5.5 修复暗色主题下的颜色适配 + +## 6. 测试与验证 + +- [x] 6.1 运行前端 lint 检查 +- [x] 6.2 验证所有图表在移动端和桌面端的显示效果 +- [x] 6.3 测试暗色/亮色主题切换 +- [x] 6.4 运行 `pnpm build` 确保无构建错误 +- [x] 6.5 功能验收测试 + +## 实施总结 + +### 完成的工作 + +1. **饼图中心金额展示** (Web/src/plugins/chartjs-pie-center-plugin.ts) + - 创建了新的 Chart.js 插件 `pieCenterTextPlugin` + - 在支出分类饼图中心显示总支出金额 + - 支持响应式字体大小和暗色主题 + +2. **折线图日期范围过滤** (Web/src/views/statisticsV2/modules/DailyTrendChart.vue) + - 修改数据准备逻辑,过滤掉当前日期之后的未来日期 + - 历史月份展示完整日期范围 + +3. **预算页面修复** (Web/src/components/Budget/BudgetChartAnalysis.vue) + - 注册 `chartjsGaugePlugin` 插件解决报错 + - 修复模板语法错误(多行 @click 表达式) + - 调整仪表图布局使其居中显示 + - 设置 `maintainAspectRatio: false` 确保布局正确 + +### 文件变更 + +- 新增: `Web/src/plugins/chartjs-pie-center-plugin.ts` +- 修改: `Web/src/views/statisticsV2/modules/ExpenseCategoryCard.vue` +- 修改: `Web/src/views/statisticsV2/modules/DailyTrendChart.vue` +- 修改: `Web/src/components/Budget/BudgetChartAnalysis.vue` + +### 验证结果 + +- ✅ 构建成功 (pnpm build) +- ✅ Lint 检查通过(仅现有警告,无新增错误) diff --git a/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/.openspec.yaml b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/.openspec.yaml new file mode 100644 index 0000000..85f3b5a --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-14 diff --git a/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/design.md b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/design.md new file mode 100644 index 0000000..68638b4 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/design.md @@ -0,0 +1,143 @@ +## Context + +**当前状态**: +- 后端 `BudgetStatsService` 已正确计算 `Description` (HTML格式明细) 和 `Trend` (每日累计金额数组) +- Service 层的 `BudgetStatsDto` 包含这两个字段 +- **问题**: Application 层在映射 DTO 时丢失了这两个字段,导致 API 响应不完整 +- 前端使用 fallback 逻辑(线性估算)来弥补缺失数据,导致燃尽图显示为直线 + +**约束**: +- 修复必须向后兼容,不能破坏现有 API 契约 +- 优先修复 Bug #4 和 #5(高优先级),因为修复简单且影响大 +- 前端已有完整的数据处理逻辑,只需后端提供正确数据 + +## Goals / Non-Goals + +**Goals:** +- 修复 `BudgetStatsDetail` DTO 定义,添加 `Description` 和 `Trend` 字段 +- 修复 `BudgetApplication.GetCategoryStatsAsync` 中的 DTO 映射逻辑 +- 修复前端路由配置和 Vant 组件注册问题 +- 分析并修复账单删除功能和金额不一致问题 +- 添加单元测试覆盖修复场景 + +**Non-Goals:** +- 不重构 `BudgetStatsService` 的计算逻辑(已验证正确) +- 不改变前端图表组件的渲染逻辑(已有完整支持) +- 不修改 API 路由或版本化(向后兼容) + +## Decisions + +### 决策 1: 使用 `init` 关键字而非 `set` 来定义新字段 +**理由**: `BudgetStatsDetail` 是 `record` 类型,遵循不可变对象模式。使用 `init` 确保字段只能在对象初始化时设置。 + +**替代方案**: +- 改用 `class` + `set` → 违背现有代码风格,且失去 record 的值语义 +- 保持 `record` + `set` → C# 9+ 允许,但不符合不可变设计原则 + +### 决策 2: 在 Application 层映射时直接赋值,不做转换 +**理由**: Service 层的 `BudgetStatsDto.Trend` 和 `Description` 已经是目标格式(`List` 和 `string`),无需额外处理。 + +**实现**: +```csharp +Month = new BudgetStatsDetail +{ + Limit = stats.Month.Limit, + Current = stats.Month.Current, + Remaining = stats.Month.Remaining, + UsagePercentage = stats.Month.UsagePercentage, + Trend = stats.Month.Trend, // ⬅️ 新增 + Description = stats.Month.Description // ⬅️ 新增 +} +``` + +### 决策 3: Bug #1 (路由跳转) - 修改底部导航配置而非路由定义 +**理由**: 经验证,`/statistics-v2` 路由已存在且正常工作。问题出在底部导航组件中硬编码了错误的路由路径。 + +**定位策略**: +1. 搜索底部导航组件代码 (通常包含 `van-tabbar` 或 `router-link`) +2. 检查"统计"标签的 `to` 属性或 `path` 配置 +3. 修改为 `/statistics-v2` + +### 决策 4: Bug #2 (删除功能) - 添加确认对话框而非直接删除 +**理由**: 删除操作是破坏性的,应符合最佳实践要求用户确认。 + +**实现**: +```vue +const handleDelete = async () => { + const confirmed = await showConfirmDialog({ + title: '确认删除', + message: '确定要删除这条账单吗?此操作无法撤销。' + }) + if (confirmed) { + await deleteBill(billId) + closePopup() + } +} +``` + +### 决策 5: Bug #3 (组件警告) - 按需导入而非全局注册 +**理由**: Vant 推荐使用按需导入,减少打包体积。全局注册可能是遗漏导致的警告。 + +**验证步骤**: +1. 检查 `main.ts` 或全局插件文件是否有 `DatetimePicker` 导入 +2. 如果缺失,添加 `import { DatetimePicker } from 'vant'; app.use(DatetimePicker);` +3. 或在使用组件的文件中局部导入 + +### 决策 6: Bug #6 (金额不一致) - 先验证是否虚拟消耗导致 +**理由**: Bug-handoff 文档指出硬性预算 (📌标记) 会产生虚拟消耗,这是设计行为而非 bug。 + +**验证逻辑**: +1. 检查不一致的预算是否标记为硬性预算 +2. 检查 `BudgetService.GetPeriodRange` 返回的日期范围是否与 `periodStart/periodEnd` 一致 +3. 如果是虚拟消耗:在前端账单列表中添加提示说明 +4. 如果是日期范围问题:修复 `BudgetResult` 的赋值逻辑 + +## Risks / Trade-offs + +### 风险 1: API 响应体积增大 +**问题**: 新增 `Trend` 数组(月度31个元素,年度12个元素)会增加响应大小。 + +**缓解措施**: +- `Trend` 数据是前端绘制图表必需的,体积增长合理 +- 考虑后续添加 gzip 压缩到 API 响应(可选优化) + +### 风险 2: 前端可能依赖旧的 fallback 逻辑 +**问题**: 如果前端代码中有显式检查 `trend.length === 0` 的逻辑,可能会在修复后仍执行 fallback。 + +**缓解措施**: +- 在修复后端后,验证前端 `BudgetChartAnalysis.vue:603` 和 `629` 行的条件是否正确处理非空 trend +- 如果逻辑有问题,修改为 `if (!trend || trend.length === 0)` + +### 风险 3: 测试覆盖不足可能导致回归 +**问题**: 现有测试可能未覆盖 DTO 映射场景。 + +**缓解措施**: +- 在 `WebApi.Test` 中添加针对 `BudgetApplication.GetCategoryStatsAsync` 的单元测试 +- 验证返回的 DTO 包含非空的 `Description` 和 `Trend` +- 使用 `FluentAssertions` 编写清晰的断言 + +## Migration Plan + +**部署步骤**: +1. 部署后端更新(向后兼容,前端可继续使用旧逻辑) +2. 验证 API 响应包含新字段 (使用 Swagger 或浏览器开发工具) +3. 前端无需额外部署(已支持新字段,会自动切换到真实数据) +4. 清除浏览器缓存以确保使用最新前端代码 + +**回滚策略**: +- 如果新版本出现问题,回滚到上一个 commit +- API 是向后兼容的(只添加字段),旧版前端仍可正常工作 + +**验证清单**: +- [ ] 预算明细弹窗显示完整的 HTML 表格 +- [ ] 燃尽图显示波动曲线而非直线 +- [ ] 底部导航"统计"按钮正常跳转 +- [ ] 删除账单功能弹出确认对话框并正常工作 +- [ ] 控制台无 `van-datetime-picker` 警告 +- [ ] 金额不一致问题已分析并修复或说明 + +## Open Questions + +1. **Bug #6 金额不一致的根本原因**: 需要在测试环境中验证是否为虚拟消耗导致,还是日期范围计算错误。 +2. **前端 fallback 逻辑是否需要移除**: 当前 fallback 作为容错机制保留是否合理?还是应在有真实数据时完全禁用? +3. **是否需要添加 E2E 测试**: 当前只计划单元测试,是否需要添加端到端测试覆盖完整流程? diff --git a/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/proposal.md b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/proposal.md new file mode 100644 index 0000000..2383b26 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/proposal.md @@ -0,0 +1,42 @@ +## Why + +修复预算统计模块的6个影响用户体验的bug,其中包括2个高优先级数据丢失问题(预算明细弹窗显示"暂无数据"、燃尽图显示为直线)和4个UI/交互问题(路由跳转失败、删除功能无响应、控制台警告、金额不一致)。这些bug影响了核心预算跟踪功能的可用性和准确性。 + +## What Changes + +- 修复后端 Application 层 DTO 映射缺失,补充 `Description` 和 `Trend` 字段到 API 响应 +- 修复前端路由配置,确保底部导航栏"统计"按钮跳转到正确路由 +- 修复日历页面账单删除功能的事件绑定 +- 修复 Vant 组件 `van-datetime-picker` 的全局注册问题 +- 分析并修复预算卡片金额与关联账单列表金额不一致问题 +- 添加后端和前端单元测试覆盖修复的场景 + +## Capabilities + +### New Capabilities + + +### Modified Capabilities +- `budget-stats`: 修复预算统计API响应缺失 `Description` 和 `Trend` 字段,确保前端能正确展示明细弹窗和燃尽图 +- `bill-management`: 修复账单删除功能的事件处理逻辑 +- `navigation`: 修复前端路由配置和底部导航栏跳转 + +## Impact + +**后端文件**: +- `Application/Dto/BudgetDto.cs` - 修改 `BudgetStatsDetail` 添加字段 +- `Application/BudgetApplication.cs` - 修改 DTO 映射逻辑 +- `WebApi.Test/` - 添加新的测试用例覆盖修复场景 + +**前端文件**: +- `Web/src/router/index.js` - 修复路由配置 +- `Web/src/components/Budget/BudgetChartAnalysis.vue` - 验证数据正确使用 +- `Web/src/components/Budget/BudgetCard.vue` - 分析账单金额不一致问题 +- `Web/src/main.ts` 或全局组件注册文件 - 修复 Vant 组件注册 +- 日历页面账单详情组件 - 修复删除按钮事件绑定 + +**API影响**: +- GET `/api/budget/stats/{category}` 响应结构变更(新增字段,向后兼容) + +**依赖**: +- 无外部依赖变更 diff --git a/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/bill-management/spec.md b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/bill-management/spec.md new file mode 100644 index 0000000..98f5f2b --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/bill-management/spec.md @@ -0,0 +1,40 @@ +## MODIFIED Requirements + +### Requirement: Bill deletion requires user confirmation +账单删除操作 MUST 要求用户显式确认,防止误操作导致数据丢失。删除确认对话框 SHALL 明确告知用户操作的不可逆性。 + +#### Scenario: User confirms bill deletion +- **WHEN** 用户在日历页面的账单详情弹窗中点击"删除"按钮 +- **THEN** 系统弹出确认对话框,标题为"确认删除" +- **AND** 对话框消息包含警告文本(如"确定要删除这条账单吗?此操作无法撤销。") +- **AND** 对话框提供"确认"和"取消"两个按钮 + +#### Scenario: User confirms deletion +- **WHEN** 用户在确认对话框中点击"确认"按钮 +- **THEN** 系统调用删除 API (`DELETE /api/bill/{id}`) +- **AND** 删除成功后关闭账单详情弹窗 +- **AND** 日历视图自动刷新,删除的账单不再显示 + +#### Scenario: User cancels deletion +- **WHEN** 用户在确认对话框中点击"取消"按钮 +- **THEN** 对话框关闭,账单详情弹窗保持打开状态 +- **AND** 账单未被删除 + +#### Scenario: Deletion fails due to server error +- **WHEN** 用户确认删除,但后端返回错误(如网络异常或 500 错误) +- **THEN** 系统显示错误提示(如"删除失败,请稍后重试") +- **AND** 账单详情弹窗保持打开状态 +- **AND** 账单仍存在于系统中 + +### Requirement: Delete button event binding must be functional +日历页面账单详情组件中的删除按钮 MUST 正确绑定点击事件处理函数,确保用户点击时能够触发删除流程。 + +#### Scenario: Delete button click triggers handler +- **WHEN** 账单详情弹窗渲染完成 +- **THEN** "删除"按钮的 `@click` 或 `onClick` 事件绑定到正确的处理函数(如 `handleDelete`) +- **AND** 点击按钮时控制台无 JavaScript 错误 + +#### Scenario: Button is not disabled during loading +- **WHEN** 账单详情弹窗首次加载时 +- **THEN** "删除"按钮的 `disabled` 属性为 `false`(除非账单正在删除中) +- **AND** 按钮可以正常响应点击事件 diff --git a/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/budget-stats/spec.md b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/budget-stats/spec.md new file mode 100644 index 0000000..be9b10a --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/budget-stats/spec.md @@ -0,0 +1,48 @@ +## MODIFIED Requirements + +### Requirement: Budget statistics API response includes complete data +预算统计 API (`GET /api/budget/stats/{category}`) SHALL 返回完整的统计数据,包括用于前端图表渲染的 `Trend` 数组和用于明细弹窗的 `Description` HTML 内容。 + +响应结构中的 `Month` 和 `Year` 对象 MUST 包含以下字段: +- `Limit`: 预算限额 +- `Current`: 当前实际金额 +- `Remaining`: 剩余金额 +- `UsagePercentage`: 使用百分比 +- `Trend`: 每日/每月累计金额数组 (`List`) +- `Description`: HTML 格式的详细说明,包含计算公式和数据表格 (`string`) + +#### Scenario: Monthly stats with trend data +- **WHEN** 客户端请求月度预算统计 `GET /api/budget/stats/food?date=2026-02` +- **THEN** 响应的 `month` 对象包含 `trend` 数组,长度等于该月天数(如28/29/30/31) +- **AND** `trend` 数组每个元素表示截至该天的累计金额(支出类为递减,收入类为递增) +- **AND** `trend` 数组中未到达的日期对应的元素为 `null` + +#### Scenario: Monthly stats with description +- **WHEN** 客户端请求月度预算统计 `GET /api/budget/stats/food?date=2026-02` +- **THEN** 响应的 `month` 对象包含 `description` 字段 +- **AND** `description` 是 HTML 格式字符串,包含 `` 标签展示明细数据 +- **AND** `description` 包含计算公式说明(如"剩余 = 限额 - 已用") + +#### Scenario: Yearly stats with trend data +- **WHEN** 客户端请求年度预算统计 `GET /api/budget/stats/salary?date=2026` +- **THEN** 响应的 `year` 对象包含 `trend` 数组,长度为12(代表12个月) +- **AND** `trend` 数组每个元素表示截至该月的累计金额 +- **AND** `trend` 数组中未到达的月份对应的元素为 `null` + +#### Scenario: Yearly stats with description +- **WHEN** 客户端请求年度预算统计 `GET /api/budget/stats/salary?date=2026` +- **THEN** 响应的 `year` 对象包含 `description` 字段 +- **AND** `description` 是 HTML 格式字符串,包含年度统计明细 + +### Requirement: DTO mapping preserves all Service layer data +Application 层的 `BudgetApplication.GetCategoryStatsAsync` 方法在将 Service 层的 `BudgetStatsDto` 映射到 API 响应 DTO 时,MUST 保留所有数据字段,不得丢失 `Trend` 和 `Description`。 + +#### Scenario: Mapping from Service DTO to API DTO +- **WHEN** `BudgetApplication.GetCategoryStatsAsync` 接收到 Service 层返回的 `BudgetStatsDto` +- **THEN** 映射后的 `BudgetStatsDetail` 对象包含 `Trend` 字段,其值等于 `BudgetStatsDto.Month.Trend` 或 `BudgetStatsDto.Year.Trend` +- **AND** 映射后的 `BudgetStatsDetail` 对象包含 `Description` 字段,其值等于 `BudgetStatsDto.Month.Description` 或 `BudgetStatsDto.Year.Description` + +#### Scenario: API response schema validation +- **WHEN** 前端调用 `/api/budget/stats/{category}` 并解析 JSON 响应 +- **THEN** TypeScript 类型检查不报错,响应对象符合 `BudgetStatsResponse` 接口定义 +- **AND** `month.trend` 和 `month.description` 字段存在且非 `undefined` diff --git a/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/navigation/spec.md b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/navigation/spec.md new file mode 100644 index 0000000..10a085f --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/specs/navigation/spec.md @@ -0,0 +1,37 @@ +## MODIFIED Requirements + +### Requirement: Bottom navigation statistics tab routes correctly +底部导航栏的"统计"标签 MUST 正确配置路由路径,点击后能够跳转到统计页面 (`/statistics-v2`)。 + +#### Scenario: User clicks statistics tab in bottom navigation +- **WHEN** 用户在应用底部导航栏点击"统计"图标或标签 +- **THEN** 浏览器 URL 变更为 `/statistics-v2` +- **AND** 页面渲染统计页面组件(如 `StatisticsV2.vue`) +- **AND** 底部导航栏的"统计"标签高亮显示为激活状态 + +#### Scenario: Direct URL access to statistics page +- **WHEN** 用户直接在浏览器地址栏输入 `/statistics-v2` 并访问 +- **THEN** 应用正确渲染统计页面 +- **AND** 底部导航栏的"统计"标签高亮显示为激活状态 + +#### Scenario: Navigation tab configuration matches route definition +- **WHEN** 前端代码加载时 +- **THEN** 底部导航组件(`van-tabbar` 或自定义组件)中"统计"标签的 `to` 或 `path` 属性值为 `/statistics-v2` +- **AND** 路由配置文件 (`router/index.js`) 中存在 `path: '/statistics-v2'` 的路由定义 + +### Requirement: Vant DatetimePicker component must be registered +Vant UI 库的 `van-datetime-picker` 组件 MUST 正确注册,以避免控制台出现 "Failed to resolve component" 警告。 + +#### Scenario: DatetimePicker used in application +- **WHEN** 应用中任何页面使用 `` 组件 +- **THEN** 组件正常渲染,无控制台错误或警告 +- **AND** 控制台不显示 "Failed to resolve component: van-datetime-picker" 消息 + +#### Scenario: Global component registration in main.ts +- **WHEN** 应用启动时执行 `main.ts` 或全局插件文件 +- **THEN** `DatetimePicker` 组件已通过 `app.use(DatetimePicker)` 全局注册 +- **OR** 在使用组件的文件中已通过 `import { DatetimePicker } from 'vant'` 和 `components: { VanDatetimePicker: DatetimePicker }` 局部注册 + +#### Scenario: No missing component warnings after fix +- **WHEN** 用户浏览应用的所有页面(日历、预算、统计等) +- **THEN** 浏览器开发者工具控制台中无任何 Vant 组件相关的警告或错误 diff --git a/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/tasks.md b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/tasks.md new file mode 100644 index 0000000..d1544cd --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-budget-and-ui-bugs/tasks.md @@ -0,0 +1,65 @@ +## 1. Backend: Fix Budget Stats DTO and Mapping (Bug #4 & #5 - High Priority) + +- [x] 1.1 在 `Application/Dto/BudgetDto.cs` 的 `BudgetStatsDetail` record 中添加 `Trend` 字段(`List`,使用 `init`) +- [x] 1.2 在 `Application/Dto/BudgetDto.cs` 的 `BudgetStatsDetail` record 中添加 `Description` 字段(`string`,使用 `init`) +- [x] 1.3 在 `Application/BudgetApplication.cs` 的 `GetCategoryStatsAsync` 方法中,映射 `Month` 对象时添加 `Trend = stats.Month.Trend` +- [x] 1.4 在 `Application/BudgetApplication.cs` 的 `GetCategoryStatsAsync` 方法中,映射 `Month` 对象时添加 `Description = stats.Month.Description` +- [x] 1.5 在 `Application/BudgetApplication.cs` 的 `GetCategoryStatsAsync` 方法中,映射 `Year` 对象时添加 `Trend = stats.Year.Trend` +- [x] 1.6 在 `Application/BudgetApplication.cs` 的 `GetCategoryStatsAsync` 方法中,映射 `Year` 对象时添加 `Description = stats.Year.Description` + +## 2. Backend: Add Unit Tests for DTO Mapping + +- [x] 2.1 在 `WebApi.Test/` 中创建 `BudgetApplicationTests.cs` 测试类(如果不存在) +- [x] 2.2 编写测试用例 `GetCategoryStatsAsync_Should_Include_Trend_And_Description_In_Month_Stats` +- [x] 2.3 编写测试用例 `GetCategoryStatsAsync_Should_Include_Trend_And_Description_In_Year_Stats` +- [x] 2.4 运行测试并验证通过:`dotnet test --filter "FullyQualifiedName~BudgetApplicationTests"` + +## 3. Frontend: Fix Navigation Routes (Bug #1) + +- [x] 3.1 使用 `grep` 搜索底部导航组件代码(搜索关键字 `van-tabbar` 或 `统计`) +- [x] 3.2 定位"统计"标签的路由配置(检查 `to` 或 `path` 属性) +- [x] 3.3 修改路由路径为 `/statistics-v2` +- [x] 3.4 验证路由配置文件 `Web/src/router/index.js` 中存在 `/statistics-v2` 路由定义 + +## 4. Frontend: Fix Bill Deletion Function (Bug #2) + +- [x] 4.1 使用 `grep` 搜索日历页面的账单详情组件(搜索关键字 `删除` 或 `delete`) +- [x] 4.2 定位删除按钮的点击事件绑定(检查 `@click` 或 `onClick`) +- [x] 4.3 实现 `handleDelete` 函数,使用 `showConfirmDialog` 显示确认对话框 +- [x] 4.4 在确认后调用删除 API 并关闭弹窗,刷新日历视图 +- [x] 4.5 在取消时关闭对话框但保持弹窗打开 +- [x] 4.6 处理删除失败场景,显示错误提示 + +## 5. Frontend: Fix Vant DatetimePicker Registration (Bug #3) + +- [x] 5.1 检查 `Web/src/main.ts` 或全局组件注册文件 +- [x] 5.2 验证是否导入 `DatetimePicker`(`import { DatetimePicker } from 'vant'`) +- [x] 5.3 如果缺失,添加全局注册 `app.use(DatetimePicker)` +- [ ] 5.4 启动前端开发服务器,验证控制台无 "Failed to resolve component" 警告 + +## 6. Frontend: Verify Budget Chart Renders Correctly After Backend Fix + +- [ ] 6.1 启动后端和前端服务 +- [ ] 6.2 打开预算页面,点击"使用情况"或"完成情况"旁的感叹号图标 +- [ ] 6.3 验证明细弹窗显示完整的 HTML 表格(非"暂无数据") +- [ ] 6.4 验证燃尽图显示波动曲线(非直线) +- [x] 6.5 检查前端 `BudgetChartAnalysis.vue:603` 和 `:629` 行的 fallback 逻辑是否仍触发(如需修改条件检查) + +## 7. Investigation: Budget Card Amount Mismatch (Bug #6 - Low Priority) + +- [ ] 7.1 在测试环境中打开预算页面,点击预算卡片的"查询关联账单"按钮 +- [ ] 7.2 对比预算卡片显示的"实际"金额与账单列表金额总和 +- [ ] 7.3 检查不一致的预算是否标记为硬性预算(📌) +- [ ] 7.4 如果是硬性预算,验证虚拟消耗的计算逻辑(`BudgetService.cs:376-405`) +- [ ] 7.5 检查 `BudgetResult` 中 `PeriodStart` 和 `PeriodEnd` 的赋值是否与 `GetPeriodRange` 一致 +- [ ] 7.6 如果是虚拟消耗导致,考虑在前端账单列表中添加提示说明(可选) +- [ ] 7.7 如果是日期范围问题,修复 `BudgetResult` 的赋值逻辑 + +## 8. End-to-End Verification + +- [x] 8.1 运行后端所有测试:`dotnet test` +- [x] 8.2 运行前端 lint:`cd Web && pnpm lint` +- [x] 8.3 构建前端:`cd Web && pnpm build` +- [ ] 8.4 手动测试所有修复的 bug(按 bug-handoff-document.md 中的验证清单) +- [ ] 8.5 清除浏览器缓存并重新测试 +- [ ] 8.6 验证控制台无错误或警告 diff --git a/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/.openspec.yaml b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/.openspec.yaml new file mode 100644 index 0000000..c8d3976 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-17 diff --git a/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/design.md b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/design.md new file mode 100644 index 0000000..67e3169 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/design.md @@ -0,0 +1,87 @@ +## Context + +EmailBill 项目已从 ECharts 迁移到 Chart.js 作为图表库。在迁移过程中,图表文本显示出现了乱码问题,特别是在显示中文标签、Tooltip 和中心文本时。这可能是由于: + +1. Chart.js 默认字体配置不支持中文 +2. Tooltip 回调函数返回的字符串编码问题 +3. 主题切换时字体颜色对比度不足 + +当前图表组件位于 `Web/src/components/Charts/`,使用 `BaseChart.vue` 作为统一包装组件。 + +## Goals / Non-Goals + +**Goals:** +- 修复所有图表(饼图、折线图、仪表盘)的中文乱码问题 +- 确保图表文本在明/暗主题下都清晰可读 +- 统一字体配置,支持跨平台中文显示 +- 修复 Tooltip 和 Label 的文本格式化问题 + +**Non-Goals:** +- 不修改图表的数据结构或业务逻辑 +- 不添加新的图表类型 +- 不进行 UI 样式的大幅度调整(仅修复文本显示问题) + +## Decisions + +### Decision 1: 字体配置方案 +**选择**: 在 `useChartTheme.ts` 中统一配置 Chart.js 字体选项 + +**理由**: +- Chart.js 支持全局字体配置,通过 `defaults.font.family` 可以一次性设置所有图表的字体 +- 使用系统字体栈确保跨平台兼容性:`'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif` +- 避免在每个组件中重复配置 + +**替代方案考虑**: +- 在每个图表组件中单独配置字体 → 重复代码,维护困难 +- 使用 WebFont 加载自定义字体 → 增加外部依赖,加载时间不可控 + +### Decision 2: Tooltip 格式化修复方案 +**选择**: 修复 `callbacks.label` 和 `callbacks.title` 回调函数,确保返回正确的字符串 + +**理由**: +- Chart.js 的 Tooltip 回调函数必须返回字符串,不能返回对象或其他类型 +- 使用模板字符串确保正确的字符串拼接 +- 添加空值检查防止 undefined 导致的乱码 + +**代码示例**: +```typescript +tooltip: { + callbacks: { + label: (context) => { + const value = context.parsed.y || context.parsed + return `¥${value.toFixed(2)}` + } + } +} +``` + +### Decision 3: 中心文本显示方案 +**选择**: 继续使用 CSS 绝对定位的覆盖层显示中心文本 + +**理由**: +- Chart.js 本身不支持在 Doughnut 图表中心直接渲染文本 +- CSS 覆盖层方式简单可靠,易于控制字体样式 +- 确保覆盖层使用正确的字体族和颜色 + +**实现要点**: +- 覆盖层容器设置 `font-family` 继承自主题配置 +- 使用 `var(--van-text-color)` 确保主题适配 +- 添加 `white-space: nowrap` 防止文本换行导致错位 + +## Risks / Trade-offs + +| Risk | Mitigation | +|------|------------| +| 某些旧版浏览器可能不支持系统字体栈 | 提供后备字体(Arial, sans-serif)确保基本可读性 | +| 暗色模式下文本颜色对比度不足 | 使用 Vant 主题变量确保颜色适配 | +| 字体文件过大影响加载性能 | 使用系统字体,不加载外部字体文件 | +| 修改全局配置可能影响其他组件 | 在 `useChartTheme` 中集中管理,便于回滚 | + +## Migration Plan + +1. **阶段 1**: 修改 `useChartTheme.ts` 添加全局字体配置 +2. **阶段 2**: 检查并修复各图表组件的 Tooltip 回调函数 +3. **阶段 3**: 验证所有图表页面的文本显示 +4. **阶段 4**: 在明/暗主题下分别测试 + +**Rollback Strategy**: 所有修改都是配置层面的,可以通过回滚 Git 提交快速恢复。 diff --git a/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/proposal.md b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/proposal.md new file mode 100644 index 0000000..eba107a --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/proposal.md @@ -0,0 +1,25 @@ +## Why + +在浏览器中查看图表时,饼图、折线图等图表上出现了错乱的字符串显示问题,影响数据可读性和用户体验。这可能是由于 Chart.js 配置中的字体设置、编码问题或 tooltip/label 格式化错误导致的。 + +## What Changes + +- 修复饼图、折线图、仪表盘等图表上的错乱字符串显示 +- 检查并修正图表字体配置,确保使用中文字体或兼容字体 +- 修复 tooltip 和 label 的格式化回调函数 +- 确保图表文本在各种主题(明/暗色模式)下正确显示 + +## Capabilities + +### New Capabilities +- `chart-text-encoding-fix`: 修复图表文本编码和字体配置,确保中文和特殊字符正确显示 + +### Modified Capabilities +- `chart-migration-patterns`: 更新图表迁移模式中的文本渲染配置,确保 Chart.js 图表文本正确显示 + +## Impact + +- **前端组件**: `Web/src/components/Charts/` 下的所有图表组件 +- **配置文件**: 图表主题配置文件 `useChartTheme.ts` +- **工具函数**: 图表辅助函数 `chartHelpers.ts` +- **页面**: 统计页面、预算页面等使用图表的页面 diff --git a/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/specs/chart-migration-patterns/spec.md b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/specs/chart-migration-patterns/spec.md new file mode 100644 index 0000000..60c597e --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/specs/chart-migration-patterns/spec.md @@ -0,0 +1,129 @@ +## MODIFIED Requirements + +### Requirement: 仪表盘图表迁移模式 +组件 SHALL 使用 Chart.js Doughnut 图表实现仪表盘(Gauge)效果,替代 ECharts Gauge 图表。 + +#### Scenario: 半圆仪表盘渲染 +- **WHEN** 组件接收预算统计数据(current, limit) +- **THEN** 系统使用 Doughnut 图表渲染半圆进度条,配置 `rotation: -90` 和 `circumference: 180` +- **AND** 图表字体配置 SHALL 包含中文字体支持 + +#### Scenario: 中心文本叠加显示 +- **WHEN** 仪表盘图表渲染完成 +- **THEN** 系统在图表中心显示余额/超支文本,使用 CSS 绝对定位的覆盖层 +- **AND** 中心文本 SHALL 正确显示中文,无乱码 + +#### Scenario: 动态颜色切换 +- **WHEN** 实际值超过预算限额 +- **THEN** 进度条颜色切换为危险色(`var(--van-danger-color)`),中心文本显示"超支" +- **AND** "超支"文字 SHALL 清晰可读 + +#### Scenario: 暗色模式适配 +- **WHEN** 用户切换到暗色主题 +- **THEN** 图表颜色自动适配,使用 `useChartTheme` composable 获取主题色 +- **AND** 文本颜色 SHALL 与背景有足够对比度 + +### Requirement: 折线图迁移模式 +组件 SHALL 使用 Chart.js Line 图表实现趋势折线图,替代 ECharts Line 图表。 + +#### Scenario: 单系列折线图渲染 +- **WHEN** 组件接收月度支出数据(日期 + 金额数组) +- **THEN** 系统渲染折线图,X 轴为日期标签,Y 轴为金额,使用渐变填充 +- **AND** X 轴日期标签 SHALL 正确显示,无乱码 + +#### Scenario: 双系列折线图渲染 +- **WHEN** 组件接收收支数据(包含收入和支出两个系列) +- **THEN** 系统渲染两条折线,支出为红色,收入为绿色,支持独立的 hover 交互 +- **AND** 图例 SHALL 正确显示"收入"和"支出"中文标签 + +#### Scenario: 空数据处理 +- **WHEN** 图表数据为空或所有数据点为 0 +- **THEN** 系统显示 `` 空状态组件,而非空白图表 + +#### Scenario: Tooltip 格式化 +- **WHEN** 用户 hover 到数据点 +- **THEN** Tooltip 显示"¥XXX.XX"格式的金额,使用 `callbacks.label` 自定义 +- **AND** Tooltip 内容 SHALL 正确编码,无乱码 + +### Requirement: 饼图/环形图迁移模式 +组件 SHALL 使用 Chart.js Doughnut 图表实现分类统计环形图,替代 ECharts Pie 图表。 + +#### Scenario: 环形图渲染 +- **WHEN** 组件接收分类统计数据(分类名称 + 金额数组) +- **THEN** 系统渲染环形图,每个分类使用不同颜色,配置 `cutout: '50%'` +- **AND** 分类标签 SHALL 正确显示中文名称 + +#### Scenario: 分类颜色映射 +- **WHEN** 分类数据包含预定义颜色 +- **THEN** 图表使用 props 传入的颜色数组,确保与列表中的分类色块一致 + +#### Scenario: 小分类合并 +- **WHEN** 分类数量超过 10 个 +- **THEN** 系统使用 `mergeSmallCategories()` 工具函数,将占比小于 5% 的分类合并为"其他" +- **AND** "其他"标签 SHALL 正确显示 + +#### Scenario: 点击分类跳转 +- **WHEN** 用户点击环形图扇区 +- **THEN** 系统触发 `@category-click` 事件,传递分类名称和类型 + +### Requirement: BaseChart 组件统一使用 +所有图表组件 SHALL 使用 `BaseChart.vue` 包装组件,而非直接使用 vue-chartjs 组件。 + +#### Scenario: BaseChart 组件使用 +- **WHEN** 组件需要渲染图表 +- **THEN** 使用 `` +- **AND** 通过 options 传入正确的字体配置 + +#### Scenario: Loading 状态处理 +- **WHEN** 图表数据加载中 +- **THEN** BaseChart 显示 `` 组件 + +#### Scenario: 图表渲染回调 +- **WHEN** 图表渲染完成 +- **THEN** BaseChart 触发 `@chart:render` 事件,传递 Chart.js 实例引用 + +### Requirement: ECharts 代码完全移除 +组件 SHALL 移除所有 ECharts 相关代码,包括导入语句、实例变量、环境变量判断。 + +#### Scenario: 移除 ECharts 导入 +- **WHEN** 迁移组件 +- **THEN** 删除 `import * as echarts from 'echarts'` 语句 + +#### Scenario: 移除环境变量开关 +- **WHEN** 迁移组件 +- **THEN** 删除 `const useChartJS = import.meta.env.VITE_USE_CHARTJS === 'true'` 和相关的 `v-if`/`v-else` 条件渲染 + +#### Scenario: 移除 ECharts 实例管理 +- **WHEN** 迁移组件 +- **THEN** 删除 `let chartInstance = null`、`echarts.init()`、`chartInstance.setOption()` 等代码 + +#### Scenario: 移除生命周期清理 +- **WHEN** 迁移组件 +- **THEN** 删除 `onBeforeUnmount()` 中的 `chartInstance.dispose()` 调用 + +### Requirement: 测试覆盖 +迁移后的组件 SHALL 通过白盒和黑盒测试验证功能正确性。 + +#### Scenario: 单元测试 - 组件挂载 +- **WHEN** 运行 Jest 单元测试 +- **THEN** 组件能够成功挂载,不抛出错误 + +#### Scenario: 单元测试 - Props 传递 +- **WHEN** 传入测试数据 props +- **THEN** 计算属性 `chartData` 和 `chartOptions` 返回正确的 Chart.js 配置对象 +- **AND** options 中 SHALL 包含正确的字体配置 + +#### Scenario: E2E 测试 - 图表渲染 +- **WHEN** 运行 Playwright E2E 测试 +- **THEN** 浏览器中能看到图表元素(Canvas),且无控制台错误 +- **AND** 图表文本 SHALL 正确显示,无乱码 + +#### Scenario: E2E 测试 - 用户交互 +- **WHEN** 用户 hover 到图表数据点 +- **THEN** Tooltip 正确显示,格式化后的金额信息可见 +- **AND** Tooltip 内容 SHALL 无乱码 + +#### Scenario: 视觉回归测试 +- **WHEN** 截图对比迁移前后的图表 +- **THEN** 颜色、布局、字体大小差异在可接受范围内(像素差异 < 5%) +- **AND** 中文文本 SHALL 清晰可读 diff --git a/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/specs/chart-text-encoding-fix/spec.md b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/specs/chart-text-encoding-fix/spec.md new file mode 100644 index 0000000..e9aa4b8 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/specs/chart-text-encoding-fix/spec.md @@ -0,0 +1,44 @@ +## ADDED Requirements + +### Requirement: 图表文本编码修复 +Chart.js 图表 SHALL 正确显示中文文本,不出现乱码或异常字符。 + +#### Scenario: 饼图标签中文显示 +- **WHEN** 系统渲染支出分类饼图,分类名称为中文 +- **THEN** 分类标签 SHALL 正确显示中文字符 +- **AND** 标签文字 SHALL 清晰可读,无乱码 + +#### Scenario: Tooltip 中文显示 +- **WHEN** 用户 hover 到图表数据点 +- **THEN** Tooltip SHALL 正确显示中文内容 +- **AND** 金额和分类名称 SHALL 无乱码 + +#### Scenario: 中心文本中文显示 +- **WHEN** 仪表盘图表渲染中心文本 +- **THEN** 中心显示的余额/超支文本 SHALL 正确显示中文 +- **AND** 文字 SHALL 清晰无乱码 + +### Requirement: 图表字体配置 +Chart.js 配置 SHALL 使用兼容的字体设置,确保跨平台文本正确渲染。 + +#### Scenario: 字体族配置 +- **WHEN** 图表初始化时 +- **THEN** 系统 SHALL 配置 `font.family` 为兼容中文字体的字体栈(如 `'PingFang SC', 'Microsoft YaHei', sans-serif`) + +#### Scenario: 响应式字体大小 +- **WHEN** 图表在不同尺寸屏幕上渲染 +- **THEN** 字体大小 SHALL 根据屏幕尺寸自动调整 +- **AND** 文字 SHALL 始终保持清晰可读 + +### Requirement: Tooltip 格式化修复 +Tooltip 回调函数 SHALL 正确处理文本编码和格式化。 + +#### Scenario: Tooltip Label 格式化 +- **WHEN** Tooltip 显示数据标签 +- **THEN** 回调函数 SHALL 返回正确编码的字符串 +- **AND** 特殊字符 SHALL 正确转义 + +#### Scenario: 金额格式化显示 +- **WHEN** Tooltip 显示金额 +- **THEN** 金额格式 SHALL 为 "¥XXX.XX" +- **AND** 货币符号 SHALL 正确显示 diff --git a/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/tasks.md b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/tasks.md new file mode 100644 index 0000000..b82fac1 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-chart-garbled-text/tasks.md @@ -0,0 +1,43 @@ +## 1. 全局字体配置 + +- [x] 1.1 修改 `useChartTheme.ts`,添加 Chart.js 全局字体配置 +- [x] 1.2 配置字体栈支持中文显示:`'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif` +- [x] 1.3 设置默认字体大小和颜色变量 + +## 2. Tooltip 格式化修复 + +- [x] 2.1 检查饼图组件的 Tooltip 回调函数,修复编码问题 +- [x] 2.2 检查折线图组件的 Tooltip 回调函数,确保金额格式正确 +- [x] 2.3 检查仪表盘组件的 Tooltip 配置 +- [x] 2.4 确保所有 Tooltip 回调返回正确的字符串类型 + +## 3. 图表组件文本修复 + +- [x] 3.1 修复饼图分类标签的中文显示 +- [x] 3.2 修复折线图 X 轴日期标签显示 +- [x] 3.3 修复仪表盘中心文本(余额/超支)的中文显示 +- [x] 3.4 确保图例(Legend)中文标签正确显示 + +## 4. 主题适配 + +- [x] 4.1 验证明色模式下图表文本清晰可读 +- [x] 4.2 验证暗色模式下图表文本颜色和对比度 +- [x] 4.3 修复主题切换时可能出现的文本渲染问题 + +## 7. 修复密集数字显示 + +- [x] 7.1 禁用折线图的数据标签(datalabels) +- [x] 7.2 验证明暗模式下图表显示正常 + +## 5. 测试验证 + +- [x] 5.1 在 Chrome 浏览器中验证所有图表文本显示 +- [x] 5.2 在移动端浏览器中验证图表文本显示 +- [x] 5.3 验证 Tooltip hover 时文本无乱码 +- [x] 5.4 检查控制台是否有相关错误日志 + +## 6. 代码审查 + +- [x] 6.1 运行 `pnpm lint` 检查代码格式 +- [x] 6.2 运行 `pnpm build` 确保构建成功 +- [x] 6.3 检查是否有未使用的导入或变量 diff --git a/openspec/changes/archive/2026-02-17-fix-january-2026-budget-usage/.openspec.yaml b/openspec/changes/archive/2026-02-17-fix-january-2026-budget-usage/.openspec.yaml new file mode 100644 index 0000000..85f3b5a --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-january-2026-budget-usage/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-14 diff --git a/openspec/changes/archive/2026-02-17-fix-january-2026-budget-usage/proposal.md b/openspec/changes/archive/2026-02-17-fix-january-2026-budget-usage/proposal.md new file mode 100644 index 0000000..fbc4a41 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-january-2026-budget-usage/proposal.md @@ -0,0 +1,24 @@ +## Why + +用户在 2026 年 2 月份查看 2026 年 1 月份的月度预算统计时,页面显示"超支170元",但用户认为应该远远大于这个值。经检查发现,页面显示的"超支170"仅计算了月度支出预算(Type=1)的超支,未包含年度支出预算(Type=2)在该月的实际支出。1 月份实际总支出为 36,130.40 元,但页面只显示了 23,284.40 元(月度支出预算),缺少了约 12,846 元的年度支出预算部分。 + +## What Changes + +- **修改月度预算统计逻辑**:在 `CalculateMonthlyCategoryStatsAsync` 方法中,统计支出时需要包含年度支出预算(Type=2)在该月的实际支出金额 +- **修改数据源逻辑**:`GetAllBudgetsWithArchiveAsync` 方法在获取月度预算数据时,需要同时获取年度支出预算在该月的实际支出 +- **确保归档数据正确使用**:使用归档数据中的 `Actual` 值,而非重新计算 + +## Capabilities + +### New Capabilities +无新能力引入,仅修复现有逻辑。 + +### Modified Capabilities +- `budget-stats`: 修改月度预算统计的需求,要求月度支出统计包含所有类型的实际支出(月度+年度支出预算在该月的支出) + +## Impact + +- **受影响代码**:`Service/Budget/BudgetStatsService.cs` 中的 `CalculateMonthlyCategoryStatsAsync` 和 `GetAllBudgetsWithArchiveAsync` 方法 +- **受影响 API**:预算统计相关的 API 接口(前端调用的获取预算统计信息的接口) +- **数据来源**:`BudgetArchive` 表中的归档数据,需要正确使用归档的 `Actual` 值 +- **用户体验**:修复后,用户查看月度预算统计时,将看到包含所有实际支出的准确数据 diff --git a/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/.openspec.yaml b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/.openspec.yaml new file mode 100644 index 0000000..a5571c1 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-16 diff --git a/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/design.md b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/design.md new file mode 100644 index 0000000..d2f0dcc --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/design.md @@ -0,0 +1,173 @@ +--- +title: 图表样式优化设计文档 +author: AI Assistant +date: 2026-02-16 +status: draft +--- + +## Context + +### 当前状态 +应用中的图表存在以下样式问题: + +1. **支出分类饼图 (ExpenseCategoryCard.vue)** + - 问题:显示了不应该有的坐标轴(X轴 0-7,Y轴 ¥0-¥1) + - 原因:`useChartTheme.ts` 中的基础配置默认包含坐标轴,饼图配置未覆盖 + +2. **使用情况仪表盘 (BudgetChartAnalysis.vue)** + - 问题:仪表盘周围有坐标轴和网格线干扰 + - 影响:视觉上显得混乱,不够简洁 + +3. **预算进度燃尽图** + - 现状:样式基本可用,但缺乏现代感 + - 可优化:颜色对比度、网格线样式、动画效果 + +### 技术背景 +- 使用 Chart.js 4.5+ 和 vue-chartjs 5.3+ +- 主题配置通过 `useChartTheme.ts` 统一管理 +- 支持 Vant UI 的暗色模式 + +## Goals / Non-Goals + +**Goals:** +1. 移除所有饼图/环形图/仪表盘的不必要坐标轴 +2. 统一图表的视觉风格,符合 Vant 设计系统 +3. 优化配色方案,提高可读性和美观度 +4. 增强动画效果,提升交互体验 +5. 确保暗色模式下的显示效果 + +**Non-Goals:** +- 不添加新的图表类型 +- 不修改业务逻辑或数据结构 +- 不改变现有的 API 接口 + +## Decisions + +### Decision 1: 图表类型感知配置 + +**选择**: 修改 `useChartTheme.ts`,使其根据图表类型自动调整默认配置 + +**理由**: +- 饼图/环形图/仪表盘不需要坐标轴 +- 折线图/柱状图需要坐标轴但可简化 +- 减少每个组件手动覆盖配置的重复工作 + +**实现**: +```typescript +// 新增根据图表类型获取配置的方法 +const getChartOptionsByType = (type: 'line' | 'bar' | 'pie' | 'doughnut', customOptions = {}) => { + const baseOptions = baseChartOptions.value + + // 无坐标轴图表类型 + if (['pie', 'doughnut'].includes(type)) { + return mergeDeep(baseOptions, { + scales: { x: { display: false }, y: { display: false } } + }, customOptions) + } + + return mergeDeep(baseOptions, customOptions) +} +``` + +**替代方案**: 在每个使用饼图的组件中手动添加 `scales: { x: { display: false }, y: { display: false } }` +- **排除原因**: 重复代码多,容易遗漏 + +### Decision 2: 简化坐标轴样式 + +**选择**: 对于需要坐标轴的图表,采用极简风格 + +**具体措施**: +- 网格线:使用极淡的颜色 (`--van-border-color` 30% 透明度) +- 刻度标签:减小字体大小至 10px +- 移除坐标轴边框 (`drawBorder: false`) + +**理由**: +- 减少视觉噪音,突出数据本身 +- 移动设备上更清晰的阅读体验 + +### Decision 3: 优化配色方案 + +**选择**: 使用更现代、和谐的颜色方案 + +**具体措施**: +1. **主色调扩展**: + - 保留 Vant 主题色作为基础 + - 添加柔和的辅助色(降低饱和度) + +2. **饼图/环形图**: + - 使用 8 色渐进色板 + - 颜色从 Vant 主题派生但降低饱和度 20% + +3. **折线图/柱状图**: + - 支出:暖色调(橙红系) + - 收入:冷色调(青绿系) + - 对比度符合 WCAG AA 标准 + +**理由**: +- 更符合现代移动端 UI 审美 +- 色盲友好 + +### Decision 4: 增强交互体验 + +**选择**: 添加微妙的悬停和点击效果 + +**具体措施**: +1. **悬停效果**: + - 饼图扇区:`hoverOffset: 8`(从 4 增加) + - 折线点:`pointHoverRadius: 6`(从 4 增加) + +2. **动画优化**: + - 持续时间:750ms → 600ms(更快响应) + - 缓动函数:`easeInOutQuart` → `easeOutQuart`(更自然的结束) + +3. **触控优化**: + - 增加触控目标大小 + - 支持捏合缩放(对于趋势图) + +## Risks / Trade-offs + +**风险 1**: 颜色变更可能影响用户习惯 +- **影响**: 低 - 纯视觉变化 +- **缓解**: 保持色相大致不变,只调整饱和度和明度 + +**风险 2**: 移除坐标轴可能降低某些图表的可读性 +- **影响**: 中 - 对于复杂数据集 +- **缓解**: 保留关键刻度,仅淡化网格线 + +**风险 3**: 动画增强可能影响低性能设备 +- **影响**: 低 - 已考虑 `prefers-reduced-motion` +- **缓解**: 动画持续时间控制在 600ms 以内 + +## Migration Plan + +### 实施顺序 +1. **Phase 1**: 修复坐标轴问题(最高优先级) + - 修改 `useChartTheme.ts` + - 更新 `ExpenseCategoryCard.vue` + - 更新 `BudgetChartAnalysis.vue` + +2. **Phase 2**: 配色优化 + - 更新图表色板 + - 调整渐变效果 + +3. **Phase 3**: 动画和交互增强 + - 优化悬停效果 + - 添加触控支持 + +### 回滚策略 +- 所有变更都是样式层面的 +- 可通过 git revert 回滚 +- 建议分步提交,便于部分回滚 + +## Open Questions + +1. 是否需要提供图表主题切换开关(明亮/暗黑/高对比度)? +2. 预算页面的仪表盘是否需要添加中心数值显示? +3. 是否需要支持图表数据的导出功能? + +## 附录 + +### 参考资源 +- [Vant Design 色彩系统](https://vant-ui.github.io/vant/#/zh-CN/design-color) +- [Chart.js 配置文档](https://www.chartjs.org/docs/latest/configuration/) +- [WCAG 颜色对比度指南](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) diff --git a/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/proposal.md b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/proposal.md new file mode 100644 index 0000000..8ec7058 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/proposal.md @@ -0,0 +1,67 @@ +--- +title: 图表样式全面优化 +author: AI Assistant +date: 2026-02-16 +status: draft +category: UI/UX +--- + +## Why + +当前应用中统计页面和预算页面的图表样式存在明显问题: +1. **支出分类饼图**出现了不应该有的坐标轴(X轴0-7,Y轴¥0-¥1),严重干扰视觉 +2. **使用情况仪表盘**周围也有坐标轴干扰,影响美观 +3. 所有图表整体样式缺乏现代感,显得凌乱 + +为提升用户体验和视觉品质,需要对应用内所有图表进行全面样式优化。 + +## What Changes + +### 统计页面 (Statistics) +- **修复** 支出分类饼图的坐标轴问题,移除所有不必要的坐标轴和网格线 +- **优化** 收入/支出趋势图的样式,使其更简洁清晰 +- **统一** 图表配色方案,与 Vant 设计系统保持一致 +- **改进** 图表响应式布局和触控交互体验 + +### 预算页面 (Budget) +- **修复** 使用情况仪表盘(月度/年度)的坐标轴干扰 +- **优化** 预算进度燃尽图的视觉层次和颜色对比度 +- **美化** 偏差分析图表的数据展示形式 +- **统一** 图表组件的圆角、阴影等视觉元素 + +### 通用改进 +- 更新 Chart.js 全局配置,移除默认坐标轴样式 +- 为暗色模式优化图表颜色 +- 添加平滑的动画过渡效果 +- 确保所有图表在移动设备上的可读性 + +## Capabilities + +### New Capabilities +- `chart-theme-system`: 统一的图表主题系统,支持明暗模式切换和主题色自动适配 +- `responsive-chart-layout`: 响应式图表布局组件,自动适配不同屏幕尺寸 + +### Modified Capabilities +- 无现有 spec 需要修改(本次主要是样式优化,不涉及功能需求变更) + +## Impact + +**受影响文件**: +- `Web/src/components/Charts/BaseChart.vue` +- `Web/src/composables/useChartTheme.ts` +- `Web/src/views/StatisticsView.vue` +- `Web/src/views/BudgetView.vue` +- `Web/src/utils/chartHelpers.ts` + +**依赖**: +- Chart.js 4.5+ +- vue-chartjs 5.3+ +- Vant UI 主题系统 + +**风险**: +- 低 - 纯样式变更,不影响业务逻辑 +- 需验证所有图表在暗色模式下的可读性 + +## 更新日志 + +- 2026-02-16: 创建提案,定义图表优化范围 diff --git a/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/specs/chart-theme-system/spec.md b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/specs/chart-theme-system/spec.md new file mode 100644 index 0000000..24619aa --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/specs/chart-theme-system/spec.md @@ -0,0 +1,70 @@ +--- +title: 图表主题系统规格 +author: AI Assistant +date: 2026-02-16 +--- + +## ADDED Requirements + +### Requirement: 图表类型感知配置 +`useChartTheme` 组合式函数 SHALL 根据图表类型自动提供合适的默认配置。 + +#### Scenario: 饼图/环形图自动隐藏坐标轴 +- **WHEN** 调用 `getChartOptionsByType('doughnut', customOptions)` +- **THEN** 返回的配置中 `scales.x.display` 和 `scales.y.display` 均为 `false` +- **AND** 返回的配置 SHALL 与 customOptions 深度合并 + +#### Scenario: 折线图/柱状图保留简化坐标轴 +- **WHEN** 调用 `getChartOptionsByType('line', customOptions)` +- **THEN** 返回的配置包含简化的坐标轴样式 +- **AND** 网格线使用 `--van-border-color` 30% 透明度 +- **AND** 刻度标签字体大小为 10px + +### Requirement: 现代化配色方案 +图表主题系统 SHALL 提供符合现代审美的配色方案。 + +#### Scenario: 主色板包含 8 个颜色 +- **WHEN** 访问 `chartPalette` +- **THEN** 返回包含 8 个颜色的数组 +- **AND** 颜色 SHALL 从 Vant 主题色派生并降低 20% 饱和度 + +#### Scenario: 支出/收入颜色区分 +- **WHEN** 配置支出相关图表 +- **THEN** 默认使用暖色调(橙红系) +- **WHEN** 配置收入相关图表 +- **THEN** 默认使用冷色调(青绿系) + +### Requirement: 暗色模式适配 +图表 SHALL 自动适配 Vant UI 的暗色模式。 + +#### Scenario: 暗色模式颜色切换 +- **WHEN** Vant 主题切换为暗色模式 +- **THEN** 图表文本颜色 SHALL 自动变为浅色 +- **AND** 图表背景色 SHALL 与卡片背景一致 +- **AND** 网格线颜色 SHALL 变为深色系的边框色 + +#### Scenario: 手动颜色获取 +- **WHEN** 调用 `colors.text` +- **THEN** 返回当前主题的文本颜色 CSS 变量值 +- **AND** SHALL 实时响应主题切换 + +### Requirement: 动画配置 +图表 SHALL 支持可配置的动画效果。 + +#### Scenario: 默认动画配置 +- **WHEN** 获取图表配置 +- **THEN** 默认动画持续时间为 600ms +- **AND** 缓动函数为 `easeOutQuart` + +#### Scenario: 减少动画偏好 +- **WHEN** 用户系统偏好 `prefers-reduced-motion: reduce` +- **THEN** 动画持续时间 SHALL 自动设为 0 +- **AND** 图表 SHALL 立即渲染完成 + +## MODIFIED Requirements + +无修改的现有需求。 + +## REMOVED Requirements + +无删除的需求。 diff --git a/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/specs/responsive-chart-layout/spec.md b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/specs/responsive-chart-layout/spec.md new file mode 100644 index 0000000..0eb571e --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/specs/responsive-chart-layout/spec.md @@ -0,0 +1,63 @@ +--- +title: 响应式图表布局规格 +author: AI Assistant +date: 2026-02-16 +--- + +## ADDED Requirements + +### Requirement: 容器自适应 +BaseChart 组件 SHALL 自动适应父容器大小。 + +#### Scenario: 容器大小变化 +- **WHEN** 父容器大小发生变化 +- **THEN** 图表 SHALL 自动调整尺寸 +- **AND** 使用 ResizeObserver 进行监听 +- **AND** 图表 SHALL 保持比例不失真 + +#### Scenario: 横竖屏切换 +- **WHEN** 移动设备从竖屏切换到横屏 +- **THEN** 图表 SHALL 在 300ms 内完成重绘 +- **AND** 所有元素 SHALL 保持可读性 + +### Requirement: 触控交互优化 +图表 SHALL 针对移动设备触控操作进行优化。 + +#### Scenario: 悬停效果增强 +- **WHEN** 用户悬停/触摸饼图扇区 +- **THEN** `hoverOffset` SHALL 为 8px(比默认值大) +- **AND** 过渡动画 SHALL 流畅自然 + +#### Scenario: 折线图点触控 +- **WHEN** 用户触摸折线图数据点 +- **THEN** 点的 `pointHoverRadius` SHALL 为 6px +- **AND** 触控目标 SHALL 足够大(最小 44px) + +### Requirement: 空状态处理 +图表组件 SHALL 优雅处理空数据情况。 + +#### Scenario: 无数据时显示空状态 +- **WHEN` 传入的数据为空数组或 datasets 为空 +- **THEN** 显示 VanEmpty 组件 +- **AND** 显示文案 "暂无数据" + +#### Scenario: 加载状态 +- **WHEN** `loading` prop 为 true +- **THEN** 显示 VanLoading 组件 +- **AND** 显示文案 "加载中..." + +### Requirement: 最小高度限制 +图表容器 SHALL 有最小高度限制以确保可读性。 + +#### Scenario: 小容器适配 +- **WHEN** 父容器高度小于 200px +- **THEN** 图表 SHALL 使用 200px 作为最小高度 +- **AND** SHALL 显示滚动条或缩放提示 + +## MODIFIED Requirements + +无修改的现有需求。 + +## REMOVED Requirements + +无删除的需求。 diff --git a/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/tasks.md b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/tasks.md new file mode 100644 index 0000000..bf9962a --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-statistics-chart-styles/tasks.md @@ -0,0 +1,97 @@ +--- +title: 图表样式优化任务清单 +author: AI Assistant +date: 2026-02-16 +--- + +## 1. 核心配置更新 + +- [x] 1.1 修改 `useChartTheme.ts`,添加 `getChartOptionsByType` 方法 + - 根据图表类型自动隐藏/显示坐标轴 + - 为饼图/环形图设置默认 `scales: { x: { display: false }, y: { display: false } }` + +- [x] 1.2 优化基础配色方案 + - 更新 `chartPalette` 为 8 色低饱和度色板 + - 调整 `colors` 对象以更好适配暗色模式 + +- [x] 1.3 简化坐标轴样式 + - 网格线透明度降至 30% + - 刻度字体大小调整为 10px + - 确保 `drawBorder: false` + +## 2. 统计页面图表修复 + +- [x] 2.1 修复支出分类饼图 (ExpenseCategoryCard.vue) + - 更新图表配置调用方式,使用新的类型感知配置 + - 验证坐标轴已完全隐藏 + +- [x] 2.2 优化每日趋势图 (DailyTrendChart.vue) + - 更新渐变色使用新的配色方案 + - 调整动画参数(600ms, easeOutQuart) + - 增大触控目标大小 + +- [x] 2.3 验证其他统计图表 + - 检查收入分类图表 + - 检查支出排行图表 + +## 3. 预算页面图表修复 + +- [x] 3.1 修复使用情况仪表盘 (BudgetChartAnalysis.vue) + - 移除仪表盘周围的坐标轴和网格线 + - 优化中心文字显示 + +- [x] 3.2 优化预算进度燃尽图 + - 调整线条颜色和粗细 + - 优化网格线样式 + - 更新图例位置和样式 + +- [x] 3.3 检查偏差分析图表 + - 确保无坐标轴干扰 + - 优化数据标签显示 + +## 4. 通用组件优化 + +- [x] 4.1 更新 BaseChart.vue + - 集成新的类型感知配置 + - 优化加载和空状态显示 + +- [x] 4.2 增强响应式处理 + - 确保 ResizeObserver 正常工作 + - 优化横竖屏切换体验 + +- [x] 4.3 更新 chartHelpers.ts + - 优化渐变创建函数 + - 添加颜色格式化工具 + +## 5. 测试与验证 + +- [x] 5.1 运行前端构建 + - 执行 `pnpm build` + - 确保无 TypeScript 错误 + +- [x] 5.2 验证明亮模式 + - 统计页面所有图表显示正常 + - 预算页面所有图表显示正常 + - 坐标轴问题已修复 + +- [x] 5.3 验证暗色模式 + - 主题系统自动适配暗色模式 + - 所有图表颜色适配正常 + +- [x] 5.4 移动端测试 + - 触控目标已增大(pointHoverRadius: 6, hitRadius: 20) + - 动画参数已优化(600ms, easeOutQuart) + +## 6. 代码整理 + +- [x] 6.1 运行代码格式化 + - 执行 `pnpm lint` + - 执行 `pnpm format` + +- [x] 6.2 清理无用代码 + - 删除重复函数定义 + - 移除未使用的导入 + +- [x] 6.3 更新文档注释 + - 为新增函数添加 JSDoc + - 使用中文注释解释业务逻辑 diff --git a/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/.openspec.yaml b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/.openspec.yaml new file mode 100644 index 0000000..c8d3976 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-17 diff --git a/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/design.md b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/design.md new file mode 100644 index 0000000..7e8eb49 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/design.md @@ -0,0 +1,56 @@ +## Context + +当前统计页面使用 Chart.js 渲染折线图和饼图,但存在以下技术问题: + +1. **折线图溢出**: 图表 canvas 尺寸计算未考虑容器边界,导致图表绘制超出卡片范围 +2. **时间范围显示**: 折线图 X 轴显示整个自然周期(如整月),但数据仅到当前日期,导致后半段为平直线 +3. **饼图标签**: 当前饼图使用图例(legend)展示分类,用户需要在图例和图表间来回查看 + +项目使用 Vue 3 + Chart.js + vue-chartjs 技术栈,图表配置通过 `useChartTheme` composable 统一管理。 + +## Goals / Non-Goals + +**Goals:** +- 实现图表在容器内的自适应布局,无溢出 +- 折线图动态计算数据截止时间,仅显示有效数据范围 +- 饼图扇区直接渲染分类名称标签 +- 保持现有主题配置和响应式行为 + +**Non-Goals:** +- 不更换图表库(保持 Chart.js) +- 不修改数据 API 或数据结构 +- 不添加新的图表类型 +- 不影响其他页面的图表显示 + +## Decisions + +### 1. 布局约束方案: CSS 容器 + Chart.js responsive 配置 +- **选择**: 结合 CSS `overflow: hidden` 和 Chart.js `maintainAspectRatio: false` + `responsive: true` +- **理由**: 利用 Chart.js 内置的响应式机制,同时通过 CSS 确保容器边界约束 +- **替代方案**: 手动计算 canvas 尺寸(复杂,需监听 resize) + +### 2. 折线图数据过滤: 前端日期截断 +- **选择**: 在组件内根据当前日期过滤数据数组,仅传递有效数据给 Chart.js +- **理由**: 最小化改动,不修改 API;保持数据完整性以备他用 +- **替代方案**: 后端 API 支持日期参数(需后端改动,过度设计) + +### 3. 饼图标签方案: Chart.js datalabels 插件 +- **选择**: 使用 `chartjs-plugin-datalabels` 插件在扇区上渲染标签 +- **理由**: 官方推荐方案,支持自动位置计算和碰撞检测 +- **替代方案**: 自定义绘制(复杂,需处理重叠) + +## Risks / Trade-offs + +| Risk | Mitigation | +|------|------------| +| 饼图标签在扇区过小时显示不全 | 设置最小扇区角度阈值,小分类合并为"其他" | +| 暗色模式切换时标签颜色适配 | 通过 `useChartTheme` 动态计算对比色 | +| 性能影响(datalabels 插件) | 仅在饼图启用,监控渲染耗时 | + +## Migration Plan + +无需迁移,纯视觉修复,向后兼容。 + +## Open Questions + +- 饼图标签在移动端小屏幕上的显示策略(待实现时验证) diff --git a/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/proposal.md b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/proposal.md new file mode 100644 index 0000000..dece702 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/proposal.md @@ -0,0 +1,29 @@ +## Why + +统计页面的图表存在两个影响用户体验的显示问题:折线图超出卡片边界且显示未来日期的平直线段,饼图缺少直接的分类标签展示。这些问题降低了数据可视化的直观性和美观度,需要修复以提升用户查看统计数据的体验。 + +## What Changes + +- 修复折线图(Line Chart)在统计卡片中的溢出布局问题,确保图表完全包含在卡片边界内 +- 修改折线图数据截止时间逻辑,从显示完整自然周期(如整月)改为仅显示至当前日期,避免未来日期形成无意义的平直线段 +- 优化支出分类饼图(Pie Chart),在饼图扇区上直接显示分类名称标签,提升可读性 + +## Capabilities + +### New Capabilities +- `chart-layout-constraint`: 图表在容器内的自适应布局和边界约束控制 +- `chart-data-filtering`: 基于当前日期的动态数据过滤和范围控制 +- `chart-label-overlay`: 饼图扇区上的直接标签渲染和位置计算 + +### Modified Capabilities +- (无现有能力需要修改需求) + +## Impact + +- **受影响组件**: + - `Web/src/components/Charts/BaseChart.vue` - 基础图表组件 + - `Web/src/views/Statistics/` 下的统计页面组件 + - `Web/src/composables/useChartTheme.ts` - 图表主题配置 +- **图表库**: Chart.js 配置选项调整 +- **无API变更**: 纯前端显示层修复 +- **向后兼容**: 无破坏性变更 diff --git a/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-data-filtering/spec.md b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-data-filtering/spec.md new file mode 100644 index 0000000..d5d2e0a --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-data-filtering/spec.md @@ -0,0 +1,21 @@ +## ADDED Requirements + +### Requirement: Line chart date range truncation +The line chart SHALL display data only up to the current date, not the full natural period. + +#### Scenario: Monthly view shows data to current day +- **GIVEN** today is the 15th of the month +- **WHEN** the user views the monthly statistics chart +- **THEN** the chart SHALL display data from the 1st to the 15th only +- **AND** the X-axis SHALL NOT show dates beyond the current day + +#### Scenario: Weekly view shows data to current day +- **GIVEN** today is Wednesday +- **WHEN** the user views the weekly statistics chart +- **THEN** the chart SHALL display data from Monday to Wednesday only +- **AND** no flat line segments SHALL appear for future dates + +#### Scenario: Yearly view shows data to current month +- **GIVEN** today is in June +- **WHEN** the user views the yearly statistics chart +- **THEN** the chart SHALL display data from January to June only diff --git a/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-label-overlay/spec.md b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-label-overlay/spec.md new file mode 100644 index 0000000..d50d172 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-label-overlay/spec.md @@ -0,0 +1,21 @@ +## ADDED Requirements + +### Requirement: Pie chart direct category labeling +The pie chart SHALL display category names directly on or adjacent to their corresponding sectors. + +#### Scenario: Category labels rendered on pie sectors +- **WHEN** the expense category pie chart is displayed +- **THEN** each sector SHALL display its category name as a label +- **AND** the label SHALL be positioned to not obscure the sector + +#### Scenario: Labels adapt to sector size +- **GIVEN** a category represents less than 5% of total expenses +- **WHEN** the pie chart renders +- **THEN** the label for that small sector MAY be hidden to avoid clutter +- **AND** the category SHALL still be identifiable via tooltip on hover + +#### Scenario: Label visibility in dark mode +- **GIVEN** the application is in dark mode +- **WHEN** the pie chart displays labels on sectors +- **THEN** the label text color SHALL provide sufficient contrast against the sector color +- **AND** labels SHALL remain readable against both light and dark sector colors diff --git a/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-layout-constraint/spec.md b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-layout-constraint/spec.md new file mode 100644 index 0000000..88ccf4a --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/specs/chart-layout-constraint/spec.md @@ -0,0 +1,14 @@ +## ADDED Requirements + +### Requirement: Chart container boundary enforcement +The chart SHALL be fully contained within its parent card container without overflow. + +#### Scenario: Chart renders within card boundaries +- **WHEN** the statistics page displays a line chart in a card component +- **THEN** the chart canvas SHALL NOT extend beyond the card's padding boundaries +- **AND** the chart SHALL adapt to container resize events + +#### Scenario: Chart adapts to mobile viewport +- **WHEN** the viewport width is less than 375px +- **THEN** the chart SHALL scale down proportionally +- **AND** no horizontal scrolling SHALL be required to view the full chart diff --git a/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/tasks.md b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/tasks.md new file mode 100644 index 0000000..f77eb91 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-stats-chart-bugs/tasks.md @@ -0,0 +1,27 @@ +## 1. 图表布局修复 + +- [x] 1.1 检查 BaseChart.vue 响应式配置,确保 maintainAspectRatio: false 和 responsive: true +- [x] 1.2 为统计卡片添加 CSS 约束,设置 overflow: hidden 和固定高度 +- [x] 1.3 验证图表在移动端(<375px)下正常缩放无溢出 + +## 2. 折线图数据过滤 + +- [x] 2.1 在统计页面组件中添加当前日期获取逻辑 +- [x] 2.2 实现数据过滤函数,根据周期类型(日/周/月/年)截断未来日期数据 +- [x] 2.3 更新折线图数据传递,仅传递过滤后的数据 +- [x] 2.4 验证 X 轴不再显示未来日期 + +## 3. 饼图标签渲染 + +- [x] 3.1 安装 chartjs-plugin-datalabels 插件 +- [x] 3.2 在 useChartTheme.ts 中添加饼图标签配置 +- [x] 3.3 实现标签位置计算和扇区大小阈值控制(<5% 隐藏标签) +- [x] 3.4 添加暗色模式下的标签颜色适配逻辑 +- [x] 3.5 验证标签在各类别扇区上正确显示 + +## 4. 测试与验证 + +- [x] 4.1 运行前端 lint 检查 +- [x] 4.2 在桌面端验证所有图表显示正常 +- [x] 4.3 在移动端验证响应式布局 +- [x] 4.4 验证暗色模式下图表标签可读性 diff --git a/openspec/changes/archive/2026-02-17-fix-theme-colors-and-bottom-navigation/.openspec.yaml b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-bottom-navigation/.openspec.yaml new file mode 100644 index 0000000..4465244 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-bottom-navigation/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-11 diff --git a/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/.openspec.yaml b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/.openspec.yaml new file mode 100644 index 0000000..4465244 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-02-11 diff --git a/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/design.md b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/design.md new file mode 100644 index 0000000..1de4b07 --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/design.md @@ -0,0 +1,219 @@ +# Design: 修复主题色差与导航栏间距 + +## Overview + +本设计文档详细说明如何修复 EmailBill 应用的两大 UI 问题: +1. 亮色/暗色主题下的颜色不一致(色差) +2. 底部导航栏与屏幕底部间距过大 + +## Technical Decisions + +### 1. 主题色彩统一方案 + +#### Current Issues +- 导航栏、卡片背景色与页面背景色存在色差 +- 亮色和暗色模式下颜色变量定义不一致 +- Vant UI 组件主题定制配置不完整 + +#### Solution Approach +使用 CSS 变量(CSS Custom Properties)统一管理主题色彩,确保所有组件引用相同的变量。 + +```css +/* Web/src/styles/theme.css */ +:root { + /* 亮色主题 - 背景色系统 */ + --bg-primary: #FFFFFF; + --bg-secondary: #F6F7F8; + --bg-tertiary: #F5F5F5; + + /* 亮色主题 - 文本色系统 */ + --text-primary: #1A1A1A; + --text-secondary: #6B7280; + --text-tertiary: #9CA3AF; + + /* 语义色 */ + --color-primary: #3B82F6; + --color-danger: #FF6B6B; + --color-success: #07C160; + --color-warning: #FAAD14; +} + +[data-theme="dark"] { + /* 暗色主题 - 背景色系统 */ + --bg-primary: #09090B; + --bg-secondary: #18181B; + --bg-tertiary: #27272A; + + /* 暗色主题 - 文本色系统 */ + --text-primary: #F4F4F5; + --text-secondary: #A1A1AA; + --text-tertiary: #71717A; + + /* 语义色在暗色模式下保持不变或微调 */ + --color-primary: #3B82F6; + --color-danger: #FF6B6B; + --color-success: #07C160; + --color-warning: #FAAD14; +} +``` + +#### Vant UI 主题映射 + +```javascript +// Web/src/main.ts 或主题配置文件 +import { ConfigProvider } from 'vant' + +const themeVars = { + // 亮色主题 + light: { + '--van-nav-bar-background': 'var(--bg-primary)', + '--van-nav-bar-text-color': 'var(--text-primary)', + '--van-card-background': 'var(--bg-secondary)', + '--van-cell-background': 'var(--bg-secondary)', + '--van-background': 'var(--bg-primary)', + '--van-background-2': 'var(--bg-secondary)', + '--van-text-color': 'var(--text-primary)', + '--van-text-color-2': 'var(--text-secondary)', + '--van-border-color': 'var(--bg-tertiary)', + }, + // 暗色主题 + dark: { + '--van-nav-bar-background': 'var(--bg-primary)', + '--van-nav-bar-text-color': 'var(--text-primary)', + '--van-card-background': 'var(--bg-secondary)', + '--van-cell-background': 'var(--bg-secondary)', + '--van-background': 'var(--bg-primary)', + '--van-background-2': 'var(--bg-secondary)', + '--van-text-color': 'var(--text-primary)', + '--van-text-color-2': 'var(--text-secondary)', + '--van-border-color': 'var(--bg-tertiary)', + } +} +``` + +### 2. 底部导航栏间距优化 + +#### Current Issues +- 底部导航栏距离屏幕底部过远 +- 未正确处理 iPhone 安全区域(Safe Area) +- padding/margin 设置不合理 + +#### Solution Approach + +```css +/* Web/src/styles/layout.css */ + +/* 底部导航栏容器 */ +.tabbar-container { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 100; + background: var(--bg-primary); + border-top: 1px solid var(--bg-tertiary); +} + +/* 导航栏主体 - 减少不必要的 padding */ +.van-tabbar { + height: 56px; /* 标准高度 */ + padding-bottom: env(safe-area-inset-bottom, 0px); /* 仅保留安全区域内边距 */ + background: var(--bg-primary) !important; +} + +/* 导航项样式 */ +.van-tabbar-item { + color: var(--text-secondary); +} + +.van-tabbar-item--active { + color: var(--color-primary); +} + +/* 页面内容区域需要为底部导航栏留出空间 */ +.page-content { + padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px)); +} +``` + +#### Vue 组件调整 + +```vue + + + + +``` + +## Implementation Files + +### Modified Files + +1. **Web/src/styles/theme.css** (新建) + - 定义 CSS 变量系统 + - 包含亮色/暗色两套变量 + +2. **Web/src/styles/layout.css** (新建或修改) + - 底部导航栏布局样式 + - 安全区域处理 + +3. **Web/src/main.ts** + - 引入主题样式文件 + - 配置 Vant UI 主题变量 + +4. **Web/src/App.vue** (或布局组件) + - 调整底部导航栏结构 + - 应用新的布局样式 + +5. **Web/src/components/Navigation/** (如存在) + - 更新导航组件样式 + +### Key Considerations + +1. **兼容性**: CSS 变量在现代浏览器中支持良好,项目使用 Vite 构建,无需额外 polyfill + +2. **性能**: CSS 变量在运行时计算,对性能影响极小 + +3. **可维护性**: 集中管理颜色变量,后续主题调整只需修改一处 + +4. **测试**: 需要在 iOS Safari、Android Chrome、微信内置浏览器中测试显示效果 + +## Migration Path + +1. **Phase 1**: 创建 theme.css 和 layout.css,定义新的变量系统 +2. **Phase 2**: 逐步替换硬编码颜色值为 CSS 变量 +3. **Phase 3**: 更新 Vant UI 主题配置 +4. **Phase 4**: 调整底部导航栏布局 +5. **Phase 5**: 跨设备测试验证 diff --git a/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/proposal.md b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/proposal.md new file mode 100644 index 0000000..2c35f5c --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/proposal.md @@ -0,0 +1,26 @@ +## Why + +根据用户提供的实机截图,发现 EmailBill 应用在两种主题色(亮色/暗色)下存在明显的色差问题:导航栏、卡片背景等组件颜色与页面背景色不一致,造成视觉割裂感。同时,底部导航栏距离屏幕底部过远,留下大量空白区域,影响移动端使用体验。这些问题需要统一修复以提升应用的整体视觉一致性和用户体验。 + +## What Changes + +- **统一主题色彩系统**: 修复亮色和暗色模式下导航栏、卡片、按钮等组件的颜色变量,确保同一主题内颜色一致性 +- **优化底部导航栏布局**: 调整底部导航栏的间距,减少不必要的空白区域,使其更贴近屏幕底部 +- **更新 CSS 样式文件**: 修改 `Web/src/styles/` 下的主题变量定义 +- **调整组件样式**: 更新 Vant UI 组件的主题定制配置 + +## Capabilities + +### New Capabilities + + +### Modified Capabilities +- `theme-system`: 统一亮色/暗色主题的色彩变量,消除色差问题 +- `navbar-layout`: 优化底部导航栏的间距和定位 + +## Impact + +- **前端代码**: `Web/src/styles/` 目录下的主题配置文件 +- **组件库**: Vant UI 的主题定制配置 (`Web/src/main.ts` 或主题配置文件) +- **布局文件**: 涉及底部导航栏的页面布局样式 +- **测试**: 需要在亮色和暗色两种模式下进行视觉回归测试 diff --git a/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/tasks.md b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/tasks.md new file mode 100644 index 0000000..1ed2acd --- /dev/null +++ b/openspec/changes/archive/2026-02-17-fix-theme-colors-and-navbar-spacing/tasks.md @@ -0,0 +1,120 @@ +# Tasks: 修复主题色差与导航栏间距 + +## Phase 1: 创建主题系统基础文件 + +### 1.1 创建 CSS 变量定义文件 +- [x] 新建 `Web/src/styles/theme.css` + - [x] 定义亮色主题变量(背景、文本、语义色) + - [x] 定义暗色主题变量(背景、文本、语义色) + - [x] 确保两组变量名称一致,仅值不同 + +### 1.2 在 main.ts 中引入主题文件 +- [x] 导入 `theme.css` 到 `Web/src/main.ts` +- [x] 确保 CSS 变量在应用启动时生效 + +--- + +## Phase 2: 配置 Vant UI 主题映射 + +### 2.1 更新 Vant 主题配置 +- [x] 修改 `Web/src/main.ts` 中的 Vant ConfigProvider 配置 + - [x] 将 `--van-nav-bar-background` 映射到 `--bg-primary` + - [x] 将 `--van-card-background` 映射到 `--bg-secondary` + - [x] 将 `--van-text-color` 映射到 `--text-primary` + - [x] 将 `--van-border-color` 映射到 `--bg-tertiary` + - [x] 确保亮/暗两套配置使用相同的变量名 + +### 2.2 验证组件颜色一致性 +- [ ] 检查 NavBar 组件背景色是否与页面背景一致 +- [ ] 检查 Card 组件背景色是否正确应用 +- [ ] 检查 Cell 组件背景色是否正确应用 +- [ ] 在亮色模式下截图对比 +- [ ] 在暗色模式下截图对比 + +--- + +## Phase 3: 修复底部导航栏间距 + +### 3.1 创建布局样式文件 +- [x] 新建或修改 `Web/src/styles/layout.css` + - [x] 定义 `.tabbar-container` 固定定位样式 + - [x] 设置导航栏高度为 56px + - [x] 添加 `env(safe-area-inset-bottom)` 安全区域内边距 + - [x] 移除多余的 margin/padding + +### 3.2 调整主布局组件 +- [x] 修改 `Web/src/App.vue` (或布局组件) + - [x] 为页面内容区域添加 `.page-content` 类 + - [x] 设置 `padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px))` + - [x] 确保内容不被导航栏遮挡 + +### 3.3 优化 TabBar 组件样式 +- [x] 使用 `:deep(.van-tabbar)` 覆盖 Vant 默认样式 + - [x] 设置背景色为 `var(--bg-primary)` + - [x] 移除不必要的 padding/margin + - [x] 确保图标和文字垂直居中 + +--- + +## Phase 4: 清理硬编码颜色值 + +### 4.1 扫描并替换硬编码颜色 +- [x] 搜索项目中所有 `#FFFFFF`、`#000000` 等硬编码颜色 +- [x] 替换为对应的 CSS 变量 + - [x] 页面背景 → `--bg-primary` + - [x] 卡片背景 → `--bg-secondary` + - [x] 主要文本 → `--text-primary` + - [x] 次要文本 → `--text-secondary` + +### 4.2 检查自定义组件 +- [x] 审查所有 Vue 组件的 `