From 11ccc8922e2584d7ab76d4f0202a68d2bfaa1abc Mon Sep 17 00:00:00 2001 From: jingchuan Date: Sun, 16 Aug 2026 17:25:33 +0800 Subject: [PATCH] fix(supply-chain): refine unified table layout --- .../src/app/intake/oa-board/OaBoardClient.tsx | 489 +++++++++++------- 1 file changed, 315 insertions(+), 174 deletions(-) diff --git a/furniture-3d-system/src/app/intake/oa-board/OaBoardClient.tsx b/furniture-3d-system/src/app/intake/oa-board/OaBoardClient.tsx index 8c4025c2..89e2a2b9 100644 --- a/furniture-3d-system/src/app/intake/oa-board/OaBoardClient.tsx +++ b/furniture-3d-system/src/app/intake/oa-board/OaBoardClient.tsx @@ -100,7 +100,8 @@ type TimingDetailDefinition = { type DetailColumnGroup = { label: string - columnSpan: number + baseColumnSpan: number + timingColumnSpan?: number toneClass: string stickyClass?: string } @@ -229,9 +230,10 @@ const COMPLETION_ESTIMATE_STATUS_OPTIONS: FilterOption[] = [ { value: 'PENDING_CONFIRMATION', label: '暂无法预估,需每日跟进' }, ] const COLUMN_WIDTHS = { - image: 110, + image: 96, openingCode: 160, - status: 130, + workflowLink: 140, + status: 110, createdDate: 120, approvalStage: 190, currentNode: 180, @@ -240,7 +242,7 @@ const COLUMN_WIDTHS = { const STICKY_LEFT = { image: 0, openingCode: COLUMN_WIDTHS.image, - status: COLUMN_WIDTHS.image + COLUMN_WIDTHS.openingCode, + workflowLink: COLUMN_WIDTHS.image + COLUMN_WIDTHS.openingCode, } as const const TIMING_DETAIL_DEFINITIONS: TimingDetailDefinition[] = [ @@ -357,45 +359,112 @@ const TIMING_DETAIL_DEFINITIONS: TimingDetailDefinition[] = [ { id: 'actual-listing', label: '实际-运营上架', metricKey: 'listing', valueType: 'actual' }, ] -const BASE_DETAIL_COLUMN_GROUPS: DetailColumnGroup[] = [ +const TIMING_DETAIL_DEFINITION_BY_ID = new Map( + TIMING_DETAIL_DEFINITIONS.map((field) => [field.id, field]), +) + +const TIMING_FIELD_IDS_BY_GROUP = { + appearance: ['target-exterior', 'actual-exterior', 'timeout-exterior'], + structure: ['target-structure', 'actual-structure', 'timeout-structure'], + sampling: ['target-sample-prepare', 'actual-sample-prepare', 'timeout-sample-prepare'], + review: ['target-sample-review', 'actual-sample-review', 'timeout-sample-review'], + production: [ + 'target-production', + 'actual-production', + 'timeout-production', + 'target-material', + 'actual-material', + 'timeout-material', + 'market-analysis', + 'target-first-order', + 'actual-first-order', + 'timeout-first-order', + 'target-pre-production', + 'actual-pre-production', + 'timeout-pre-production', + 'target-shipment', + 'actual-shipment', + 'timeout-shipment', + 'target-warehouse-qc', + 'actual-warehouse-qc', + 'target-sample-send', + 'actual-sample-send', + 'target-listing', + 'actual-listing', + ], + drawing: [ + 'target-install-manual', + 'actual-install-manual', + 'target-art', + 'actual-art', + 'timeout-art', + ], + pricing: [ + 'target-pricing', + 'actual-pricing', + 'timeout-pricing', + 'actual-pricing-parallel', + 'target-pricing-flow', + 'actual-pricing-flow', + 'timeout-pricing-flow', + ], +} as const + +const DETAIL_COLUMN_GROUPS: DetailColumnGroup[] = [ { label: '流程标识', - columnSpan: 2, + baseColumnSpan: 3, toneClass: 'bg-zinc-100 text-zinc-700', stickyClass: 'sticky left-0 z-50', }, + { label: '总流程', baseColumnSpan: 4, toneClass: 'bg-sky-50 text-sky-800' }, + { label: '时效指标', baseColumnSpan: 4, toneClass: 'bg-slate-100 text-slate-700' }, + { label: '产品信息', baseColumnSpan: 17, toneClass: 'bg-emerald-50 text-emerald-800' }, + { label: '发起信息', baseColumnSpan: 3, toneClass: 'bg-cyan-50 text-cyan-800' }, + { label: '人员信息', baseColumnSpan: 3, toneClass: 'bg-amber-50 text-amber-800' }, { - label: '总流程', - columnSpan: 1, - toneClass: 'bg-sky-50 text-sky-800', - stickyClass: 'sticky left-[270px] z-50 shadow-[8px_0_10px_-10px_rgba(0,0,0,0.18)]', + label: '外观', + baseColumnSpan: 1, + timingColumnSpan: TIMING_FIELD_IDS_BY_GROUP.appearance.length, + toneClass: 'bg-rose-50 text-rose-800', }, - { label: '发起信息', columnSpan: 1, toneClass: 'bg-cyan-50 text-cyan-800' }, - { label: '总流程', columnSpan: 3, toneClass: 'bg-sky-50 text-sky-800' }, - { label: '产品信息', columnSpan: 14, toneClass: 'bg-emerald-50 text-emerald-800' }, - { label: '人员信息', columnSpan: 8, toneClass: 'bg-amber-50 text-amber-800' }, - { label: '流程标识', columnSpan: 1, toneClass: 'bg-zinc-100 text-zinc-700' }, - { label: '产品信息', columnSpan: 7, toneClass: 'bg-emerald-50 text-emerald-800' }, - { label: '发起信息', columnSpan: 1, toneClass: 'bg-cyan-50 text-cyan-800' }, - { label: '时效指标', columnSpan: 3, toneClass: 'bg-slate-100 text-slate-700' }, - { label: '跟进信息', columnSpan: 1, toneClass: 'bg-rose-50 text-rose-800' }, - { label: '阶段时效', columnSpan: 1, toneClass: 'bg-slate-100 text-slate-700' }, -] - -const TIMING_DETAIL_COLUMN_GROUPS: DetailColumnGroup[] = [ - { label: '外观阶段', columnSpan: 3, toneClass: 'bg-rose-50 text-rose-800' }, - { label: '结构阶段', columnSpan: 3, toneClass: 'bg-indigo-50 text-indigo-800' }, - { label: '打样阶段', columnSpan: 3, toneClass: 'bg-amber-50 text-amber-800' }, - { label: '审样阶段', columnSpan: 3, toneClass: 'bg-fuchsia-50 text-fuchsia-800' }, - { label: '生产与资料', columnSpan: 6, toneClass: 'bg-teal-50 text-teal-800' }, - { label: '产品信息', columnSpan: 1, toneClass: 'bg-emerald-50 text-emerald-800' }, - { label: '生产阶段', columnSpan: 9, toneClass: 'bg-teal-50 text-teal-800' }, - { label: '限价阶段', columnSpan: 4, toneClass: 'bg-yellow-50 text-yellow-800' }, - { label: '质检与图纸', columnSpan: 4, toneClass: 'bg-cyan-50 text-cyan-800' }, - { label: '寄样阶段', columnSpan: 2, toneClass: 'bg-pink-50 text-pink-800' }, - { label: '出图阶段', columnSpan: 3, toneClass: 'bg-blue-50 text-blue-800' }, - { label: '限价阶段', columnSpan: 3, toneClass: 'bg-yellow-50 text-yellow-800' }, - { label: '上架阶段', columnSpan: 2, toneClass: 'bg-lime-50 text-lime-800' }, + { + label: '结构', + baseColumnSpan: 1, + timingColumnSpan: TIMING_FIELD_IDS_BY_GROUP.structure.length, + toneClass: 'bg-indigo-50 text-indigo-800', + }, + { + label: '打样', + baseColumnSpan: 2, + timingColumnSpan: TIMING_FIELD_IDS_BY_GROUP.sampling.length, + toneClass: 'bg-amber-50 text-amber-800', + }, + { + label: '审样', + baseColumnSpan: 1, + timingColumnSpan: TIMING_FIELD_IDS_BY_GROUP.review.length, + toneClass: 'bg-fuchsia-50 text-fuchsia-800', + }, + { + label: '生产', + baseColumnSpan: 1, + timingColumnSpan: TIMING_FIELD_IDS_BY_GROUP.production.length, + toneClass: 'bg-teal-50 text-teal-800', + }, + { + label: '出图', + baseColumnSpan: 1, + timingColumnSpan: TIMING_FIELD_IDS_BY_GROUP.drawing.length, + toneClass: 'bg-blue-50 text-blue-800', + }, + { + label: '限价', + baseColumnSpan: 1, + timingColumnSpan: TIMING_FIELD_IDS_BY_GROUP.pricing.length, + toneClass: 'bg-yellow-50 text-yellow-800', + }, + { label: '跟进信息', baseColumnSpan: 1, toneClass: 'bg-rose-50 text-rose-800' }, ] const STAGE_GRAPH_NODE_WIDTH = 104 @@ -453,6 +522,25 @@ function formatCellText(value: string | null | undefined) { return text || '-' } +function DetailCellText({ + value, + className = '', +}: { + value: string | null | undefined + className?: string +}) { + const text = formatCellText(value) + + return ( + + {text} + + ) +} + function formatDateText(value: string | null | undefined) { const text = String(value ?? '').trim() return text ? text.slice(0, 10) : '-' @@ -776,6 +864,50 @@ function buildTimingDetailFields(item: OaBoardProjectListItemDTO): TimingDetailF }) } +function TimingDetailCells({ + itemKey, + fieldsById, + fieldIds, +}: { + itemKey: string + fieldsById: Map + fieldIds: readonly string[] +}) { + return fieldIds.map((fieldId) => { + const field = fieldsById.get(fieldId) + if (!field) return null + + return ( + + {field.timeoutState === null || field.timeoutState === undefined ? ( + + ) : ( + + )} + + ) + }) +} + +function TimingDetailHeaderCells({ fieldIds }: { fieldIds: readonly string[] }) { + return fieldIds.map((fieldId) => { + const field = TIMING_DETAIL_DEFINITION_BY_ID.get(fieldId) + if (!field) return null + + return ( + + {field.label} + + ) + }) +} + function getOvertimeStages(item: OaBoardProjectListItemDTO): OvertimeStage[] { const stageNames = splitStageText( item.formSummary.approvalStageDisplay || item.currentApprovalStageName, @@ -2322,6 +2454,7 @@ function DetailTableRow({ onPreviewImage: (image: StyleImagePreview) => void }) { const timingFields = buildTimingDetailFields(item) + const timingFieldsById = new Map(timingFields.map((field) => [field.id, field])) const currentOvertimeStages = getOvertimeStages(item) const historicalOvertimeStages = getHistoricalOvertimeStages(item) const followupDisplayStages = getFollowupDisplayStages( @@ -2335,7 +2468,7 @@ function DetailTableRow({ const openingCode = formatCellText(item.formSummary.openingCode || item.openingCode) return ( - + {/* eslint-disable-next-line @next/next/no-img-element */} {primaryStyleImage.alt ) : ( -
+
无图片
)} @@ -2381,12 +2514,22 @@ function DetailTableRow({ maxWidth: COLUMN_WIDTHS.openingCode, }} > - {formatCellText(item.formSummary.openingCode || item.openingCode)} + + + + - - {formatDateText(item.createdDate)} - - {formatCellText(item.formSummary.approvalStageDisplay || item.currentApprovalStageName)} + - {formatCellText(item.formSummary.currentNodeDisplay)} + - {resolveCurrentOwnerDisplay(item)} + - {formatCellText(item.formSummary.demandType)} - {formatCellText(item.formSummary.developmentPlan)} - {formatCellText(item.formSummary.productType || item.productType)} - {formatCellText(item.formSummary.shippingType)} - {formatCellText(item.formSummary.needVisualDesign)} - {formatCellText(item.formSummary.plannedSalesBrands)} - {formatCellText(item.formSummary.category)} - {formatCellText(item.formSummary.categoryName)} - {formatCellText(item.formSummary.salesPriceBand)} - {formatCellText(item.formSummary.platformPriceBand)} + + + + + + + + + + + + + + + + + + + + {competitorLink.summary === '-' ? ( '-' ) : competitorLink.href ? ( -
+
{competitorLink.summary} @@ -2475,39 +2624,50 @@ function DetailTableRow({
) : ( - + {competitorLink.summary} )} - {formatCellText(item.formSummary.openingReason)} - {formatCellText(item.formSummary.isTestStyle)} - {formatCellText(item.formSummary.testStyleCode)} - {formatCellText(item.formSummary.productPlanner)} - {formatCellText(item.formSummary.operationBrandOwner)} - {formatCellText(item.formSummary.spuOwner)} - {formatCellText(item.formSummary.appearanceDesigner)} - {formatCellText(item.formSummary.structureDesigner)} - {formatCellText(item.formSummary.purchaseOwner)} - {formatCellText(item.formSummary.merchandiserOwner)} - {formatCellText(item.formSummary.pricingOwner)} - {formatCellText(item.formSummary.workflowLink)} - {formatCellText(item.formSummary.needPatent)} - {formatCellText(item.formSummary.needTrademark)} - {formatCellText(item.formSummary.reviewResult)} - {formatCellText(item.formSummary.sampleReviewResult)} - {formatCellText(item.formSummary.salesBrands)} - {formatCellText(item.formSummary.spu || item.spu)} - {formatCellText(item.formSummary.openingBrand)} - {formatCellText(readCreatorDisplay(item))} - {formatDayValue(item.overallTargetHours)} - {formatDayValue(item.overallActualHours)} - - - + + + + + + + + + + + + + + + + + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + {timeoutFollowupSummary ? ( - - {showTimingColumns - ? timingFields.map((field) => ( - - {field.timeoutState === null || field.timeoutState === undefined ? ( - field.value - ) : ( - - )} - - )) - : null} ) } function DetailColumnGroupRow({ showTimingColumns }: { showTimingColumns: boolean }) { - const groups = showTimingColumns - ? [...BASE_DETAIL_COLUMN_GROUPS, ...TIMING_DETAIL_COLUMN_GROUPS] - : BASE_DETAIL_COLUMN_GROUPS - return ( - {groups.map((group, index) => ( + {DETAIL_COLUMN_GROUPS.map((group, index) => ( {group.label} @@ -4369,12 +4499,15 @@ export default function OaBoardClient({ ) : ( <>
- +
+ - @@ -4405,9 +4538,19 @@ export default function OaBoardClient({ 开款编号 + - + + + + + - - - - + + - - + - - - - - - - - - - - + + + + - - - + + + + + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + + + {showTimingColumns ? : null} + - - {showTimingColumns - ? TIMING_DETAIL_DEFINITIONS.map((field) => ( - - )) - : null}
+ 流程链接 + 总流程状态 - 创建日期 - 当前负责人 总目标时效总实际时效是否超时时效详情需求类型 开发方案产品类型发货类型是否需要视觉出图计划售卖品牌产品类型计划售卖品牌 品类 类目 售卖价格带 平台价格带竞品链接开款原因竞品链接 是否测款 测款编号商品企划运营品牌负责人SPU负责人外观设计师结构设计师采购负责人跟单负责人限价负责人由壹点产品开发流程链接地址 是否申请专利 是否注册商标 产品开款评审结果审样结果 售卖品牌SPUSPU 开款品牌创建日期开款原因 创建人总目标时效总实际时效是否超时商品企划运营品牌负责人SPU负责人外观设计师结构设计师采购负责人跟单负责人审样结果发货类型是否需要视觉出图限价负责人超时跟进时效详情 - {field.label} -