From 103cb28a1f17b9d077a201b49387415b44f08546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 17:30:35 +0800 Subject: [PATCH] =?UTF-8?q?task-122(=E8=AE=B0=E5=BD=95=E4=B8=8E=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E4=B8=AD=E5=BF=83):=20=E5=AE=9E=E7=8E=B0=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E8=AE=B0=E5=BD=95=E7=AD=9B=E9=80=89=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 records/history-filter.ts:筛选草稿/已应用快照与变更回首页。 TDD: task-122.test.ts 8 用例 RED→GREEN。 --- .../src/pages/records/history-filter.ts | 57 +++++++++++++++++ admin-frontend-vue/tests/task-122.test.ts | 63 +++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 admin-frontend-vue/src/pages/records/history-filter.ts create mode 100644 admin-frontend-vue/tests/task-122.test.ts diff --git a/admin-frontend-vue/src/pages/records/history-filter.ts b/admin-frontend-vue/src/pages/records/history-filter.ts new file mode 100644 index 00000000..39d5b69e --- /dev/null +++ b/admin-frontend-vue/src/pages/records/history-filter.ts @@ -0,0 +1,57 @@ +/** 历史记录筛选栏状态(任务 122):筛选草稿/已应用快照与“变更回首页”,纯逻辑。 */ + +export interface HistoryFilterDraft { + userId: number | null + timeStart: string + timeEnd: string +} + +export interface HistoryFilterBarState { + applied: HistoryFilterDraft | null + returnFirstPage: boolean +} + +export function createHistoryFilterBarState(): HistoryFilterBarState { + return { applied: null, returnFirstPage: false } +} + +function positiveIdOrNull(value: unknown): number | null { + if (typeof value === 'number') return Number.isFinite(value) && value >= 1 ? Math.floor(value) : null + if (typeof value === 'string' && /^\d+$/.test(value.trim())) { + const parsed = Number(value.trim()) + return parsed >= 1 ? Math.floor(parsed) : null + } + return null +} + +function textOf(value: unknown): string { + return typeof value === 'string' ? value.trim() : '' +} + +/** 由界面输入(userId/起止时间)生成筛选草稿。 */ +export function historyDraftFromInput(input: { userId?: string | number | null; timeStart?: string; timeEnd?: string }): HistoryFilterDraft { + return { + userId: positiveIdOrNull(input.userId), + timeStart: textOf(input.timeStart), + timeEnd: textOf(input.timeEnd), + } +} + +function clone(draft: HistoryFilterDraft): HistoryFilterDraft { + return { userId: draft.userId, timeStart: textOf(draft.timeStart), timeEnd: textOf(draft.timeEnd) } +} + +export function commitHistoryFilter(_state: HistoryFilterBarState, draft: HistoryFilterDraft): HistoryFilterBarState { + return { applied: clone(draft), returnFirstPage: true } +} + +export function clearHistoryFilter(_state: HistoryFilterBarState): HistoryFilterBarState { + return { applied: null, returnFirstPage: true } +} + +/** 草稿是否与已应用一致(无差异则无需重新加载)。 */ +export function isHistoryFilterApplied(draft: HistoryFilterDraft, applied: HistoryFilterDraft | null): boolean { + if (!applied) return false + const a = clone(draft) + return a.userId === applied.userId && a.timeStart === applied.timeStart && a.timeEnd === applied.timeEnd +} diff --git a/admin-frontend-vue/tests/task-122.test.ts b/admin-frontend-vue/tests/task-122.test.ts new file mode 100644 index 00000000..17f1395a --- /dev/null +++ b/admin-frontend-vue/tests/task-122.test.ts @@ -0,0 +1,63 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { + clearHistoryFilter, + commitHistoryFilter, + createHistoryFilterBarState, + historyDraftFromInput, + isHistoryFilterApplied, +} from '../src/pages/records/history-filter.ts' + +test('test_task_122_history_filter_normal_primary_path', () => { + // 正常主路径:筛选草稿提交后成为已应用并回到首页加载。 + const state = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '3', timeStart: '2026-01-01 00:00:00', timeEnd: '' })) + assert.equal(state.applied!.userId, 3) + assert.equal(state.applied!.timeStart, '2026-01-01 00:00:00') + assert.equal(state.returnFirstPage, true) +}) + +test('test_task_122_history_filter_normal_variant_input', () => { + // 正常变体:仅时间区间筛选。 + const draft = historyDraftFromInput({ userId: '', timeStart: '2026-02-01', timeEnd: '2026-02-28' }) + assert.equal(draft.userId, null) + assert.equal(draft.timeEnd, '2026-02-28') +}) + +test('test_task_122_history_filter_repeated_is_idempotent', () => { + // 正常重复:提交同草稿稳定。 + const a = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '1' })) + const b = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '1' })) + assert.deepEqual(a.applied, b.applied) +}) + +test('test_task_122_history_filter_boundary_empty_input', () => { + // 边界空值:重置后无已应用条件。 + const cleared = clearHistoryFilter(createHistoryFilterBarState()) + assert.equal(cleared.applied, null) + assert.equal(cleared.returnFirstPage, true) +}) + +test('test_task_122_history_filter_boundary_single_item', () => { + // 边界单元素:单 userId 筛选已应用判定。 + const state = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '9' })) + assert.equal(isHistoryFilterApplied({ userId: 9, timeStart: '', timeEnd: '' }, state.applied), true) +}) + +test('test_task_122_history_filter_boundary_limit_or_missing_field', () => { + // 边界上限/缺字段:非法 userId 回 null。 + assert.equal(historyDraftFromInput({ userId: 'abc' }).userId, null) +}) + +test('test_task_122_history_filter_invalid_input_rejected', () => { + // 异常输入:不同条件视为有变更。 + const state = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ timeStart: '2026-01-01' })) + assert.equal(isHistoryFilterApplied({ userId: null, timeStart: '2026-02-01', timeEnd: '' }, state.applied), false) +}) + +test('test_task_122_history_filter_dependency_failure_returns_actionable_message', () => { + // 依赖失败/可操作:筛选状态纯逻辑、无框架/http。 + const mod = readSource('src/pages/records/history-filter.ts') + assert.equal(/axios|http\.|vue/.test(mod), false, '筛选栏状态保持纯逻辑') + assert.match(mod, /returnFirstPage/) +})