Files
zkxw/web-admin/src/utils/qc-inspection-batch.ts
T
2026-08-18 14:40:15 +08:00

215 lines
6.2 KiB
TypeScript

import request from '@/utils/http'
import type { QcTask } from '@/api/qc-task'
export type QcInspectionBatch = {
id: string
workOrderId?: string
batchNo: string
batchName: string
orderNum?: number
assigneeUserIds: string[]
assigneeUserNames?: string
hasLab?: boolean
hasThirdParty?: boolean
labItemIds?: string[]
thirdPartyItemIds?: string[]
taskCount?: number
completedTaskCount?: number
tasks?: QcTask[]
}
export type QcInspectionBatchConfigureItem = {
id?: string
batchName: string
assigneeUserIds: string[]
hasLab: boolean
hasThirdParty: boolean
labItemIds: string[]
thirdPartyItemIds: string[]
}
export function fetchQcInspectionBatches(workOrderId: string) {
return request.get<QcInspectionBatch[]>({
url: `/web/admin/qc-work-orders/${workOrderId}/inspection-batches`
})
}
export function fetchQcInspectionBatchesBatch(workOrderIds: string[]) {
return request.post<Record<string, QcInspectionBatch[]>>({
url: '/web/admin/qc-work-orders/inspection-batches/batch',
data: { workOrderIds: workOrderIds.filter(Boolean) }
})
}
/** 优先使用详情内嵌批次,否则独立 GET */
export async function loadQcInspectionBatches(
workOrderId: string,
embedded?: QcInspectionBatch[] | null
): Promise<QcInspectionBatch[]> {
if (embedded?.length) return embedded
if (!workOrderId) return []
try {
return (await fetchQcInspectionBatches(workOrderId)) || []
} catch {
return []
}
}
type QcBatchTaskLike = {
id?: string
batchId?: string
taskStatus?: string
assigneeUserNames?: string
}
export type QcBatchTaskSection<T extends QcBatchTaskLike = QcBatchTaskLike> = {
key: string
batchId: string
batchNo: string
batchName: string
assigneeUserNames: string
taskCount: number
completedTaskCount: number
tasks: T[]
}
export function batchTasksPanelSlot(batchKey: string) {
return `batch-tasks-${batchKey}`
}
function resolveBatchSectionTasks<T extends QcBatchTaskLike>(
batch: QcInspectionBatch,
fallbackTasks?: T[]
): T[] {
const enrichedById = new Map<string, T>()
if (fallbackTasks?.length) {
for (const t of fallbackTasks) {
if (t.id) enrichedById.set(t.id, t)
}
}
const rawFromBatch = (batch.tasks || []) as T[]
if (enrichedById.size && rawFromBatch.length) {
return rawFromBatch.map((t) => (t.id && enrichedById.get(t.id)) || t)
}
if (enrichedById.size && batch.id) {
return fallbackTasks!.filter((t) => (t.batchId || '').trim() === batch.id)
}
return rawFromBatch
}
export function buildBatchSectionsFromBatches<T extends QcBatchTaskLike>(
batches: QcInspectionBatch[],
fallbackTasks?: T[]
): QcBatchTaskSection<T>[] {
if (batches.length) {
return batches.map((b) => {
const tasks = resolveBatchSectionTasks(b, fallbackTasks)
const completed = tasks.filter((t) => t.taskStatus === 'ywc').length
return {
key: b.id || b.batchNo,
batchId: b.id,
batchNo: b.batchNo,
batchName: b.batchName || b.batchNo || '检测批次',
assigneeUserNames: b.assigneeUserNames || '',
taskCount: b.taskCount ?? tasks.length,
completedTaskCount: b.completedTaskCount ?? completed,
tasks
}
})
}
if (fallbackTasks?.length) {
return buildBatchSectionsFromTasks(fallbackTasks)
}
return []
}
export function buildBatchSectionsFromTasks<T extends QcBatchTaskLike>(
tasks: T[]
): QcBatchTaskSection<T>[] {
const map = new Map<string, T[]>()
const order: string[] = []
for (const t of tasks) {
const bid = (t.batchId || '').trim() || '__legacy__'
if (!map.has(bid)) {
map.set(bid, [])
order.push(bid)
}
map.get(bid)!.push(t)
}
return order.map((bid) => {
const group = map.get(bid)!
const completed = group.filter((t) => t.taskStatus === 'ywc').length
return {
key: bid,
batchId: bid === '__legacy__' ? '' : bid,
batchNo: '',
batchName: bid === '__legacy__' ? '历史任务' : '检测批次',
assigneeUserNames: group[0]?.assigneeUserNames || '',
taskCount: group.length,
completedTaskCount: completed,
tasks: group
}
})
}
export function resolveBatchSections<T extends QcBatchTaskLike>(
batches: QcInspectionBatch[] | undefined | null,
tasks: T[] | undefined | null
): QcBatchTaskSection<T>[] {
const fromBatches = buildBatchSectionsFromBatches(batches || [], tasks || undefined)
if (fromBatches.length) return fromBatches
if (tasks?.length) return buildBatchSectionsFromTasks(tasks)
return []
}
/** 细胞存储 / 科研捐赠:批次名默认用实验室入库编码 */
const INBOUND_LAB_CODE_BATCH_SOURCES = new Set(['cell_storage', 'research_donation'])
export type ResolveDefaultQcBatchNameOpts = {
receptionSource?: string | null
labStorageCode?: string | null
/** 已占用名称;仅非入库类型做 YYYYMMDD检测 去重时使用 */
usedNames?: Iterable<string>
}
/**
* 配置检测弹窗新增批次的默认名称。
* - 细胞存储 / 科研捐赠:有实验室入库编码则用该编码(多批次相同);无则空串
* - 其它类型:YYYYMMDD检测,冲突则 -2、-3…
*/
export function resolveDefaultQcBatchName(opts: ResolveDefaultQcBatchNameOpts = {}): string {
const source = String(opts.receptionSource || '').trim()
if (INBOUND_LAB_CODE_BATCH_SOURCES.has(source)) {
return String(opts.labStorageCode || '').trim()
}
const today = new Date()
const y = today.getFullYear()
const m = String(today.getMonth() + 1).padStart(2, '0')
const d = String(today.getDate()).padStart(2, '0')
const base = `${y}${m}${d}检测`
const used = new Set(
[...(opts.usedNames || [])].map((n) => String(n || '').trim()).filter(Boolean)
)
if (!used.has(base)) return base
let n = 2
while (used.has(`${base}-${n}`)) n += 1
return `${base}-${n}`
}
export function createEmptyInspectionBatch(
defaultAssigneeUserIds?: string[],
nameOpts?: ResolveDefaultQcBatchNameOpts
): QcInspectionBatchConfigureItem {
const assigneeUserIds = (defaultAssigneeUserIds || [])
.map((id) => String(id || '').trim())
.filter(Boolean)
return {
batchName: resolveDefaultQcBatchName(nameOpts),
assigneeUserIds,
hasLab: false,
hasThirdParty: false,
labItemIds: [],
thirdPartyItemIds: []
}
}