feat: 邮箱账单自动拉取
- 新增邮箱配置、发件人规则、待处理账单三张表(SQLite/MySQL 双脚本)及 CRUD 接口 - 按邮件发件人匹配账单类型,未命中忽略;发件人规则可配置解压密码与默认分类/标签 - 支持 IMAP IDLE 实时监听、定时兜底与手动立即检查;拉取范围可配置(仅新邮件/最近 N 天/最近 N 封) - 附件保存至本地 data/email-bill,解析预览后手动导入,复用账单解析与规则引擎 - 立即检查通过 SSE 实时推送执行进度 - 前端新增「邮箱账单」页(邮箱配置/发件人规则/待处理账单三 Tab),支持子路由与 Tab 偏好记忆,配置弹窗提供常用 IMAP 服务器候选项 - 新用户注册自动初始化默认发件人规则(支付宝/微信/美团/招商银行/交通银行) - 抽取共用账单预览与结果组件,供上传导入与邮箱账单复用 同时包含: - 统计新增口径筛选(仅计入/全部/仅不计入),支付方式改为后端动态获取 - 统一实体 createdAt/updatedAt 位置至 id 之后,调整部分日志级别
This commit is contained in:
@@ -81,11 +81,13 @@ export default {
|
||||
// 主布局
|
||||
layout: {
|
||||
appTitle: { zh: '收支管理', en: 'Finance Manager', ja: '収支管理' },
|
||||
appSubtitle: { zh: '个人财务管理', en: 'Personal Finance', ja: '個人財務管理' },
|
||||
dashboard: { zh: '统计面板', en: 'Dashboard', ja: 'ダッシュボード' },
|
||||
transactions: { zh: '收支记录', en: 'Transactions', ja: '収支記録' },
|
||||
categories: { zh: '分类管理', en: 'Categories', ja: 'カテゴリ管理' },
|
||||
tags: { zh: '标签管理', en: 'Tags', ja: 'タグ管理' },
|
||||
rules: { zh: '规则管理', en: 'Rules', ja: 'ルール管理' },
|
||||
emailBills: { zh: '邮箱账单', en: 'Email Bills', ja: 'メール請求' },
|
||||
logs: { zh: '系统日志', en: 'Logs', ja: 'システムログ' },
|
||||
settings: { zh: '系统设置', en: 'Settings', ja: 'システム設定' },
|
||||
user: { zh: '用户', en: 'User', ja: 'ユーザー' },
|
||||
@@ -285,6 +287,12 @@ export default {
|
||||
quarter: { zh: '按季', en: 'Quarterly', ja: '四半期別' },
|
||||
year: { zh: '按年', en: 'Yearly', ja: '年別' },
|
||||
},
|
||||
filterStatsScope: { zh: '统计口径', en: 'Stats Scope', ja: '集計範囲' },
|
||||
statsScope: {
|
||||
included: { zh: '仅计入统计', en: 'Included Only', ja: '集計対象のみ' },
|
||||
all: { zh: '全部记录', en: 'All Records', ja: 'すべての記録' },
|
||||
excluded: { zh: '仅不计入', en: 'Excluded Only', ja: '集計対象外のみ' },
|
||||
},
|
||||
filterType: { zh: '类型', en: 'Type', ja: '種類' },
|
||||
filterCategory: { zh: '分类', en: 'Category', ja: 'カテゴリ' },
|
||||
filterPaymentMethod: { zh: '支付方式', en: 'Payment Method', ja: '支払方法' },
|
||||
@@ -688,4 +696,143 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// 邮箱账单
|
||||
emailBill: {
|
||||
title: { zh: '邮箱账单', en: 'Email Bills', ja: 'メール請求' },
|
||||
tab: {
|
||||
configs: { zh: '邮箱配置', en: 'Mailboxes', ja: 'メール設定' },
|
||||
senderRules: { zh: '发件人规则', en: 'Sender Rules', ja: '送信者ルール' },
|
||||
messages: { zh: '待处理账单', en: 'Pending Bills', ja: '未処理の請求' },
|
||||
},
|
||||
imapHosts: {
|
||||
qq: { zh: 'QQ 邮箱', en: 'QQ Mail', ja: 'QQメール' },
|
||||
qqExmail: { zh: '腾讯企业邮', en: 'Tencent Exmail', ja: 'Tencent Exmail' },
|
||||
netease163: { zh: '网易 163 邮箱', en: 'NetEase 163', ja: 'NetEase 163' },
|
||||
netease126: { zh: '网易 126 邮箱', en: 'NetEase 126', ja: 'NetEase 126' },
|
||||
neteaseQiye: { zh: '网易企业邮', en: 'NetEase Enterprise', ja: 'NetEase Enterprise' },
|
||||
aliyun: { zh: '阿里云邮箱', en: 'Alibaba Mail', ja: 'Alibaba Mail' },
|
||||
sina: { zh: '新浪邮箱', en: 'Sina Mail', ja: 'Sina Mail' },
|
||||
mail139: { zh: '139 邮箱', en: '139 Mail', ja: '139メール' },
|
||||
mail189: { zh: '189 邮箱', en: '189 Mail', ja: '189メール' },
|
||||
gmail: { zh: 'Gmail', en: 'Gmail', ja: 'Gmail' },
|
||||
outlook: { zh: 'Outlook / Hotmail', en: 'Outlook / Hotmail', ja: 'Outlook / Hotmail' },
|
||||
icloud: { zh: 'iCloud 邮箱', en: 'iCloud Mail', ja: 'iCloudメール' },
|
||||
},
|
||||
common: {
|
||||
add: { zh: '新增', en: 'Add', ja: '追加' },
|
||||
edit: { zh: '编辑', en: 'Edit', ja: '編集' },
|
||||
delete: { zh: '删除', en: 'Delete', ja: '削除' },
|
||||
actions: { zh: '操作', en: 'Actions', ja: '操作' },
|
||||
enabled: { zh: '启用', en: 'Enabled', ja: '有効' },
|
||||
save: { zh: '保存', en: 'Save', ja: '保存' },
|
||||
cancel: { zh: '取消', en: 'Cancel', ja: 'キャンセル' },
|
||||
confirm: { zh: '确定', en: 'Confirm', ja: '確定' },
|
||||
refresh: { zh: '刷新', en: 'Refresh', ja: '更新' },
|
||||
loadFailed: { zh: '加载失败', en: 'Failed to load', ja: '読み込みに失敗しました' },
|
||||
saveSuccess: { zh: '保存成功', en: 'Saved', ja: '保存しました' },
|
||||
deleteSuccess: { zh: '删除成功', en: 'Deleted', ja: '削除しました' },
|
||||
deleteConfirm: { zh: '确定删除吗?', en: 'Delete this item?', ja: '削除しますか?' },
|
||||
empty: { zh: '暂无数据', en: 'No data', ja: 'データがありません' },
|
||||
},
|
||||
config: {
|
||||
name: { zh: '名称', en: 'Name', ja: '名称' },
|
||||
host: { zh: 'IMAP 服务器', en: 'IMAP Server', ja: 'IMAPサーバー' },
|
||||
hostPlaceholder: { zh: '选择或输入 IMAP 服务器', en: 'Select or enter IMAP server', ja: 'IMAPサーバーを選択または入力' },
|
||||
port: { zh: '端口', en: 'Port', ja: 'ポート' },
|
||||
ssl: { zh: '使用 SSL', en: 'Use SSL', ja: 'SSLを使用' },
|
||||
username: { zh: '邮箱账号', en: 'Email', ja: 'メールアドレス' },
|
||||
password: { zh: '授权码', en: 'Auth Code', ja: '認証コード' },
|
||||
passwordPlaceholder: { zh: '留空表示不修改', en: 'Leave blank to keep unchanged', ja: '空欄なら変更しません' },
|
||||
folder: { zh: '监听文件夹', en: 'Folder', ja: 'フォルダ' },
|
||||
status: { zh: '状态', en: 'Status', ja: '状態' },
|
||||
lastError: { zh: '最近错误', en: 'Last Error', ja: '最新エラー' },
|
||||
defaultExpenseCategory: { zh: '默认支出分类', en: 'Default Expense Category', ja: '既定の支出カテゴリ' },
|
||||
defaultIncomeCategory: { zh: '默认收入分类', en: 'Default Income Category', ja: '既定の収入カテゴリ' },
|
||||
defaultTags: { zh: '默认标签', en: 'Default Tags', ja: '既定のタグ' },
|
||||
fetchScope: { zh: '拉取范围', en: 'Fetch Scope', ja: '取得範囲' },
|
||||
fetchScopeIncrement: { zh: '仅新邮件', en: 'New only', ja: '新着のみ' },
|
||||
fetchScopeDays: { zh: '最近 N 天', en: 'Last N days', ja: '直近 N 日' },
|
||||
fetchScopeCount: { zh: '最近 N 封', en: 'Last N emails', ja: '直近 N 件' },
|
||||
fetchLimitDays: { zh: '天数', en: 'Days', ja: '日数' },
|
||||
fetchLimitCount: { zh: '封数', en: 'Count', ja: '件数' },
|
||||
test: { zh: '测试连接', en: 'Test', ja: '接続テスト' },
|
||||
check: { zh: '立即检查', en: 'Check Now', ja: '今すぐ確認' },
|
||||
testSuccess: { zh: '连接成功', en: 'Connected', ja: '接続に成功しました' },
|
||||
testFailed: { zh: '连接失败', en: 'Connection failed', ja: '接続に失敗しました' },
|
||||
checkResult: { zh: '新增 {count} 条待处理账单', en: '{count} new pending bill(s)', ja: '未処理の請求を {count} 件追加しました' },
|
||||
checkLogTitle: { zh: '检查进度', en: 'Check Progress', ja: '確認の進行状況' },
|
||||
checkRunning: { zh: '检查中…', en: 'Checking…', ja: '確認中…' },
|
||||
checkClear: { zh: '清空', en: 'Clear', ja: 'クリア' },
|
||||
checkFailed: { zh: '检查失败', en: 'Check failed', ja: '確認に失敗しました' },
|
||||
statusText: {
|
||||
DISCONNECTED: { zh: '未连接', en: 'Disconnected', ja: '未接続' },
|
||||
CONNECTED: { zh: '已连接', en: 'Connected', ja: '接続済み' },
|
||||
POLLING: { zh: '轮询中', en: 'Polling', ja: 'ポーリング中' },
|
||||
ERROR: { zh: '异常', en: 'Error', ja: 'エラー' },
|
||||
},
|
||||
},
|
||||
senderRule: {
|
||||
name: { zh: '规则名称', en: 'Rule Name', ja: 'ルール名' },
|
||||
patterns: { zh: '发件人匹配串', en: 'Sender Patterns', ja: '送信者パターン' },
|
||||
patternsHint: { zh: '每行一个,支持完整地址、@域名或关键字,包含匹配且忽略大小写', en: 'One per line; full address, @domain or keyword, case-insensitive contains', ja: '1行に1つ。完全なアドレス、@ドメイン、キーワードに対応(大文字小文字を区別しない部分一致)' },
|
||||
billType: { zh: '账单类型', en: 'Bill Type', ja: '請求タイプ' },
|
||||
archivePassword: { zh: '解压密码', en: 'Archive Password', ja: '解凍パスワード' },
|
||||
archivePasswordPlaceholder: { zh: '压缩包加密时填写', en: 'Fill if the archive is encrypted', ja: '暗号化されている場合に入力' },
|
||||
sortOrder: { zh: '优先级', en: 'Priority', ja: '優先度' },
|
||||
defaultExpenseCategory: { zh: '默认支出分类', en: 'Default Expense Category', ja: '既定の支出カテゴリ' },
|
||||
defaultIncomeCategory: { zh: '默认收入分类', en: 'Default Income Category', ja: '既定の収入カテゴリ' },
|
||||
defaultTags: { zh: '默认标签', en: 'Default Tags', ja: '既定のタグ' },
|
||||
patternsRequired: { zh: '请至少填写一个发件人匹配串', en: 'Please enter at least one sender pattern', ja: '送信者パターンを1つ以上入力してください' },
|
||||
},
|
||||
message: {
|
||||
receivedAt: { zh: '接收时间', en: 'Received', ja: '受信日時' },
|
||||
fromAddress: { zh: '发件人', en: 'From', ja: '送信者' },
|
||||
subject: { zh: '主题', en: 'Subject', ja: '件名' },
|
||||
attachmentName: { zh: '附件', en: 'Attachment', ja: '添付ファイル' },
|
||||
billType: { zh: '账单类型', en: 'Bill Type', ja: '請求タイプ' },
|
||||
status: { zh: '状态', en: 'Status', ja: '状態' },
|
||||
error: { zh: '错误', en: 'Error', ja: 'エラー' },
|
||||
parse: { zh: '解析', en: 'Parse', ja: '解析' },
|
||||
ignore: { zh: '忽略', en: 'Ignore', ja: '無視' },
|
||||
statusText: {
|
||||
PULLED: { zh: '待解析', en: 'Pending', ja: '未解析' },
|
||||
CONFIRMED: { zh: '已导入', en: 'Imported', ja: '取込済み' },
|
||||
IGNORED: { zh: '已忽略', en: 'Ignored', ja: '無視' },
|
||||
},
|
||||
filterStatus: { zh: '状态筛选', en: 'Filter by status', ja: '状態で絞り込み' },
|
||||
},
|
||||
parse: {
|
||||
title: { zh: '解析账单', en: 'Parse Bill', ja: '請求を解析' },
|
||||
billType: { zh: '账单类型', en: 'Bill Type', ja: '請求タイプ' },
|
||||
password: { zh: '解压密码', en: 'Archive Password', ja: '解凍パスワード' },
|
||||
parseButton: { zh: '解析', en: 'Parse', ja: '解析' },
|
||||
parseFailed: { zh: '解析失败', en: 'Parse failed', ja: '解析に失敗しました' },
|
||||
needParse: { zh: '请先解析账单', en: 'Please parse the bill first', ja: '先に請求を解析してください' },
|
||||
expenseDefaultCategory: { zh: '支出默认分类', en: 'Expense Category', ja: '支出カテゴリ' },
|
||||
incomeDefaultCategory: { zh: '收入默认分类', en: 'Income Category', ja: '収入カテゴリ' },
|
||||
defaultTags: { zh: '默认标签', en: 'Default Tags', ja: '既定のタグ' },
|
||||
selectAll: { zh: '全选', en: 'Select All', ja: 'すべて選択' },
|
||||
setIncluded: { zh: '计入统计', en: 'Include', ja: '集計に含める' },
|
||||
setExcluded: { zh: '不计入', en: 'Exclude', ja: '集計から除外' },
|
||||
previewSummary: { zh: '共 {total} 行,筛选后 {filtered} 行,已选 {checked} 行', en: '{total} rows, {filtered} after filter, {checked} selected', ja: '全 {total} 行、絞り込み後 {filtered} 行、選択 {checked} 行' },
|
||||
confirmImport: { zh: '导入 {count} 条', en: 'Import {count}', ja: '{count} 件を取込' },
|
||||
importSuccess: { zh: '导入完成', en: 'Import completed', ja: '取込が完了しました' },
|
||||
resultSummary: { zh: '成功 {imported} 条,跳过 {skipped} 条,失败 {failed} 条', en: 'Imported {imported}, skipped {skipped}, failed {failed}', ja: '成功 {imported} 件、スキップ {skipped} 件、失敗 {failed} 件' },
|
||||
done: { zh: '完成', en: 'Done', ja: '完了' },
|
||||
prevStep: { zh: '返回', en: 'Back', ja: '戻る' },
|
||||
column: {
|
||||
transactionTime: { zh: '交易时间', en: 'Time', ja: '取引日時' },
|
||||
type: { zh: '类型', en: 'Type', ja: '種別' },
|
||||
amount: { zh: '金额', en: 'Amount', ja: '金額' },
|
||||
counterparty: { zh: '交易对方', en: 'Counterparty', ja: '取引先' },
|
||||
product: { zh: '商品', en: 'Item', ja: '商品' },
|
||||
status: { zh: '状态', en: 'Status', ja: '状態' },
|
||||
matchedRule: { zh: '匹配规则/建议', en: 'Rule / Suggestion', ja: 'ルール/提案' },
|
||||
},
|
||||
notImportable: { zh: '不可导入', en: 'Not importable', ja: '取込不可' },
|
||||
notIncluded: { zh: '不计收支', en: 'Excluded', ja: '集計外' },
|
||||
duplicate: { zh: '重复', en: 'Duplicate', ja: '重複' },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user