feat: 接入 DeepSeek,新增 AI 分类建议、自然语言查询与智能记账

- 后端新增 AI 模块:DeepSeek 客户端、用户级配置表 t_ai_config、用量记录表 t_ai_usage
- 批量设置支持 AI 推荐分类与标签,并可一键应用
- 交易列表支持自然语言查询,解析为筛选条件确认后应用
- 支持自然语言记账,解析后预填新增表单
- 新增 AI 设置页:启用开关、API Key、接口地址、模型、连通性测试与用量统计
- 交易列表重排:AI 查询区、已激活筛选标签、备注与查询历史缓存
- 账单导入默认分类下拉补充分类图标
- 顶部用户入口改为下拉菜单(AI 设置 / 退出登录)
This commit is contained in:
2026-09-17 18:26:03 +08:00
parent 10fa1b5e96
commit 49c70a732d
38 changed files with 3951 additions and 27 deletions
+105
View File
@@ -81,6 +81,7 @@ export default {
transactions: { zh: '收支记录', en: 'Transactions' },
categories: { zh: '分类管理', en: 'Categories' },
tags: { zh: '标签管理', en: 'Tags' },
aiSettings: { zh: 'AI 设置', en: 'AI Settings' },
user: { zh: '用户', en: 'User' },
logout: { zh: '退出', en: 'Sign Out' },
},
@@ -141,6 +142,9 @@ export default {
amountRange: { zh: '金额区间', en: 'Amount Range' },
search: { zh: '查询', en: 'Search' },
reset: { zh: '重置', en: 'Reset' },
activeFilters: { zh: '已激活:', en: 'Active:' },
clearAll: { zh: '清空全部', en: 'Clear all' },
exampleLabel: { zh: '示例', en: 'Examples' },
summary: { zh: '总计 {total} 条 · 第 {page} / {pages} 页 · 每页 {size} 条', en: 'Total {total} · Page {page} / {pages} · {size} per page' },
pageExpense: { zh: '本页支出', en: 'Page Expense' },
pageIncome: { zh: '本页收入', en: 'Page Income' },
@@ -193,6 +197,7 @@ export default {
amountPlaceholder: { zh: '请输入金额', en: 'Enter amount' },
paymentPlaceholder: { zh: '选择支付方式', en: 'Select payment method' },
remarkPlaceholder: { zh: '可选备注说明', en: 'Optional remark' },
remarkHistoryClear: { zh: '清空历史', en: 'Clear history' },
tagPlaceholder: { zh: '选择标签', en: 'Select tags' },
noRawData: { zh: '该记录没有原始账单数据', en: 'This record has no raw bill data' },
remarkHiddenSuccess: { zh: '已隐藏备注', en: 'Remark hidden' },
@@ -299,4 +304,104 @@ export default {
loadCategoriesFailed: { zh: '加载分类失败', en: 'Failed to load categories' },
loadTagsFailed: { zh: '加载标签失败', en: 'Failed to load tags' },
},
// AI 智能建议
ai: {
title: { zh: 'AI 智能建议', en: 'AI Suggestions' },
intro: { zh: '配置 DeepSeek 后,可在收支记录页的「一键设置」中让 AI 根据记录内容推荐分类与标签。', en: 'After configuring DeepSeek, you can let AI recommend categories and tags for records in the "Batch Set" dialog on the Transactions page.' },
configTitle: { zh: '模型配置', en: 'Model Configuration' },
enabledOn: { zh: '已启用', en: 'Enabled' },
enabledOff: { zh: '未启用', en: 'Disabled' },
enabled: { zh: '启用 AI 建议', en: 'Enable AI suggestions' },
enabledHint: { zh: '关闭后不会发起任何 AI 请求', en: 'No AI requests are sent when disabled' },
apiKey: { zh: 'API Key', en: 'API Key' },
apiKeyPlaceholder: { zh: '请输入 DeepSeek API Key', en: 'Enter your DeepSeek API Key' },
apiKeyKeep: { zh: '已配置,留空表示不修改', en: 'Configured; leave blank to keep unchanged' },
baseUrl: { zh: '接口地址', en: 'Base URL' },
model: { zh: '模型', en: 'Model' },
save: { zh: '保存', en: 'Save' },
test: { zh: '测试连接', en: 'Test connection' },
saveSuccess: { zh: '保存成功', en: 'Saved successfully' },
loadFailed: { zh: '加载 AI 配置失败', en: 'Failed to load AI config' },
testSuccess: { zh: '连接成功', en: 'Connection succeeded' },
testFailed: { zh: '连接失败', en: 'Connection failed' },
suggestButton: { zh: 'AI 智能建议', en: 'AI suggestions' },
suggestTitle: { zh: 'AI 分类与标签建议', en: 'AI category & tag suggestions' },
suggesting: { zh: '正在分析,请稍候…', en: 'Analyzing, please wait…' },
suggestFailed: { zh: 'AI 建议生成失败', en: 'Failed to generate AI suggestions' },
noSuggestion: { zh: 'AI 未返回有效建议', en: 'AI returned no valid suggestions' },
applyAll: { zh: '全部应用({count})', en: 'Apply all ({count})' },
applySuccess: { zh: '已应用 {count} 条建议', en: 'Applied {count} suggestions' },
applyFailed: { zh: '应用建议失败', en: 'Failed to apply suggestions' },
applyHint: { zh: '将覆盖分类并追加标签,确认后立即写入', en: 'Categories will be overwritten and tags appended; changes are written immediately.' },
needConfig: { zh: 'AI 未启用,请先在「AI 设置」中配置', en: 'AI is not enabled. Please configure it in AI Settings first.' },
query: {
placeholder: { zh: '用自然语言查询,如:上个月餐饮花了多少', en: 'Ask in natural language, e.g. dining expenses last month' },
examples: { zh: '上个月餐饮|超过500的支出|待报销的支出|微信支付', en: 'Dining last month|Expenses over 500|Pending reimbursement|Paid by WeChat' },
button: { zh: 'AI 查询', en: 'AI Query' },
emptyInput: { zh: '请输入查询内容', en: 'Please enter a query' },
clearHistory: { zh: '清空历史', en: 'Clear history' },
failed: { zh: 'AI 查询解析失败', en: 'Failed to parse the query' },
resultTitle: { zh: 'AI 解析结果', en: 'AI Query Result' },
apply: { zh: '应用筛选', en: 'Apply filters' },
applied: { zh: '已应用 AI 查询筛选条件', en: 'AI query filters applied' },
noCondition: { zh: '未识别到可用的筛选条件,请换一种说法', en: 'No usable filter conditions recognized, please rephrase' },
type: { zh: '类型', en: 'Type' },
category: { zh: '分类', en: 'Category' },
tag: { zh: '标签', en: 'Tag' },
payment: { zh: '支付方式', en: 'Payment' },
dateRange: { zh: '时间范围', en: 'Date range' },
amountRange: { zh: '金额范围', en: 'Amount range' },
remark: { zh: '备注', en: 'Remark' },
includeInStats: { zh: '计入统计', en: 'In stats' },
includeYes: { zh: '计入', en: 'Included' },
includeNo: { zh: '不计入', en: 'Excluded' },
unmatched: { zh: '未识别', en: 'Unmatched' },
},
record: {
button: { zh: '智能记账', en: 'Smart Entry' },
title: { zh: 'AI 智能记账', en: 'AI Smart Entry' },
hint: { zh: '用一句话描述这笔收支,AI 会帮你填好表单,确认后再保存。', en: 'Describe the transaction in one sentence; AI fills the form for you to confirm.' },
placeholder: { zh: '例如:昨天中午吃饭 35 支付宝', en: 'e.g. Lunch yesterday 35 Alipay' },
parse: { zh: '解析', en: 'Parse' },
emptyInput: { zh: '请输入记账内容', en: 'Please enter the transaction description' },
failed: { zh: 'AI 记账解析失败', en: 'Failed to parse the transaction' },
},
column: {
record: { zh: '记录', en: 'Record' },
category: { zh: '建议分类', en: 'Suggested category' },
tags: { zh: '建议标签', en: 'Suggested tags' },
reason: { zh: '理由', en: 'Reason' },
},
usage: {
title: { zh: '使用情况', en: 'Usage' },
recentTitle: { zh: '最近调用记录', en: 'Recent calls' },
refresh: { zh: '刷新', en: 'Refresh' },
totalCalls: { zh: '调用次数', en: 'Calls' },
successCalls: { zh: '成功', en: 'Succeeded' },
failedCalls: { zh: '失败', en: 'Failed' },
promptTokens: { zh: '输入 Token', en: 'Prompt tokens' },
completionTokens: { zh: '输出 Token', en: 'Completion tokens' },
totalTokens: { zh: '总 Token', en: 'Total tokens' },
cachedTokens: { zh: '缓存命中', en: 'Cached tokens' },
success: { zh: '成功', en: 'Success' },
failed: { zh: '失败', en: 'Failed' },
loadFailed: { zh: '加载使用情况失败', en: 'Failed to load usage' },
empty: { zh: '暂无调用记录', en: 'No usage records yet' },
type: {
suggest: { zh: '生成建议', en: 'Suggest' },
test: { zh: '测试连接', en: 'Test' },
},
column: {
time: { zh: '时间', en: 'Time' },
type: { zh: '类型', en: 'Type' },
count: { zh: '分析条数', en: 'Records' },
promptTokens: { zh: '输入', en: 'Prompt' },
completionTokens: { zh: '输出', en: 'Completion' },
totalTokens: { zh: '总计', en: 'Total' },
duration: { zh: '耗时', en: 'Duration' },
status: { zh: '状态', en: 'Status' },
},
},
},
}