落库并展示质检工单级结论(可编辑/任务派生),库位树支持批量新增子节点与货架格子检索,协议/捐赠列表补充接收与门禁质检摘要,并同步生产物料与工单卡片展示优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
298 lines
9.1 KiB
TypeScript
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
|
|
}
|