feat: 新增了周期性交易列表、账户卡片、分类选择器等多个核心组件和页面,并引入了AI服务。

This commit is contained in:
2026-01-28 15:41:16 +08:00
parent c7f1571a73
commit 71472e00b6
24 changed files with 1129 additions and 1109 deletions

View File

@@ -1,425 +1,58 @@
/**
* 分类图标配置
* 使用 Iconify + Material Design Icons
* 分类图标配置 - 动态版本
* 移除硬编码映射,改为完全依赖后端数据或默认值
*/
export const categoryIconMap: Record<number, string> = {
// ============================================
// 支出主分类 (1-21)
// ============================================
1: 'mdi:food-fork-drink', // 餐饮
2: 'mdi:car', // 交通
3: 'mdi:shopping', // 购物
4: 'mdi:gamepad-variant', // 娱乐
5: 'mdi:home', // 居住
6: 'mdi:hospital-box', // 医疗
7: 'mdi:school', // 教育
8: 'mdi:cellphone', // 通讯
9: 'mdi:gift', // 人情往来
10: 'mdi:bank', // 金融保险
11: 'mdi:face-woman', // 美容护理
12: 'mdi:paw', // 宠物
13: 'mdi:heart', // 慈善捐赠
14: 'mdi:baby-carriage', // 子女教育
15: 'mdi:human-cane', // 老人赡养
16: 'mdi:laptop', // 数码办公
17: 'mdi:dumbbell', // 运动健身
18: 'mdi:palette', // 文化艺术
19: 'mdi:airplane', // 旅游度假
20: 'mdi:car-side', // 汽车相关
21: 'mdi:dots-horizontal', // 其他支出
// ============================================
// 收入主分类 (100-109)
// ============================================
100: 'mdi:cash-multiple', // 工资薪金
101: 'mdi:gift-outline', // 奖金福利
102: 'mdi:chart-line', // 投资收益
103: 'mdi:briefcase', // 兼职副业
104: 'mdi:store', // 经营所得
105: 'mdi:wallet-giftcard', // 礼金收入
106: 'mdi:cash-refund', // 退款返现
107: 'mdi:file-document', // 报销补贴
108: 'mdi:home-city', // 资产处置
109: 'mdi:cash', // 其他收入
// ============================================
// 餐饮子分类 (201-212)
// ============================================
201: 'mdi:coffee', // 早餐
202: 'mdi:food', // 午餐
203: 'mdi:food-variant', // 晚餐
204: 'mdi:noodles', // 夜宵
205: 'mdi:popcorn', // 零食
206: 'mdi:cup', // 饮料
207: 'mdi:fruit-cherries', // 水果
208: 'mdi:moped', // 外卖
209: 'mdi:glass-cocktail', // 聚餐
210: 'mdi:tea', // 下午茶
211: 'mdi:cupcake', // 烘焙
212: 'mdi:cart', // 食材采购
// ============================================
// 交通子分类 (220-231)
// ============================================
220: 'mdi:subway-variant', // 公交地铁
221: 'mdi:taxi', // 打车
222: 'mdi:gas-station', // 加油
223: 'mdi:parking', // 停车费
224: 'mdi:highway', // 过路费
225: 'mdi:car-wrench', // 车辆保养
226: 'mdi:shield-car', // 车辆保险
227: 'mdi:train', // 火车票
228: 'mdi:airplane-takeoff', // 飞机票
229: 'mdi:bike', // 共享单车
230: 'mdi:ferry', // 船票
231: 'mdi:bus', // 长途客车
// ============================================
// 购物子分类 (240-253)
// ============================================
240: 'mdi:spray-bottle', // 日用品
241: 'mdi:tshirt-crew', // 服饰鞋包
242: 'mdi:laptop', // 数码产品
243: 'mdi:television', // 家电
244: 'mdi:sofa', // 家具
245: 'mdi:book-open-page-variant', // 图书
246: 'mdi:baby-bottle', // 母婴用品
247: 'mdi:gift', // 礼品
248: 'mdi:paperclip', // 办公用品
249: 'mdi:diamond-stone', // 珠宝首饰
250: 'mdi:bag-personal', // 箱包配饰
251: 'mdi:basketball', // 运动装备
252: 'mdi:tent', // 户外用品
253: 'mdi:teddy-bear', // 玩具
// ============================================
// 娱乐子分类 (260-273)
// ============================================
260: 'mdi:movie', // 电影
261: 'mdi:controller', // 游戏
262: 'mdi:beach', // 旅游
263: 'mdi:run', // 运动健身
264: 'mdi:theater', // 演出票务
265: 'mdi:microphone', // KTV
266: 'mdi:glass-mug-variant', // 酒吧
267: 'mdi:dice-multiple', // 桌游
268: 'mdi:camera', // 摄影
269: 'mdi:lock', // 密室逃脱
270: 'mdi:book-open', // 剧本杀
271: 'mdi:ferris-wheel', // 游乐场
272: 'mdi:image-frame', // 展览
273: 'mdi:bank-outline', // 博物馆
// ============================================
// 居住子分类 (280-291)
// ============================================
280: 'mdi:home-outline', // 房租
281: 'mdi:home-city-outline', // 房贷
282: 'mdi:water', // 水电费
283: 'mdi:fire', // 燃气费
284: 'mdi:office-building', // 物业费
285: 'mdi:wifi', // 网费
286: 'mdi:wrench', // 维修
287: 'mdi:brush', // 装修
288: 'mdi:broom', // 家政服务
289: 'mdi:radiator', // 暖气费
290: 'mdi:delete', // 垃圾费
291: 'mdi:bed', // 家居用品
// ============================================
// 医疗子分类 (300-311)
// ============================================
300: 'mdi:hospital', // 挂号费
301: 'mdi:pill', // 药品
302: 'mdi:test-tube', // 检查费
303: 'mdi:needle', // 治疗费
304: 'mdi:bed-empty', // 住院费
305: 'mdi:clipboard-check', // 体检
306: 'mdi:tooth', // 牙科
307: 'mdi:glasses', // 眼科
308: 'mdi:bottle-tonic-plus', // 保健品
309: 'mdi:leaf', // 中医
310: 'mdi:hand-heart', // 康复理疗
311: 'mdi:stethoscope', // 医疗器械
// ============================================
// 教育子分类 (320-329)
// ============================================
320: 'mdi:school-outline', // 学费
321: 'mdi:book-education', // 培训费
322: 'mdi:book-multiple', // 教材
323: 'mdi:pencil', // 文具
324: 'mdi:monitor', // 在线课程
325: 'mdi:file-document-edit', // 考试费
326: 'mdi:palette-outline', // 兴趣班
327: 'mdi:certificate', // 证书费
328: 'mdi:file-document-multiple', // 学习资料
329: 'mdi:account-tie', // 辅导费
// ============================================
// 通讯子分类 (340-347)
// ============================================
340: 'mdi:cellphone', // 手机话费
341: 'mdi:wifi', // 宽带费
342: 'mdi:email', // 邮费
343: 'mdi:star-circle', // 会员订阅
344: 'mdi:television-play', // 视频会员
345: 'mdi:music', // 音乐会员
346: 'mdi:cloud', // 云存储
347: 'mdi:application', // 软件订阅
// ============================================
// 人情往来子分类 (360-368)
// ============================================
360: 'mdi:wallet-giftcard', // 红包
361: 'mdi:cash', // 礼金
362: 'mdi:gift', // 送礼
363: 'mdi:silverware-fork-knife', // 请客
364: 'mdi:human-male-female', // 孝敬长辈
365: 'mdi:ring', // 婚礼
366: 'mdi:baby-face', // 满月酒
367: 'mdi:cake-variant', // 生日
368: 'mdi:party-popper', // 节日
// ============================================
// 金融保险子分类 (380-389)
// ============================================
380: 'mdi:bank-transfer', // 银行手续费
381: 'mdi:cash-minus', // 利息支出
382: 'mdi:shield-account', // 人寿保险
383: 'mdi:shield-plus', // 医疗保险
384: 'mdi:shield-home', // 财产保险
385: 'mdi:chart-line-variant', // 投资亏损
386: 'mdi:shield-alert', // 意外险
387: 'mdi:shield-check', // 养老保险
388: 'mdi:shield-star', // 教育保险
389: 'mdi:credit-card', // 信用卡年费
// ============================================
// 美容护理子分类 (400-409)
// ============================================
400: 'mdi:content-cut', // 理发
401: 'mdi:face-woman-shimmer', // 美容
402: 'mdi:lipstick', // 化妆品
403: 'mdi:lotion', // 护肤品
404: 'mdi:hand-back-right', // 美甲
405: 'mdi:spa', // 按摩
406: 'mdi:hot-tub', // SPA
407: 'mdi:eye', // 美睫
408: 'mdi:eyebrow', // 纹眉
409: 'mdi:yoga', // 美体
// ============================================
// 宠物子分类 (420-426)
// ============================================
420: 'mdi:food-drumstick', // 宠物食品
421: 'mdi:tennis', // 宠物用品
422: 'mdi:hospital-box', // 宠物医疗
423: 'mdi:content-cut', // 宠物美容
424: 'mdi:home-variant', // 宠物寄养
425: 'mdi:school', // 宠物训练
426: 'mdi:shield', // 宠物保险
// ============================================
// 慈善捐赠子分类 (440-444)
// ============================================
440: 'mdi:hand-heart', // 公益捐款
441: 'mdi:hands-pray', // 扶贫助困
442: 'mdi:book-heart', // 教育捐赠
443: 'mdi:leaf', // 环保公益
444: 'mdi:lifebuoy', // 灾害救助
// ============================================
// 收入子分类
// ============================================
// 工资薪金 (1001-1008)
1001: 'mdi:cash', // 基本工资
1002: 'mdi:clock-time-eight', // 加班费
1003: 'mdi:target', // 绩效奖金
1004: 'mdi:cash-plus', // 津贴补贴
1005: 'mdi:car', // 交通补贴
1006: 'mdi:food', // 餐补
1007: 'mdi:phone', // 通讯补贴
1008: 'mdi:home', // 住房补贴
// 奖金福利 (1020-1025)
1020: 'mdi:gift', // 年终奖
1021: 'mdi:trophy', // 项目奖金
1022: 'mdi:briefcase', // 销售提成
1023: 'mdi:chart-bar', // 季度奖
1024: 'mdi:check-circle', // 全勤奖
1025: 'mdi:star', // 优秀员工
// 投资收益 (1040-1049)
1040: 'mdi:chart-line', // 股票收益
1041: 'mdi:chart-areaspline', // 基金收益
1042: 'mdi:piggy-bank', // 理财收益
1043: 'mdi:percent', // 利息收入
1044: 'mdi:cash-multiple', // 分红
1045: 'mdi:home-city', // 租金收入
1046: 'mdi:file-certificate', // 债券收益
1047: 'mdi:chart-timeline-variant', // 期货收益
1048: 'mdi:currency-usd', // 外汇收益
1049: 'mdi:bitcoin', // 数字货币
// 兼职副业 (1060-1066)
1060: 'mdi:briefcase-variant', // 自由职业
1061: 'mdi:cash', // 兼职工资
1062: 'mdi:pen', // 稿费
1063: 'mdi:palette', // 设计费
1064: 'mdi:lightbulb', // 咨询费
1065: 'mdi:teach', // 讲课费
1066: 'mdi:translate', // 翻译费
// 经营所得 (1080-1083)
1080: 'mdi:store', // 营业收入
1081: 'mdi:handshake', // 服务收入
1082: 'mdi:cart', // 销售收入
1083: 'mdi:briefcase', // 佣金收入
// 礼金收入 (1100-1102)
1100: 'mdi:wallet-giftcard', // 红包
1101: 'mdi:cash', // 礼金
1102: 'mdi:gift', // 压岁钱
// 退款返现 (1120-1123)
1120: 'mdi:undo-variant', // 购物退款
1121: 'mdi:credit-card-refund', // 信用卡返现
1122: 'mdi:star-circle', // 积分兑换
1123: 'mdi:ticket', // 优惠券
// 报销补贴 (1140-1143)
1140: 'mdi:airplane', // 差旅报销
1141: 'mdi:hospital-box', // 医疗报销
1142: 'mdi:phone', // 通讯报销
1143: 'mdi:car', // 交通报销
};
/**
* 分类颜色配置
* 使用柔和的渐变色系
*/
export const categoryColorMap: Record<number, string> = {
// 支出主分类
1: '#FF6B6B', // 餐饮 - 珊瑚红
2: '#4ECDC4', // 交通 - 青绿色
3: '#95E1D3', // 购物 - 薄荷绿
4: '#F38181', // 娱乐 - 粉红色
5: '#AA96DA', // 居住 - 淡紫色
6: '#FCBAD3', // 医疗 - 粉色
7: '#FFE66D', // 教育 - 金黄色
8: '#A8D8EA', // 通讯 - 天蓝色
9: '#FFAAA7', // 人情往来 - 橙粉色
10: '#FFD3B5', // 金融保险 - 杏色
11: '#FFAAA5', // 美容护理 - 浅粉色
12: '#DCEDC1', // 宠物 - 浅绿色
13: '#FFD93D', // 慈善捐赠 - 金色
14: '#A8E6CF', // 子女教育 - 薄荷绿
15: '#FFB6B9', // 老人赡养 - 浅粉红
16: '#BAE1FF', // 数码办公 - 浅蓝色
17: '#C7CEEA', // 运动健身 - 淡紫色
18: '#FFDAC1', // 文化艺术 - 杏色
19: '#B5EAD7', // 旅游度假 - 薄荷色
20: '#E2F0CB', // 汽车相关 - 浅绿色
21: '#C7CEEA', // 其他支出 - 灰紫色
// 收入主分类 - 使用绿色系
100: '#06D6A0', // 工资薪金 - 翠绿色
101: '#118AB2', // 奖金福利 - 蓝色
102: '#073B4C', // 投资收益 - 深蓝色
103: '#06D6A0', // 兼职副业 - 翠绿色
104: '#118AB2', // 经营所得 - 蓝色
105: '#FFD166', // 礼金收入 - 金黄色
106: '#EF476F', // 退款返现 - 玫红色
107: '#06D6A0', // 报销补贴 - 翠绿色
108: '#118AB2', // 资产处置 - 蓝色
109: '#073B4C', // 其他收入 - 深蓝色
};
/**
* 渐变色预设
*/
export const gradientPresets: Record<string, string> = {
food: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
transport: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
shopping: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)',
entertainment: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)',
housing: 'linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)',
medical: 'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)',
education: 'linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%)',
communication: 'linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%)',
social: 'linear-gradient(135deg, #fbc2eb 0%, #a6c1ee 100%)',
finance: 'linear-gradient(135deg, #fdcbf1 0%, #e6dee9 100%)',
beauty: 'linear-gradient(135deg, #fccb90 0%, #d57eeb 100%)',
pet: 'linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%)',
charity: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
income: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
};
/**
* 深色模式颜色配置
*/
export const darkModeColors: Record<number, string> = {
1: '#FF8787', // 餐饮
2: '#5EDDD4', // 交通
3: '#A5F1E3', // 购物
4: '#F49191', // 娱乐
5: '#BAA6EA', // 居住
6: '#FDCAE3', // 医疗
7: '#FFF67D', // 教育
8: '#B8E8FA', // 通讯
9: '#FFBAB7', // 人情往来
10: '#FFE3C5', // 金融保险
11: '#FFBAB5', // 美容护理
12: '#ECFDD1', // 宠物
13: '#FFE94D', // 慈善捐赠
14: '#B8F6DF', // 子女教育
15: '#FFC6C9', // 老人赡养
16: '#CAF1FF', // 数码办公
17: '#D7DEFA', // 运动健身
18: '#FFEAD1', // 文化艺术
19: '#C5FAE7', // 旅游度假
20: '#F2FFDB', // 汽车相关
21: '#D7DEFA', // 其他支出
// 收入分类
100: '#16E6B0',
101: '#21AABB',
102: '#174B5C',
103: '#16E6B0',
104: '#21AABB',
105: '#FFE176',
106: '#FF577F',
107: '#16E6B0',
108: '#21AABB',
109: '#174B5C',
};
/**
* 获取分类图标
* 如果未提供图标,则返回默认图标
* @param categoryId - 保留参数接口,虽然不再用于查表
* @param defaultIcon - 可选的默认图标
*/
export const getCategoryIcon = (categoryId: number): string => {
return categoryIconMap[categoryId] || 'mdi:help-circle';
// 不再维护本地大表,统一返回默认图标
// 组件应当优先使用 props 传入的 icon
return 'solar:question-circle-bold-duotone';
};
/**
* 更加丰富的预设颜色池
*/
const COLOR_PALETTE = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEEAD',
'#D4A5A5', '#9B59B6', '#3498DB', '#E67E22', '#2ECC71',
'#F1C40F', '#E74C3C', '#1ABC9C', '#34495E', '#95A5A6',
'#FF8787', '#5EDDD4', '#A5F1E3', '#F49191', '#BAA6EA',
'#FDCAE3', '#FFF67D', '#B8E8FA', '#FFBAB7', '#FFE3C5',
];
/**
* 获取分类颜色
* 使用 consistent hashing 算法根据 ID 生成一致的颜色
*/
export const getCategoryColor = (categoryId: number, isDarkMode = false): string => {
if (isDarkMode) {
return darkModeColors[categoryId] || '#999';
}
return categoryColorMap[categoryId] || '#999';
// 简单的哈希算法确保同一个ID总是对应同一个颜色
const index = Math.abs(categoryId) % COLOR_PALETTE.length;
return COLOR_PALETTE[index];
};
/**
* 获取分类渐变色
* @deprecated 建议不再使用,或使用 CSS 变量
*/
export const getCategoryGradient = (categoryId: number): string => {
// 根据分类ID范围返回对应的渐变色
if (categoryId >= 1 && categoryId < 10) return gradientPresets.food;
if (categoryId >= 10 && categoryId < 20) return gradientPresets.transport;
if (categoryId >= 100) return gradientPresets.income;
return gradientPresets.food;
const color = getCategoryColor(categoryId);
// 简单模拟一个同色系的渐变
return `linear-gradient(135deg, ${color} 0%, ${adjustColor(color, -20)} 100%)`;
};
// 辅助函数:调整颜色亮度 (简单实现)
function adjustColor(color: string, amount: number) {
return color; // 暂不需要复杂计算,直接返回原色
}
// 导出空映射以防有遗留引用
export const categoryIconMap: Record<number, string> = {};
export const categoryColorMap: Record<number, string> = {};
export const darkModeColors: Record<number, string> = {};
export const gradientPresets: Record<string, string> = {};