diff --git a/admin-frontend-vue/src/pages/asin/product-category-model.ts b/admin-frontend-vue/src/pages/asin/product-category-model.ts index 5749607a..dba5635d 100644 --- a/admin-frontend-vue/src/pages/asin/product-category-model.ts +++ b/admin-frontend-vue/src/pages/asin/product-category-model.ts @@ -165,7 +165,9 @@ export function makeCategoryLoadMoreNode( loadedCount: number, totalCount: number, ): CategoryLoadMoreNode { - const syntheticId = parentId ? -parentId : -1 + // 合成 id 独立负数域(-2_000_000 以下):父级 id=1 时 -parentId=-1 会与根级(-1)撞 key, + // el-tree node-key 重复会致节点状态错位(子级挂错父级)。placeholder 域(-1_000_000)与此错开。 + const syntheticId = -(parentId ?? 0) - 2_000_000 return { id: syntheticId, parentId, diff --git a/admin-frontend-vue/tests/align-category-tree.test.ts b/admin-frontend-vue/tests/align-category-tree.test.ts index 194b0d31..e971acb9 100644 --- a/admin-frontend-vue/tests/align-category-tree.test.ts +++ b/admin-frontend-vue/tests/align-category-tree.test.ts @@ -1,7 +1,7 @@ import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' -import { isCategoryPlaceholderNode, toProductCategoryNode, makeCategoryPlaceholderNode } from '../src/pages/asin/product-category-model.ts' +import { isCategoryPlaceholderNode, toProductCategoryNode, makeCategoryPlaceholderNode, makeCategoryLoadMoreNode } from '../src/pages/asin/product-category-model.ts' // 验收反馈:类目树太紧凑、没有折叠展开、父子级效果对齐旧版(旧版为缩进树形表格带 +/- 展开)。 // 不变式:childCount>0 的未加载节点带占位子级(children 非空),el-tree 因此显示展开箭头; @@ -42,3 +42,19 @@ test('align_category_tree_dependency_failure_returns_actionable_message', () => assert.match(page, /\.node-row\s*\{[^}]*padding/, '节点行有 padding') assert.match(page, /el-tree-node__expand-icon|expand-icon/, '展开图标样式定制') }) + +test('align_category_tree_synthetic_ids_are_unique', () => { + // 修复回归:合成节点 id 唯一——根级 loadMore 与父级1的 loadMore 不得同 key(曾因 -1 撞 key 致子级挂错父级)。 + const ids = new Set() + const samples = [ + makeCategoryLoadMoreNode(null, 1, 2).id, + makeCategoryLoadMoreNode(1, 1, 2).id, + makeCategoryLoadMoreNode(2, 1, 2).id, + makeCategoryPlaceholderNode(null).id, + makeCategoryPlaceholderNode(1).id, + makeCategoryPlaceholderNode(2).id, + ] + for (const id of samples) ids.add(id) + assert.equal(ids.size, samples.length, '合成节点 id 两两唯一') + assert.ok(makeCategoryLoadMoreNode(null, 1, 2).id !== -1, '根级 loadMore 不得使用 -1(与父级1的旧式 -parentId 撞 key)') +})