Files
zkxw/web-admin/src/utils/production-work-order-card-meta.ts
T
2026-09-01 13:43:14 +08:00

307 lines
10 KiB
TypeScript

import { formatDisplayOrDash } from '@/utils/datetime-display'
import type { ProductionWorkOrderItem } from '@/api/production-work-order'
import { receptionSourceLabel, receptionSourceTagType } from '@/utils/qc-labels'
import {
donationStatusDisplayLabel,
donationStatusTagType,
protocolStatusDisplayLabel,
protocolStatusTagType,
workOrderStatusLabel,
workOrderStatusTagType
} from '@/utils/status-tag-registry'
import type { TagType } from '@/utils/status-tag-registry'
import type { ProductionWorkOrderTabKey } from '@/utils/production-work-order-tabs'
import { resolveTabKeyFromWorkOrder } from '@/utils/production-work-order-tabs'
import { formatMoneyDisplay } from '@/utils/money-format'
export type CardMetaFieldKind = 'text' | 'tag'
export type CardMetaField = {
label: string
kind?: CardMetaFieldKind
getValue: (row: ProductionWorkOrderItem) => string
getTagType?: (row: ProductionWorkOrderItem) => TagType | undefined
visible?: (row: ProductionWorkOrderItem) => boolean
wide?: boolean
/** 跨列数(如 2 表示占两列,宽于默认但不独占一行) */
colSpan?: number
}
export type ProductionWorkOrderCardMetaConfig = {
tabKey: ProductionWorkOrderTabKey
showCategoryTag: boolean
showProtocolFeeSummary: boolean
showOutboundFeeSummary: boolean
/** 卡片展示关联申请单/输注计划的细胞产品明细 */
showCellProducts: boolean
metaFields: CardMetaField[]
allowDispatchQc: boolean
allowSampleReception: boolean
allowDispatchComplete: boolean
allowEditRemark: boolean
allowDelete: boolean
}
function formatOutboundScheduleSortNo(row: ProductionWorkOrderItem) {
const n = row.sortNo
if (n == null || n === '' || Number(n) <= 0) return '—'
return `第${Number(n)}次`
}
function formatOutboundScheduleTotalCount(row: ProductionWorkOrderItem) {
const n = row.totalCourseCount
if (n == null || n === '' || Number(n) <= 0) return '—'
return `共${Number(n)}次`
}
const plannedStartTimeRangeField: CardMetaField = {
label: '计划开始时间',
colSpan: 3,
getValue: (row) => formatDisplayOrDash(row.plannedStartTimeRange)
}
function protocolStatusLabelForTab(
row: ProductionWorkOrderItem,
tabKey: ProductionWorkOrderTabKey
): string {
const status = String(row.protocolStatus || '')
const voidType = String(row.voidType || '')
if (tabKey === 'inbound_research_donation') return donationStatusDisplayLabel(status, voidType)
return protocolStatusDisplayLabel(status, voidType)
}
function protocolStatusTagTypeForTab(
row: ProductionWorkOrderItem,
tabKey: ProductionWorkOrderTabKey
): TagType {
const status = String(row.protocolStatus || '')
if (tabKey === 'inbound_research_donation') return donationStatusTagType(status)
return protocolStatusTagType(status)
}
function buildInboundMetaFields(tabKey: ProductionWorkOrderTabKey): CardMetaField[] {
const defaultSource = tabKey === 'inbound_research_donation' ? 'research_donation' : 'cell_storage'
return [
{ label: '实验室入库编码', getValue: (row) => row.labStorageCode || '—' },
{ label: '样本类型', getValue: (row) => row.sampleTypeName || '—' },
{ label: '接收经手人', getValue: (row) => row.handlerName || '—' },
{ label: '接收时间', getValue: (row) => formatDisplayOrDash(row.receptionTime) },
{
label: '来源类型',
kind: 'tag',
getValue: (row) =>
row.receptionSourceLabel || receptionSourceLabel(row.receptionSource || defaultSource),
getTagType: (row) => receptionSourceTagType(row.receptionSource || defaultSource)
},
{
label: tabKey === 'inbound_research_donation' ? '捐赠状态' : '协议状态',
kind: 'tag',
getValue: (row) => protocolStatusLabelForTab(row, tabKey),
getTagType: (row) => protocolStatusTagTypeForTab(row, tabKey),
visible: (row) => !!String(row.protocolStatus || '').trim()
},
{
label: '质检工单号',
getValue: (row) => row.qcWorkOrderNo || '—'
},
{
label: '质检工单状态',
kind: 'tag',
getValue: (row) =>
row.qcWorkOrderStatusLabel ||
workOrderStatusLabel(String(row.qcWorkOrderStatus || '')) ||
'—',
getTagType: (row) =>
workOrderStatusTagType(String(row.qcWorkOrderStatus || '')) || 'info',
visible: (row) => !!String(row.qcWorkOrderStatus || '').trim()
},
{
label: '质检结论',
kind: 'tag',
getValue: (row) =>
row.qcConclusionLabel ||
(row.qcConclusion === 'tg'
? '合格'
: row.qcConclusion === 'btg'
? '不合格'
: '—'),
getTagType: (row) => {
const c = String(row.qcConclusion || '').trim()
if (c === 'tg') return 'success'
if (c === 'btg') return 'danger'
return 'info'
}
}
]
}
const outboundPilotClinicalFields: CardMetaField[] = [
{ label: '申请单号', getValue: (row) => row.applicationNo || '—' },
{ label: '申请单名称', getValue: (row) => row.applicationName || '—' },
{ label: '渠道名称', getValue: (row) => row.channelName || '—' },
{ label: '患者姓名', getValue: (row) => row.patientName || '—' },
{ label: '第几次', getValue: formatOutboundScheduleSortNo },
{ label: '总次数', getValue: formatOutboundScheduleTotalCount },
{ label: '输注时间', getValue: (row) => formatDisplayOrDash(row.infusionTime) },
plannedStartTimeRangeField,
{
label: '备注',
getValue: (row) => row.remark || '—',
visible: (row) => !!row.remark,
wide: true
}
]
const outboundBasicExperimentFields: CardMetaField[] = [
{ label: '申请单号', getValue: (row) => row.applicationNo || '—' },
{
label: '课题名称',
getValue: (row) => row.projectTitle || row.applicationName || '—'
},
{ label: '客户名称', getValue: (row) => row.customerName || '—' },
{ label: '交付时间', getValue: (row) => formatDisplayOrDash(row.infusionTime) },
{
label: '售卖金额',
getValue: (row) => {
const amount = row.feeSummary?.saleAmount
if (amount == null || amount === '') return '—'
return `¥${formatMoneyDisplay(amount)}`
},
visible: (row) => row.feeSummary?.saleAmount != null && row.feeSummary?.saleAmount !== ''
},
plannedStartTimeRangeField,
{
label: '备注',
getValue: (row) => row.remark || '—',
visible: (row) => !!row.remark,
wide: true
}
]
const temporaryFields: CardMetaField[] = []
const CARD_META_BY_TAB: Record<ProductionWorkOrderTabKey, ProductionWorkOrderCardMetaConfig> = {
all: {
tabKey: 'all',
showCategoryTag: true,
showProtocolFeeSummary: false,
showOutboundFeeSummary: false,
showCellProducts: false,
metaFields: [],
allowDispatchQc: false,
allowSampleReception: false,
allowDispatchComplete: false,
allowEditRemark: false,
allowDelete: false
},
inbound_cell_storage: {
tabKey: 'inbound_cell_storage',
showCategoryTag: false,
showProtocolFeeSummary: false,
showOutboundFeeSummary: false,
showCellProducts: false,
metaFields: buildInboundMetaFields('inbound_cell_storage'),
allowDispatchQc: true,
allowSampleReception: true,
allowDispatchComplete: true,
allowEditRemark: true,
allowDelete: true
},
inbound_research_donation: {
tabKey: 'inbound_research_donation',
showCategoryTag: false,
showProtocolFeeSummary: false,
showOutboundFeeSummary: false,
showCellProducts: false,
metaFields: buildInboundMetaFields('inbound_research_donation'),
allowDispatchQc: true,
allowSampleReception: true,
allowDispatchComplete: true,
allowEditRemark: true,
allowDelete: true
},
outbound_pilot_service: {
tabKey: 'outbound_pilot_service',
showCategoryTag: false,
showProtocolFeeSummary: false,
showOutboundFeeSummary: false,
showCellProducts: true,
metaFields: outboundPilotClinicalFields,
allowDispatchQc: true,
allowSampleReception: false,
allowDispatchComplete: true,
allowEditRemark: true,
allowDelete: false
},
outbound_basic_experiment: {
tabKey: 'outbound_basic_experiment',
showCategoryTag: false,
showProtocolFeeSummary: false,
showOutboundFeeSummary: true,
showCellProducts: true,
metaFields: outboundBasicExperimentFields,
allowDispatchQc: true,
allowSampleReception: false,
allowDispatchComplete: true,
allowEditRemark: true,
allowDelete: false
},
outbound_clinical_application: {
tabKey: 'outbound_clinical_application',
showCategoryTag: false,
showProtocolFeeSummary: false,
showOutboundFeeSummary: true,
showCellProducts: false,
metaFields: outboundPilotClinicalFields,
allowDispatchQc: false,
allowSampleReception: false,
allowDispatchComplete: true,
allowEditRemark: true,
allowDelete: false
},
temporary: {
tabKey: 'temporary',
showCategoryTag: false,
showProtocolFeeSummary: false,
showOutboundFeeSummary: false,
showCellProducts: false,
metaFields: temporaryFields,
allowDispatchQc: true,
allowSampleReception: false,
allowDispatchComplete: true,
allowEditRemark: true,
allowDelete: true
}
}
/** 旧 3 Tab 兼容映射(P5 壳层改造前) */
const LEGACY_TAB_MAP: Record<'inbound' | 'outbound' | 'temporary', ProductionWorkOrderTabKey> = {
inbound: 'inbound_cell_storage',
outbound: 'outbound_pilot_service',
temporary: 'temporary'
}
export function getCardMetaConfig(tabKey: ProductionWorkOrderTabKey): ProductionWorkOrderCardMetaConfig {
return CARD_META_BY_TAB[tabKey]
}
export function resolveCardMetaForRow(
row: ProductionWorkOrderItem,
tabKey: ProductionWorkOrderTabKey
): ProductionWorkOrderCardMetaConfig {
if (tabKey !== 'all') return getCardMetaConfig(tabKey)
const rowTabKey = resolveTabKeyFromWorkOrder(row)
const rowMeta = getCardMetaConfig(rowTabKey)
return {
...rowMeta,
tabKey: 'all',
showCategoryTag: true
}
}
export function getCardMetaConfigForLegacyTab(
activeTypeTab: 'inbound' | 'outbound' | 'temporary'
): ProductionWorkOrderCardMetaConfig {
return getCardMetaConfig(LEGACY_TAB_MAP[activeTypeTab])
}