From a5e877b2ff1629e31aea096214297636eb4288c2 Mon Sep 17 00:00:00 2001 From: zhaoxing <2558691827@qq.com> Date: Sat, 14 Nov 2026 14:23:09 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=AF=84=E5=8D=96=E4=BB=93?= =?UTF-8?q?=E5=BA=93=E4=B8=8E=E6=8F=90=E6=8A=A5=E5=AE=A1=E6=A0=B8=E8=A7=84?= =?UTF-8?q?=E6=A0=BC=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 规格标签只显示值,0 璀璨不再显示「无」 - 仓库列表长规格文本改为纯值一行 - 列表物品名展示层清理历史 0 璀璨残留 - 补充规格展示回归测试 --- .../panels/ConsignmentInventoryPanel.tsx | 125 ++++++++++-------- .../panels/ConsignmentSubmissionsPanel.tsx | 11 +- .../src/utils/consignment-spec-display.ts | 50 +++++++ .../consignment-admin-dynamic-specs.test.ts | 30 ++++- .../tests/consignment-spec-display.test.ts | 114 ++++++++++++++++ 5 files changed, 264 insertions(+), 66 deletions(-) diff --git a/apps/frontend/src/pages/admin/panels/ConsignmentInventoryPanel.tsx b/apps/frontend/src/pages/admin/panels/ConsignmentInventoryPanel.tsx index f3310a33..8c73608a 100644 --- a/apps/frontend/src/pages/admin/panels/ConsignmentInventoryPanel.tsx +++ b/apps/frontend/src/pages/admin/panels/ConsignmentInventoryPanel.tsx @@ -67,8 +67,10 @@ import { buildConsignmentCategoryGroups } from '@/utils/consignment-category-tre import { describeGiftEvidence, resolveGiftEvidenceBadge } from '@/utils/consignment-evidence' import { CONSIGNMENT_SPEC_TAG_COLORS, - resolveConsignmentSpecDisplayItems, + resolveConsignmentDisplayItemName, + resolveConsignmentSpecTagItems, resolveConsignmentSpecText, + resolveConsignmentSpecValueText, } from '@/utils/consignment-spec-display' import { CONSIGNMENT_SYSTEM_OPTIONS, CONSIGNMENT_ZONE_OPTIONS } from '@/utils/consignment-spec' import { formatDateTime } from '@/utils/date-time' @@ -480,64 +482,69 @@ export default function ConsignmentInventoryPanel() { title: '物品/寄卖单号', key: 'item', width: 240, - render: (_, record) => ( -
-
{record.itemName}
- {record.specText ? ( - - {record.specText} - - ) : null} - - - 寄卖单号 - - - {resolveConsignmentNo(record)} - - - {record.giftWorkOrderNo ? ( + render: (_, record) => { + const specLine = resolveConsignmentSpecValueText(record, { omitZeroBrilliance: true }) + return ( +
+
+ {resolveConsignmentDisplayItemName(record)} +
+ {specLine ? ( + + {specLine} + + ) : null} + + + 寄卖单号 + + + {resolveConsignmentNo(record)} + + + {record.giftWorkOrderNo ? ( +
+ + + 关联工单 + + + {record.giftWorkOrderNo} + + {record.giftWorkOrderStatus ? ( + + {formatGiftWorkOrderStatus(record.giftWorkOrderStatus)} + + ) : null} + +
+ ) : null}
- - - 关联工单 - - - {record.giftWorkOrderNo} - - {record.giftWorkOrderStatus ? ( - - {formatGiftWorkOrderStatus(record.giftWorkOrderStatus)} - - ) : null} - + {record.sourceType === 'worker_portal' ? ( + + 打手端提报 + + ) : record.sourceType === 'recycle' ? ( + + 回收 + + ) : ( + + 人工入库 + + )}
- ) : null} -
- {record.sourceType === 'worker_portal' ? ( - - 打手端提报 - - ) : record.sourceType === 'recycle' ? ( - - 回收 - - ) : ( - - 人工入库 - - )}
-
- ), + ) + }, }, { title: '寄卖分类', @@ -562,7 +569,7 @@ export default function ConsignmentInventoryPanel() { width: 220, render: (_, record) => { // 规格按该分类已发布的寄卖模板渲染;没有规格配置的历史库存才回退旧字段。 - const specItems = resolveConsignmentSpecDisplayItems(record.specs, record.specValues) + const specItems = resolveConsignmentSpecTagItems(record.specs, record.specValues) if (specItems.length > 0) { return ( @@ -570,8 +577,10 @@ export default function ConsignmentInventoryPanel() { - {item.label} {item.value} + {item.value} ))} diff --git a/apps/frontend/src/pages/admin/panels/ConsignmentSubmissionsPanel.tsx b/apps/frontend/src/pages/admin/panels/ConsignmentSubmissionsPanel.tsx index 92ffaa1d..8c34067a 100644 --- a/apps/frontend/src/pages/admin/panels/ConsignmentSubmissionsPanel.tsx +++ b/apps/frontend/src/pages/admin/panels/ConsignmentSubmissionsPanel.tsx @@ -46,7 +46,8 @@ import { formatConsignmentContactSupporter } from '@/utils/consignment-contact' import { describeGiftEvidence, resolveGiftEvidenceBadge } from '@/utils/consignment-evidence' import { CONSIGNMENT_SPEC_TAG_COLORS, - resolveConsignmentSpecDisplayItems, + resolveConsignmentDisplayItemName, + resolveConsignmentSpecTagItems, resolveConsignmentSpecText, } from '@/utils/consignment-spec-display' import { CONSIGNMENT_SYSTEM_OPTIONS, CONSIGNMENT_ZONE_OPTIONS } from '@/utils/consignment-spec' @@ -245,18 +246,20 @@ export default function ConsignmentSubmissionsPanel() { width: 230, render: (_, record) => { // 规格按该分类已发布的寄卖模板渲染;没配置规格的历史提报才回退旧字段。 - const specItems = resolveConsignmentSpecDisplayItems(record.specs, record.specValues) + const specItems = resolveConsignmentSpecTagItems(record.specs, record.specValues) return ( -
{record.itemName}
+
{resolveConsignmentDisplayItemName(record)}
{specItems.length > 0 ? ( specItems.map((item, index) => ( - {item.label} {item.value} + {item.value} )) ) : ( diff --git a/apps/frontend/src/utils/consignment-spec-display.ts b/apps/frontend/src/utils/consignment-spec-display.ts index 9c78e280..ef9a1db0 100644 --- a/apps/frontend/src/utils/consignment-spec-display.ts +++ b/apps/frontend/src/utils/consignment-spec-display.ts @@ -72,6 +72,56 @@ export function formatLegacyConsignmentSpec(record: ConsignmentLegacySpecRecord) return parts.join(' / ') } +/** + * 列表用规格标签:只保留值,0 璀璨属于默认值,不占标签位。 + * 详情/审核弹窗需要字段名时仍用 resolveConsignmentSpecDisplayItems。 + */ +export function resolveConsignmentSpecTagItems( + specs: ConsignmentSpecField[] | null | undefined, + values: ConsignmentSpecValues | null | undefined, +): ConsignmentSpecDisplayItem[] { + return resolveConsignmentSpecDisplayItems(specs, values).filter((item) => { + if (item.key !== 'brillianceCount') return true + if (item.value === '无') return false + const raw = String(values?.[item.key] ?? '').trim() + const numeric = Number(raw) + return !(raw && Number.isFinite(numeric) && numeric <= 0) + }) +} + +/** + * 列表用纯值规格文本:不带字段名,方便和物品名并排扫读;0 璀璨省略。 + * 没有动态规格的历史数据回退旧 5 项(本身也是纯值)。 + */ +export function resolveConsignmentSpecValueText( + input: { + specs?: ConsignmentSpecField[] | null + specValues?: ConsignmentSpecValues | null + } & ConsignmentLegacySpecRecord, + options: { omitZeroBrilliance?: boolean } = {}, +): string { + const items = options.omitZeroBrilliance + ? resolveConsignmentSpecTagItems(input.specs, input.specValues) + : resolveConsignmentSpecDisplayItems(input.specs, input.specValues) + if (items.length > 0) return items.map((item) => item.value).join(' / ') + return formatLegacyConsignmentSpec(input) +} + +/** + * 列表展示用物品名:历史 0 璀璨数据会留下「N钻璀璨」这种断尾单位, + * 只在展示层去掉,不改库;详情仍显示原始 item_name。 + * 只匹配「钻」后面紧跟的「璀璨」,避免误伤系列名里真实包含的「璀璨」。 + */ +export function resolveConsignmentDisplayItemName(input: { + itemName?: string | null + brillianceCount?: number | null +}): string { + const name = String(input.itemName || '').trim() + if (!name) return '' + if (Number(input.brillianceCount || 0) > 0) return name + return name.replace(/(\d+\s*钻)\s*(?:0\s*)?璀璨/g, '$1').trim() +} + /** * 详情页规格文本:后端 specText 优先(已含选项名称),其次按模板现拼,最后回退旧字段。 */ diff --git a/apps/frontend/tests/consignment-admin-dynamic-specs.test.ts b/apps/frontend/tests/consignment-admin-dynamic-specs.test.ts index ecbc5220..c7e8e9b0 100644 --- a/apps/frontend/tests/consignment-admin-dynamic-specs.test.ts +++ b/apps/frontend/tests/consignment-admin-dynamic-specs.test.ts @@ -14,8 +14,17 @@ test('提报审核:规格按寄卖模板渲染,有模板时不再写死旧 const page = readPanel('ConsignmentSubmissionsPanel.tsx') assert.ok( - page.includes('resolveConsignmentSpecDisplayItems(record.specs, record.specValues)'), - '提报列表必须按模板字段渲染规格', + page.includes('resolveConsignmentSpecTagItems(record.specs, record.specValues)'), + '提报列表必须按模板字段渲染规格标签', + ) + assert.ok( + page.includes('resolveConsignmentDisplayItemName(record)'), + '提报列表物品名必须清掉历史 0 璀璨残留', + ) + assert.ok(!page.includes('{item.label} {item.value}'), '提报列表规格标签只显示值') + assert.ok( + page.includes('title={`${item.label}:${item.value}`}'), + '提报列表规格标签悬浮显示字段名', ) assert.ok( page.includes('resolveConsignmentSpecText(viewingSubmission)'), @@ -40,8 +49,21 @@ test('寄售仓库:列表/详情按模板渲染,编辑不再写死旧字段' assert.ok(!page.includes("title: '和平精英规格'"), '仓库列表列名不得写死和平精英') assert.ok(page.includes("title: '寄卖规格'"), '仓库列表列名应为通用「寄卖规格」') assert.ok( - page.includes('resolveConsignmentSpecDisplayItems(record.specs, record.specValues)'), - '仓库列表必须按模板字段渲染规格', + page.includes('resolveConsignmentSpecTagItems(record.specs, record.specValues)'), + '仓库列表必须按模板字段渲染规格标签', + ) + assert.ok( + page.includes('resolveConsignmentSpecValueText(record, { omitZeroBrilliance: true })'), + '仓库列表规格行必须只显示值并省略 0 璀璨', + ) + assert.ok( + page.includes('resolveConsignmentDisplayItemName(record)'), + '仓库列表物品名必须清掉历史 0 璀璨残留', + ) + assert.ok(!page.includes('{record.specText}'), '仓库列表不得再显示重复的长规格文本') + assert.ok( + page.includes('title={`${item.label}:${item.value}`}'), + '仓库列表规格标签悬浮显示字段名', ) assert.ok( page.includes('resolveConsignmentSpecText(viewingItem)'), diff --git a/apps/frontend/tests/consignment-spec-display.test.ts b/apps/frontend/tests/consignment-spec-display.test.ts index 49484e62..8b76ff3a 100644 --- a/apps/frontend/tests/consignment-spec-display.test.ts +++ b/apps/frontend/tests/consignment-spec-display.test.ts @@ -4,8 +4,11 @@ import test from 'node:test' import type { ConsignmentSpecField } from '../src/types/consignment.ts' import { formatLegacyConsignmentSpec, + resolveConsignmentDisplayItemName, resolveConsignmentSpecDisplayItems, + resolveConsignmentSpecTagItems, resolveConsignmentSpecText, + resolveConsignmentSpecValueText, } from '../src/utils/consignment-spec-display.ts' function makeSpec( @@ -110,3 +113,114 @@ test('规格文本:没有模板规格时回退旧列,0 璀璨不展示', () assert.equal(formatLegacyConsignmentSpec({ diamondLevel: 1, brillianceCount: 0 }), '1钻') assert.equal(resolveConsignmentSpecText({}), '-') }) + +const diamondSpec = makeSpec({ + key: 'diamondLevel', + label: '钻石等级', + type: 'select', + sortOrder: 2, + options: [ + { label: '3钻', value: '3', enabled: true }, + { label: '4钻', value: '4', enabled: true }, + ], +}) + +const brillianceSpec = makeSpec({ + key: 'brillianceCount', + label: '璀璨数', + type: 'select', + sortOrder: 3, + options: [ + { label: '无', value: '0', enabled: true }, + { label: '1璀璨', value: '1', enabled: true }, + { label: '2璀璨', value: '2', enabled: true }, + ], +}) + +test('列表规格标签:只保留值,0 璀璨不占标签位', () => { + const zero = resolveConsignmentSpecTagItems([systemSpec, diamondSpec, brillianceSpec], { + system: 'ios', + diamondLevel: '4', + brillianceCount: '0', + }) + assert.deepEqual( + zero.map((item) => item.value), + ['苹果', '4钻'], + ) + + const withBrilliance = resolveConsignmentSpecTagItems([systemSpec, diamondSpec, brillianceSpec], { + system: 'ios', + diamondLevel: '4', + brillianceCount: '2', + }) + assert.deepEqual( + withBrilliance.map((item) => item.value), + ['苹果', '4钻', '2璀璨'], + ) +}) + +test('列表规格纯值文本:不含字段名,0 璀璨省略', () => { + assert.equal( + resolveConsignmentSpecValueText( + { + specs: [systemSpec, diamondSpec, brillianceSpec], + specValues: { system: 'ios', diamondLevel: '4', brillianceCount: '0' }, + }, + { omitZeroBrilliance: true }, + ), + '苹果 / 4钻', + ) + assert.equal( + resolveConsignmentSpecValueText( + { + specs: [systemSpec, diamondSpec, brillianceSpec], + specValues: { system: 'ios', diamondLevel: '4', brillianceCount: '1' }, + }, + { omitZeroBrilliance: true }, + ), + '苹果 / 4钻 / 1璀璨', + ) + // 没有动态规格的历史提报/库存回退旧字段,本身就是纯值 + assert.equal( + resolveConsignmentSpecValueText({ + system: '苹果', + serverZone: '微信区', + series: '暗潮', + diamondLevel: 3, + brillianceCount: 2, + }), + '苹果 / 微信区 / 暗潮 / 3钻 / 2璀璨', + ) +}) + +test('列表物品名:0 璀璨的历史断尾单位只在展示层清掉', () => { + assert.equal( + resolveConsignmentDisplayItemName({ + itemName: '安卓暗潮2钻璀璨 苏尔南', + brillianceCount: 0, + }), + '安卓暗潮2钻 苏尔南', + ) + assert.equal( + resolveConsignmentDisplayItemName({ + itemName: '安卓暗潮2钻0璀璨 苏尔南', + brillianceCount: 0, + }), + '安卓暗潮2钻 苏尔南', + ) + assert.equal( + resolveConsignmentDisplayItemName({ + itemName: '苹果血宴1钻1璀璨 苏尔南', + brillianceCount: 1, + }), + '苹果血宴1钻1璀璨 苏尔南', + ) + // 系列名本身含「璀璨」时不能误删 + assert.equal( + resolveConsignmentDisplayItemName({ + itemName: '璀璨系列2钻 苏尔南', + brillianceCount: 0, + }), + '璀璨系列2钻 苏尔南', + ) +})