docs: 补充管理页批量操作说明并同步规则/AI 规则文档

为规则、分类、标签管理页新增的「批量操作模式」与多选删除补充使用与开发文档,同时补上此前已完成但未同步的 AI 规则建议可编辑、编辑后实时重算去重等文档,并登记批量模式截图脚本。

变更内容:
- guide/categories-tags.md:补充分类与标签的批量删除说明,并回填 tags-batch.png、categories-batch.png 配图。
- guide/rules.md:批量删除与应用匹配账单改为需先进入「批量操作」模式(默认不显示勾选框)。
- dev/api.md:新增 DELETE /api/tag/batch、/api/category/batch,并补齐规则命中账单预览/一键应用、AI 规则去重校验等接口。
- dev/data-model.md:说明标签删除清理交易标签与规则标签关联、分类删除保留逻辑引用(账单显示未分类)。
- dev/rule-engine.md、dev/ai.md、dev/preferences.md、guide/ai.md、guide/transactions.md:同步 AI 规则建议可编辑、编辑后实时重算去重、应用开关默认开启并持久化等说明。
- scripts/ts/capture-elements.ts:新增 captureBatchModes()(标签/分类批量模式截图)并纳入 main()。

说明:
- 截图经测试环境(前端 5174 / 后端 8081,虚构数据)生成,已上传 OSS,正文以 OSS 长期地址引用;public/screenshots 目录不入库。
This commit is contained in:
2026-10-14 01:07:46 +08:00
parent d94d4e1ed5
commit 41d1a4d5a9
10 changed files with 145 additions and 17 deletions
+2 -1
View File
@@ -94,7 +94,8 @@
- 仅加载属于当前用户且被勾选的记录(单次 ≤ 500 条),附带其标签名称与分类名;提示词字段为 `type / remark / channel / category / tags`(不含金额与 `rawData`),并对相同「类型 + 备注 + 渠道 + 标签」组合去重。 - 仅加载属于当前用户且被勾选的记录(单次 ≤ 500 条),附带其标签名称与分类名;提示词字段为 `type / remark / channel / category / tags`(不含金额与 `rawData`),并对相同「类型 + 备注 + 渠道 + 标签」组合去重。
- 解析并校验模型输出(分类 ID 归属与类型、标签归属、名称长度、颜色格式等)。 - 解析并校验模型输出(分类 ID 归属与类型、标签归属、名称长度、颜色格式等)。
- **去重**:按「类型 + 归一化关键词 + 分类 + 排序标签集合」计算签名;与已有规则或本次建议内部重复的条目标记 `duplicate=true`(前端默认不勾选且禁止导入),`apply` 时后端再次校验并跳过。 - **去重**:按「类型 + 归一化关键词 + 分类 + 排序标签集合」计算签名;与已有规则或本次建议内部重复的条目标记 `duplicate=true`(前端默认不勾选且禁止导入),`apply` 时后端再次校验并跳过。
- **应用到交易**:`POST /api/ai/rule/apply-transactions`(`applyToTransactions`)按传入规则对指定交易做**本地匹配**(不调用大模型、不外发数据),命中时覆盖分类、追加标签,必要时新建分类/标签;用于收支记录页勾选「同时应用到账单」的场景。 - **编辑后重算去重**:规则弹窗(两处入口共用的 `AiRuleEditorTable.vue`)支持逐字段编辑;任一参与签名的字段(类型/关键词/金额区间/分类/标签)变化后防抖调用 `POST /api/ai/rule/check-duplicates`(`AiRuleServiceImpl.checkDuplicates`,纯本地复用签名逻辑)刷新 `duplicate`,并自动增删勾选。
- **应用到交易**:`POST /api/ai/rule/apply-transactions`(`applyToTransactions`)按传入规则对指定交易做**本地匹配**(不调用大模型、不外发数据),命中时覆盖分类、追加标签,必要时新建分类/标签;用于收支记录页勾选「同时应用到账单」的场景,该开关默认开启并经 `ledgerly.transaction.aiRuleApplyToSelection` 持久化。
## 统计搜索 ## 统计搜索
+6 -1
View File
@@ -46,7 +46,7 @@
| `ChannelController` | `/api/channel` | 用户自定义交易渠道的增删改查 | | `ChannelController` | `/api/channel` | 用户自定义交易渠道的增删改查 |
| `CategoryController` | `/api/category` | 分类管理 | | `CategoryController` | `/api/category` | 分类管理 |
| `TagController` | `/api/tag` | 标签管理 | | `TagController` | `/api/tag` | 标签管理 |
| `RuleController` | `/api/rule` | 自定义规则(列表、分页查询、增删改、启停、批量删除) | | `RuleController` | `/api/rule` | 自定义规则(列表、分页查询、增删改、启停、批量删除、命中账单预览与一键应用) |
| `StatisticsController` | `/api/statistics` | 统计汇总、分类、趋势、分组 | | `StatisticsController` | `/api/statistics` | 统计汇总、分类、趋势、分组 |
| `AiController` | `/api/ai` | AI 建议、查询、智能查数、文字/图片记账、渠道/关系/规则生成、统计搜索、用量 | | `AiController` | `/api/ai` | AI 建议、查询、智能查数、文字/图片记账、渠道/关系/规则生成、统计搜索、用量 |
| `NotificationController` | `/api/notification` | 通知配置、渠道、发送日志 | | `NotificationController` | `/api/notification` | 通知配置、渠道、发送日志 |
@@ -77,9 +77,14 @@
| GET / DELETE | `/api/ai/sql/history` | 查询 / 清空智能查数提问历史(`limit` 上限 200) | | GET / DELETE | `/api/ai/sql/history` | 查询 / 清空智能查数提问历史(`limit` 上限 200) |
| GET | `/api/rule/page` | 分页查询规则(`keyword` / `enabled` / `type`) | | GET | `/api/rule/page` | 分页查询规则(`keyword` / `enabled` / `type`) |
| DELETE | `/api/rule/batch` | 批量删除规则(body `{ ids }`,返回实际删除数) | | DELETE | `/api/rule/batch` | 批量删除规则(body `{ ids }`,返回实际删除数) |
| DELETE | `/api/tag/batch` | 批量删除标签(body `{ ids }`,先清理交易-标签与规则-标签关联再删除,返回实际删除数) |
| DELETE | `/api/category/batch` | 批量删除分类(body `{ ids }`,返回实际删除数) |
| POST | `/api/rule/apply-preview` | 预览所选规则命中的历史账单及将应用的目标分类/标签(body `{ ruleIds }`,返回 `RuleApplyPreviewVO`,明细上限 500 条,纯本地匹配) |
| POST | `/api/rule/apply` | 一键将所选规则的分类与标签应用到其命中的历史账单(分类覆盖、标签追加,返回 `RuleApplyResultVO`;纯本地匹配,不外发数据) |
| POST | `/api/ai/rule/suggest` | 依据所选已打标签记录生成规则建议(含 `duplicate` 标记) | | POST | `/api/ai/rule/suggest` | 依据所选已打标签记录生成规则建议(含 `duplicate` 标记) |
| POST | `/api/ai/rule/apply` | 批量创建规则(自动去重、按需新建分类/标签) | | POST | `/api/ai/rule/apply` | 批量创建规则(自动去重、按需新建分类/标签) |
| POST | `/api/ai/rule/apply-transactions` | 按传入规则对指定交易做本地匹配并应用(命中则覆盖分类、追加标签,必要时新建;纯本地匹配,不外发数据) | | POST | `/api/ai/rule/apply-transactions` | 按传入规则对指定交易做本地匹配并应用(命中则覆盖分类、追加标签,必要时新建;纯本地匹配,不外发数据) |
| POST | `/api/ai/rule/check-duplicates` | 校验编辑后的规则是否与已有规则或批内其它规则重复(body `{ rules }`,返回逐条布尔标记;纯本地,不调用大模型) |
| POST | `/api/ai/statistics` | 自然语言解析为统计面板参数 | | POST | `/api/ai/statistics` | 自然语言解析为统计面板参数 |
| GET / PUT | `/api/setting/excluded-display-style` | 查询 / 更新「不计入统计」记录展示样式 | | GET / PUT | `/api/setting/excluded-display-style` | 查询 / 更新「不计入统计」记录展示样式 |
| GET / PUT | `/api/setting/show-hidden-data` | 查询 / 更新是否显示已隐藏备注的记录 | | GET / PUT | `/api/setting/show-hidden-data` | 查询 / 更新是否显示已隐藏备注的记录 |
+1
View File
@@ -107,6 +107,7 @@
- 分类:`name`、`type`、`icon`(Emoji)、`sort_order`。 - 分类:`name`、`type`、`icon`(Emoji)、`sort_order`。
- 标签:`name`、`color`、`sort_order`,唯一索引 `uk_user_name (user_id, name)` 保证用户内标签名唯一。 - 标签:`name`、`color`、`sort_order`,唯一索引 `uk_user_name (user_id, name)` 保证用户内标签名唯一。
- 删除标签时会一并清理 `t_transaction_tag` 与 `t_rule_tag` 中的关联记录(单条与批量删除行为一致);删除分类仅移除分类本身,账单与规则上的 `category_id` 作为逻辑外键保留(账单会显示为「未分类」)。
### t_rule ### t_rule
+1
View File
@@ -41,6 +41,7 @@
| `ledgerly.transaction.filterPrefs` | 收支列表筛选、分页、排序 | `views/TransactionList.vue` | 变量 `watch` 自动 | `onMounted` | | `ledgerly.transaction.filterPrefs` | 收支列表筛选、分页、排序 | `views/TransactionList.vue` | 变量 `watch` 自动 | `onMounted` |
| `ledgerly.transaction.aiScope` | 批量 AI 建议范围 | `views/TransactionList.vue` | 变量 `watch` 自动 | ref 初始化 | | `ledgerly.transaction.aiScope` | 批量 AI 建议范围 | `views/TransactionList.vue` | 变量 `watch` 自动 | ref 初始化 |
| `ledgerly.transaction.aiRecordTab` | AI 记账方式(文字/图片) | `views/TransactionList.vue` | 变量 `watch` 自动 | ref 初始化 | | `ledgerly.transaction.aiRecordTab` | AI 记账方式(文字/图片) | `views/TransactionList.vue` | 变量 `watch` 自动 | ref 初始化 |
| `ledgerly.transaction.aiRuleApplyToSelection` | AI 生成规则时是否同时应用到勾选账单(`true`/`false`,默认 `true`) | `components/AiRuleSuggestModal.vue` | `useStringPreference` 自动 | 弹窗初始化 |
| `ledgerly.transaction.aiQueryHistory` | AI 自然语言查询历史 | `views/TransactionList.vue` / `components/AiQueryWindow.vue` | 提交查询时 | 浮窗聚焦时 | | `ledgerly.transaction.aiQueryHistory` | AI 自然语言查询历史 | `views/TransactionList.vue` / `components/AiQueryWindow.vue` | 提交查询时 | 浮窗聚焦时 |
| `ledgerly.dashboard.aiSearchHistory` | 统计面板 AI 查询历史 | `views/Dashboard.vue` | 提交 AI 搜索时 | 浮窗聚焦时 | | `ledgerly.dashboard.aiSearchHistory` | 统计面板 AI 查询历史 | `views/Dashboard.vue` | 提交 AI 搜索时 | 浮窗聚焦时 |
| `ledgerly.transaction.aiQueryWindow` | 收支记录 AI 查询浮窗位置与尺寸 | `components/FloatingPanel.vue` | 拖拽/缩放结束时 | 浮窗初始化 | | `ledgerly.transaction.aiQueryWindow` | 收支记录 AI 查询浮窗位置与尺寸 | `components/FloatingPanel.vue` | 拖拽/缩放结束时 | 浮窗初始化 |
+11 -1
View File
@@ -29,6 +29,7 @@
| 账单解析预览 | 展示命中的规则名与建议分类/标签 | | 账单解析预览 | 展示命中的规则名与建议分类/标签 |
| 账单确认导入 | 规则建议分类优先于面板默认分类;标签与默认标签合并去重 | | 账单确认导入 | 规则建议分类优先于面板默认分类;标签与默认标签合并去重 |
| 新建/编辑记录 | 不自动应用,但可借助 AI 推荐 | | 新建/编辑记录 | 不自动应用,但可借助 AI 推荐 |
| 规则管理页「应用」 | 手动把规则应用到命中的历史账单(先预览确认;分类覆盖、标签追加) |
## 列表查询与分页 ## 列表查询与分页
@@ -36,6 +37,14 @@
- `GET /api/rule/page`:分页查询,支持 `keyword`(匹配规则名称或关键词)、`enabled`、`type` 筛选,返回 `PageResult<RuleVO>`。 - `GET /api/rule/page`:分页查询,支持 `keyword`(匹配规则名称或关键词)、`enabled`、`type` 筛选,返回 `PageResult<RuleVO>`。
- `DELETE /api/rule/batch`:批量删除(body `{ ids }`);先按 `userId` 过滤归属,清理规则-标签关联后删除,返回实际删除数。 - `DELETE /api/rule/batch`:批量删除(body `{ ids }`);先按 `userId` 过滤归属,清理规则-标签关联后删除,返回实际删除数。
## 一键应用到匹配账单
规则默认只在导入时生效,不会改写历史账单;`RuleService.previewApply` / `applyToMatchedTransactions` 支持把规则手动应用到**历史账单**(前端规则管理页的行内「应用」/ 勾选后的「应用匹配账单」):
- `POST /api/rule/apply-preview`(`previewApply`):校验规则归属后加载当前用户**全部交易**,按规则条件(类型 + 关键词 + 金额区间;关键词匹配备注、忽略大小写)逐条匹配;**分类**取所选规则中优先级最高(`sort_order` 最小)且类型一致者,**标签**取所有命中规则标签的并集。返回命中明细 `RuleMatchedTransactionVO`(含当前分类/标签与目标分类/标签)与统计 `RuleApplyPreviewVO`(命中数、将改分类数、将加标签数、是否截断)。明细最多 500 条(`PREVIEW_MAX_ROWS`),超出置 `truncated`,统计仍按全部命中计算。
- `POST /api/rule/apply`(`applyToMatchedTransactions`):复用同一匹配逻辑,**分类覆盖写入**(仅类型一致时)、**标签仅追加缺失项**(不删除已有标签),返回 `RuleApplyResultVO`(命中/变更/新增标签数),幂等。
- 入参 DTO `RuleApplyRequest`(`ruleIds`,非空);均为**纯本地匹配**,不调用大模型;**不限规则是否启用**(停用规则也可手动应用)。
## AI 生成规则 ## AI 生成规则
`AiRuleServiceImpl` 提供两个入口:规则管理页(生成 + 创建)与收支记录页批量操作(生成 + 可选应用到账单)。 `AiRuleServiceImpl` 提供两个入口:规则管理页(生成 + 创建)与收支记录页批量操作(生成 + 可选应用到账单)。
@@ -43,7 +52,8 @@
- 生成建议(`suggest`,`POST /api/ai/rule/suggest`):依据用户勾选的记录生成规则建议;规则管理页限定**已打标签记录**,收支记录页为**任意勾选账单**。输入字段为记录的类型、备注、交易渠道、已有分类与标签(不含金额与原始账单数据),并按「类型 + 备注 + 渠道 + 标签」组合去重。 - 生成建议(`suggest`,`POST /api/ai/rule/suggest`):依据用户勾选的记录生成规则建议;规则管理页限定**已打标签记录**,收支记录页为**任意勾选账单**。输入字段为记录的类型、备注、交易渠道、已有分类与标签(不含金额与原始账单数据),并按「类型 + 备注 + 渠道 + 标签」组合去重。
- 创建规则(`apply`,`POST /api/ai/rule/apply`):输出规则经 `RuleService.create` 落库,必要时新建分类/标签,规则默认启用。 - 创建规则(`apply`,`POST /api/ai/rule/apply`):输出规则经 `RuleService.create` 落库,必要时新建分类/标签,规则默认启用。
- 应用到交易(`applyToTransactions`,`POST /api/ai/rule/apply-transactions`):**纯本地匹配、不调用大模型**,按规则的类型/关键词/金额条件匹配指定交易,命中则聚合分类与标签,委托 `AiSuggestionService.apply` 落库(覆盖分类、追加标签,必要时新建)。 - 应用到交易(`applyToTransactions`,`POST /api/ai/rule/apply-transactions`):**纯本地匹配、不调用大模型**,按规则的类型/关键词/金额条件匹配指定交易,命中则聚合分类与标签,委托 `AiSuggestionService.apply` 落库(覆盖分类、追加标签,必要时新建)。
- **去重**:以「类型 + 归一化关键词 + 分类 + 排序标签集合」为签名;与已有规则或同批已创建规则重复时跳过(`apply` 返回实际创建数),建议阶段则标记 `duplicate` 供前端禁止勾选。 - 去重校验(`checkDuplicates`,`POST /api/ai/rule/check-duplicates`):**纯本地、不调用大模型**,复用同一签名逻辑,对**编辑后的规则**逐条返回是否重复(与已有规则签名一致或批内先前规则重复),入参 DTO `AiRuleDuplicateCheckRequest`(复用 `AiRuleApplyRequest.Item`)。前端规则弹窗编辑任一字段后防抖(约 300ms)调用它刷新 `duplicate` 标记。
- **去重签名**:以「类型 + 归一化关键词 + 分类 + 排序标签集合」为签名;与已有规则或同批已创建规则重复时跳过(`apply` 返回实际创建数),建议阶段与编辑重算阶段则标记 `duplicate`;前端重复项默认不勾选且禁止勾选,编辑后重算时「重复→不重复」自动勾选、「不重复→重复」自动取消并禁用。
## 校验 ## 校验
+4 -4
View File
@@ -51,7 +51,7 @@ API Key 仅保存在本地数据库中,仅用于向你配置的接口地址发
| 生成渠道 | 新增/编辑记录 → 交易渠道 → AI 新建 | 由账单描述生成渠道名称并创建 | | 生成渠道 | 新增/编辑记录 → 交易渠道 → AI 新建 | 由账单描述生成渠道名称并创建 |
| 推荐 Emoji | 分类管理 → AI 推荐 | 为分类推荐 Emoji 图标 | | 推荐 Emoji | 分类管理 → AI 推荐 | 为分类推荐 Emoji 图标 |
| 推荐颜色 | 标签管理 → AI 推荐 | 为标签推荐颜色 | | 推荐颜色 | 标签管理 → AI 推荐 | 为标签推荐颜色 |
| 生成规则 | 规则管理 → AI 生成规则(已打标签记录)/收支记录 → 批量操作 → AI 生成规则(勾选账单) | 由所选记录归纳分类与标签规则,可同时应用到勾选账单 | | 生成规则 | 规则管理 → AI 生成规则(已打标签记录)/收支记录 → 批量操作 → AI 生成规则(勾选账单) | 由所选记录归纳分类与标签规则,规则可编辑、去重实时重算,可同时应用到勾选账单(默认开启) |
| 统计搜索 | 统计面板 → AI 搜索 | 自然语言解析为统计筛选条件与维度 | | 统计搜索 | 统计面板 → AI 搜索 | 自然语言解析为统计筛选条件与维度 |
| 生成关系名称 | 收支记录 → 账单关系 → AI 生成 | 依据关联账单的类型、渠道、分类与备注生成关系名称与备注 | | 生成关系名称 | 收支记录 → 账单关系 → AI 生成 | 依据关联账单的类型、渠道、分类与备注生成关系名称与备注 |
| 智能查数 | 左侧菜单 → 智能查数 | 自然语言转只读 SQL 查询并展示结果 | | 智能查数 | 左侧菜单 → 智能查数 | 自然语言转只读 SQL 查询并展示结果 |
@@ -116,10 +116,10 @@ API Key 仅保存在本地数据库中,仅用于向你配置的接口地址发
有两种生成方式: 有两种生成方式:
- **规则管理页**:点「AI 生成规则」,选择用于归纳的**已打标签记录**(默认全选当前页,支持搜索、筛选与分页),AI 依据这些记录的**类型、备注、交易渠道、已有分类与标签**生成规则建议,预览编辑后批量创建。 - **规则管理页**:点「AI 生成规则」,选择用于归纳的**已打标签记录**(默认全选当前页,支持搜索、筛选与分页),AI 依据这些记录的**类型、备注、交易渠道、已有分类与标签**生成规则建议;建议可**逐字段编辑**(名称、类型、关键词、金额区间、分类、标签),编辑后自动重算去重,勾选后批量创建。
- **收支记录页**:在**批量操作**区勾选若干账单后点「AI 生成规则」,直接以**勾选的账单**为依据归纳规则(不要求已打标签);弹窗中还可勾选「**同时应用到当前勾选的账单**」,命中时覆盖这些账单的分类并追加标签。 - **收支记录页**:在**批量操作**区勾选若干账单后点「AI 生成规则」,直接以**勾选的账单**为依据归纳规则(不要求已打标签);规则同样可编辑、去重实时重算,弹窗中还可勾选「**同时应用到当前勾选的账单**」(**默认开启**并记住上次选择),命中时覆盖这些账单的分类并追加标签。
两种方式都会对与已有规则重复的建议标记「已存在」并禁止导入。详见[规则管理](/guide/rules#ai-生成规则)与[收支记录](/guide/transactions#ai-生成规则从勾选账单)。 两种方式都会对与已有规则重复的建议标记「已存在」并禁止导入,且**编辑后实时重算**(改到不重复自动勾选、改成重复自动取消并禁用)。详见[规则管理](/guide/rules#ai-生成规则)与[收支记录](/guide/transactions#ai-生成规则从勾选账单)。
## 用量统计 ## 用量统计
+6
View File
@@ -26,9 +26,12 @@
- **编辑**:在分类行点击「编辑」修改。 - **编辑**:在分类行点击「编辑」修改。
- **删除**:点击「删除」并确认;已使用该分类的记录会变为「未分类」。 - **删除**:点击「删除」并确认;已使用该分类的记录会变为「未分类」。
- **批量删除**:点右上角「**批量操作**」进入批量选择模式,支出与收入两张表格都会出现勾选列,勾选后点「**批量删除**」并确认即可一次删除多个(两表勾选会合并处理);点「**退出批量**」退出并清空勾选。
- **图标**:使用 Emoji 选择器挑选,也可自定义输入。 - **图标**:使用 Emoji 选择器挑选,也可自定义输入。
- **AI 生成分类**:启用 AI 后,输入用途描述(如「日常通勤打车地铁」),点「AI 生成」自动填充名称、类型与图标。 - **AI 生成分类**:启用 AI 后,输入用途描述(如「日常通勤打车地铁」),点「AI 生成」自动填充名称、类型与图标。
![分类批量删除](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/categories-batch.png)
新注册用户会自动获得一套默认分类(如餐饮、交通、购物、居住、通讯、娱乐、医疗、教育等)。 新注册用户会自动获得一套默认分类(如餐饮、交通、购物、居住、通讯、娱乐、医疗、教育等)。
## 标签管理 ## 标签管理
@@ -46,6 +49,9 @@
- **颜色**:使用颜色选择器,内置常用色板;启用 AI 后点「AI 推荐」可按标签名称推荐颜色。 - **颜色**:使用颜色选择器,内置常用色板;启用 AI 后点「AI 推荐」可按标签名称推荐颜色。
- 标签以彩色标签形式在收支记录、导入预览等位置展示。 - 标签以彩色标签形式在收支记录、导入预览等位置展示。
- 删除标签会从所有交易记录中移除该标签,操作前会二次确认。 - 删除标签会从所有交易记录中移除该标签,操作前会二次确认。
- **批量删除**:点右上角「**批量操作**」后表格出现勾选列,勾选要删除的标签后点「**批量删除**」并确认,即可一次删除多个;点「**退出批量**」退出并清空勾选。批量删除同样会从所有交易记录中移除这些标签。
![标签批量删除](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/tags-batch.png)
## 使用建议 ## 使用建议
+22 -5
View File
@@ -54,12 +54,29 @@
## 多选与批量删除 ## 多选与批量删除
规则列表每行前都有勾选框,表头可**全选当前页**: 为保持列表清爽,规则表格默认不显示勾选框。需要进行批量操作时,点右上角「**批量操作**」进入批量选择模式,此时每行前会出现勾选框,表头可**全选当前页**;完成后点「**退出批量**」退出并清空勾选。
- 勾选若干规则后,点右上角「**批量删除**」并确认,即可一次性删除选中的规则。 - 勾选若干规则后,点右上角「**批量删除**」并确认,即可一次性删除选中的规则。
- 未勾选任何规则时「批量删除」不可点击。 - 未勾选任何规则时「批量删除」不可点击。
> 删除规则只影响后续导入时的自动归类,已应用的历史记录分类与标签不会被撤销。 > 删除规则只影响后续导入与手动应用,已应用的历史记录分类与标签不会被撤销。
## 一键应用到匹配账单
规则默认只在**账单导入**时对新增记录生效,不会自动改写已有记录。如需把规则应用到**历史账单**,可在规则列表使用「应用」:
1. **单条规则**:点击规则行的「**应用**」按钮。
2. **多条规则**:点右上角「**批量操作**」进入批量选择模式,勾选若干规则后点「**应用匹配账单**」。
两种方式都会先弹出**预览**:按规则条件(类型 + 关键词 + 金额区间)列出全部命中的账单,并显示每条账单的**当前分类/标签 → 目标分类/标签**,以及命中总数、将更新分类数与将追加标签数;确认后点「**确认应用**」执行。
![应用规则到匹配账单](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/elements/rule-apply-preview.png)
- **分类**:覆盖写入(仅当规则分类与账单收支类型一致时)。
- **标签**:追加到账单已有标签之后,**不会删除**原有标签。
- 匹配范围为**全部历史账单**;**停用的规则**也可手动应用(应用后再启用不影响本次结果)。
- 命中超过 500 条时预览仅展示前 500 条,应用仍作用于全部命中;重复执行不会产生重复标签(幂等)。
- 全程本地运算,不调用 AI。
## AI 生成规则 ## AI 生成规则
@@ -71,14 +88,14 @@
![AI 生成规则 · 选择记录](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/elements/ai-rule-select.png) ![AI 生成规则 · 选择记录](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/elements/ai-rule-select.png)
2. **预览规则**:AI 依据所选记录的**类型、备注、交易渠道、已有分类与标签**归纳出规则建议,展示规则名称、关键词、目标分类与标签、理由。名称与关键词可编辑,勾选后点击「创建勾选规则」批量创建;系统会自动创建建议中不存在的新分类与标签。 2. **预览与编辑规则**:AI 依据所选记录的**类型、备注、交易渠道、已有分类与标签**归纳出规则建议,展示规则名称、类型、关键词、金额区间、目标分类与标签、理由。所有字段都可**直接编辑**(名称、类型、关键词、金额区间、分类、标签),AI 建议的新分类/新标签以下拉里的「**新建:xxx**」选项保留;勾选后点击「创建勾选规则」批量创建,系统会自动创建建议中不存在的新分类与标签。
![AI 生成规则 · 预览规则](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/elements/ai-rule-preview.png) ![AI 生成规则 · 预览规则](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/elements/ai-rule-preview.png)
> 此外,也可在[收支记录页的批量操作区](/guide/transactions#ai-生成规则从勾选账单)勾选账单后直接生成规则(不要求已打标签),并可选「同时应用到勾选账单」。 > 此外,也可在[收支记录页的批量操作区](/guide/transactions#ai-生成规则从勾选账单)勾选账单后直接生成规则(不要求已打标签),并可选「同时应用到勾选账单」(默认开启,会记住上次选择)。
::: tip 与已有规则去重 ::: tip 与已有规则去重
AI 建议若与**数据库中已有规则重复**(按「类型 + 关键词 + 分类 + 标签集合」判定),会标注「**已存在**」,**默认不勾选且不可勾选**,无法导入,避免产生重复规则。 AI 建议若与**数据库中已有规则重复**(按「类型 + 关键词 + 分类 + 标签集合」判定),会标注「**已存在**」,默认不勾选且不可勾选。**编辑规则后会自动重新判定**:改成不重复会自动勾选,改成重复会自动取消勾选并禁用,避免产生重复规则。
::: :::
## 示例 ## 示例
+6 -4
View File
@@ -27,19 +27,21 @@
| 一键设置 | 批量设置分类、计入统计与标签;**有勾选时作用于勾选记录,未勾选时作用于当前页全部记录**,详见[一键设置(批量设置)](#一键设置-批量设置) | | 一键设置 | 批量设置分类、计入统计与标签;**有勾选时作用于勾选记录,未勾选时作用于当前页全部记录**,详见[一键设置(批量设置)](#一键设置-批量设置) |
| 批量删除 | 勾选至少一条后出现,确认后删除所选记录 | | 批量删除 | 勾选至少一条后出现,确认后删除所选记录 |
| 建立关系 | 勾选至少两条后出现,为所选记录[建立账单关系](/guide/relations) | | 建立关系 | 勾选至少两条后出现,为所选记录[建立账单关系](/guide/relations) |
| AI 生成规则 | 启用 AI 后出现;勾选至少一条账单,由 AI 归纳分类/标签规则,可在弹窗中勾选「同时应用到当前勾选的账单」,详见 [AI 生成规则](#ai-生成规则从勾选账单) | | AI 生成规则 | 启用 AI 后出现;勾选至少一条账单,由 AI 归纳分类/标签规则,规则可编辑、去重实时重算,可「同时应用到当前勾选的账单」(默认开启并记住),详见 [AI 生成规则](#ai-生成规则从勾选账单) |
> 翻页、刷新或退出批量模式都会清空已勾选记录。**批量删除**与**建立关系**只作用于勾选记录;**一键设置**在未勾选时才作用于当前页全部记录,且当次勾选范围以打开弹窗时的选择为准。 > 翻页、刷新或退出批量模式都会清空已勾选记录。**批量删除**与**建立关系**只作用于勾选记录;**一键设置**在未勾选时才作用于当前页全部记录,且当次勾选范围以打开弹窗时的选择为准。
## AI 生成规则(从勾选账单) ## AI 生成规则(从勾选账单)
启用 AI 后,批量操作区会出现「**AI 生成规则**」按钮。勾选若干账单后点击,AI 会依据这些账单的**类型、备注、交易渠道、已有分类与标签**归纳出「关键词 → 分类/标签」规则;在弹窗中预览、勾选要创建的规则后点击「创建勾选规则」批量创建,系统会自动创建建议中不存在的新分类与标签。 启用 AI 后,批量操作区会出现「**AI 生成规则**」按钮。勾选若干账单后点击,AI 会依据这些账单的**类型、备注、交易渠道、已有分类与标签**归纳出「关键词 → 分类/标签」规则。
弹窗底部可勾选「**同时应用到当前勾选的 N 条账单**」——命中规则时会**覆盖这些账单的分类并追加标签**(不改动备注与金额)。生成与「同时应用」全程不发送金额、原始账单数据等敏感字段。 在弹窗中可**直接编辑**每条规则(名称、类型、关键词、金额区间、分类、标签;AI 建议的新分类/新标签以「新建:xxx」选项保留);编辑后系统会自动重新判定是否与已有规则重复,**改成不重复自动勾选、改成重复自动取消并禁用**。勾选后点「创建勾选规则」批量创建,系统会自动创建建议中不存在的新分类与标签。
弹窗底部可勾选「**同时应用到当前勾选的 N 条账单**」——命中规则时会**覆盖这些账单的分类并追加标签**(不改动备注与金额)。该开关**默认开启**,并会记住上次选择。生成与「同时应用」全程不发送金额、原始账单数据等敏感字段。
![AI 生成规则(收支记录)](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/elements/ai-rule-transactions.png) ![AI 生成规则(收支记录)](https://adcarac.oss-cn-beijing.aliyuncs.com/finance-app-docs/screenshots/elements/ai-rule-transactions.png)
> 与[规则管理](/guide/rules#ai-生成规则)页的「AI 生成规则」(依据**已打标签记录**归纳)不同,此入口直接以**勾选的账单**为依据,不要求已打标签;两处都会对与已有规则重复的建议标记「已存在」并禁止导入。 > 与[规则管理](/guide/rules#ai-生成规则)页的「AI 生成规则」(依据**已打标签记录**归纳)不同,此入口直接以**勾选的账单**为依据,不要求已打标签;两处的规则建议都可编辑、都会对与已有规则重复的建议标记「已存在」并禁止导入,且编辑后实时重算。
## 账单导入 ## 账单导入
+86 -1
View File
@@ -1,7 +1,7 @@
// 元素截图:截取登录/注册卡片、各功能弹窗与账单解析预览,写入 public/screenshots/elements/。 // 元素截图:截取登录/注册卡片、各功能弹窗与账单解析预览,写入 public/screenshots/elements/。
import { resolve } from 'node:path' import { resolve } from 'node:path'
import { pathToFileURL } from 'node:url' import { pathToFileURL } from 'node:url'
import type { Page } from 'playwright-core' import type { Locator, Page } from 'playwright-core'
import { clickText, injectSession, launchBrowser, login, newContext, waitPage } from './browser.js' import { clickText, injectSession, launchBrowser, login, newContext, waitPage } from './browser.js'
import { BASE_URL, ELEM_DIR, OUT_DIR, SAMPLE_BILL, SAMPLE_IMAGE, SAMPLE_IMAGE2 } from './config.js' import { BASE_URL, ELEM_DIR, OUT_DIR, SAMPLE_BILL, SAMPLE_IMAGE, SAMPLE_IMAGE2 } from './config.js'
@@ -480,6 +480,89 @@ export async function captureAiRuleFromTransactions(page: Page): Promise<void> {
} }
} }
/**
* 截取规则管理页「应用」命中的账单预览弹窗(elements/rule-apply-preview.png)。
*
* <p>打开首条规则的行内「应用」按钮,等待命中账单预览表加载后截图;只预览、不点击「确认应用」,不修改数据。
* 需目标环境存在至少一条可命中账单的规则。</p>
*
* @param page Page 实例
*/
export async function captureRuleApplyPreview(page: Page): Promise<void> {
try {
await page.setViewportSize({ width: 1440, height: 1000 })
await page.goto(BASE_URL + '/rules')
await waitPage(page, { selector: PAGE_READY })
// 首条规则行的「应用」按钮(避免误点顶部的「应用匹配账单」)
const applyBtn = page.locator('.n-data-table-tbody tr').first().locator('button', { hasText: '应用' }).first()
await applyBtn.waitFor({ state: 'visible', timeout: 20000 })
await applyBtn.evaluate((el) => (el as HTMLElement).click())
const modal = page.locator('.n-modal').first()
await modal.waitFor({ state: 'visible', timeout: 20000 })
// 命中账单表异步加载,等待数据行出现
await modal.locator('.n-data-table-tbody tr').first().waitFor({ state: 'visible', timeout: 20000 })
await page.waitForLoadState('networkidle', { timeout: 20000 }).catch(() => undefined)
await page.waitForTimeout(800)
await modal.screenshot({ path: resolve(ELEM_DIR, 'rule-apply-preview.png') })
console.log('[OK] modal/rule-apply-preview')
} catch (err) {
console.log(`[FAIL] modal/rule-apply-preview: ${err}`)
}
}
/**
* 勾选表格前 n 行(用于进入批量模式后展示多选效果)。
*
* @param page Page 实例
* @param table 目标表格 Locator
* @param n 勾选行数
*/
async function pickRows(page: Page, table: Locator, n: number): Promise<void> {
const rows = table.locator('.n-data-table-tbody tr')
const count = Math.min(n, await rows.count())
for (let i = 0; i < count; i++) {
await rows.nth(i).locator('.n-checkbox').first().click({ force: true }).catch(() => undefined)
}
await page.waitForTimeout(300)
}
/**
* 截取标签/分类管理页的「批量操作模式」(勾选列与批量删除入口)。
*
* <p>进入批量选择模式后勾选若干行,展示勾选列;仅截图,不执行任何删除。</p>
*
* @param page Page 实例
*/
export async function captureBatchModes(page: Page): Promise<void> {
// 标签管理批量模式
try {
await page.setViewportSize({ width: 1440, height: 900 })
await page.goto(BASE_URL + '/tags')
await waitPage(page, { selector: '.n-data-table-tbody tr' })
if (!(await clickText(page, '批量操作'))) throw new Error('未找到「批量操作」按钮')
await pickRows(page, page.locator('.n-data-table').first(), 2)
await page.screenshot({ path: resolve(OUT_DIR, 'tags-batch.png') })
console.log('[OK] page/tags-batch')
} catch (err) {
console.log(`[FAIL] tags-batch: ${err}`)
}
// 分类管理批量模式(支出/收入两表各勾选若干)
try {
await page.setViewportSize({ width: 1440, height: 1100 })
await page.goto(BASE_URL + '/categories')
await waitPage(page, { selector: '.n-data-table-tbody tr' })
if (!(await clickText(page, '批量操作'))) throw new Error('未找到「批量操作」按钮')
const tables = page.locator('.n-data-table')
await pickRows(page, tables.nth(0), 2)
await pickRows(page, tables.nth(1), 2)
await page.screenshot({ path: resolve(OUT_DIR, 'categories-batch.png') })
console.log('[OK] page/categories-batch')
} catch (err) {
console.log(`[FAIL] categories-batch: ${err}`)
}
}
/** /**
* 截取系统设置的配置面板。 * 截取系统设置的配置面板。
* *
@@ -680,6 +763,8 @@ async function main(): Promise<void> {
await captureInlineCreate(page) await captureInlineCreate(page)
await captureAiRule(page) await captureAiRule(page)
await captureAiRuleFromTransactions(page) await captureAiRuleFromTransactions(page)
await captureRuleApplyPreview(page)
await captureBatchModes(page)
await captureAiRecordImage(page) await captureAiRecordImage(page)
await captureBillDetail(page) await captureBillDetail(page)
await captureChannelAi(page) await captureChannelAi(page)