diff --git a/admin-frontend-vue/src/pages/account/GroupsPage.vue b/admin-frontend-vue/src/pages/account/GroupsPage.vue index a62d3929..52327ead 100644 --- a/admin-frontend-vue/src/pages/account/GroupsPage.vue +++ b/admin-frontend-vue/src/pages/account/GroupsPage.vue @@ -1,4 +1,5 @@ + + + + diff --git a/admin-frontend-vue/src/pages/records/RecordsHistoryPage.vue b/admin-frontend-vue/src/pages/records/RecordsHistoryPage.vue new file mode 100644 index 00000000..be63417d --- /dev/null +++ b/admin-frontend-vue/src/pages/records/RecordsHistoryPage.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/admin-frontend-vue/src/pages/records/RecordsSoftwareVersionPage.vue b/admin-frontend-vue/src/pages/records/RecordsSoftwareVersionPage.vue new file mode 100644 index 00000000..ea43e35e --- /dev/null +++ b/admin-frontend-vue/src/pages/records/RecordsSoftwareVersionPage.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/admin-frontend-vue/src/pages/shop/ShopKeysPage.vue b/admin-frontend-vue/src/pages/shop/ShopKeysPage.vue new file mode 100644 index 00000000..9dd5be15 --- /dev/null +++ b/admin-frontend-vue/src/pages/shop/ShopKeysPage.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/admin-frontend-vue/src/pages/shop/ShopManagePage.vue b/admin-frontend-vue/src/pages/shop/ShopManagePage.vue new file mode 100644 index 00000000..e9756c5f --- /dev/null +++ b/admin-frontend-vue/src/pages/shop/ShopManagePage.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/admin-frontend-vue/src/pages/tasks/ImageVideoTasksPage.vue b/admin-frontend-vue/src/pages/tasks/ImageVideoTasksPage.vue new file mode 100644 index 00000000..cf8fa7eb --- /dev/null +++ b/admin-frontend-vue/src/pages/tasks/ImageVideoTasksPage.vue @@ -0,0 +1,305 @@ + + + + + diff --git a/admin-frontend-vue/src/pages/tasks/ShopDataTasksPage.vue b/admin-frontend-vue/src/pages/tasks/ShopDataTasksPage.vue new file mode 100644 index 00000000..d7c8b48b --- /dev/null +++ b/admin-frontend-vue/src/pages/tasks/ShopDataTasksPage.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/admin-frontend-vue/tests/datetime-sweep.test.ts b/admin-frontend-vue/tests/datetime-sweep.test.ts new file mode 100644 index 00000000..e0f3c082 --- /dev/null +++ b/admin-frontend-vue/tests/datetime-sweep.test.ts @@ -0,0 +1,87 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' + +// module13(时间统一 YYYY-MM-DD HH:mm:ss)全页接入不变式: +// 所有时间展示列/文本走 formatDateTime;不残留“裸日期 prop 列”或“时间 || '—'”旧写法。 +// 例外:DuplicateCheckPage 不动。 + +const PAGES = [ + 'src/pages/account/GroupsPage.vue', + 'src/pages/account/MenusPage.vue', + 'src/pages/account/UsersPage.vue', + 'src/pages/asin/AsinInvalidPage.vue', + 'src/pages/asin/DedupeRegistryPage.vue', + 'src/pages/asin/QueryAsinPage.vue', + 'src/pages/asin/SkipPricePage.vue', + 'src/pages/records/RecordsHistoryPage.vue', + 'src/pages/records/RecordsSoftwareVersionPage.vue', + 'src/pages/records/RecordsDigitalHumanVersionPage.vue', + 'src/pages/shop/ShopKeysPage.vue', + 'src/pages/shop/ShopManagePage.vue', + 'src/pages/tasks/ImageVideoTasksPage.vue', + 'src/pages/tasks/ShopDataTasksPage.vue', +] + +const TIME_PROPS = 'createdAt|updatedAt|releasedAt|submittedAt|completedAt' + +test('test_datetime_sweep_normal_primary_path', () => { + for (const page of PAGES) { + const src = readSource(page) + assert.match(src, /formatDateTime/, `${page} 需引用 formatDateTime`) + } +}) + +test('test_datetime_sweep_normal_variant_input', () => { + // 各页时间列/文本确实经由 formatDateTime 包裹(至少含一次调用点)。 + for (const page of PAGES) { + const src = readSource(page) + assert.ok((src.match(/formatDateTime\(/g) || []).length >= 1, `${page} 至少一处 formatDateTime 调用`) + } +}) + +test('test_datetime_sweep_normal_repeated_operation_is_idempotent', () => { + const check = (src: string) => new RegExp(`prop="(${TIME_PROPS})"[^>]*/>`).test(src) + for (const page of PAGES) { + const src = readSource(page) + assert.equal(check(src), false, `${page} 不得残留无 formatter 的裸日期 prop 列`) + } +}) + +test('test_datetime_sweep_boundary_empty_input', () => { + // 不再使用“时间字段 || '—'”旧兜底写法(空值由 formatDateTime 统一处理; + // 允许 updatedAt || createdAt 这种字段优先级写法,其值最终仍进 formatDateTime)。 + for (const page of PAGES) { + const src = readSource(page) + assert.ok(!new RegExp(`\\.(${TIME_PROPS})\\s*\\|\\|\\s*['"]—['"]`).test(src), `${page} 不得再写 时间||'—'`) + } +}) + +test('test_datetime_sweep_boundary_single_item', () => { + // 时间标签仍存在(列还在),只是改成走格式化。 + for (const page of PAGES) { + const src = readSource(page) + assert.match(src, /时间/, `${page} 仍应含“时间”列/文案`) + } +}) + +test('test_datetime_sweep_boundary_limit_or_missing_field', () => { + // 工具模块可被各页 import;路径为 @/utils/datetime。 + for (const page of PAGES) { + const src = readSource(page) + assert.match(src, /from ['"]@\/utils\/datetime['"]/, `${page} 需 import 自 @/utils/datetime`) + } +}) + +test('test_datetime_sweep_invalid_input_rejected', () => { + // 例外页 DuplicateCheckPage 未接入且未改动(保持自身实现)。 + const dup = readSource('src/pages/tasks/DuplicateCheckPage.vue') + assert.equal(/formatDateTime/.test(dup), false, 'DuplicateCheckPage 不应引入统一时间格式化') +}) + +test('test_datetime_sweep_dependency_failure_returns_actionable_message', () => { + // 时间字段名清单应覆盖当前出现过的展示字段。 + const util = readSource('src/utils/datetime.ts') + assert.match(util, /formatDateTime/) + assert.match(util, /\\d\{2\}:\\d\{2\}:\\d\{2\}/, '工具输出含 HH:mm:ss 正则') +})