UI 重设计(P1-P5): - 主题从靛蓝 accent 切换为近黑白单色(#111318),品牌感靠排版与财务语义色 - 暗色模式适配 OLED 纯黑,财务色整体提亮一档保证对比度 - 圆角体系新增 xl(24),卡片/弹窗统一大圆角 - 移除 Caveat/Quicksand 自定义字体,切换为系统字体 - 新增 display(34/800) 字阶用于净资产等大数字展示 - 分类/标签/渠道颜色统一到 palette.ts 色板(12 色循环 + FNV 哈希) 记一笔 NumpadSheet(P3 录入闭环): - 全新计算器风格录入面板:方向 chip → 大金额 → 账户 chip → 分类快捷行 → 数字键盘 - 支持表达式输入(20+15-5.5)与字符串十进制求值,避免浮点精度问题 - 高级模式保留多行分录编辑器,简单模式自动推断双腿 - postingLegs.ts 实现 buildPostings 的逆操作(编辑/草稿预填) - 未保存守卫:整页 beforeRemove + modal 脏状态上报 - 全局弹层(NumpadSheetHost)由+按钮唤起,可通过设置开关 Tab 栏重构: - AppTabBar 替换默认 tab bar,中央+按钮唤起全局记账面板 - Tab 从 5 个精简为 4 个(首页/交易/报表/设置),+按钮独立 管理页模板 ManagementScreen: - 统一「列表 + 新增 + 点按编辑 + 长按删除 + FormModal」模式 - 预算/分类/标签/信用卡等管理页消除手写样板 其他: - OCR 模型从 PP-OCRv5 升级到 v6(det/rec/dict 全部替换) - GBK 解码器 import 修复(text-encoding-gbk ESM 兼容) - 批量确认失败数计算修正(用 failedPayees.length 替代 store 快照差值) - 新增 diagnostics 诊断页、TodoStrip 首页待办条、SwipeableTransactionCard 左滑操作 - periodNav.ts 报表周期导航(anchor+period 统一状态管理,本地时区运算) - 测试覆盖 numpadExpression/postingLegs/periodNav/palette/search/transactionGroups/categoryIcons/dateGrid
99 lines
4.1 KiB
TypeScript
99 lines
4.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { lightTheme, darkTheme, presetThemes } from '../src/theme/presets';
|
||
import { createTheme } from '../src/theme/createTheme';
|
||
import type { ThemeTokens } from '../src/theme/tokens';
|
||
|
||
describe('预置主题(明亮 Bento 设计系统)', () => {
|
||
it('lightTheme 与 darkTheme 关键色彩不同', () => {
|
||
expect(lightTheme.colors.bgPrimary).not.toBe(darkTheme.colors.bgPrimary);
|
||
expect(lightTheme.colors.fgPrimary).not.toBe(darkTheme.colors.fgPrimary);
|
||
});
|
||
|
||
it('浅色背景为 #F6F7F9,暗色为 OLED 纯黑 #040508', () => {
|
||
expect(lightTheme.colors.bgPrimary).toBe('#F6F7F9');
|
||
expect(darkTheme.colors.bgPrimary).toBe('#040508');
|
||
});
|
||
|
||
it('accent 为近黑白单色:浅色 #111318,暗色反转为 #F3F4F6', () => {
|
||
expect(lightTheme.colors.accent).toBe('#111318');
|
||
expect(darkTheme.colors.accent).toBe('#F3F4F6');
|
||
});
|
||
|
||
it('accentLight 为 accent 的半透明底色(不再是靛蓝族)', () => {
|
||
expect(lightTheme.colors.accentLight).toBe('rgba(17,19,24,0.08)');
|
||
expect(darkTheme.colors.accentLight).toBe('rgba(243,244,246,0.10)');
|
||
});
|
||
|
||
it('财务语义色:暗色整体提亮一档', () => {
|
||
expect(lightTheme.colors.financial.income).toBe('#10B981');
|
||
expect(lightTheme.colors.financial.expense).toBe('#EF4444');
|
||
expect(lightTheme.colors.financial.transfer).toBe('#3B82F6');
|
||
expect(darkTheme.colors.financial.income).toBe('#34D399');
|
||
expect(darkTheme.colors.financial.expense).toBe('#F87171');
|
||
expect(darkTheme.colors.financial.transfer).toBe('#60A5FA');
|
||
});
|
||
|
||
it('圆角含 xl(24),卡片默认大圆角', () => {
|
||
for (const theme of [lightTheme, darkTheme]) {
|
||
expect(theme.radii.sm).toBe(8);
|
||
expect(theme.radii.md).toBe(12);
|
||
expect(theme.radii.lg).toBe(16);
|
||
expect(theme.radii.xl).toBe(24);
|
||
expect(theme.radii.full).toBe(9999);
|
||
}
|
||
});
|
||
|
||
it('字阶含 display(34),且不指定 fontFamily(系统字体)', () => {
|
||
for (const theme of [lightTheme, darkTheme] as ThemeTokens[]) {
|
||
expect(theme.typography.display.fontSize).toBe(34);
|
||
expect(theme.typography.h1.fontSize).toBe(28);
|
||
expect(theme.typography.h2.fontSize).toBe(22);
|
||
expect(theme.typography.h3.fontSize).toBe(17);
|
||
for (const key of ['display', 'h1', 'h2', 'h3', 'body', 'bodySmall', 'caption'] as const) {
|
||
expect(theme.typography[key].fontFamily).toBeUndefined();
|
||
}
|
||
}
|
||
});
|
||
|
||
it('presetThemes 注册表含 light 与 dark', () => {
|
||
expect(presetThemes.light).toBe(lightTheme);
|
||
expect(presetThemes.dark).toBe(darkTheme);
|
||
});
|
||
|
||
it('spacing/shadows 结构完整', () => {
|
||
for (const theme of [lightTheme, darkTheme] as ThemeTokens[]) {
|
||
expect(theme.spacing).toHaveProperty('xs');
|
||
expect(theme.spacing).toHaveProperty('xl');
|
||
expect(theme.shadows).toHaveProperty('sm');
|
||
expect(theme.shadows).toHaveProperty('lg');
|
||
}
|
||
});
|
||
});
|
||
|
||
describe('createTheme 自定义主题工厂', () => {
|
||
it('未覆盖时等价于 lightTheme', () => {
|
||
const custom = createTheme({});
|
||
expect(custom.colors.bgPrimary).toBe(lightTheme.colors.bgPrimary);
|
||
expect(custom.spacing).toEqual(lightTheme.spacing);
|
||
});
|
||
|
||
it('覆盖 accent 颜色时其他颜色保留', () => {
|
||
const custom = createTheme({ colors: { ...lightTheme.colors, accent: '#FF5722' } });
|
||
expect(custom.colors.accent).toBe('#FF5722');
|
||
expect(custom.colors.bgPrimary).toBe(lightTheme.colors.bgPrimary);
|
||
expect(custom.colors.success).toBe(lightTheme.colors.success);
|
||
});
|
||
|
||
it('覆盖 financial 子对象时深度合并', () => {
|
||
const custom = createTheme({ colors: { ...lightTheme.colors, financial: { ...lightTheme.colors.financial, income: '#000' } } });
|
||
expect(custom.colors.financial.income).toBe('#000');
|
||
expect(custom.colors.financial.expense).toBe(lightTheme.colors.financial.expense);
|
||
});
|
||
|
||
it('覆盖 spacing 部分键时其他键保留', () => {
|
||
const custom = createTheme({ spacing: { ...lightTheme.spacing, lg: 32 } });
|
||
expect(custom.spacing.lg).toBe(32);
|
||
expect(custom.spacing.md).toBe(lightTheme.spacing.md);
|
||
});
|
||
});
|