From a756c8c3c8cfdbe53b280e3f291f9d8a38be4806 Mon Sep 17 00:00:00 2001 From: yeuimu <2197651308@qq.com> Date: Sun, 30 Aug 2026 03:07:16 +0800 Subject: [PATCH] =?UTF-8?q?fix(admin,api):=20always=20show=20family=20comp?= =?UTF-8?q?osition=20badge=20N=E6=9D=A1=E9=93=BE=E6=8E=A5=C2=B7M=E4=B8=AA?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按用户反馈(PLTM006 明明能查到配置数却不显示):左树徽标此前只在 同分类内 ≥2 个配置时才出现,单配置族一行上什么都不显示,而右树同族 链接都挂着绿色已配置徽标,两边数字对不上,反复造成「配置数没显示」 的误解。 - goods API:GOOD_INCLUDE 的 family 带 _count.originGoods, GoodDto.originGood.family 增加 memberCount(族成员链接数) - admin:distinctByFamily 记录 linkCount;一族一行的徽标常显 「N 条链接 · M 个配置」(链接数=族成员数,配置数=该族官网商品数), 悬浮仍列出全部配置可打开 - 文档同步徽标语义 效果:PLTM006 行显示「4 条链接 · 1 个配置」(真值:4 条链接仅配了 1 个官网商品);PLTF026 的「4 条链接 · 4 个配置」同屏可对比,其中 4 个配置为历史同国家重复配置,建议另行清理。 验证:admin vue-tsc 干净 + vitest 22/22;api goods spec 18/18; Prisma _count 查询实测返回 originGoods=4 --- apps/admin/src/types/index.ts | 2 +- apps/admin/src/views/goods/GoodsView.vue | 9 ++++++--- apps/api/src/goods/dto/good.dto.ts | 5 +++-- apps/api/src/goods/goods.service.ts | 10 +++++++++- docs/references/product-center.md | 5 +++-- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/admin/src/types/index.ts b/apps/admin/src/types/index.ts index 3e2fffa..073bb0f 100644 --- a/apps/admin/src/types/index.ts +++ b/apps/admin/src/types/index.ts @@ -360,7 +360,7 @@ export interface OriginGood { sizeRowCount?: number packageRowCount?: number productCode?: string | null - family?: { familyId: string; familyCode: string | null; familyName: string } | null + family?: { familyId: string; familyCode: string | null; familyName: string; memberCount?: number } | null } // Origin Goods Tree types diff --git a/apps/admin/src/views/goods/GoodsView.vue b/apps/admin/src/views/goods/GoodsView.vue index 89bd67a..37bbff0 100644 --- a/apps/admin/src/views/goods/GoodsView.vue +++ b/apps/admin/src/views/goods/GoodsView.vue @@ -216,7 +216,8 @@ const filteredGoods = computed(() => { }) /** 左树一族只显示一行:同款(同 familyId)多条配置保留代表行(排序最前), - * 其余配置计数挂在 configCount,悬浮徽标可查看并打开 */ + * 构成常显在徽标上:「N 条链接 · M 个配置」(链接数=族成员数,配置数=该族官网商品数), + * 悬浮徽标可查看全部配置并打开 */ function distinctByFamily(nodes: any[]): any[] { const seen = new Set() return nodes.map(n => { @@ -230,6 +231,8 @@ function distinctByFamily(nodes: any[]): any[] { String(m.raw?.originGood?.family?.familyId ?? '') === fid) n.configCount = siblings.length n.familyConfigs = siblings + n.linkCount = siblings.find(m => m.raw?.originGood?.family?.memberCount != null) + ?.raw.originGood.family.memberCount ?? null return n }).filter(Boolean) } @@ -1170,14 +1173,14 @@ onMounted(() => loadAll()) ×{{ data.mergedCount }}
diff --git a/apps/api/src/goods/dto/good.dto.ts b/apps/api/src/goods/dto/good.dto.ts index 90e4682..2533cc3 100644 --- a/apps/api/src/goods/dto/good.dto.ts +++ b/apps/api/src/goods/dto/good.dto.ts @@ -13,7 +13,7 @@ export interface GoodRelations { goodName: string | null; goodImage: string | null; goodPrice: unknown; - family?: { id: bigint; familyCode: string | null; familyName: string } | null; + family?: { id: bigint; familyCode: string | null; familyName: string; _count?: { originGoods: number } } | null; detail?: { productCode: string | null; syncedAt: Date; @@ -128,7 +128,7 @@ export class GoodDto { sizeRowCount: number; packageRowCount: number; productCode: string | null; - family?: { familyId: string; familyCode: string | null; familyName: string } | null; + family?: { familyId: string; familyCode: string | null; familyName: string; memberCount?: number } | null; } | null; static from( @@ -223,6 +223,7 @@ export class GoodDto { familyId: rel.originGood.family.id.toString(), familyCode: rel.originGood.family.familyCode, familyName: rel.originGood.family.familyName, + memberCount: rel.originGood.family._count?.originGoods, } : null, } diff --git a/apps/api/src/goods/goods.service.ts b/apps/api/src/goods/goods.service.ts index da98884..d7a99f7 100644 --- a/apps/api/src/goods/goods.service.ts +++ b/apps/api/src/goods/goods.service.ts @@ -33,7 +33,15 @@ const GOOD_INCLUDE = { detail: true, variants: { orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] }, _count: { select: { variants: true } }, - family: { select: { id: true, familyCode: true, familyName: true, stale: true } }, + family: { + select: { + id: true, + familyCode: true, + familyName: true, + stale: true, + _count: { select: { originGoods: true } }, + }, + }, }, }, goodTags: { include: { tag: true } }, diff --git a/docs/references/product-center.md b/docs/references/product-center.md index 1cc8c6a..b30a67a 100644 --- a/docs/references/product-center.md +++ b/docs/references/product-center.md @@ -173,8 +173,9 @@ curl -X POST /product-families/organize -H "$AUTH" # 后台「整理 主链接 → 同族 → 旧副源顺序定位并高亮);「配置」按钮仅在本链接及其同款族均未配置时出现, 拖拽到左侧分类/国家的路径同样被拦截(提示同款已配置、不允许重复配置); - 族分组头部计数与「仅未配置」筛选同样按族语义(族已配置 → 计入已配置)。 -- **左树一族一行**(国家/品类模式):同款多条配置只显示排序最前的一条,名称旁 - 「N 个配置」徽标悬浮列出其余配置(可打开编辑),分组头部计数仍按全部商品统计。 +- **左树一族一行**(国家/品类模式):同款多条配置只显示排序最前的一条,名称旁常显 + 「N 条链接 · M 个配置」徽标(链接数=族成员数,配置数=该族官网商品数;单链接单配置也显示, + 避免漏看),悬浮列出全部配置(名称/国家·分类/打开),分组头部计数仍按全部商品统计。 - 人工改价/自动成族等族管理 API(`/product-families/*`)保留,供脚本或后续界面使用; - 页面组件化:`GoodsView.vue` 拆出 `components/` 下的 GoodsEditDialog(编辑弹窗)、 GoodsConfigDialog(配置弹窗)、CustomGoodDialog(自定义商品)、TagFilterPopover(标签筛选弹层)。