diff --git a/.opencode/skills/pancli-design/SKILL.md b/.opencode/skills/pancli-design/SKILL.md new file mode 100644 index 0000000..c76ed4f --- /dev/null +++ b/.opencode/skills/pancli-design/SKILL.md @@ -0,0 +1,856 @@ +--- +name: pancli-design +description: 专业的设计技能,用于使用 pancli (pencil tools) 创建现代化、一致的 EmailBill 移动端 UI 设计 +license: MIT +compatibility: Requires pencil_* tools (batch_design, batch_get, etc.) +metadata: + author: EmailBill Design Team + version: "2.0.0" + generatedBy: opencode + lastUpdated: "2026-02-03" + source: ".pans/v2.pen 日历设计 (亮色/暗色)" +--- + +# pancli-design - EmailBill UI 设计系统 + +> 专业的设计技能,用于使用 pancli (pencil tools) 创建现代化、一致的移动端 UI 设计。 + +## 何时使用此技能 + +**总是使用此技能当:** +- 使用 pancli 创建新的 UI 界面或组件 +- 修改现有的 .pen 设计文件 +- 处理亮色/暗色主题设计 +- 为 EmailBill 项目设计移动端优先的界面 + +**触发条件:** +- 用户提到 "画设计图"、"设计"、"UI"、"界面"、"pancli"、".pen" +- 任务涉及 `pencil_*` 工具 +- 创建视觉原型或模型 + +## 核心设计原则 + +### 1. 现代移动端优先设计 + +**布局标准:** +- 移动视口: 375px 宽度 (iPhone SE 基准) +- 安全区域: 尊重 iOS/Android 安全区域边距 +- 触摸目标: 交互元素最小 44x44px +- 间距比例: 4px, 8px, 12px, 16px, 24px, 32px (8px 基础网格) +- 圆角半径: 12px (卡片), 16px (对话框), 24px (药丸/标签), 8px (按钮) +- 卡片阴影: `0 2px 12px rgba(0,0,0,0.08)` 用于突出表面 + +**避免 AI 设计痕迹:** +- 不要使用通用的 "Dashboard" 占位符文本 +- 不要使用图库照片或 Lorem Ipsum +- 不要使用过饱和的主色 (#007AFF, 不是 #0088FF) +- 不要使用生硬的阴影或渐变 +- 不要使用 Comic Sans, Papyrus 或装饰性字体 +- 使用代码库中的真实中文业务术语 + +### 2. 统一色彩系统 (基于实际 v2.pen 日历设计) + +**亮色主题:** +```css +/* 背景色 - 基于实际设计 */ +--background-page: #FFFFFF /* 页面背景 (Calendar frame fill) */ +--background-card: #F6F7F8 /* 卡片背景 (statsCard, tCard fills) */ +--background-accent: #F5F5F5 /* 强调背景 (notif button) */ + +/* 文本色 - 基于实际设计 */ +--text-primary: #1A1A1A /* 主文本 (titles, labels) */ +--text-secondary: #6B7280 /* 次要文本 (dates, subtitles) */ +--text-tertiary: #9CA3AF /* 三级文本 (weekday labels) */ + +/* 语义色 - 基于实际设计 */ +--accent-red: #FF6B6B /* 支出/负数 (expense icon) */ +--accent-yellow: #FCD34D /* 警告/中性 (coffee icon) */ +--accent-green: #F0FDF4 /* 收入/正数 (badge background) */ +--accent-blue: #E0E7FF /* 智能标签 (smart button) */ +--accent-warm: #FFFBEB /* 温暖色调 (badge background) */ + +/* 主操作色 */ +--primary: #3B82F6 /* 主色调 (FAB button from Budget Stats) */ + +/* 边框与分割线 */ +--border: #E5E7EB /* 边框颜色 (从设计推断) */ +``` + +**暗色主题:** +```css +/* 背景色 - 基于实际暗色设计 */ +--background-page: #09090B /* 页面背景 (Calendar Dark frame) */ +--background-card: #18181B /* 卡片背景 (dark statsCard, tCard) */ +--background-accent: #27272A /* 强调背景 (dark notif, tCat) */ + +/* 文本色 - 基于实际暗色设计 */ +--text-primary: #F4F4F5 /* 主文本 (dark titles) */ +--text-secondary: #A1A1AA /* 次要文本 (dark dates, subtitles) */ +--text-tertiary: #71717A /* 三级文本 (dark weekday labels) */ + +/* 语义色 - 暗色模式适配 */ +--accent-red: #FF6B6B /* 保持一致 */ +--accent-yellow: #FCD34D /* 保持一致 */ +--accent-green: #064E3B /* 深绿 (dark badge) */ +--accent-blue: #312E81 /* 深蓝 (dark smart button) */ +--accent-warm: #451A03 /* 深暖色 (dark badge) */ + +/* 主操作色 */ +--primary: #3B82F6 /* 保持一致 */ + +/* 边框与分割线 */ +--border: #3F3F46 /* 深色边框 */ +``` + +**颜色使用规则:** +- **页面背景**: 亮色 `#FFFFFF`, 暗色 `#09090B` +- **卡片背景**: 亮色 `#F6F7F8`, 暗色 `#18181B` +- **主文本**: 亮色 `#1A1A1A`, 暗色 `#F4F4F5` +- **次要文本**: 亮色 `#6B7280`, 暗色 `#A1A1AA` +- **支出/负数**: `#FF6B6B` (两种模式一致) +- **主操作按钮**: `#3B82F6` (两种模式一致) +- **圆角**: 12px (小按钮), 16px (卡片), 20px (统计卡片), 22px (图标按钮) +- **阴影**: 亮色使用柔和阴影, 暗色使用更深的阴影或无阴影 +- **避免**: 纯黑 (#000000) 或纯白 (#FFFFFF) 文本 + +### 3. 排版系统 (基于实际 v2.pen 设计) + +**字体栈:** +```css +/* 标题与大标题 - 基于 v2.pen */ +font-family: 'Bricolage Grotesque', -apple-system, BlinkMacSystemFont, system-ui, sans-serif; + +/* 正文与界面 - 基于 v2.pen */ +font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'PingFang SC', sans-serif; + +/* 备选: 系统默认 */ +font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', sans-serif; +``` + +**字号比例 (从实际设计提取):** +| 用途 | 字体 | 大小 | 粗细 | 示例 | +|------|------|------|------|------| +| 大标题数值 | Bricolage Grotesque | 32px | 800 | ¥ 1,248.50 (statsVal) | +| 页面标题 | DM Sans | 24px | 500 | 2026年1月 (subtitle) | +| 章节标题 | Bricolage Grotesque | 18px | 700 | 每日统计, 交易记录 (titles) | +| 正文文本 | DM Sans | 15px | 600 | Lunch, Coffee (交易名称) | +| 说明文字 | DM Sans | 13px | 500 | 12:30 PM, Total Spent (标签) | +| 微型文字 | DM Sans | 12px | 600 | 一二三四五六日 (星期) | + +**中文文本规则:** +- 使用简体中文 +- 真实业务术语: "每日统计" (daily stats), "交易记录" (transactions) +- 行高: 1.4-1.6 保证可读性 +- 使用真实业务术语,避免 Lorem Ipsum + +### 4. 组件库 (基于实际 v2.pen 设计) + +**卡片 (基于 statsCard, tCard):** +``` +统计卡片 (大卡片): + - 背景: #F6F7F8 (亮色), #18181B (暗色) + - 内边距: 20px + - 圆角: 20px + - 间距: 12px (元素之间) + - 布局: 垂直 + +交易卡片 (列表卡片): + - 背景: #F6F7F8 (亮色), #18181B (暗色) + - 内边距: 16px + - 圆角: 16px + - 间距: 14px (水平元素) + - 高度: 自适应内容 +``` + +**按钮 (基于实际设计):** +``` +图标按钮 (通知按钮): + - 尺寸: 44x44px + - 圆角: 22px (完全圆形) + - 背景: #F5F5F5 (亮色), #27272A (暗色) + - 图标大小: 20px + +标签按钮: + - 内边距: 6px 10px / 6px 12px + - 圆角: 12px + - 字体: DM Sans 13px/500 + - 颜色: + - 温暖色: #FFFBEB (亮色), #451A03 (暗色) + - 绿色: #F0FDF4 (亮色), #064E3B (暗色) + - 蓝色: #E0E7FF (亮色), #312E81 (暗色) + +悬浮按钮 (FAB): + - 尺寸: 56x56px + - 圆角: 28px + - 背景: #3B82F6 + - 描边: 4px 白色边框 + - 阴影: 提升效果 +``` + +**图标与文字:** +``` +图标容器: + - 尺寸: 44x44px + - 圆角: 22px + - 背景: #FFFFFF (亮色), #27272A (暗色) + - 图标: 20px (lucide 字体) + - 颜色: #FF6B6B (星标), #FCD34D (咖啡) + +章节标题: + - 字体: Bricolage Grotesque 18px/700 + - 颜色: #1A1A1A (亮色), #F4F4F5 (暗色) + +大数值: + - 字体: Bricolage Grotesque 32px/800 + - 颜色: #1A1A1A (亮色), #F4F4F5 (暗色) +``` + +**布局模式 (基于 Calendar 结构):** +``` +页面容器: + - 宽度: 402px (设计视口) + - 布局: 垂直 + - 内边距: 24px (容器边距) + - 间距: 16px (章节之间) + +头部区域: + - 内边距: 8px 24px + - 布局: 水平, 两端对齐 + - 对齐项: 居中 + +内容区域: + - 内边距: 24px + - 间距: 12-16px + - 布局: 垂直 +``` + +### 5. 布局模式 + +**页面结构 (Flex 容器):** +``` +.page-container-flex: + - display: flex + - flex-direction: column + - height: 100% + - overflow: hidden + + 结构: + 1. van-nav-bar (固定高度) + 2. van-tabs 或 sticky-header + 3. scroll-content (flex: 1, overflow-y: auto) + 4. bottom-button 或 van-tabbar (固定) +``` + +**导航栏背景透明化 (一致性模式):** +```css +/* 设置页面容器背景色 */ +:deep(.van-nav-bar) { + background: transparent !important; +} +``` +**重要:** 项目中所有视图都采用此模式,使导航栏背景透明,与页面背景融合。实现此效果时,请确保: +- 页面容器有明确的背景色 (亮色: #FFFFFF, 暗色: #09090B) +- 导航栏始终使用 `:deep(.van-nav-bar)` 选择器 +- 必须添加 `!important` 覆盖 Vant 默认样式 +- 在 ` +``` + +```vue + + + + +``` + +**模块化的好处:** +- ✅ **关注点分离**:Index 只管布局和编排,模块专注业务逻辑 +- ✅ **独立维护**:修改某个模块不影响其他模块 +- ✅ **可复用性**:模块可以在其他页面复用 +- ✅ **可测试性**:每个模块可以独立测试 +- ✅ **代码清晰**:职责明确,代码结构清晰 + +**反模式(不要这样做):** +```vue + + + + +``` + +**什么时候需要模块化?** +- 页面有 3 个以上独立功能区域 +- 每个区域有自己的数据查询需求 +- 页面代码超过 300 行 +- 功能区域可能被其他页面复用 + +**什么时候不需要模块化?** +- 简单页面(如登录页、详情页) +- 只有一个主要功能 +- 代码少于 200 行 +- 无明显的功能分区 + +### 1. **数据优先,提前确认** + +在开始编码前,**必须先确认数据表结构**: + +**检查清单:** +- [ ] 读取 .pen 设计文件,理解所有数据需求 +- [ ] 检查现有 Entity 层,确认是否支持设计需求 +- [ ] 如果需要新增字段/表/枚举值,**先询问用户确认** +- [ ] 列出所有数据变更,等待用户批准后再继续 + +**示例确认流程:** +```markdown +## 数据表结构确认 + +根据设计图分析,需要以下数据支持: + +### 现有实体: +✅ TransactionRecord - 支持交易记录 +✅ BudgetRecord - 支持预算管理 +✅ TransactionCategory - 支持分类 + +### 需要变更: +1. **TransactionCategory 表新增字段** + - 字段名: IconName (string) + - 用途: 存储 lucide 图标名称(如 "utensils"、"shopping-cart") + - 原因: 设计图使用图标名称渲染,现有 Icon 字段存储 SVG 不适合 + +2. **BudgetRecord 新增计算字段** + - 字段名: ProgressPercentage (decimal, 计算属性) + - 用途: 当前支出占预算的百分比 + - 原因: 设计图显示进度条需要 + +### 是否批准以上变更?如不批准,请提供替代方案。 +``` + +**不得做:** +- ❌ 未经确认直接修改 Entity 层 +- ❌ 假设字段存在而不检查 +- ❌ 跳过数据表确认直接实施前端 + +### 2. **交互细节,逐一确认** + +设计图无法表达所有交互逻辑,**必须向用户确认**: + +**需要确认的交互:** +1. **按钮点击行为** + - 跳转到哪个页面? + - 弹出什么对话框? + - 触发什么 API? + +2. **周期切换逻辑**(如周/月/年) + - 周:本周一到周日?还是最近7天? + - 月:自然月?还是最近30天? + - 年:当年1-12月?还是最近12个月? + +3. **数据计算逻辑** + - 环比百分比如何计算? + - 趋势图数据范围? + - 空状态显示什么? + +4. **跳转目标** + - "查看全部"跳转到哪里? + - "管理预算"跳转到哪个页面? + - 通知按钮打开什么? + +**示例确认:** +```markdown +## 交互行为确认 + +### 1. 顶部通知按钮 +设计图:bell 图标按钮 +**问题:** 点击后跳转到哪里? +- A. 消息中心(MessageView.vue) +- B. 通知列表(新页面) +- C. 弹出通知面板 + +### 2. 周期切换(周/月/年) +设计图:三个 segment 切换按钮 +**问题:** 周期定义? +- 周:本周一到周日 or 最近7天 +- 月:当前自然月 or 最近30天 +- 年:当年1-12月 or 最近12个月 + +### 3. 核心指标徽章(如 "-15%") +设计图:显示百分比变化 +**问题:** 对比哪个周期? +- A. 与上一周期对比(上周/上月/去年) +- B. 与同期对比(去年同周/去年同月) +- C. 与预算目标对比 + +请明确以上交互逻辑。 +``` + +### 3. **高度还原设计稿** + +**视觉还原标准:** +- 严格按照设计图的间距、字号、颜色、圆角 +- 使用设计图中的实际字体(如 DM Sans, Bricolage Grotesque, JetBrains Mono) +- 匹配卡片阴影、边框样式 +- 复现设计图的布局层级 + +**对照检查:** +```typescript +// 设计图: +// - padding: 24px +// - gap: 16px +// - cornerRadius: 12px +// - fontSize: 14px +// - fontWeight: 600 + +// 实现: +
+ +
+``` + +**字体映射:** +| 设计图字体 | CSS 实现 | +|-----------|---------| +| Bricolage Grotesque | `font-family: 'Bricolage Grotesque', system-ui, sans-serif` | +| DM Sans | `font-family: 'DM Sans', -apple-system, sans-serif` | +| JetBrains Mono | `font-family: 'JetBrains Mono', 'SF Mono', monospace` | +| Newsreader | `font-family: 'Newsreader', Georgia, serif` | + +### 4. **前后端同步开发** + +**开发顺序:** +``` +1. 确认数据表 → 2. 开发后端 API → 3. 实施前端组件 → 4. 集成测试 → 5. 视觉验证 +``` + +**后端开发标准:** +- 在 `Entity/` 中新增/修改实体 +- 在 `Repository/` 中添加数据访问方法 +- 在 `Service/` 中实现业务逻辑 +- 在 `WebApi/Controllers/` 中创建 API 端点 +- 编写 xUnit 测试验证逻辑 + +**前端开发标准:** +- 在 `Web/src/views/` 中创建/修改页面组件 +- 在 `Web/src/components/` 中提取可复用组件 +- 在 `Web/src/api/` 中定义 API 客户端 +- 使用 Vue 3 Composition API + ` + + +``` + +**B. 业务模块 (Business Module)** +`src/views/Statistics/modules/MetricsCard.vue` +- **职责**:独立负责特定功能区域的 UI 和数据获取。 +- **原则**:自包含 (Self-contained) - 自己 import api,自己 fetch data。 + +```vue + + + + + +``` + +### 阶段 5: 集成测试与验证 + +**5.1 运行测试** +```bash +# 后端测试 +dotnet test WebApi.Test/WebApi.Test.csproj + +# 前端构建 +cd Web +pnpm lint +pnpm build +``` + +**5.2 视觉验证** +```typescript +// 使用 pencil 截图对比 +pencil_get_screenshot(nodeId: "jF3SD") // 设计图截图 + +// 在浏览器中截图实际效果 +// 对比以下方面: +// - 间距是否一致(padding, gap) +// - 字号是否匹配(fontSize) +// - 颜色是否准确(fill, textColor) +// - 圆角是否正确(cornerRadius) +// - 布局是否对齐(layout, justifyContent, alignItems) +``` + +**5.3 交互验证** +- [ ] 周期切换按钮工作正常 +- [ ] 核心指标数据正确显示 +- [ ] 徽章百分比计算准确 +- [ ] 点击卡片跳转正确 +- [ ] 空状态显示合理 +- [ ] 加载状态流畅 +- [ ] 错误处理友好 + +## 避免的反模式 + +### ❌ 不要这样做: + +**1. 未经确认修改数据表** +```csharp +// ❌ 直接新增字段 +public class TransactionCategory : BaseEntity +{ + public string IconName { get; set; } // ❌ 未经用户确认 +} +``` + +**2. 猜测交互逻辑** +```vue +// ❌ 假设跳转目标 + + +``` + +**3. 不按设计图样式** +```scss +// 设计图: padding: 24px +.card { + padding: 20px; // ❌ 不匹配设计 +} +``` + +**4. 跳过测试** +```bash +# ❌ 不运行测试直接提交 +git add . +git commit -m "完成实施" +``` + +**5. 忽略暗色模式** +```scss +.card { + background: #FFFFFF; // ❌ 只实现亮色模式 +} +``` + +### ✅ 应该这样做: + +**1. 数据变更先确认** +```markdown +## 数据表变更请求 + +需要在 TransactionCategory 新增以下字段: +- IconName (string): 存储图标名称 +- 用途: 前端渲染 lucide 图标 + +是否批准? +``` + +**2. 交互逻辑先确认** +```markdown +## 交互确认 + +通知按钮点击后: +- A. 跳转到 MessageView +- B. 弹出通知列表 +- C. 其他 + +请明确选择。 +``` + +**3. 严格按照设计图** +```scss +// 设计图: padding: 24px, gap: 16px, borderRadius: 12px +.card { + padding: 24px; // ✅ 匹配设计 + gap: 16px; // ✅ 匹配设计 + border-radius: 12px; // ✅ 匹配设计 +} +``` + +**4. 完整测试** +```bash +# ✅ 运行所有测试 +dotnet test +cd Web && pnpm lint && pnpm build +``` + +**5. 支持暗色模式** +```scss +.card { + background: #FFFFFF; + + @media (prefers-color-scheme: dark) { + background: #18181B; // ✅ 暗色适配 + } +} +``` + +## 委派与任务管理 + +**使用此技能时:** + +```typescript +delegate_task( + category: "visual-engineering", + load_skills: ["pancli-implement", "frontend-ui-ux"], + description: "根据 v2.pen 设计图实施统计概览页面", + prompt: ` + ## 任务 + 根据 .pans/v2.pen 中的统计概览设计图(节点 ID: jF3SD)实施完整的前后端功能 + + ## 预期结果 + - [ ] Entity 层变更(如需要,经用户确认) + - [ ] 后端 API 开发并通过测试 + - [ ] 前端页面高度还原设计图 + - [ ] 交互逻辑符合用户确认 + - [ ] 亮色/暗色主题都正常 + - [ ] 所有测试通过(dotnet test, pnpm build) + + ## 必需工具 + - pencil_batch_get: 读取设计文件 + - pencil_get_screenshot: 设计图截图 + - read: 读取现有代码 + - glob: 查找文件 + - edit/write: 修改/新增代码 + - bash: 运行测试 + + ## 必须做 + 1. **数据确认阶段** + - 读取设计文件分析数据需求 + - 检查现有 Entity 是否支持 + - 如需变更,生成确认清单并等待用户批准 + + 2. **交互确认阶段** + - 列出所有不确定的交互逻辑 + - 生成确认清单并等待用户明确 + + 3. **后端开发** + - 按用户批准修改 Entity + - 实现 Repository 数据访问 + - 创建 Controller API + - 编写 xUnit 测试并通过 + + 4. **前端开发** + - **复杂页面强制模块化**: 采用 Index.vue + modules/ 结构 + - Index.vue 仅编排,modules 负责数据获取 + - 定义 API 客户端(src/api/) + - 严格按照设计图样式(间距、字号、颜色、圆角) + - 使用设计图中的实际字体 + - 支持亮色/暗色主题 + + 5. **验证阶段** + - 运行后端测试:dotnet test + - 运行前端构建:pnpm lint && pnpm build + - 视觉对比设计图截图 + - 验证交互逻辑 + + ## 不得做 + - ❌ 未经确认直接修改 Entity + - ❌ 猜测交互逻辑而不询问 + - ❌ 禁止复杂页面写成单文件组件 (Monolithic) + - ❌ 不按设计图样式随意实现 + - ❌ 跳过测试 + - ❌ 忽略暗色模式 + - ❌ 使用占位数据而不调用真实 API + + ## 上下文 + - 设计文件: .pans/v2.pen + - 设计节点 ID: jF3SD (Statistics - Overview - Light) + - 现有页面: Web/src/views/StatisticsView.vue + - 现有 API: Web/src/api/statistics.js + - Entity 层: Entity/*.cs + - Controller 层: WebApi/Controllers/*.cs + + ## 实施流程 + 1. 数据确认 → 等待批准 + 2. 交互确认 → 等待明确 + 3. 后端开发 → 测试通过 + 4. 前端开发 → 视觉验证 + 5. 集成测试 → 全部通过 + `, + run_in_background: false +) +``` + +## 快速参考 + +### 数据表确认模板 + +```markdown +## 数据表结构确认 + +### 现有实体分析 +✅ [实体名] - 支持 [功能] +❌ [实体名] - 缺少 [字段] + +### 需要变更 +1. **[表名] 新增字段** + - 字段名: [字段名] ([类型]) + - 用途: [用途说明] + - 原因: [为什么需要] + +2. **[表名] 修改字段** + - 字段名: [字段名] + - 变更: [原类型] → [新类型] + - 原因: [为什么修改] + +### 是否批准? +``` + +### 交互确认模板 + +```markdown +## 交互逻辑确认 + +### 1. [交互元素名称] +**设计位置:** [描述] +**问题:** [需要确认的问题] +**选项:** +- A. [选项1] +- B. [选项2] +- C. 其他:[请说明] + +### 2. [数据计算逻辑] +**设计显示:** [设计图显示的内容] +**问题:** [计算逻辑或数据来源] +**选项:** +- A. [选项1] +- B. [选项2] + +请逐一明确。 +``` + +### 样式映射表 + +| 设计图属性 | CSS 属性 | 示例 | +|-----------|---------|------| +| padding | padding | `padding: 24px` | +| gap | gap | `gap: 16px` | +| cornerRadius | border-radius | `border-radius: 12px` | +| fill | background | `background: #FFFFFF` | +| textColor | color | `color: #1A1A1A` | +| fontSize | font-size | `font-size: 14px` | +| fontWeight | font-weight | `font-weight: 600` | +| fontFamily | font-family | `font-family: 'DM Sans', sans-serif` | +| width: "fill_container" | width: 100% | `width: 100%` | +| height: "hug_contents" | height: auto | `height: auto` | +| layout: "vertical" | flex-direction: column | `flex-direction: column` | +| layout: "horizontal" | flex-direction: row | `flex-direction: row` | +| justifyContent: "space_between" | justify-content: space-between | `justify-content: space-between` | +| alignItems: "center" | align-items: center | `align-items: center` | + +--- + +**版本:** 1.0.0 +**最后更新:** 2026-02-03 +**维护者:** EmailBill 开发团队 diff --git a/.pans/v2.pen b/.pans/v2.pen index c660472..84026cd 100644 --- a/.pans/v2.pen +++ b/.pans/v2.pen @@ -6,12 +6,71 @@ "id": "jF3SD", "x": -983, "y": 369, - "name": "Statistics", + "name": "Statistics - Overview - Light", "clip": true, - "width": 402, + "width": 375, "fill": "#FFFFFF", "layout": "vertical", "children": [ + { + "type": "frame", + "id": "HdrL1", + "name": "header", + "width": "fill_container", + "gap": 4, + "padding": [ + 8, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "HdrC1", + "name": "headerContent", + "width": 142, + "height": 39, + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "TxtL1", + "name": "subtitle", + "fill": "#1a1a1a", + "content": "2026年", + "fontFamily": "DM Sans", + "fontSize": 24, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "BtnL1", + "name": "notifBtn", + "width": 44, + "height": 44, + "fill": "#F5F5F5", + "cornerRadius": 22, + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "IcnL1", + "name": "icon", + "width": 20, + "height": 20, + "iconFontName": "bell", + "iconFontFamily": "lucide", + "fill": "#1A1A1A" + } + ] + } + ] + }, { "type": "frame", "id": "AMTax", @@ -56,8 +115,8 @@ "id": "H0yK4", "name": "dayLabel", "fill": "#1A1A1A", - "content": "日", - "fontFamily": "Inter", + "content": "周", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "600" } @@ -77,8 +136,8 @@ "id": "qMr9M", "name": "weekLabel", "fill": "#888888", - "content": "周", - "fontFamily": "Inter", + "content": "月", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -98,8 +157,8 @@ "id": "NJAly", "name": "monthLabel", "fill": "#888888", - "content": "月", - "fontFamily": "Inter", + "content": "年", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -149,7 +208,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -174,7 +233,7 @@ "name": "card1Label", "fill": "#888888", "content": "总支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -230,7 +289,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -255,7 +314,7 @@ "name": "card2Label", "fill": "#888888", "content": "交易笔数", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -290,7 +349,7 @@ "name": "card2Bottom", "width": "fill_container", "height": 24, - "gap": 3, + "gap": 4, "alignItems": "end", "children": [ { @@ -383,7 +442,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -408,7 +467,7 @@ "name": "incomeLabel", "fill": "#888888", "content": "总收入", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -464,7 +523,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -489,7 +548,7 @@ "name": "balanceLabel", "fill": "#888888", "content": "净收支", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -593,7 +652,7 @@ "name": "linkText", "fill": "#0D6E6E", "content": "查看全部", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" }, @@ -624,7 +683,7 @@ }, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", @@ -968,7 +1027,7 @@ }, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", @@ -1319,7 +1378,7 @@ "name": "budgetLinkText", "fill": "#0D6E6E", "content": "管理预算", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" }, @@ -1350,7 +1409,7 @@ }, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", @@ -1745,8 +1804,8 @@ "id": "PlVHb", "x": -1413, "y": 369, - "name": "Calendar (Dark)", - "width": 402, + "name": "Calendar - Main View - Dark", + "width": 375, "fill": "#09090B", "layout": "vertical", "children": [ @@ -3229,7 +3288,7 @@ "cornerRadius": 20, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", @@ -3256,7 +3315,7 @@ "fill": "#451a03", "cornerRadius": 12, "padding": [ - 6, + 8, 10 ], "children": [ @@ -3329,7 +3388,7 @@ "fill": "#064e3b", "cornerRadius": 12, "padding": [ - 6, + 8, 12 ], "children": [ @@ -3351,9 +3410,9 @@ "name": "smartBtn", "fill": "#312e81", "cornerRadius": 12, - "gap": 6, + "gap": 8, "padding": [ - 6, + 8, 12 ], "alignItems": "center", @@ -3391,7 +3450,7 @@ "width": "fill_container", "fill": "#18181b", "cornerRadius": 16, - "gap": 14, + "gap": 16, "padding": 16, "alignItems": "center", "children": [ @@ -3467,7 +3526,7 @@ "width": "fill_container", "fill": "#18181b", "cornerRadius": 16, - "gap": 14, + "gap": 16, "padding": 16, "alignItems": "center", "children": [ @@ -3545,8 +3604,8 @@ "id": "uxvyO", "x": -1841, "y": 369, - "name": "Calendar", - "width": 402, + "name": "Calendar - Main View - Light", + "width": 375, "fill": "#FFFFFF", "layout": "vertical", "children": [ @@ -5004,20 +5063,10 @@ "id": "LMdOg", "name": "statsTitle", "fill": "#1A1A1A", - "content": "每日统计", + "content": "2026年1月12日", "fontFamily": "Bricolage Grotesque", "fontSize": 18, "fontWeight": "700" - }, - { - "type": "text", - "id": "1TSK3", - "name": "statsDate", - "fill": "#6B7280", - "content": "2026年1月12日", - "fontFamily": "DM Sans", - "fontSize": 13, - "fontWeight": "500" } ] }, @@ -5030,13 +5079,14 @@ "cornerRadius": 20, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", "id": "UiJYD", "name": "statsRow", - "width": "fill_container", + "width": 91, + "height": 17, "justifyContent": "space_between", "alignItems": "center", "children": [ @@ -5045,33 +5095,10 @@ "id": "QBHWG", "name": "statsLabel", "fill": "#6B7280", - "content": "Total Spent", + "content": "支出", "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" - }, - { - "type": "frame", - "id": "6TcZ1", - "name": "b1", - "fill": "#FFFBEB", - "cornerRadius": 12, - "padding": [ - 6, - 10 - ], - "children": [ - { - "type": "text", - "id": "znYtx", - "name": "t29", - "fill": "#D97706", - "content": "Daily Limit: 2500", - "fontFamily": "DM Sans", - "fontSize": 11, - "fontWeight": "600" - } - ] } ] }, @@ -5130,7 +5157,7 @@ "fill": "#F0FDF4", "cornerRadius": 12, "padding": [ - 6, + 8, 12 ], "children": [ @@ -5152,9 +5179,9 @@ "name": "smartBtn", "fill": "#E0E7FF", "cornerRadius": 12, - "gap": 6, + "gap": 8, "padding": [ - 6, + 8, 12 ], "alignItems": "center", @@ -5192,7 +5219,7 @@ "width": "fill_container", "fill": "#F6F7F8", "cornerRadius": 16, - "gap": 14, + "gap": 16, "padding": 16, "alignItems": "center", "children": [ @@ -5268,7 +5295,7 @@ "width": "fill_container", "fill": "#F6F7F8", "cornerRadius": 16, - "gap": 14, + "gap": 16, "padding": 16, "alignItems": "center", "children": [ @@ -5346,12 +5373,71 @@ "id": "SNGbj", "x": -538, "y": 369, - "name": "Statistics Dark", + "name": "Statistics - Overview - Dark", "clip": true, - "width": 402, - "fill": "#0F0F0F", + "width": 375, + "fill": "#09090B", "layout": "vertical", "children": [ + { + "type": "frame", + "id": "HdrD1", + "name": "header", + "width": "fill_container", + "gap": 4, + "padding": [ + 8, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "HdrCD1", + "name": "headerContent", + "width": 142, + "height": 39, + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "TxtD1", + "name": "subtitle", + "fill": "#a1a1aa", + "content": "2026年", + "fontFamily": "DM Sans", + "fontSize": 24, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "BtnD1", + "name": "notifBtn", + "width": 44, + "height": 44, + "fill": "#27272a", + "cornerRadius": 22, + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "IcnD1", + "name": "icon", + "width": 20, + "height": 20, + "iconFontName": "bell", + "iconFontFamily": "lucide", + "fill": "#f4f4f5" + } + ] + } + ] + }, { "type": "frame", "id": "Ne4V3", @@ -5397,7 +5483,7 @@ "name": "dayLabel", "fill": "#1A1A1A", "content": "日", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "600" } @@ -5419,7 +5505,7 @@ "name": "weekLabel", "fill": "#888888", "content": "周", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -5441,7 +5527,7 @@ "name": "monthLabel", "fill": "#888888", "content": "月", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -5491,7 +5577,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -5516,7 +5602,7 @@ "name": "card1Label", "fill": "#888888", "content": "总支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -5572,7 +5658,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -5597,7 +5683,7 @@ "name": "card2Label", "fill": "#888888", "content": "交易笔数", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -5632,7 +5718,7 @@ "name": "card2Bottom", "width": "fill_container", "height": 24, - "gap": 3, + "gap": 4, "alignItems": "end", "children": [ { @@ -5725,7 +5811,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -5750,7 +5836,7 @@ "name": "incomeLabel", "fill": "#888888", "content": "总收入", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -5806,7 +5892,7 @@ }, "layout": "vertical", "gap": 12, - "padding": 20, + "padding": 24, "justifyContent": "space_between", "children": [ { @@ -5831,7 +5917,7 @@ "name": "balanceLabel", "fill": "#888888", "content": "净收支", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "500" }, @@ -5935,7 +6021,7 @@ "name": "linkText", "fill": "#0D6E6E", "content": "查看全部", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" }, @@ -5966,7 +6052,7 @@ }, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", @@ -6310,7 +6396,7 @@ }, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", @@ -6661,7 +6747,7 @@ "name": "budgetLinkText", "fill": "#0D6E6E", "content": "管理预算", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" }, @@ -6692,7 +6778,7 @@ }, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "frame", @@ -7085,13 +7171,72 @@ { "type": "frame", "id": "BCxxA", - "x": -36, + "x": -100, "y": 369, - "name": "Budget Stats (Light)", - "width": 402, + "name": "Budget - Statistics - Light", + "width": 375, "fill": "#FFFFFF", "layout": "vertical", "children": [ + { + "type": "frame", + "id": "HdrL2", + "name": "header", + "width": "fill_container", + "gap": 4, + "padding": [ + 8, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "HdrCL2", + "name": "headerContent", + "width": 142, + "height": 39, + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "TxtL2", + "name": "subtitle", + "fill": "#1a1a1a", + "content": "2026年1月", + "fontFamily": "DM Sans", + "fontSize": 24, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "BtnL2", + "name": "notifBtn", + "width": 44, + "height": 44, + "fill": "#F5F5F5", + "cornerRadius": 22, + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "IcnL2", + "name": "icon", + "width": 20, + "height": 20, + "iconFontName": "bell", + "iconFontFamily": "lucide", + "fill": "#1A1A1A" + } + ] + } + ] + }, { "type": "frame", "id": "LXM6A", @@ -7099,55 +7244,6 @@ "width": "fill_container", "layout": "vertical", "children": [ - { - "type": "frame", - "id": "4R8MH", - "name": "navTop", - "width": "fill_container", - "padding": [ - 12, - 16 - ], - "justifyContent": "space_between", - "alignItems": "center", - "children": [ - { - "type": "frame", - "id": "5kBJY", - "name": "dateBtn", - "fill": "#F5F5F5", - "cornerRadius": 8, - "gap": 4, - "padding": [ - 6, - 12 - ], - "alignItems": "center", - "children": [ - { - "type": "text", - "id": "N6BSi", - "name": "dateText", - "fill": "#18181B", - "content": "2026年1月", - "fontFamily": "Inter", - "fontSize": 16, - "fontWeight": "500" - }, - { - "type": "icon_font", - "id": "wSRWZ", - "name": "dateIcon", - "width": 16, - "height": 16, - "iconFontName": "chevron-down", - "iconFontFamily": "lucide", - "fill": "#71717A" - } - ] - } - ] - }, { "type": "frame", "id": "SRWHt", @@ -7178,7 +7274,7 @@ "name": "tab1Text", "fill": "#1A1A1A", "content": "支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "600" } @@ -7203,7 +7299,7 @@ "name": "tab2Text", "fill": "#888888", "content": "收入", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -7228,7 +7324,7 @@ "name": "tab3Text", "fill": "#888888", "content": "计划", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -7256,7 +7352,7 @@ "cornerRadius": 16, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "text", @@ -7264,7 +7360,7 @@ "name": "overallTitle", "fill": "#71717A", "content": "本月预算", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 14, "fontWeight": "normal" }, @@ -7289,7 +7385,7 @@ "name": "overallCurrent", "fill": "#18181B", "content": "¥8,523", - "fontFamily": "Inter", + "fontFamily": "Bricolage Grotesque", "fontSize": 28, "fontWeight": "700" }, @@ -7299,7 +7395,7 @@ "name": "overallLimit", "fill": "#71717A", "content": "/ ¥15,000", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "normal" } @@ -7311,7 +7407,7 @@ "name": "overallPercent", "fill": "#18181B", "content": "56.8%", - "fontFamily": "Inter", + "fontFamily": "Bricolage Grotesque", "fontSize": 24, "fontWeight": "600" } @@ -7372,7 +7468,7 @@ "name": "cardTitle1", "fill": "#18181B", "content": "餐饮美食", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "600" }, @@ -7393,7 +7489,7 @@ "name": "cardBadgeText1", "fill": "#3B82F6", "content": "月度", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" } @@ -7441,7 +7537,7 @@ "name": "cardLabel1", "fill": "#71717A", "content": "已支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -7451,7 +7547,7 @@ "name": "cardAmount1", "fill": "#EF4444", "content": "¥2,456", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -7471,7 +7567,7 @@ "name": "cardLabel2", "fill": "#71717A", "content": "余额", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -7481,7 +7577,7 @@ "name": "cardAmount2", "fill": "#10B981", "content": "¥544", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -7516,7 +7612,7 @@ "name": "cardTitle2", "fill": "#18181B", "content": "交通出行", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "600" }, @@ -7537,7 +7633,7 @@ "name": "cardBadgeText2", "fill": "#F59E0B", "content": "年度", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" } @@ -7585,7 +7681,7 @@ "name": "cardLabel3", "fill": "#71717A", "content": "已支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -7595,7 +7691,7 @@ "name": "cardAmount3", "fill": "#EF4444", "content": "¥1,280", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -7615,7 +7711,7 @@ "name": "cardLabel4", "fill": "#71717A", "content": "余额", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -7625,7 +7721,7 @@ "name": "cardAmount4", "fill": "#10B981", "content": "¥3,720", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -7660,7 +7756,7 @@ "name": "cardTitle3", "fill": "#18181B", "content": "购物消费", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "600" }, @@ -7681,7 +7777,7 @@ "name": "cardBadgeText3", "fill": "#3B82F6", "content": "月度", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" } @@ -7729,7 +7825,7 @@ "name": "cardLabel5", "fill": "#71717A", "content": "已支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -7739,7 +7835,7 @@ "name": "cardAmount5", "fill": "#EF4444", "content": "¥1,850", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -7759,7 +7855,7 @@ "name": "cardLabel6", "fill": "#71717A", "content": "余额", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -7769,7 +7865,7 @@ "name": "cardAmount6", "fill": "#10B981", "content": "¥650", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -7782,47 +7878,78 @@ ] } ] - }, - { - "type": "frame", - "id": "hzxw1", - "name": "fab", - "width": 56, - "height": 56, - "fill": "#3B82F6", - "cornerRadius": 28, - "stroke": { - "align": "outside", - "thickness": 4, - "fill": "#FFFFFF" - }, - "justifyContent": "center", - "alignItems": "center", - "children": [ - { - "type": "icon_font", - "id": "VDoNM", - "name": "fabIcon", - "width": 24, - "height": 24, - "iconFontName": "pencil", - "iconFontFamily": "lucide", - "fill": "#FFFFFF" - } - ] } ] }, { "type": "frame", "id": "zYTEk", - "x": 466, + "x": 402, "y": 369, - "name": "Budget Stats (Dark)", - "width": 402, - "fill": "#0F0F0F", + "name": "Budget - Statistics - Dark", + "width": 375, + "fill": "#09090B", "layout": "vertical", "children": [ + { + "type": "frame", + "id": "HdrD2", + "name": "header", + "width": "fill_container", + "gap": 4, + "padding": [ + 8, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "HdrCD2", + "name": "headerContent", + "width": 142, + "height": 39, + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "TxtD2", + "name": "subtitle", + "fill": "#a1a1aa", + "content": "2026年1月", + "fontFamily": "DM Sans", + "fontSize": 24, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "BtnD2", + "name": "notifBtn", + "width": 44, + "height": 44, + "fill": "#27272a", + "cornerRadius": 22, + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "IcnD2", + "name": "icon", + "width": 20, + "height": 20, + "iconFontName": "bell", + "iconFontFamily": "lucide", + "fill": "#f4f4f5" + } + ] + } + ] + }, { "type": "frame", "id": "UcVry", @@ -7830,55 +7957,6 @@ "width": "fill_container", "layout": "vertical", "children": [ - { - "type": "frame", - "id": "yKDZC", - "name": "navTop", - "width": "fill_container", - "padding": [ - 12, - 16 - ], - "justifyContent": "space_between", - "alignItems": "center", - "children": [ - { - "type": "frame", - "id": "QTSlc", - "name": "dateBtn", - "fill": "#27272A", - "cornerRadius": 8, - "gap": 4, - "padding": [ - 6, - 12 - ], - "alignItems": "center", - "children": [ - { - "type": "text", - "id": "hEsJE", - "name": "dateText", - "fill": "#FAFAFA", - "content": "2026年1月", - "fontFamily": "Inter", - "fontSize": 16, - "fontWeight": "500" - }, - { - "type": "icon_font", - "id": "SrQTT", - "name": "dateIcon", - "width": 16, - "height": 16, - "iconFontName": "chevron-down", - "iconFontFamily": "lucide", - "fill": "#A1A1AA" - } - ] - } - ] - }, { "type": "frame", "id": "TmjqN", @@ -7909,7 +7987,7 @@ "name": "tab1Text", "fill": "#FFFFFF", "content": "支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "600" } @@ -7934,7 +8012,7 @@ "name": "tab2Text", "fill": "#888888", "content": "收入", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -7959,7 +8037,7 @@ "name": "tab3Text", "fill": "#888888", "content": "计划", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 13, "fontWeight": "500" } @@ -7987,7 +8065,7 @@ "cornerRadius": 16, "layout": "vertical", "gap": 16, - "padding": 20, + "padding": 24, "children": [ { "type": "text", @@ -7995,7 +8073,7 @@ "name": "overallTitle", "fill": "#A1A1AA", "content": "本月预算", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 14, "fontWeight": "normal" }, @@ -8020,7 +8098,7 @@ "name": "overallCurrent", "fill": "#FAFAFA", "content": "¥8,523", - "fontFamily": "Inter", + "fontFamily": "Bricolage Grotesque", "fontSize": 28, "fontWeight": "700" }, @@ -8030,7 +8108,7 @@ "name": "overallLimit", "fill": "#A1A1AA", "content": "/ ¥15,000", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "normal" } @@ -8042,7 +8120,7 @@ "name": "overallPercent", "fill": "#FAFAFA", "content": "56.8%", - "fontFamily": "Inter", + "fontFamily": "Bricolage Grotesque", "fontSize": 24, "fontWeight": "600" } @@ -8103,7 +8181,7 @@ "name": "cardTitle1", "fill": "#FAFAFA", "content": "餐饮美食", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "600" }, @@ -8124,7 +8202,7 @@ "name": "cardBadgeText1", "fill": "#60A5FA", "content": "月度", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" } @@ -8172,7 +8250,7 @@ "name": "cardLabel1", "fill": "#A1A1AA", "content": "已支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -8182,7 +8260,7 @@ "name": "cardAmount1", "fill": "#EF4444", "content": "¥2,456", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -8202,7 +8280,7 @@ "name": "cardLabel2", "fill": "#A1A1AA", "content": "余额", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -8212,7 +8290,7 @@ "name": "cardAmount2", "fill": "#10B981", "content": "¥544", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -8247,7 +8325,7 @@ "name": "cardTitle2", "fill": "#FAFAFA", "content": "交通出行", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "600" }, @@ -8268,7 +8346,7 @@ "name": "cardBadgeText2", "fill": "#FCD34D", "content": "年度", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" } @@ -8316,7 +8394,7 @@ "name": "cardLabel3", "fill": "#A1A1AA", "content": "已支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -8326,7 +8404,7 @@ "name": "cardAmount3", "fill": "#EF4444", "content": "¥1,280", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -8346,7 +8424,7 @@ "name": "cardLabel4", "fill": "#A1A1AA", "content": "余额", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -8356,7 +8434,7 @@ "name": "cardAmount4", "fill": "#10B981", "content": "¥3,720", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -8391,7 +8469,7 @@ "name": "cardTitle3", "fill": "#FAFAFA", "content": "购物消费", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 16, "fontWeight": "600" }, @@ -8412,7 +8490,7 @@ "name": "cardBadgeText3", "fill": "#60A5FA", "content": "月度", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" } @@ -8460,7 +8538,7 @@ "name": "cardLabel5", "fill": "#A1A1AA", "content": "已支出", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -8470,7 +8548,7 @@ "name": "cardAmount5", "fill": "#EF4444", "content": "¥1,850", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -8490,7 +8568,7 @@ "name": "cardLabel6", "fill": "#A1A1AA", "content": "余额", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 12, "fontWeight": "normal" }, @@ -8500,7 +8578,7 @@ "name": "cardAmount6", "fill": "#10B981", "content": "¥650", - "fontFamily": "Inter", + "fontFamily": "DM Sans", "fontSize": 18, "fontWeight": "600" } @@ -8513,566 +8591,6 @@ ] } ] - }, - { - "type": "frame", - "id": "Euc04", - "name": "fab", - "width": 56, - "height": 56, - "fill": "#3B82F6", - "cornerRadius": 28, - "stroke": { - "align": "outside", - "thickness": 4, - "fill": "#FFFFFF" - }, - "justifyContent": "center", - "alignItems": "center", - "children": [ - { - "type": "icon_font", - "id": "zKjKJ", - "name": "fabIcon", - "width": 24, - "height": 24, - "iconFontName": "pencil", - "iconFontFamily": "lucide", - "fill": "#FFFFFF" - } - ] - } - ] - }, - { - "type": "frame", - "id": "Rg59j", - "x": 900, - "y": 369, - "name": "Budget Analytics", - "width": 402, - "fill": "#F5F7FA", - "layout": "vertical", - "gap": 16, - "padding": 20, - "children": [ - { - "type": "frame", - "id": "Nq8k9", - "name": "header", - "width": "fill_container", - "layout": "vertical", - "gap": 8, - "children": [ - { - "type": "text", - "id": "j7M8v", - "name": "titleText", - "fill": "#1A1A1A", - "content": "预算分析", - "fontFamily": "Inter", - "fontSize": 24, - "fontWeight": "700" - }, - { - "type": "text", - "id": "Dj7FE", - "name": "dateText", - "fill": "#6B7280", - "content": "2026年1月", - "fontFamily": "Inter", - "fontSize": 14, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "rUUNa", - "name": "remainCard", - "width": "fill_container", - "fill": "#FFFFFF", - "cornerRadius": 12, - "layout": "vertical", - "gap": 12, - "padding": 16, - "children": [ - { - "type": "text", - "id": "Lsb2n", - "name": "remainTitle", - "fill": "#6B7280", - "content": "预算剩余", - "fontFamily": "Inter", - "fontSize": 14, - "fontWeight": "500" - }, - { - "type": "text", - "id": "FPMMo", - "name": "remainAmount", - "fill": "#10B981", - "content": "¥3,250", - "fontFamily": "Inter", - "fontSize": 32, - "fontWeight": "700" - }, - { - "type": "text", - "id": "lMDn7", - "name": "remainDesc", - "fill": "#9CA3AF", - "content": "总预算 ¥10,000 | 已使用 68%", - "fontFamily": "Inter", - "fontSize": 12, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "12oX3", - "name": "burndownCard", - "width": "fill_container", - "fill": "#FFFFFF", - "cornerRadius": 12, - "layout": "vertical", - "gap": 12, - "padding": 16, - "children": [ - { - "type": "text", - "id": "XIsI0", - "name": "burnTitle", - "fill": "#1A1A1A", - "content": "预算燃尽图", - "fontFamily": "Inter", - "fontSize": 16, - "fontWeight": "600" - }, - { - "type": "frame", - "id": "ryx65", - "name": "chartArea1", - "width": "fill_container", - "height": 200, - "fill": "#F9FAFB", - "cornerRadius": 8, - "layout": "none", - "children": [ - { - "type": "line", - "id": "h85XX", - "x": 20, - "y": 20, - "name": "idealLine", - "width": 310, - "height": 160, - "stroke": { - "thickness": 2, - "fill": "#D1D5DB" - } - }, - { - "type": "path", - "id": "c6Bys", - "x": 20, - "y": 20, - "name": "actualLine", - "geometry": "M0 0l100 40 100 60 110 40", - "width": 310, - "height": 160, - "stroke": { - "thickness": 3, - "fill": "#3B82F6" - } - }, - { - "type": "ellipse", - "id": "RQY2V", - "x": 15, - "y": 15, - "name": "point1", - "fill": "#3B82F6", - "width": 10, - "height": 10 - }, - { - "type": "ellipse", - "id": "SE4dr", - "x": 115, - "y": 55, - "name": "point2", - "fill": "#3B82F6", - "width": 10, - "height": 10 - }, - { - "type": "ellipse", - "id": "9fGPC", - "x": 215, - "y": 115, - "name": "point3", - "fill": "#3B82F6", - "width": 10, - "height": 10 - }, - { - "type": "ellipse", - "id": "7HZDX", - "x": 315, - "y": 155, - "name": "point4", - "fill": "#3B82F6", - "width": 10, - "height": 10 - } - ] - }, - { - "type": "frame", - "id": "maILp", - "name": "legend1", - "width": "fill_container", - "gap": 16, - "justifyContent": "center", - "children": [ - { - "type": "frame", - "id": "VyDB0", - "name": "ideal", - "gap": 6, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "id": "RkZ6D", - "name": "idealDot", - "fill": "#D1D5DB", - "width": 12, - "height": 2 - }, - { - "type": "text", - "id": "tpHGY", - "name": "idealLabel", - "fill": "#6B7280", - "content": "理想消耗", - "fontFamily": "Inter", - "fontSize": 12, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "7yVCB", - "name": "actual", - "gap": 6, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "id": "sBgPG", - "name": "actualDot", - "fill": "#3B82F6", - "width": 12, - "height": 2 - }, - { - "type": "text", - "id": "XkLVA", - "name": "actualLabel", - "fill": "#6B7280", - "content": "实际消耗", - "fontFamily": "Inter", - "fontSize": 12, - "fontWeight": "normal" - } - ] - } - ] - } - ] - }, - { - "type": "frame", - "id": "GiIWx", - "name": "varianceCard", - "width": "fill_container", - "fill": "#FFFFFF", - "cornerRadius": 12, - "layout": "vertical", - "gap": 12, - "padding": 16, - "children": [ - { - "type": "text", - "id": "6Kket", - "name": "varTitle", - "fill": "#1A1A1A", - "content": "预算偏差分析", - "fontFamily": "Inter", - "fontSize": 16, - "fontWeight": "600" - }, - { - "type": "frame", - "id": "i5z6p", - "name": "chartArea2", - "width": "fill_container", - "height": 240, - "fill": "#F9FAFB", - "cornerRadius": 8, - "layout": "none", - "children": [ - { - "type": "frame", - "id": "TFE88", - "x": 30, - "y": 180, - "name": "bar1Frame", - "width": 50, - "height": 80, - "layout": "vertical", - "gap": 4, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "cornerRadius": 4, - "id": "uWXEp", - "name": "bar1", - "fill": "#EF4444", - "width": 40, - "height": 60 - }, - { - "type": "text", - "id": "b1CKO", - "name": "label1", - "fill": "#6B7280", - "content": "餐饮", - "fontFamily": "Inter", - "fontSize": 11, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "lHkYX", - "x": 100, - "y": 140, - "name": "bar2Frame", - "width": 50, - "height": 120, - "layout": "vertical", - "gap": 4, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "cornerRadius": 4, - "id": "UCaPk", - "name": "bar2", - "fill": "#F59E0B", - "width": 40, - "height": 100 - }, - { - "type": "text", - "id": "qpX6L", - "name": "label2", - "fill": "#6B7280", - "content": "交通", - "fontFamily": "Inter", - "fontSize": 11, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "XzvK2", - "x": 170, - "y": 160, - "name": "bar3Frame", - "width": 50, - "height": 100, - "layout": "vertical", - "gap": 4, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "cornerRadius": 4, - "id": "IJypN", - "name": "bar3", - "fill": "#10B981", - "width": 40, - "height": 80 - }, - { - "type": "text", - "id": "inAFN", - "name": "label3", - "fill": "#6B7280", - "content": "购物", - "fontFamily": "Inter", - "fontSize": 11, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "mwQeE", - "x": 240, - "y": 150, - "name": "bar4Frame", - "width": 50, - "height": 110, - "layout": "vertical", - "gap": 4, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "cornerRadius": 4, - "id": "jgJBQ", - "name": "bar4", - "fill": "#3B82F6", - "width": 40, - "height": 90 - }, - { - "type": "text", - "id": "hweUc", - "name": "label4", - "fill": "#6B7280", - "content": "娱乐", - "fontFamily": "Inter", - "fontSize": 11, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "o1uNO", - "x": 310, - "y": 190, - "name": "bar5Frame", - "width": 50, - "height": 70, - "layout": "vertical", - "gap": 4, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "cornerRadius": 4, - "id": "0MwYg", - "name": "bar5", - "fill": "#8B5CF6", - "width": 40, - "height": 50 - }, - { - "type": "text", - "id": "9iXy6", - "name": "label5", - "fill": "#6B7280", - "content": "其他", - "fontFamily": "Inter", - "fontSize": 11, - "fontWeight": "normal" - } - ] - }, - { - "type": "line", - "id": "98osi", - "x": 20, - "y": 200, - "name": "axisLine", - "width": 350, - "height": 0, - "stroke": { - "thickness": 1, - "fill": "#E5E7EB" - } - } - ] - }, - { - "type": "frame", - "id": "aOAnk", - "name": "legend2", - "width": "fill_container", - "gap": 12, - "justifyContent": "center", - "children": [ - { - "type": "frame", - "id": "yKfLI", - "name": "over", - "gap": 6, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "cornerRadius": 2, - "id": "tsGkr", - "name": "overDot", - "fill": "#EF4444", - "width": 8, - "height": 8 - }, - { - "type": "text", - "id": "cMKm7", - "name": "overLabel", - "fill": "#6B7280", - "content": "超支", - "fontFamily": "Inter", - "fontSize": 12, - "fontWeight": "normal" - } - ] - }, - { - "type": "frame", - "id": "BL57C", - "name": "normal", - "gap": 6, - "alignItems": "center", - "children": [ - { - "type": "rectangle", - "cornerRadius": 2, - "id": "1Vkqa", - "name": "normalDot", - "fill": "#10B981", - "width": 8, - "height": 8 - }, - { - "type": "text", - "id": "MSLKe", - "name": "normalLabel", - "fill": "#6B7280", - "content": "正常", - "fontFamily": "Inter", - "fontSize": 12, - "fontWeight": "normal" - } - ] - } - ] - } - ] } ] } diff --git a/.sisyphus/notepads/calendar-refactor/learnings.md b/.sisyphus/notepads/calendar-refactor/learnings.md new file mode 100644 index 0000000..6718db9 --- /dev/null +++ b/.sisyphus/notepads/calendar-refactor/learnings.md @@ -0,0 +1,325 @@ + +## Vue 3 Composition API Research - Modular Architecture Best Practices + +### 研究日期: 2026-02-03 + +--- + +## 1. 官方 Vue 3 组件组织原则 + +### 1.1 Composables 用于代码组织 +来源: Vue 官方文档 - https://vuejs.org/guide/reusability/composables + +**核心原则:** +- Composables 不仅用于复用,也用于**代码组织** +- 当组件变得过于复杂时,应该将逻辑按**关注点分离**提取到更小的函数中 +- 可以将提取的 composables 视为**组件级别的服务**,它们可以相互通信 + +**官方示例模式:** +```vue + +``` + +**关键洞察:** +- Composables 应返回**普通对象**包含多个 refs,保持响应式 +- 避免返回 reactive 对象,因为解构会失去响应性 +- Composables 可以接收其他 composables 的返回值作为参数 + +--- + +## 2. 代码分割与懒加载 + +### 2.1 defineAsyncComponent 用于模块懒加载 +来源: Vue 官方文档 - https://github.com/vuejs/docs/blob/main/src/guide/best-practices/performance.md + +**适用场景:** +- 将大型组件树分割成独立的 chunks +- 仅在组件渲染时才加载,改善初始加载时间 + +```js +import { defineAsyncComponent } from 'vue' + +// Foo.vue 及其依赖被单独打包成一个 chunk +// 只有在组件被渲染时才会按需获取 +const Foo = defineAsyncComponent(() => import('./Foo.vue')) +``` + +### 2.2 动态导入用于 JS 代码分割 +```js +// lazy.js 及其依赖会被分割成单独的 chunk +// 只在 loadLazy() 被调用时才加载 +function loadLazy() { + return import('./lazy.js') +} +``` + +--- + +## 3. 真实世界的模块化架构模式 + +### 3.1 Dashboard 模块化架构 - 成功案例 + +**案例 1: Soybean Admin (MIT License)** +来源: https://github.com/soybeanjs/soybean-admin/blob/main/src/views/home/index.vue + +```vue + +``` + +**架构特点:** +- Index.vue 作为**容器组件**,只负责布局和响应式计算 +- 每个 modules/*.vue 是**独立的功能模块** +- 模块命名清晰: header-banner, card-data, line-chart 等 +- 使用 Pinia store 进行状态共享 + +**案例 2: Art Design Pro (MIT License)** +来源: https://github.com/Daymychen/art-design-pro/blob/main/src/views/dashboard/ecommerce/index.vue + +```vue + +``` + +**架构特点:** +- 电商 dashboard 包含 13 个独立模块 +- 每个模块代表一个业务功能卡片 +- Index.vue **不传递数据**,模块自治 + +--- + +## 4. 模块间通信模式 + +### 4.1 defineEmits 用于子到父通信 +来源: Vue 核心仓库 - https://github.com/vuejs/core/blob/main/packages/runtime-core/src/apiSetupHelpers.ts + +**TypeScript 类型声明模式:** +```ts +const emit = defineEmits<{ + 'update:modelValue': [value: string]; + 'change': [event: Event]; + 'custom-event': [payload: CustomPayload]; +}>(); +``` + +**Runtime 声明模式:** +```js +const emit = defineEmits(['change', 'update']) +``` + +### 4.2 Props 模式 - 数据传递 vs 自取数据 + +**案例研究: Halo CMS (GPL-3.0)** +来源: https://github.com/halo-dev/halo/blob/main/ui/console-src/modules/system/users/components/GrantPermissionModal.vue + +```vue + +``` + +**模式总结:** +- **Props 传递身份标识** (如 user ID),而非完整数据 +- **模块自己获取详细数据** (通过 composables) +- 这样保持模块的**高内聚低耦合** + +--- + +## 5. 何时模块应该自取数据 vs 接收 Props + +### 5.1 自取数据的场景 +- 模块是**独立的业务单元**(如日历、统计卡片) +- 数据获取逻辑属于模块内部关注点 +- 模块需要**定期刷新**或**重新加载**数据 +- 多个平行模块各自管理自己的状态 + +**示例:** +```vue + + +``` + +### 5.2 接收 Props 的场景 +- 模块是**展示组件**(Presentational Component) +- 父组件需要**协调多个子组件**的数据 +- 数据来源于**全局状态管理**(如 Pinia store) +- 需要在父组件层面做**数据聚合或转换** + +**示例:** +```vue + + +``` + +--- + +## 6. TypeScript vs JavaScript 在 Vue 3 项目中 + +### 6.1 EmailBill 项目的选择 +**当前状况:** +- ESLint 配置中禁用了 TypeScript 规则 +- 使用 ` - - diff --git a/Web/src/views/calendarV2/Index.vue b/Web/src/views/calendarV2/Index.vue new file mode 100644 index 0000000..6c36351 --- /dev/null +++ b/Web/src/views/calendarV2/Index.vue @@ -0,0 +1,380 @@ + + + + + diff --git a/Web/src/views/calendarV2/modules/Calendar.vue b/Web/src/views/calendarV2/modules/Calendar.vue new file mode 100644 index 0000000..c69fafc --- /dev/null +++ b/Web/src/views/calendarV2/modules/Calendar.vue @@ -0,0 +1,453 @@ + + + + + diff --git a/Web/src/views/calendarV2/modules/Stats.vue b/Web/src/views/calendarV2/modules/Stats.vue new file mode 100644 index 0000000..81a2953 --- /dev/null +++ b/Web/src/views/calendarV2/modules/Stats.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/Web/src/views/calendarV2/modules/TransactionList.vue b/Web/src/views/calendarV2/modules/TransactionList.vue new file mode 100644 index 0000000..860573d --- /dev/null +++ b/Web/src/views/calendarV2/modules/TransactionList.vue @@ -0,0 +1,387 @@ + + + + +