import request from '@/utils/http' import type { ProductionWorkOrderTabKey } from '@/utils/production-work-order-tabs' import type { QcWorkOrderReceptionDetail, OutboundQcDetailApplication, OutboundQcDetailPatient, OutboundQcDetailInfusionPlan, OutboundQcDetailInfusionSchedule, OutboundQcDetailProduct, QcProtocolPaymentSummary, QcBasicExperimentFeeSummary } from '@/api/qc-work-order' export interface ProductionWorkOrderItem { id: string workOrderNo: string workOrderType?: string protocolNo?: string sampleCode?: string receptionSource?: string receptionSourceLabel?: string sampleTypeName?: string isSpecial?: boolean specialRemark?: string handlerName?: string receptionTime?: string workOrderStatus: string /** 门禁质检结论:tg=合格 / btg=不合格(细胞存储/科研捐赠入库) */ qcConclusion?: string qcConclusionLabel?: string createTime?: string /** 卡片展示:工单下全部工艺步骤计划开始时间区间(前端聚合) */ plannedStartTimeRange?: string remark?: string labStorageCode?: string protocolStatus?: string bizType?: string bizTypeLabel?: string /** 出库工单:关联申请单号 */ applicationNo?: string applicationName?: string channelName?: string /** 出库工单:患者/用户姓名 */ patientName?: string /** 出库工单:当前为第几次输注计划 */ sortNo?: number | null /** 出库工单:输注计划总次数 */ totalCourseCount?: number | null infusionTime?: string /** 基础实验出库:课题名称(列表 enrich) */ projectTitle?: string /** 基础实验出库:客户名称(列表 enrich) */ customerName?: string /** 出库工单:输注计划细胞类型(列表 enrich,无产品明细时兜底) */ cellType?: string /** 出库工单:输注计划规格(列表 enrich,无产品明细时兜底) */ specText?: string /** 出库工单:输注计划关联细胞产品明细(列表 enrich) */ products?: OutboundQcDetailProduct[] paymentSummary?: QcProtocolPaymentSummary | null feeSummary?: QcBasicExperimentFeeSummary | null /** 细胞存储 / 科研捐赠:母亲姓名 */ motherName?: string /** 细胞存储 / 科研捐赠:新生儿姓名 */ newbornName?: string } export type ProductionWorkOrderDetail = ProductionWorkOrderItem & { reception?: QcWorkOrderReceptionDetail productionReception?: | import('@/utils/sample-production-reception-fields').ProductionReceptionRecord | null bizType?: string bizTypeLabel?: string /** 临床应用出库:简要申请单 */ pilotApplication?: { applicationNo?: string applicationName?: string applicationStatus?: string applicationStatusLabel?: string } /** 临床应用出库:简要输注计划 */ infusionSchedule?: { cellType?: string specText?: string infusionTime?: string infusionLocation?: string infusionMethod?: string scheduleStatus?: string } /** 中试服务出库:与出库质检工单查看弹窗一致 */ application?: OutboundQcDetailApplication patient?: OutboundQcDetailPatient infusionPlan?: OutboundQcDetailInfusionPlan products?: OutboundQcDetailProduct[] } export function fetchProductionWorkOrderList(params?: { page?: number pageSize?: number keyword?: string workOrderStatus?: string workOrderType?: string receptionSource?: string bizType?: string createTimeStart?: string createTimeEnd?: string infusionTimeStart?: string infusionTimeEnd?: string plannedStartTimeStart?: string plannedStartTimeEnd?: string excludeVoidProtocol?: boolean }) { return request.get<{ list: ProductionWorkOrderItem[] total: number page: number pageSize: number totalPages: number }>({ url: '/web/admin/production-work-orders', params }) } export function fetchProductionWorkOrderDetail(id: string) { return request.get({ url: `/web/admin/production-work-orders/${id}` }) } export function fetchCompleteProductionWorkOrder(id: string, data: Record) { return request.put({ url: `/web/admin/production-work-orders/${id}/complete`, params: data }) } export function fetchCreateProductionWorkOrder(data: { workOrderType?: string protocolNo?: string sampleCode?: string receptionSource?: string remark?: string }) { return request.post({ url: '/web/admin/production-work-orders', data }) } export function fetchConfigureProductionWorkOrder( id: string, data: { taskName?: string taskType?: string routeId?: string description?: string remark?: string } ) { return request.post({ url: `/web/admin/production-work-orders/${id}/configure`, data }) } export function fetchUpdateProductionWorkOrder(id: string, data: { remark?: string }) { return request.put({ url: `/web/admin/production-work-orders/${id}`, data }) } export function fetchDeleteProductionWorkOrder(id: string) { return request.del({ url: `/web/admin/production-work-orders/${id}` }) } export interface ProductionQcDispatchOption { id: string stepOutputId?: string taskId?: string taskNo?: string taskName?: string stepId?: string processName?: string outputTypeId?: string outputTypeName?: string outputName?: string outputBarcode?: string cellProductId?: string } /** @deprecated 使用 ProductionQcDispatchOption */ export type ProductionCellProductOption = ProductionQcDispatchOption export function fetchProductionWorkOrderQcDispatchOptions(workOrderId: string) { return request.get({ url: `/web/admin/production-work-orders/${workOrderId}/cell-product-options` }) } /** @deprecated 使用 fetchProductionWorkOrderQcDispatchOptions */ export function fetchProductionWorkOrderCellProductOptions(workOrderId: string) { return fetchProductionWorkOrderQcDispatchOptions(workOrderId) } export function fetchCreateQcFromProduction( workOrderId: string, data: { productionStepOutputId: string; remark?: string } ) { return request.post({ url: `/web/admin/production-work-orders/${workOrderId}/qc-work-orders`, data }) } export type ProductionWorkOrderQcInfoDetail = { showGateSection?: boolean gateQcInfo?: import('@/components/business/sample-qc-info-cards/index.vue').SampleQcInfo | null processQcInfoList?: import('@/components/business/sample-qc-info-cards/index.vue').SampleQcInfo[] sampleReception?: | import('@/components/business/sample-qc-info-cards/index.vue').SampleReceptionBrief | null } export function fetchProductionWorkOrderQcInfo(workOrderId: string) { return request.get({ url: `/web/admin/production-work-orders/${workOrderId}/qc-info` }) } export type ProductionWorkOrderTabStepCount = { overdueIncomplete: number futureIncomplete: number } export type ProductionWorkOrderTabStepCounts = Record< ProductionWorkOrderTabKey, ProductionWorkOrderTabStepCount > export function formatProductionWorkOrderTabStepBadge( count: ProductionWorkOrderTabStepCount ): string { const fmt = (n: number) => (n > 99 ? '99+' : String(n)) return `${fmt(count.overdueIncomplete)}+${fmt(count.futureIncomplete)}` } export function isProductionWorkOrderTabStepBadgeZero( count: ProductionWorkOrderTabStepCount ): boolean { return (count.overdueIncomplete || 0) === 0 && (count.futureIncomplete || 0) === 0 } /** 汇总可见 Tab 的步骤角标(左侧菜单展示各 Tab 数值之和) */ export function sumProductionWorkOrderTabStepCounts( counts: Partial | null | undefined, tabKeys: ProductionWorkOrderTabKey[] ): ProductionWorkOrderTabStepCount { return tabKeys.reduce( (acc, key) => { const c = counts?.[key] acc.overdueIncomplete += c?.overdueIncomplete || 0 acc.futureIncomplete += c?.futureIncomplete || 0 return acc }, { overdueIncomplete: 0, futureIncomplete: 0 } ) } export function fetchProductionWorkOrderTabStepCounts() { return request.get({ url: '/web/admin/production-work-orders/tab-step-counts' }) } let manageTabStepCountsInflight: Promise | null = null /** 并发去重:Tab 栏与侧栏角标共享同一次 tab-step-counts 请求 */ export function fetchProductionWorkOrderTabStepCountsDeduped() { if (!manageTabStepCountsInflight) { manageTabStepCountsInflight = fetchProductionWorkOrderTabStepCounts().finally(() => { manageTabStepCountsInflight = null }) } return manageTabStepCountsInflight } export function fetchMineProductionWorkOrderTabStepCounts() { return request.get({ url: '/web/admin/production-work-orders/mine-tab-step-counts' }) } let mineTabStepCountsInflight: Promise | null = null export function fetchMineProductionWorkOrderTabStepCountsDeduped() { if (!mineTabStepCountsInflight) { mineTabStepCountsInflight = fetchMineProductionWorkOrderTabStepCounts().finally(() => { mineTabStepCountsInflight = null }) } return mineTabStepCountsInflight }