From 6bd935dc6baeae5142e320044cde966e999a0bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sun, 6 Sep 2026 02:45:46 +0800 Subject: [PATCH] =?UTF-8?q?task-282(=E9=AA=8C=E6=94=B6=E5=8F=8D=E9=A6=88):?= =?UTF-8?q?=20=E7=B1=BB=E7=9B=AE=E6=94=B9=E5=9B=9E=E6=97=A7=E7=89=88?= =?UTF-8?q?=E6=A0=91=E5=BD=A2=E8=A1=A8=E6=A0=BC(=E7=BC=A9=E8=BF=9B+[+-]?= =?UTF-8?q?=E6=8A=98=E5=8F=A0=E6=A0=87=E8=AE=B0+=E5=90=8D=E7=A7=B0/?= =?UTF-8?q?=E5=B1=82=E7=BA=A7=E8=B7=AF=E5=BE=84/=E6=8E=92=E5=BA=8F/?= =?UTF-8?q?=E6=9D=A5=E6=BA=90/=E8=AF=B4=E6=98=8E/=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=88=97,=E6=87=92=E5=8A=A0=E8=BD=BD=E4=B8=8E=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=9B=B4=E5=A4=9A=E4=BF=9D=E7=95=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/asin/ProductCategoryPage.vue | 187 ++++++++++++------ .../src/pages/asin/product-category-model.ts | 30 --- .../tests/align-category-tree.test.ts | 85 ++++---- 3 files changed, 167 insertions(+), 135 deletions(-) diff --git a/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue b/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue index 50771846..36d8b0f3 100644 --- a/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue +++ b/admin-frontend-vue/src/pages/asin/ProductCategoryPage.vue @@ -8,10 +8,8 @@ import { createProductCategory, deleteProductCategory, updateProductCategory } f import { appendCategoryChildren, isCategoryLoadMoreNode, - isCategoryPlaceholderNode, makeCategoryLoadMoreNode, PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE, - productCategoryNodeMeta, productCategorySourceLabel, type ProductCategoryNode, } from './product-category-model.ts' @@ -45,7 +43,7 @@ function stateKeyOf(parentId: number | null): string { function flatten(nodes: ProductCategoryNode[]): ProductCategoryNode[] { const out: ProductCategoryNode[] = [] for (const node of nodes || []) { - if (!isCategoryLoadMoreNode(node) && !isCategoryPlaceholderNode(node)) { + if (!isCategoryLoadMoreNode(node)) { out.push(node) out.push(...flatten(node.children)) } @@ -74,12 +72,49 @@ async function loadTree(): Promise { } } -/** 节点展开时按需加载子级第一页(childCount>0 且未加载过真实子级才拉取;占位子级不算已加载)。 */ -async function onNodeExpand(node: ProductCategoryNode): Promise { - if (isCategoryLoadMoreNode(node) || isCategoryPlaceholderNode(node) || node.childCount <= 0) return - const hasRealChildren = node.children.some((child) => !isCategoryPlaceholderNode(child) && !isCategoryLoadMoreNode(child)) - if (hasRealChildren) return - await loadChildrenPage(node, 1, false) +/** 树形表格(对齐旧版 panel-product-categories):客户端展开状态 + 按展开扁平化行集。 */ +interface TreeRow { + node: ProductCategoryNode + depth: number +} + +const expandedIds = ref>(new Set()) + +function collectRows(nodes: ProductCategoryNode[], depth: number, out: TreeRow[]): void { + for (const node of nodes || []) { + out.push({ node, depth }) + if (!isCategoryLoadMoreNode(node) && expandedIds.value.has(node.id)) { + collectRows(node.children, depth + 1, out) + } + } +} + +const treeRows = computed(() => { + const out: TreeRow[] = [] + collectRows(tree.value, 0, out) + return out +}) + +/** 展开/折叠:未加载过子级的先拉取第一页(懒加载),加载更多行点击翻页。 */ +async function toggleExpand(node: ProductCategoryNode): Promise { + if (isCategoryLoadMoreNode(node)) { + void loadMoreChildren(node) + return + } + if (node.childCount <= 0) return + if (expandedIds.value.has(node.id)) { + const next = new Set(expandedIds.value) + next.delete(node.id) + expandedIds.value = next + return + } + const hasRealChildren = node.children.some((child) => !isCategoryLoadMoreNode(child)) + if (!hasRealChildren) { + await loadChildrenPage(node, 1, false) + } + const next = new Set(expandedIds.value) + next.add(node.id) + expandedIds.value = next } /** 加载某父级一页子级(append=false 重置;append=true 追加并重建「加载更多」节点)。 */ @@ -279,45 +314,71 @@ onMounted(loadTree) - -