Files
zkxw/web-admin/src/api/production-work-order.ts
T
zlwuandCursor a3af039e74 feat: 质检工单结论、库位批量/筛选与协议样本接收质检摘要
落库并展示质检工单级结论(可编辑/任务派生),库位树支持批量新增子节点与货架格子检索,协议/捐赠列表补充接收与门禁质检摘要,并同步生产物料与工单卡片展示优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 18:46:24 +08:00

298 lines
9.1 KiB
TypeScript

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<ProductionWorkOrderDetail>({ url: `/web/admin/production-work-orders/${id}` })
}
export function fetchCompleteProductionWorkOrder(id: string, data: Record<string, any>) {
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<ProductionWorkOrderItem>({
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<ProductionWorkOrderItem>({
url: `/web/admin/production-work-orders/${id}/configure`,
data
})
}
export function fetchUpdateProductionWorkOrder(id: string, data: { remark?: string }) {
return request.put<ProductionWorkOrderItem>({
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<ProductionQcDispatchOption[]>({
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<ProductionWorkOrderQcInfoDetail>({
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<ProductionWorkOrderTabStepCounts> | 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<ProductionWorkOrderTabStepCounts>({
url: '/web/admin/production-work-orders/tab-step-counts'
})
}
let manageTabStepCountsInflight: Promise<ProductionWorkOrderTabStepCounts> | null = null
/** 并发去重:Tab 栏与侧栏角标共享同一次 tab-step-counts 请求 */
export function fetchProductionWorkOrderTabStepCountsDeduped() {
if (!manageTabStepCountsInflight) {
manageTabStepCountsInflight = fetchProductionWorkOrderTabStepCounts().finally(() => {
manageTabStepCountsInflight = null
})
}
return manageTabStepCountsInflight
}
export function fetchMineProductionWorkOrderTabStepCounts() {
return request.get<ProductionWorkOrderTabStepCounts>({
url: '/web/admin/production-work-orders/mine-tab-step-counts'
})
}
let mineTabStepCountsInflight: Promise<ProductionWorkOrderTabStepCounts> | null = null
export function fetchMineProductionWorkOrderTabStepCountsDeduped() {
if (!mineTabStepCountsInflight) {
mineTabStepCountsInflight = fetchMineProductionWorkOrderTabStepCounts().finally(() => {
mineTabStepCountsInflight = null
})
}
return mineTabStepCountsInflight
}