Compare commits
1 Commits
main
...
codex/jing
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11ccc8922e |
@ -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 (
|
||||
<span
|
||||
className={`line-clamp-2 break-words leading-4 ${className}`}
|
||||
title={text === '-' ? undefined : text}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
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<string, TimingDetailField>
|
||||
fieldIds: readonly string[]
|
||||
}) {
|
||||
return fieldIds.map((fieldId) => {
|
||||
const field = fieldsById.get(fieldId)
|
||||
if (!field) return null
|
||||
|
||||
return (
|
||||
<td key={`${itemKey}-${field.id}`} className="px-3 py-3 align-top text-[11px]">
|
||||
{field.timeoutState === null || field.timeoutState === undefined ? (
|
||||
<DetailCellText value={field.value} />
|
||||
) : (
|
||||
<StatusBadge
|
||||
label={field.timeoutState ? '是' : '否'}
|
||||
tone={field.timeoutState ? 'danger' : 'success'}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function TimingDetailHeaderCells({ fieldIds }: { fieldIds: readonly string[] }) {
|
||||
return fieldIds.map((fieldId) => {
|
||||
const field = TIMING_DETAIL_DEFINITION_BY_ID.get(fieldId)
|
||||
if (!field) return null
|
||||
|
||||
return (
|
||||
<th
|
||||
key={`timing-header-${field.id}`}
|
||||
className="w-[120px] px-3 py-3 font-apple-semibold leading-4"
|
||||
>
|
||||
{field.label}
|
||||
</th>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
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 (
|
||||
<tr className="border-b border-apple-separator odd:bg-white even:bg-[#fbfcfd]">
|
||||
<tr className="h-24 border-b border-apple-separator odd:bg-white even:bg-[#fbfcfd]">
|
||||
<td
|
||||
className={`${stickyCellClass(STICKY_LEFT.image, 'z-10')} px-3 py-3 align-top`}
|
||||
style={{
|
||||
@ -2356,18 +2489,18 @@ function DetailTableRow({
|
||||
})
|
||||
}
|
||||
title="打开图片预览"
|
||||
className="group relative block h-14 w-14 overflow-hidden rounded-apple-md border border-apple-separator bg-white outline-none transition hover:border-[color:var(--color-blue)] focus-visible:ring-2 focus-visible:ring-[color:var(--color-blue)]"
|
||||
className="group relative block h-[72px] w-[72px] overflow-hidden rounded-apple-md border border-apple-separator bg-white outline-none transition hover:border-[color:var(--color-blue)] focus-visible:ring-2 focus-visible:ring-[color:var(--color-blue)]"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={primaryStyleImage.url}
|
||||
alt={primaryStyleImage.alt || openingCode}
|
||||
className="h-full w-full object-cover transition duration-200 group-hover:scale-[1.04]"
|
||||
className="h-full w-full object-contain transition duration-200 group-hover:scale-[1.04]"
|
||||
/>
|
||||
<span className="pointer-events-none absolute inset-0 bg-black/0 transition group-hover:bg-black/8" />
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex h-14 w-14 items-center justify-center rounded-apple-md border border-apple-separator bg-[#f4f6f7] text-[10px] text-apple-label-tertiary">
|
||||
<div className="flex h-[72px] w-[72px] items-center justify-center rounded-apple-md border border-apple-separator bg-[#f4f6f7] text-[10px] text-apple-label-tertiary">
|
||||
无图片
|
||||
</div>
|
||||
)}
|
||||
@ -2381,12 +2514,22 @@ function DetailTableRow({
|
||||
maxWidth: COLUMN_WIDTHS.openingCode,
|
||||
}}
|
||||
>
|
||||
{formatCellText(item.formSummary.openingCode || item.openingCode)}
|
||||
<DetailCellText value={item.formSummary.openingCode || item.openingCode} />
|
||||
</td>
|
||||
<td
|
||||
className={`${stickyCellClass(STICKY_LEFT.status, 'z-10 shadow-[8px_0_10px_-10px_rgba(0,0,0,0.18)]')} px-3 py-3 align-top text-[11px]`}
|
||||
className={`${stickyCellClass(STICKY_LEFT.workflowLink, 'z-10 shadow-[8px_0_10px_-10px_rgba(0,0,0,0.18)]')} px-3 py-3 align-top text-[11px]`}
|
||||
style={{
|
||||
left: STICKY_LEFT.workflowLink,
|
||||
width: COLUMN_WIDTHS.workflowLink,
|
||||
minWidth: COLUMN_WIDTHS.workflowLink,
|
||||
maxWidth: COLUMN_WIDTHS.workflowLink,
|
||||
}}
|
||||
>
|
||||
<DetailCellText value={item.formSummary.workflowLink} className="break-all" />
|
||||
</td>
|
||||
<td
|
||||
className="px-3 py-3 align-top text-[11px]"
|
||||
style={{
|
||||
left: STICKY_LEFT.status,
|
||||
width: COLUMN_WIDTHS.status,
|
||||
minWidth: COLUMN_WIDTHS.status,
|
||||
maxWidth: COLUMN_WIDTHS.status,
|
||||
@ -2403,16 +2546,6 @@ function DetailTableRow({
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
className="px-3 py-3 align-top text-[11px] whitespace-nowrap"
|
||||
style={{
|
||||
width: COLUMN_WIDTHS.createdDate,
|
||||
minWidth: COLUMN_WIDTHS.createdDate,
|
||||
maxWidth: COLUMN_WIDTHS.createdDate,
|
||||
}}
|
||||
>
|
||||
{formatDateText(item.createdDate)}
|
||||
</td>
|
||||
<td
|
||||
className="px-3 py-3 align-top text-[11px]"
|
||||
style={{
|
||||
@ -2421,7 +2554,7 @@ function DetailTableRow({
|
||||
maxWidth: COLUMN_WIDTHS.approvalStage,
|
||||
}}
|
||||
>
|
||||
{formatCellText(item.formSummary.approvalStageDisplay || item.currentApprovalStageName)}
|
||||
<DetailCellText value={item.formSummary.approvalStageDisplay || item.currentApprovalStageName} />
|
||||
</td>
|
||||
<td
|
||||
className="px-3 py-3 align-top text-[11px]"
|
||||
@ -2431,7 +2564,7 @@ function DetailTableRow({
|
||||
maxWidth: COLUMN_WIDTHS.currentNode,
|
||||
}}
|
||||
>
|
||||
{formatCellText(item.formSummary.currentNodeDisplay)}
|
||||
<DetailCellText value={item.formSummary.currentNodeDisplay} />
|
||||
</td>
|
||||
<td
|
||||
className="px-3 py-3 align-top text-[11px]"
|
||||
@ -2441,25 +2574,41 @@ function DetailTableRow({
|
||||
maxWidth: COLUMN_WIDTHS.currentOwner,
|
||||
}}
|
||||
>
|
||||
{resolveCurrentOwnerDisplay(item)}
|
||||
<DetailCellText value={resolveCurrentOwnerDisplay(item)} />
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.demandType)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.developmentPlan)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.productType || item.productType)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.shippingType)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.needVisualDesign)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.plannedSalesBrands)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.category)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.categoryName)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.salesPriceBand)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.platformPriceBand)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
<DetailCellText value={formatDayValue(item.overallTargetHours)} />
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
<DetailCellText value={formatDayValue(item.overallActualHours)} />
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
<StatusBadge label={isOvertime ? '是' : '否'} tone={isOvertime ? 'danger' : 'success'} />
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggleTimingColumns}
|
||||
className="rounded-full border border-black/8 bg-white px-3 py-1.5 text-[11px] font-apple-medium text-apple-label-primary transition hover:bg-[#f4f6f7]"
|
||||
>
|
||||
{showTimingColumns ? '收起时效详情' : '查看时效详情'}
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.demandType} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.developmentPlan} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.productType || item.productType} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.plannedSalesBrands} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.category} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.categoryName} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.salesPriceBand} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.platformPriceBand} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
{competitorLink.summary === '-' ? (
|
||||
'-'
|
||||
) : competitorLink.href ? (
|
||||
<div className="flex max-w-[260px] items-center gap-2">
|
||||
<div className="flex max-w-[240px] items-start gap-2">
|
||||
<span
|
||||
className="min-w-0 flex-1 truncate text-apple-label-primary"
|
||||
className="line-clamp-2 min-w-0 flex-1 break-all leading-4 text-apple-label-primary"
|
||||
title={competitorLink.title}
|
||||
>
|
||||
{competitorLink.summary}
|
||||
@ -2475,39 +2624,50 @@ function DetailTableRow({
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<span className="block max-w-[260px] truncate text-apple-label-primary" title={competitorLink.title}>
|
||||
<span className="line-clamp-2 block max-w-[240px] break-all leading-4 text-apple-label-primary" title={competitorLink.title}>
|
||||
{competitorLink.summary}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px] break-all">{formatCellText(item.formSummary.openingReason)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.isTestStyle)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.testStyleCode)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.productPlanner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.operationBrandOwner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.spuOwner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.appearanceDesigner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.structureDesigner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.purchaseOwner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.merchandiserOwner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.pricingOwner)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px] break-all">{formatCellText(item.formSummary.workflowLink)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.needPatent)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.needTrademark)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.reviewResult)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.sampleReviewResult)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.salesBrands)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.spu || item.spu)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(item.formSummary.openingBrand)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatCellText(readCreatorDisplay(item))}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatDayValue(item.overallTargetHours)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">{formatDayValue(item.overallActualHours)}</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
<StatusBadge
|
||||
label={isOvertime ? '是' : '否'}
|
||||
tone={isOvertime ? 'danger' : 'success'}
|
||||
/>
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.isTestStyle} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.testStyleCode} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.needPatent} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.needTrademark} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.reviewResult} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.salesBrands} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.spu || item.spu} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.openingBrand} /></td>
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px] whitespace-nowrap"><DetailCellText value={formatDateText(item.createdDate)} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.openingReason} className="break-all" /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={readCreatorDisplay(item)} /></td>
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.productPlanner} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.operationBrandOwner} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.spuOwner} /></td>
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.appearanceDesigner} /></td>
|
||||
{showTimingColumns ? <TimingDetailCells itemKey={item.boardKey} fieldsById={timingFieldsById} fieldIds={TIMING_FIELD_IDS_BY_GROUP.appearance} /> : null}
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.structureDesigner} /></td>
|
||||
{showTimingColumns ? <TimingDetailCells itemKey={item.boardKey} fieldsById={timingFieldsById} fieldIds={TIMING_FIELD_IDS_BY_GROUP.structure} /> : null}
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.purchaseOwner} /></td>
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.merchandiserOwner} /></td>
|
||||
{showTimingColumns ? <TimingDetailCells itemKey={item.boardKey} fieldsById={timingFieldsById} fieldIds={TIMING_FIELD_IDS_BY_GROUP.sampling} /> : null}
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.sampleReviewResult} /></td>
|
||||
{showTimingColumns ? <TimingDetailCells itemKey={item.boardKey} fieldsById={timingFieldsById} fieldIds={TIMING_FIELD_IDS_BY_GROUP.review} /> : null}
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.shippingType} /></td>
|
||||
{showTimingColumns ? <TimingDetailCells itemKey={item.boardKey} fieldsById={timingFieldsById} fieldIds={TIMING_FIELD_IDS_BY_GROUP.production} /> : null}
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.needVisualDesign} /></td>
|
||||
{showTimingColumns ? <TimingDetailCells itemKey={item.boardKey} fieldsById={timingFieldsById} fieldIds={TIMING_FIELD_IDS_BY_GROUP.drawing} /> : null}
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]"><DetailCellText value={item.formSummary.pricingOwner} /></td>
|
||||
{showTimingColumns ? <TimingDetailCells itemKey={item.boardKey} fieldsById={timingFieldsById} fieldIds={TIMING_FIELD_IDS_BY_GROUP.pricing} /> : null}
|
||||
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
{timeoutFollowupSummary ? (
|
||||
<button
|
||||
@ -2522,48 +2682,18 @@ function DetailTableRow({
|
||||
'-'
|
||||
)}
|
||||
</td>
|
||||
<td className="px-3 py-3 align-top text-[11px]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggleTimingColumns}
|
||||
className="rounded-full border border-black/8 bg-white px-3 py-1.5 text-[11px] font-apple-medium text-apple-label-primary transition hover:bg-[#f4f6f7]"
|
||||
>
|
||||
{showTimingColumns ? '收起时效详情' : '查看时效详情'}
|
||||
</button>
|
||||
</td>
|
||||
{showTimingColumns
|
||||
? timingFields.map((field) => (
|
||||
<td
|
||||
key={`${item.boardKey}-${field.id}`}
|
||||
className="px-3 py-3 align-top text-[11px]"
|
||||
>
|
||||
{field.timeoutState === null || field.timeoutState === undefined ? (
|
||||
field.value
|
||||
) : (
|
||||
<StatusBadge
|
||||
label={field.timeoutState ? '是' : '否'}
|
||||
tone={field.timeoutState ? 'danger' : 'success'}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
))
|
||||
: null}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
function DetailColumnGroupRow({ showTimingColumns }: { showTimingColumns: boolean }) {
|
||||
const groups = showTimingColumns
|
||||
? [...BASE_DETAIL_COLUMN_GROUPS, ...TIMING_DETAIL_COLUMN_GROUPS]
|
||||
: BASE_DETAIL_COLUMN_GROUPS
|
||||
|
||||
return (
|
||||
<tr className="h-8 border-b border-apple-separator bg-[#f8fafb]">
|
||||
{groups.map((group, index) => (
|
||||
{DETAIL_COLUMN_GROUPS.map((group, index) => (
|
||||
<th
|
||||
key={`${group.label}-${index}`}
|
||||
scope="colgroup"
|
||||
colSpan={group.columnSpan}
|
||||
colSpan={group.baseColumnSpan + (showTimingColumns ? group.timingColumnSpan ?? 0 : 0)}
|
||||
className={`h-8 border-r border-white/80 px-3 py-0 text-left text-[10px] font-apple-semibold leading-none ${group.toneClass} ${group.stickyClass ?? ''}`}
|
||||
>
|
||||
{group.label}
|
||||
@ -4369,12 +4499,15 @@ export default function OaBoardClient({
|
||||
) : (
|
||||
<>
|
||||
<div className="relative isolate min-h-0 flex-1 overflow-auto">
|
||||
<table className={`table-fixed text-left text-[11px] ${showTimingColumns ? 'min-w-[7180px]' : 'min-w-[4780px]'}`}>
|
||||
<table
|
||||
className="table-fixed text-left text-[11px]"
|
||||
style={{ minWidth: showTimingColumns ? 10400 : 5200 }}
|
||||
>
|
||||
<colgroup>
|
||||
<col style={{ width: COLUMN_WIDTHS.image }} />
|
||||
<col style={{ width: COLUMN_WIDTHS.openingCode }} />
|
||||
<col style={{ width: COLUMN_WIDTHS.workflowLink }} />
|
||||
<col style={{ width: COLUMN_WIDTHS.status }} />
|
||||
<col style={{ width: COLUMN_WIDTHS.createdDate }} />
|
||||
<col style={{ width: COLUMN_WIDTHS.approvalStage }} />
|
||||
<col style={{ width: COLUMN_WIDTHS.currentNode }} />
|
||||
<col style={{ width: COLUMN_WIDTHS.currentOwner }} />
|
||||
@ -4405,9 +4538,19 @@ export default function OaBoardClient({
|
||||
开款编号
|
||||
</th>
|
||||
<th
|
||||
className={`${stickyCellClass(STICKY_LEFT.status, `${embeddedTab ? 'top-8' : 'top-0'} z-50 shadow-[8px_0_10px_-10px_rgba(0,0,0,0.18)]`)} px-3 py-3 font-apple-semibold`}
|
||||
className={`${stickyCellClass(STICKY_LEFT.workflowLink, `${embeddedTab ? 'top-8' : 'top-0'} z-50 shadow-[8px_0_10px_-10px_rgba(0,0,0,0.18)]`)} px-3 py-3 font-apple-semibold`}
|
||||
style={{
|
||||
left: STICKY_LEFT.workflowLink,
|
||||
width: COLUMN_WIDTHS.workflowLink,
|
||||
minWidth: COLUMN_WIDTHS.workflowLink,
|
||||
maxWidth: COLUMN_WIDTHS.workflowLink,
|
||||
}}
|
||||
>
|
||||
流程链接
|
||||
</th>
|
||||
<th
|
||||
className="px-3 py-3 font-apple-semibold"
|
||||
style={{
|
||||
left: STICKY_LEFT.status,
|
||||
width: COLUMN_WIDTHS.status,
|
||||
minWidth: COLUMN_WIDTHS.status,
|
||||
maxWidth: COLUMN_WIDTHS.status,
|
||||
@ -4415,16 +4558,6 @@ export default function OaBoardClient({
|
||||
>
|
||||
总流程状态
|
||||
</th>
|
||||
<th
|
||||
className="px-3 py-3 font-apple-semibold"
|
||||
style={{
|
||||
width: COLUMN_WIDTHS.createdDate,
|
||||
minWidth: COLUMN_WIDTHS.createdDate,
|
||||
maxWidth: COLUMN_WIDTHS.createdDate,
|
||||
}}
|
||||
>
|
||||
创建日期
|
||||
</th>
|
||||
<th
|
||||
className="px-3 py-3 font-apple-semibold"
|
||||
style={{
|
||||
@ -4455,52 +4588,60 @@ export default function OaBoardClient({
|
||||
>
|
||||
当前负责人
|
||||
</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">总目标时效</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">总实际时效</th>
|
||||
<th className="w-[100px] px-3 py-3 font-apple-semibold">是否超时</th>
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">时效详情</th>
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">需求类型</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">开发方案</th>
|
||||
<th className="w-[220px] px-3 py-3 font-apple-semibold">产品类型</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">发货类型</th>
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">是否需要视觉出图</th>
|
||||
<th className="w-[220px] px-3 py-3 font-apple-semibold">计划售卖品牌</th>
|
||||
<th className="w-[200px] px-3 py-3 font-apple-semibold">产品类型</th>
|
||||
<th className="w-[200px] px-3 py-3 font-apple-semibold">计划售卖品牌</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">品类</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">类目</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">售卖价格带</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">平台价格带</th>
|
||||
<th className="w-[280px] px-3 py-3 font-apple-semibold">竞品链接</th>
|
||||
<th className="w-[260px] px-3 py-3 font-apple-semibold">开款原因</th>
|
||||
<th className="w-[240px] px-3 py-3 font-apple-semibold">竞品链接</th>
|
||||
<th className="w-[100px] px-3 py-3 font-apple-semibold">是否测款</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">测款编号</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">商品企划</th>
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">运营品牌负责人</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">SPU负责人</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">外观设计师</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">结构设计师</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">采购负责人</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">跟单负责人</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">限价负责人</th>
|
||||
<th className="w-[280px] px-3 py-3 font-apple-semibold">由壹点产品开发流程链接地址</th>
|
||||
<th className="w-[100px] px-3 py-3 font-apple-semibold">是否申请专利</th>
|
||||
<th className="w-[100px] px-3 py-3 font-apple-semibold">是否注册商标</th>
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">产品开款评审结果</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">审样结果</th>
|
||||
<th className="w-[180px] px-3 py-3 font-apple-semibold">售卖品牌</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">SPU</th>
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">SPU</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">开款品牌</th>
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">创建日期</th>
|
||||
<th className="w-[240px] px-3 py-3 font-apple-semibold">开款原因</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">创建人</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">总目标时效</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">总实际时效</th>
|
||||
<th className="w-[100px] px-3 py-3 font-apple-semibold">是否超时</th>
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">商品企划</th>
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">运营品牌负责人</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">SPU负责人</th>
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">外观设计师</th>
|
||||
{showTimingColumns ? <TimingDetailHeaderCells fieldIds={TIMING_FIELD_IDS_BY_GROUP.appearance} /> : null}
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">结构设计师</th>
|
||||
{showTimingColumns ? <TimingDetailHeaderCells fieldIds={TIMING_FIELD_IDS_BY_GROUP.structure} /> : null}
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">采购负责人</th>
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">跟单负责人</th>
|
||||
{showTimingColumns ? <TimingDetailHeaderCells fieldIds={TIMING_FIELD_IDS_BY_GROUP.sampling} /> : null}
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">审样结果</th>
|
||||
{showTimingColumns ? <TimingDetailHeaderCells fieldIds={TIMING_FIELD_IDS_BY_GROUP.review} /> : null}
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">发货类型</th>
|
||||
{showTimingColumns ? <TimingDetailHeaderCells fieldIds={TIMING_FIELD_IDS_BY_GROUP.production} /> : null}
|
||||
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">是否需要视觉出图</th>
|
||||
{showTimingColumns ? <TimingDetailHeaderCells fieldIds={TIMING_FIELD_IDS_BY_GROUP.drawing} /> : null}
|
||||
|
||||
<th className="w-[120px] px-3 py-3 font-apple-semibold">限价负责人</th>
|
||||
{showTimingColumns ? <TimingDetailHeaderCells fieldIds={TIMING_FIELD_IDS_BY_GROUP.pricing} /> : null}
|
||||
|
||||
<th className="w-[154px] px-3 py-3 font-apple-semibold">超时跟进</th>
|
||||
<th className="w-[140px] px-3 py-3 font-apple-semibold">时效详情</th>
|
||||
{showTimingColumns
|
||||
? TIMING_DETAIL_DEFINITIONS.map((field) => (
|
||||
<th
|
||||
key={`timing-header-${field.id}`}
|
||||
className="w-[160px] px-3 py-3 font-apple-semibold"
|
||||
>
|
||||
{field.label}
|
||||
</th>
|
||||
))
|
||||
: null}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user