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:
@@ -1,7 +1,7 @@
|
||||
// 元素截图:截取登录/注册卡片、各功能弹窗与账单解析预览,写入 public/screenshots/elements/。
|
||||
import { resolve } from 'node:path'
|
||||
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 { 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 captureAiRule(page)
|
||||
await captureAiRuleFromTransactions(page)
|
||||
await captureRuleApplyPreview(page)
|
||||
await captureBatchModes(page)
|
||||
await captureAiRecordImage(page)
|
||||
await captureBillDetail(page)
|
||||
await captureChannelAi(page)
|
||||
|
||||
Reference in New Issue
Block a user