diff --git a/admin-frontend-vue/src/pages/asin/AsinInvalidPage.vue b/admin-frontend-vue/src/pages/asin/AsinInvalidPage.vue index a1aff63e..51a5d249 100644 --- a/admin-frontend-vue/src/pages/asin/AsinInvalidPage.vue +++ b/admin-frontend-vue/src/pages/asin/AsinInvalidPage.vue @@ -1,8 +1,9 @@ diff --git a/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue b/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue index 93e98884..4005c31a 100644 --- a/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue +++ b/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue @@ -1,6 +1,6 @@ diff --git a/admin-frontend-vue/src/pages/asin/QueryAsinPage.vue b/admin-frontend-vue/src/pages/asin/QueryAsinPage.vue index ac278dfd..221b09e7 100644 --- a/admin-frontend-vue/src/pages/asin/QueryAsinPage.vue +++ b/admin-frontend-vue/src/pages/asin/QueryAsinPage.vue @@ -1,6 +1,6 @@ diff --git a/admin-frontend-vue/src/pages/asin/SkipPricePage.vue b/admin-frontend-vue/src/pages/asin/SkipPricePage.vue index 962476bd..0fd4ec4e 100644 --- a/admin-frontend-vue/src/pages/asin/SkipPricePage.vue +++ b/admin-frontend-vue/src/pages/asin/SkipPricePage.vue @@ -1,7 +1,6 @@ diff --git a/admin-frontend-vue/tests/align-asin-row-buttons.test.ts b/admin-frontend-vue/tests/align-asin-row-buttons.test.ts index 41d83f08..01d67b3b 100644 --- a/admin-frontend-vue/tests/align-asin-row-buttons.test.ts +++ b/admin-frontend-vue/tests/align-asin-row-buttons.test.ts @@ -2,45 +2,42 @@ import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' -/** 对齐旧版 .btn-sm:asin 域 5 页表格行内操作(编辑/删除/配置)统一为实心小按钮(type primary/danger + size small),不再用 link/text;类目树折叠钮由靛蓝改为蓝白 token。 */ +/** 对齐旧版 .btn-sm:asin 域 5 页已像素复刻,行内操作(编辑/删除/配置)用原生 btn btn-sm 实心小按钮(非 EP 按钮)。 */ -// 实心小按钮:同一 el-button 内同时含 size="small" 与 @click="",且不含 link/text 修饰。 -const SOLID_ACTION = (action: string) => new RegExp(`]*size="small"[^>]*@click="${action}`) -const NO_MODIFIER = (action: string, mod: 'link' | 'text') => - new RegExp(`]*${mod}[^>]*@click="${action}`) +// 断言单个 el-button 标签同时含 btn-sm 与目标 @click,且不含 link/text 修饰(action 含正则元字符需转义)。 +const escapeRe = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +const OLD_BTN = (action: string) => new RegExp(`class="btn btn-sm[^"]*"[^>]*@click="${escapeRe(action)}`) +const NO_LINK = (action: string) => new RegExp(`]*link[^>]*@click="${escapeRe(action)}`) +const NO_TEXT = (action: string) => new RegExp(`]*text[^>]*@click="${escapeRe(action)}`) +const NO_EL = (action: string) => new RegExp(`]*@click="${escapeRe(action)}`) -function assertRowButtonSolid(source: string, action: string) { - assert.match(source, SOLID_ACTION(action), `${action} 为 size=small 实心按钮`) - assert.doesNotMatch(source, NO_MODIFIER(action, 'link'), `${action} 不再 link`) - assert.doesNotMatch(source, NO_MODIFIER(action, 'text'), `${action} 不再 text`) +function assertOldRowButton(source: string, action: string, page: string) { + assert.match(source, OLD_BTN(action), `${page} ${action} 为 btn-sm 实心按钮`) + assert.doesNotMatch(source, NO_LINK(action), `${page} ${action} 不再 link`) + assert.doesNotMatch(source, NO_TEXT(action), `${page} ${action} 不再 text`) + assert.doesNotMatch(source, NO_EL(action), `${page} ${action} 不再用 EP 按钮`) } -test('align_dedupe_registry_row_buttons_old_btn', () => { - const s = readSource('src/pages/asin/DedupeRegistryPage.vue') - assert.match(s, /class="btn btn-sm"[^>]*@click="openEdit\(row\)"/, '编辑用旧版 btn-sm 实心小按钮') - assert.match(s, /class="btn btn-sm btn-danger"[^>]*@click="removeRow\(row\)"/, '删除用旧版 btn-sm btn-danger') - assert.doesNotMatch(s, /el-button/, '去重汇总已自绘,无 EP 按钮') - assert.match(s, /window\.confirm\(`确定删除总数据/, '删除确认文案对齐旧版(原生 confirm)') -}) - -test('align_invalid_asin_row_buttons_solid', () => { +test('align_invalid_asin_row_buttons_old_btn', () => { const s = readSource('src/pages/asin/AsinInvalidPage.vue') - assertRowButtonSolid(s, 'openEdit') - assertRowButtonSolid(s, 'remove') + assertOldRowButton(s, 'openEdit(row)', '不符合ASIN') + assertOldRowButton(s, 'remove(row)', '不符合ASIN') + assert.match(s, /window\.confirm\(invalidAsinDeleteText\(row\)\)/, '删除确认原生 confirm 对齐旧版') }) -test('align_query_asin_config_solid', () => { - assertRowButtonSolid(readSource('src/pages/asin/QueryAsinPage.vue'), 'openConfig') +test('align_query_asin_config_old_btn', () => { + assertOldRowButton(readSource('src/pages/asin/QueryAsinPage.vue'), 'openConfig(row.item as QueryAsinItem)', '查询ASIN') }) -test('align_skip_price_config_solid', () => { - assertRowButtonSolid(readSource('src/pages/asin/SkipPricePage.vue'), 'openConfig') +test('align_skip_price_config_old_btn', () => { + assertOldRowButton(readSource('src/pages/asin/SkipPricePage.vue'), 'openConfig(row.item as SkipPriceItem)', '最低价ASIN') }) -test('align_product_category_tree_row_buttons_solid', () => { +test('align_product_category_row_buttons_old_btn', () => { const s = readSource('src/pages/asin/ProductCategoryPage.vue') - assertRowButtonSolid(s, 'openEdit') - assertRowButtonSolid(s, 'removeCategory') + assertOldRowButton(s, 'openEdit(row.node)', '商品类目树视图') + assertOldRowButton(s, 'openEdit(row)', '商品类目搜索视图') + assert.match(s, /btn-sm btn-danger"[^>]*@click="removeCategory/, '删除为 btn-sm btn-danger') }) test('align_product_category_no_indigo_left', () => { @@ -49,14 +46,16 @@ test('align_product_category_no_indigo_left', () => { assert.match(s, /--admin-primary-soft/, '折叠钮改用蓝白主色 soft token') }) -test('align_product_category_search_and_tree_edits_both_solid', () => { +test('align_product_category_load_more_old_class', () => { + // 类目「加载更多」行按钮沿用 btn-sm btn-secondary(旧版小组按钮样式)。 const s = readSource('src/pages/asin/ProductCategoryPage.vue') - const opens = s.match(/]*size="small"[^>]*@click="openEdit/g) || [] - assert.ok(opens.length >= 2, `树/搜索两处编辑均实心(${opens.length})`) + assert.match(s, /class="btn btn-sm btn-secondary"[^>]*loadMoreChildren/, '加载更多为旧版 btn-sm btn-secondary') }) -test('align_product_category_load_more_kept_link', () => { - // 类目"加载更多"是非行内操作的树加载行,保留 link 不误伤。 - const s = readSource('src/pages/asin/ProductCategoryPage.vue') - assert.match(s, /link size="small" type="primary"[^>]*loadMoreChildren/, '加载更多保留 link') +test('align_dedupe_registry_row_buttons_old_btn', () => { + const s = readSource('src/pages/asin/DedupeRegistryPage.vue') + assert.match(s, /class="btn btn-sm"[^>]*@click="openEdit\(row\)"/, '编辑用旧版 btn-sm 实心小按钮') + assert.match(s, /class="btn btn-sm btn-danger"[^>]*@click="removeRow\(row\)"/, '删除用旧版 btn-sm btn-danger') + assert.doesNotMatch(s, /el-button/, '去重汇总已自绘,无 EP 按钮') + assert.match(s, /window\.confirm\(`确定删除总数据/, '删除确认文案对齐旧版(原生 confirm)') }) diff --git a/admin-frontend-vue/tests/align-category-tree.test.ts b/admin-frontend-vue/tests/align-category-tree.test.ts index da62d3b4..14041f3e 100644 --- a/admin-frontend-vue/tests/align-category-tree.test.ts +++ b/admin-frontend-vue/tests/align-category-tree.test.ts @@ -16,9 +16,9 @@ test('align_category_tree_table_normal_primary_path', () => { test('align_category_tree_table_normal_variant_input', () => { const page = readSource('src/pages/asin/ProductCategoryPage.vue') assert.match(page, /expandedIds/, '客户端展开状态集合') - assert.match(page, /flatRows|treeRows/, '按展开状态扁平化行集') - for (const col of ['层级路径', '排序', '来源', '说明']) { - assert.match(page, new RegExp(col), `树形表格含「${col}」列`) + assert.match(page, /treeRows/, '按展开状态扁平化行集') + for (const col of ['层级路径', '排序', '来源', '备注']) { + assert.match(page, new RegExp(col), `树形表格含「${col}」列(旧版列名为备注)`) } }) @@ -46,8 +46,8 @@ test('align_category_tree_table_repeated_operation_is_idempotent', () => { }) test('align_category_tree_table_dependency_failure_returns_actionable_message', () => { - // 视觉复刻旧版:20px 圆角方块标记、缩进宽度变量、名称加粗。 + // 视觉复刻旧版:圆角方块标记、层级缩进(24px/级)、名称加粗。 const page = readSource('src/pages/asin/ProductCategoryPage.vue') assert.match(page, /tree-node-mark/, '展开标记样式存在') - assert.match(page, /var\(--indent|--indent/, '缩进变量') + assert.match(page, /row\.depth \* 24/, '缩进 24px/级') }) diff --git a/admin-frontend-vue/tests/align-column-width.test.ts b/admin-frontend-vue/tests/align-column-width.test.ts index b520799c..b78de4a8 100644 --- a/admin-frontend-vue/tests/align-column-width.test.ts +++ b/admin-frontend-vue/tests/align-column-width.test.ts @@ -7,14 +7,10 @@ import { readSource } from './helpers.ts' // 例外:DuplicateCheckPage 不动;dialog/表单的 width 不属于表格列。 const PAGES = [ - 'src/pages/asin/AsinInvalidPage.vue', - 'src/pages/asin/ProductCategoryPage.vue', - 'src/pages/asin/QueryAsinPage.vue', - 'src/pages/asin/SkipPricePage.vue', - 'src/pages/records/RecordsDigitalHumanVersionPage.vue', - 'src/pages/records/RecordsSoftwareVersionPage.vue', 'src/pages/shop/ShopKeysPage.vue', 'src/pages/shop/ShopManagePage.vue', + 'src/pages/records/RecordsDigitalHumanVersionPage.vue', + 'src/pages/records/RecordsSoftwareVersionPage.vue', 'src/pages/tasks/ImageVideoTasksPage.vue', 'src/pages/tasks/ShopDataTasksPage.vue', ] @@ -62,6 +58,16 @@ test('align_column_width_boundary_empty_input', () => { const groups = readSource('src/pages/account/GroupsPage.vue') assert.equal(/el-table-column/.test(groups), false, '分组管理已像素复刻 admin panel-group-manage 原生表格,不再有 el-table-column') assert.match(groups, /class="table-scroll"/, '分组管理用原生 .table-scroll 表格') + for (const [page, cls] of [ + ['src/pages/asin/AsinInvalidPage.vue', 'invalid-asin-table-scroll'], + ['src/pages/asin/QueryAsinPage.vue', 'query-asin-table-scroll'], + ['src/pages/asin/SkipPricePage.vue', 'skip-price-asin-table-scroll'], + ['src/pages/asin/ProductCategoryPage.vue', 'category-table-scroll'], + ]) { + const src = readSource(page) + assert.equal(/el-table-column/.test(src), false, `${page} 已像素复刻原生表格,不再有 el-table-column`) + assert.match(src, new RegExp(cls), `${page} 用原生 .table-scroll 表格`) + } }) test('align_column_width_dependency_failure_returns_actionable_message', () => { diff --git a/admin-frontend-vue/tests/align-pagination.test.ts b/admin-frontend-vue/tests/align-pagination.test.ts index 6aa19ef5..199bbc4e 100644 --- a/admin-frontend-vue/tests/align-pagination.test.ts +++ b/admin-frontend-vue/tests/align-pagination.test.ts @@ -7,10 +7,9 @@ import { readSource } from './helpers.ts' // 菜单管理是树结构(非列表),商品类目树用懒加载+加载更多节点,均豁免。 test('align_pagination_normal_primary_path', () => { - // 分组管理已像素复刻旧版(panel-group-manage 无分页),豁免;其余列表保留分页。 + // 商品类目已像素复刻旧版(搜索态旧式分页);保留 EP 分页的剩余列表。 for (const page of [ 'src/pages/records/RecordsSoftwareVersionPage.vue', - 'src/pages/asin/ProductCategoryPage.vue', ]) { const src = readSource(page) assert.match(src, /el-pagination/, `${page} 应包含分页组件`) @@ -21,7 +20,6 @@ test('align_pagination_normal_variant_input', () => { // 分页组件需含上一页/下一页(prev/next),中文文案由全局 zh-cn locale 渲染。 for (const page of [ 'src/pages/records/RecordsSoftwareVersionPage.vue', - 'src/pages/asin/ProductCategoryPage.vue', ]) { const src = readSource(page) assert.match(src, /el-pagination[^>]*layout="[^"]*prev[^"]*next/, `${page} 分页 layout 需含 prev/next`) @@ -45,9 +43,8 @@ test('align_pagination_boundary_single_item', () => { }) test('align_pagination_repeated_operation_is_idempotent', () => { - // 已有分页的页面保持原样,不被破坏;已像素复刻(自绘旧式分页)的页单独断言。 + // 已有分页的页面保持原样,不被破坏;已像素复刻(自绘旧式分页/原生表格)的页单独断言。 for (const page of [ - 'src/pages/asin/AsinInvalidPage.vue', 'src/pages/shop/ShopManagePage.vue', ]) { const src = readSource(page) @@ -59,4 +56,7 @@ test('align_pagination_repeated_operation_is_idempotent', () => { const groups = readSource('src/pages/account/GroupsPage.vue') assert.doesNotMatch(groups, /el-pagination/, '分组管理已像素复刻旧版(panel-group-manage 无分页)') assert.doesNotMatch(groups, /pagedRows/, '分组管理全量渲染无客户端切片') + const invalid = readSource('src/pages/asin/AsinInvalidPage.vue') + assert.match(invalid, /class="pagination"/, '品牌库已像素复刻,用旧式分页容器') + assert.doesNotMatch(invalid, /el-pagination/, '品牌库不再用 EP 分页') }) diff --git a/admin-frontend-vue/tests/align-query-asin.test.ts b/admin-frontend-vue/tests/align-query-asin.test.ts index 5cf98a49..7e41206a 100644 --- a/admin-frontend-vue/tests/align-query-asin.test.ts +++ b/admin-frontend-vue/tests/align-query-asin.test.ts @@ -80,7 +80,7 @@ test('align_query_asin_create_api_wiring', () => { test('align_query_asin_page_layout_wiring', () => { const page = readSource('src/pages/asin/QueryAsinPage.vue') assert.match(page, /新增 ASIN/, '顶栏补新增 ASIN 按钮') - assert.match(page, /span-method/, '表格用 span-method 做 rowspan 合并') + assert.match(page, /:rowspan="row\.rowspan"/, '表格用原生 rowspan 合并(像素复刻旧版)') assert.match(page, /CopyText/, 'ASIN 单元格点击复制') assert.doesNotMatch(page, /更新时间/, '去掉更新时间列(参考无此列)') assert.match(page, /queryAsinDisplayRows/, '行展开走纯模型') diff --git a/admin-frontend-vue/tests/align-skip-price.test.ts b/admin-frontend-vue/tests/align-skip-price.test.ts index 85b4b5f0..b5d335d5 100644 --- a/admin-frontend-vue/tests/align-skip-price.test.ts +++ b/admin-frontend-vue/tests/align-skip-price.test.ts @@ -80,7 +80,7 @@ test('align_skip_price_create_api_wiring', () => { test('align_skip_price_page_layout_wiring', () => { const page = readSource('src/pages/asin/SkipPricePage.vue') assert.match(page, /新增 ASIN/, '顶栏补新增 ASIN 按钮') - assert.match(page, /span-method/, '表格用 span-method 做 rowspan 合并') + assert.match(page, /:rowspan="row\.rowspan"/, '表格用原生 rowspan 合并(像素复刻旧版)') assert.match(page, /CopyText/, 'ASIN 单元格点击复制') assert.doesNotMatch(page, /更新时间/, '去掉更新时间列(参考无此列)') assert.match(page, /skipPriceDisplayRows/, '行展开走纯模型') @@ -93,7 +93,7 @@ test('align_skip_price_filter_price_range_same_row', () => { // 验收反馈:筛选条件「最低价」的 ≥/≤ 输入并排同一排(price-range-row),不换行拆开。 const page = readSource('src/pages/asin/SkipPricePage.vue') assert.match(page, /price-range-row/, '最低价范围筛选并排容器存在') - assert.match(page, /placeholder="≥ 最低值"/, '下限占位文案') - assert.match(page, /placeholder="≤ 最高值"/, '上限占位文案') + assert.match(page, /placeholder="最低价 ≥"/, '下限占位文案(旧版)') + assert.match(page, /placeholder="最高价 ≤"/, '上限占位文案(旧版)') assert.ok(!page.includes('label="最低价 ≥"'), '不再有独立的最低价 ≥ 筛选项') }) diff --git a/admin-frontend-vue/tests/task-255.test.ts b/admin-frontend-vue/tests/task-255.test.ts index 8cbd594d..10f4bcca 100644 --- a/admin-frontend-vue/tests/task-255.test.ts +++ b/admin-frontend-vue/tests/task-255.test.ts @@ -9,7 +9,7 @@ test('test_task_255_invalidasin_normal_primary_path', () => { const page = readSource('src/pages/asin/AsinInvalidPage.vue') assert.match(page, /分组/, '筛选需含分组下拉') assert.match(page, /recordSourceLabel/, '来源列需用 recordSourceLabel 文案') - assert.match(page, /el-tag/, '来源宜用 tag 形态') + assert.match(page, /source-text/, '来源用旧版源样式(自绘)') }) test('test_task_255_invalidasin_normal_variant_input', () => { diff --git a/admin-frontend-vue/tests/task-258.test.ts b/admin-frontend-vue/tests/task-258.test.ts index 134592ba..a127592f 100644 --- a/admin-frontend-vue/tests/task-258.test.ts +++ b/admin-frontend-vue/tests/task-258.test.ts @@ -17,9 +17,9 @@ test('test_task_258_skipprice_normal_variant_input', () => { const page = readSource('src/pages/asin/SkipPricePage.vue') assert.match(page, /填写最低价时必须填写 ASIN/, '低价必带 ASIN 校验文案') assert.match(page, /最低价格式不正确/, '低价格式校验文案') - assert.match(page, /导入添加/, '页头导入添加') - assert.match(page, /导入删除/, '页头删除导入') - assert.match(page, /导出/, '页头导出') + assert.match(page, /导入文件新增/, '页头导入文件新增(旧版文案)') + assert.match(page, /导入文件删除/, '页头导入文件删除(旧版文案)') + assert.match(page, /导出 XLSX/, '页头导出 XLSX(旧版文案)') }) test('test_task_258_skipprice_normal_repeated_operation_is_idempotent', () => {