307 lines
10 KiB
TypeScript
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])
|
|
}
|