提交
This commit is contained in:
1 parent
4daf990fab
commit
1ca2668112
219 files changed
+29817
-20945
No files matched your search
@@ -1,9 +1,19 @@
|
||||
/**
|
||||
* 将用户粘贴的中文日期时间(如「2026年6月22日12时18分」)解析为系统标准格式。
|
||||
*/
|
||||
export type ChineseDatetimeFormat = 'date' | 'datetime' | 'datetime-minute'
|
||||
|
||||
/** 将日期时间规范为 `YYYY-MM-DD HH:mm`(去掉秒,兼容历史带秒数据) */
|
||||
export function formatDatetimeToMinute(val: string): string {
|
||||
const s = String(val || '').trim()
|
||||
if (!s) return ''
|
||||
const parsed = parseChineseDatetimeText(s, 'datetime-minute')
|
||||
return parsed || s
|
||||
}
|
||||
|
||||
export function parseChineseDatetimeText(
|
||||
text: string,
|
||||
format: 'date' | 'datetime'
|
||||
format: ChineseDatetimeFormat
|
||||
): string | null {
|
||||
const raw = text.trim()
|
||||
if (!raw) return null
|
||||
@@ -20,6 +30,9 @@ export function parseChineseDatetimeText(
|
||||
const timeParts = time.split(':')
|
||||
const hh = timeParts[0]?.padStart(2, '0') ?? '00'
|
||||
const mm = timeParts[1]?.padStart(2, '0') ?? '00'
|
||||
if (format === 'datetime-minute') {
|
||||
return `${parts[0]} ${hh}:${mm}`
|
||||
}
|
||||
const ss = timeParts[2]?.padStart(2, '0') ?? '00'
|
||||
return `${parts[0]} ${hh}:${mm}:${ss}`
|
||||
}
|
||||
@@ -38,13 +51,16 @@ export function parseChineseDatetimeText(
|
||||
|
||||
const h = String(Number(cnMatch[4] || 0)).padStart(2, '0')
|
||||
const mi = String(Number(cnMatch[5] || 0)).padStart(2, '0')
|
||||
if (format === 'datetime-minute') {
|
||||
return `${dateStr} ${h}:${mi}`
|
||||
}
|
||||
const s = String(Number(cnMatch[6] || 0)).padStart(2, '0')
|
||||
return `${dateStr} ${h}:${mi}:${s}`
|
||||
}
|
||||
|
||||
export function handleChineseDatetimePaste(
|
||||
e: ClipboardEvent,
|
||||
format: 'date' | 'datetime',
|
||||
format: ChineseDatetimeFormat,
|
||||
setter: (val: string) => void
|
||||
) {
|
||||
const text = e.clipboardData?.getData('text')?.trim()
|
||||
|
||||
@@ -90,13 +90,15 @@ function parseScore(value: string): number | null | undefined {
|
||||
return Number.isFinite(n) ? n : undefined
|
||||
}
|
||||
|
||||
function parseDatetime(value: string): string | undefined {
|
||||
function parseDatetime(value: string, minutePrecision = false): string | undefined {
|
||||
const v = value.trim()
|
||||
if (!v) return undefined
|
||||
if (MATERNAL_BLOOD_EMPTY_MARKERS.some((m) => v === m || v.includes(m))) {
|
||||
return undefined
|
||||
}
|
||||
return parseChineseDatetimeText(v, 'datetime') || undefined
|
||||
return (
|
||||
parseChineseDatetimeText(v, minutePrecision ? 'datetime-minute' : 'datetime') || undefined
|
||||
)
|
||||
}
|
||||
|
||||
function isMaternalBloodEmpty(value: string): boolean {
|
||||
@@ -153,7 +155,7 @@ export function parseDonationStandardImportText(text: string): DonationStandardI
|
||||
break
|
||||
}
|
||||
case 'sampleCollectTime': {
|
||||
const dt = parseDatetime(kv.value)
|
||||
const dt = parseDatetime(kv.value, true)
|
||||
if (dt) result.sampleCollectTime = dt
|
||||
break
|
||||
}
|
||||
@@ -161,7 +163,7 @@ export function parseDonationStandardImportText(text: string): DonationStandardI
|
||||
if (isMaternalBloodEmpty(kv.value)) {
|
||||
result.maternalBloodExplicitEmpty = true
|
||||
} else {
|
||||
const dt = parseDatetime(kv.value)
|
||||
const dt = parseDatetime(kv.value, true)
|
||||
if (dt) result.maternalBloodCollectTime = dt
|
||||
}
|
||||
break
|
||||
|
||||
@@ -80,3 +80,69 @@ export function normalizePlannedStartDate(raw?: string | null): string | null {
|
||||
const formatted = formatPlannedStartDate(raw)
|
||||
return formatted || null
|
||||
}
|
||||
|
||||
export function productionStepAssigneeDisplayName(raw: string): string {
|
||||
const s = String(raw || '').trim()
|
||||
if (!s) return ''
|
||||
const matched = s.match(/^(.+?)[((][^))]*[))]$/)
|
||||
if (matched) return matched[1].trim()
|
||||
return s
|
||||
}
|
||||
|
||||
export function productionStepAssigneeLabels(row: Record<string, any>): string[] {
|
||||
const namesStr = String(row.assigneeUserNames || '').trim()
|
||||
if (namesStr) {
|
||||
return namesStr
|
||||
.split(/[,,、]/)
|
||||
.map((n) => productionStepAssigneeDisplayName(n))
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
const namesArr: string[] = Array.isArray(row.assigneeNames) ? row.assigneeNames : []
|
||||
const fromNamesArr = namesArr
|
||||
.map((n) => productionStepAssigneeDisplayName(String(n || '')))
|
||||
.filter(Boolean)
|
||||
if (fromNamesArr.length) return fromNamesArr
|
||||
|
||||
const assignees: Array<{ userId?: string; nickName?: string; userNo?: string }> = Array.isArray(
|
||||
row.assignees
|
||||
)
|
||||
? row.assignees
|
||||
: []
|
||||
const assigneeIds: string[] = Array.isArray(row.assigneeUserIds)
|
||||
? row.assigneeUserIds.map((id) => String(id || '').trim()).filter(Boolean)
|
||||
: []
|
||||
|
||||
if (assigneeIds.length) {
|
||||
const byId = new Map(
|
||||
assignees
|
||||
.map((a) => [String(a.userId || '').trim(), a] as const)
|
||||
.filter(([id]) => Boolean(id))
|
||||
)
|
||||
const fromIds = assigneeIds
|
||||
.map((id) => {
|
||||
const matched = byId.get(id)
|
||||
const name = String(matched?.nickName || matched?.userNo || '').trim()
|
||||
return name ? productionStepAssigneeDisplayName(name) : ''
|
||||
})
|
||||
.filter(Boolean)
|
||||
if (fromIds.length) return fromIds
|
||||
}
|
||||
|
||||
if (assignees.length) {
|
||||
return assignees
|
||||
.map((a) => String(a.nickName || a.userNo || '').trim())
|
||||
.map((n) => productionStepAssigneeDisplayName(n))
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
export function productionStepAssigneeText(row: Record<string, any>): string {
|
||||
return productionStepAssigneeLabels(row).join('、')
|
||||
}
|
||||
|
||||
export function productionStepPlannedStartText(row: Record<string, any>): string {
|
||||
return formatPlannedStartDate(row.plannedStartTime)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
fetchMineProductionWorkOrderTabStepCounts,
|
||||
fetchProductionWorkOrderTabStepCounts,
|
||||
fetchMineProductionWorkOrderTabStepCountsDeduped,
|
||||
fetchProductionWorkOrderTabStepCountsDeduped,
|
||||
sumProductionWorkOrderTabStepCounts
|
||||
} from '@/api/production-work-order'
|
||||
import {
|
||||
@@ -57,7 +57,7 @@ export async function refreshProductionMenuBadges() {
|
||||
|
||||
if (visibleCodes.includes(PRODUCTION_MENU_CODE_WORK_ORDER)) {
|
||||
tasks.push(
|
||||
fetchProductionWorkOrderTabStepCounts()
|
||||
fetchProductionWorkOrderTabStepCountsDeduped()
|
||||
.then((data) =>
|
||||
badgeStore.setBadge(
|
||||
PRODUCTION_MENU_CODE_WORK_ORDER,
|
||||
@@ -75,7 +75,7 @@ export async function refreshProductionMenuBadges() {
|
||||
|
||||
if (visibleCodes.includes(PRODUCTION_MENU_CODE_MY_TASK)) {
|
||||
tasks.push(
|
||||
fetchMineProductionWorkOrderTabStepCounts()
|
||||
fetchMineProductionWorkOrderTabStepCountsDeduped()
|
||||
.then((data) =>
|
||||
badgeStore.setBadge(
|
||||
PRODUCTION_MENU_CODE_MY_TASK,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
getProductionWorkOrderTab,
|
||||
toWorkOrderListQuery
|
||||
} from '@/utils/production-work-order-tabs'
|
||||
import { matchesWorkOrderStatusFilter } from '@/utils/status-tag-registry'
|
||||
|
||||
export type ProductionTaskMineFilters = {
|
||||
keyword?: string
|
||||
@@ -17,7 +18,7 @@ export type ProductionTaskMineFilters = {
|
||||
|
||||
export type MineWorkOrderSearchFilters = {
|
||||
keyword?: string
|
||||
workOrderStatus?: string
|
||||
workOrderStatus?: string | string[]
|
||||
createTimeStart?: string
|
||||
createTimeEnd?: string
|
||||
infusionTimeStart?: string
|
||||
@@ -59,7 +60,7 @@ function keywordMatchesWorkOrder(
|
||||
? [row.workOrderNo, row.applicationNo, row.projectTitle, row.customerName, row.applicationName]
|
||||
: tab.group === 'outbound'
|
||||
? [row.workOrderNo, row.applicationNo, row.applicationName, row.patientName]
|
||||
: [row.workOrderNo, row.sampleCode, row.labStorageCode]
|
||||
: [row.workOrderNo, row.sampleCode, row.labStorageCode, row.motherName, row.newbornName]
|
||||
return fields.some((f) => (f || '').toLowerCase().includes(kw))
|
||||
}
|
||||
|
||||
@@ -100,6 +101,8 @@ export function buildWorkOrderFromTasks(tasks: ProductionTaskRow[]): ProductionW
|
||||
handlerName: t.handlerName,
|
||||
receptionTime: t.receptionTime,
|
||||
labStorageCode: t.labStorageCode,
|
||||
motherName: t.motherName,
|
||||
newbornName: t.newbornName,
|
||||
remark: t.workOrderRemark || t.remark,
|
||||
createTime: t.workOrderCreateTime || t.createTime || ''
|
||||
}
|
||||
@@ -222,7 +225,7 @@ export function filterMineEntriesByWorkOrderSearch(
|
||||
const row = entry.workOrder
|
||||
if (resolveTabKeyFromWorkOrder(row) !== tabKey) return false
|
||||
if (entry.tabKey !== tabKey) return false
|
||||
if (filters.workOrderStatus && row.workOrderStatus !== filters.workOrderStatus) return false
|
||||
if (!matchesWorkOrderStatusFilter(row.workOrderStatus, filters.workOrderStatus)) return false
|
||||
if (!keywordMatchesWorkOrder(row, filters.keyword || '', tabKey)) return false
|
||||
if (tabUsesInfusionTimeFilter(tab)) {
|
||||
if (!dateInRange(row.infusionTime, filters.infusionTimeStart, filters.infusionTimeEnd)) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/** 生产任务编辑弹窗 · 工艺库与步骤配置参考数据(会话级缓存,减少重复 editor-init 请求) */
|
||||
|
||||
export type ProductionTaskEditorReferenceOptions = {
|
||||
processCategories: Record<string, any>[]
|
||||
sops: Record<string, any>[]
|
||||
outputTypes: Record<string, any>[]
|
||||
equipments: Record<string, any>[]
|
||||
reagents: Record<string, any>[]
|
||||
consumables: Record<string, any>[]
|
||||
itemUnits: Record<string, any>[]
|
||||
}
|
||||
|
||||
export type ProductionTaskEditorReferenceCache = {
|
||||
processLibrary: Record<string, any>[]
|
||||
options: ProductionTaskEditorReferenceOptions
|
||||
}
|
||||
|
||||
let cachedReference: ProductionTaskEditorReferenceCache | null = null
|
||||
|
||||
export function getCachedProductionTaskEditorReference(): ProductionTaskEditorReferenceCache | null {
|
||||
return cachedReference
|
||||
}
|
||||
|
||||
export function setCachedProductionTaskEditorReference(data: ProductionTaskEditorReferenceCache) {
|
||||
cachedReference = data
|
||||
}
|
||||
|
||||
export function hasCachedProductionTaskEditorReference(): boolean {
|
||||
return cachedReference !== null
|
||||
}
|
||||
|
||||
export function clearProductionTaskEditorReferenceCache() {
|
||||
cachedReference = null
|
||||
}
|
||||
@@ -99,6 +99,8 @@ function buildInboundMetaFields(tabKey: ProductionWorkOrderTabKey): CardMetaFiel
|
||||
})
|
||||
}
|
||||
fields.push(
|
||||
{ label: '母亲姓名', getValue: (row) => row.motherName || '—' },
|
||||
{ label: '新生儿姓名', getValue: (row) => row.newbornName || '—' },
|
||||
{
|
||||
label: tabKey === 'inbound_research_donation' ? '捐赠状态' : '协议状态',
|
||||
kind: 'tag',
|
||||
|
||||
@@ -34,6 +34,13 @@ export function fetchQcInspectionBatches(workOrderId: string) {
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -70,21 +77,45 @@ 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) => ({
|
||||
key: b.id || b.batchNo,
|
||||
batchId: b.id,
|
||||
batchNo: b.batchNo,
|
||||
batchName: b.batchName || b.batchNo || '检测批次',
|
||||
assigneeUserNames: b.assigneeUserNames || '',
|
||||
taskCount: b.taskCount ?? (b.tasks?.length || 0),
|
||||
completedTaskCount: b.completedTaskCount ?? 0,
|
||||
tasks: (b.tasks || []) as T[]
|
||||
}))
|
||||
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)
|
||||
@@ -131,14 +162,17 @@ export function resolveBatchSections<T extends QcBatchTaskLike>(
|
||||
return []
|
||||
}
|
||||
|
||||
export function createEmptyInspectionBatch(): QcInspectionBatchConfigureItem {
|
||||
export function createEmptyInspectionBatch(defaultAssigneeUserIds?: string[]): QcInspectionBatchConfigureItem {
|
||||
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 assigneeUserIds = (defaultAssigneeUserIds || [])
|
||||
.map((id) => String(id || '').trim())
|
||||
.filter(Boolean)
|
||||
return {
|
||||
batchName: `${y}${m}${d}检测`,
|
||||
assigneeUserIds: [],
|
||||
assigneeUserIds,
|
||||
hasLab: false,
|
||||
hasThirdParty: false,
|
||||
labItemIds: [],
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { QcWorkOrder } from '@/api/qc-work-order'
|
||||
import { fetchQcWorkOrderDetail } from '@/api/qc-work-order'
|
||||
import { fetchMyQcTaskList, type QcTask } from '@/api/qc-task'
|
||||
import {
|
||||
groupInboundQcWorkOrders,
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
toAllTabListQuery,
|
||||
workOrderMatchesCategory
|
||||
} from '@/utils/qc-work-order-category'
|
||||
import { matchesWorkOrderStatusFilter } from '@/utils/status-tag-registry'
|
||||
|
||||
export type QcTaskMineFilters = {
|
||||
keyword?: string
|
||||
@@ -28,7 +28,7 @@ export type QcTaskMineFilters = {
|
||||
|
||||
export type MineWorkOrderSearchFilters = {
|
||||
keyword?: string
|
||||
workOrderStatus?: string
|
||||
workOrderStatus?: string | string[]
|
||||
triggerSource?: string
|
||||
qcCategory?: string
|
||||
createTimeStart?: string
|
||||
@@ -76,7 +76,14 @@ function keywordMatchesWorkOrder(
|
||||
]
|
||||
: tab.group === 'outbound'
|
||||
? [row.workOrderNo, row.applicationNo, row.applicationName, row.patientName]
|
||||
: [row.workOrderNo, row.protocolNo, row.sampleCode, row.labStorageCode]
|
||||
: [
|
||||
row.workOrderNo,
|
||||
row.protocolNo,
|
||||
row.sampleCode,
|
||||
row.labStorageCode,
|
||||
row.motherName,
|
||||
row.newbornName
|
||||
]
|
||||
return fields.some((f) => (f || '').toLowerCase().includes(kw))
|
||||
}
|
||||
|
||||
@@ -94,7 +101,7 @@ export function filterMineEntriesByWorkOrderSearch(
|
||||
if (isAllQcWorkOrderTab(tabKey) && !workOrderMatchesCategory(row, filters.qcCategory || '')) {
|
||||
return false
|
||||
}
|
||||
if (filters.workOrderStatus && row.workOrderStatus !== filters.workOrderStatus) return false
|
||||
if (!matchesWorkOrderStatusFilter(row.workOrderStatus, filters.workOrderStatus)) return false
|
||||
if (filters.triggerSource) {
|
||||
if (isAllQcWorkOrderTab(tabKey)) {
|
||||
if (!matchesAllTabTriggerSource(row, filters.triggerSource)) return false
|
||||
@@ -149,30 +156,8 @@ export async function fetchAllMineTasks(filters: QcTaskMineFilters): Promise<QcT
|
||||
export async function enrichMineEntriesWithWorkOrders(
|
||||
entries: MineWorkOrderEntry[]
|
||||
): Promise<MineWorkOrderEntry[]> {
|
||||
const cache = new Map<string, QcWorkOrder>()
|
||||
const uniqueIds = [...new Set(entries.map((entry) => entry.workOrderId))]
|
||||
await Promise.all(
|
||||
uniqueIds.map(async (id) => {
|
||||
try {
|
||||
const detail = await fetchQcWorkOrderDetail(id)
|
||||
cache.set(id, detail)
|
||||
} catch {
|
||||
// 保留任务聚合的工单摘要
|
||||
}
|
||||
})
|
||||
)
|
||||
return entries.map((entry) => {
|
||||
const detail = cache.get(entry.workOrderId)
|
||||
if (!detail) return entry
|
||||
return {
|
||||
...entry,
|
||||
workOrder: {
|
||||
...detail,
|
||||
hasLab: detail.hasLab ?? entry.workOrder.hasLab,
|
||||
hasThirdParty: detail.hasThirdParty ?? entry.workOrder.hasThirdParty
|
||||
}
|
||||
}
|
||||
})
|
||||
// 工单摘要已由 /qc-tasks/mine 接口批量 enrich,无需再逐条请求详情
|
||||
return entries
|
||||
}
|
||||
|
||||
export function paginateMineEntries<T>(items: T[], page: number, pageSize: number) {
|
||||
@@ -221,11 +206,12 @@ export function buildWorkOrderFromTasks(tasks: QcTask[]): QcWorkOrder {
|
||||
workOrderNo: t.workOrderNo || '—',
|
||||
workOrderType: woType,
|
||||
workOrderStatus: t.workOrderStatus || '',
|
||||
receptionId: '',
|
||||
protocolSampleId: '',
|
||||
collectionProtocolId: '',
|
||||
receptionId: t.receptionId || '',
|
||||
protocolSampleId: t.protocolSampleId || '',
|
||||
collectionProtocolId: t.collectionProtocolId || '',
|
||||
protocolNo: t.protocolNo || '',
|
||||
sampleCode: t.sampleCode || '',
|
||||
labStorageCode: t.labStorageCode,
|
||||
receptionSource: t.receptionSource,
|
||||
receptionSourceLabel: t.receptionSourceLabel,
|
||||
qcBizType: t.qcBizType || woType,
|
||||
@@ -239,13 +225,34 @@ export function buildWorkOrderFromTasks(tasks: QcTask[]): QcWorkOrder {
|
||||
sampleTypeName: t.sampleTypeName,
|
||||
receptionTime: t.receptionTime,
|
||||
handlerName: t.handlerName,
|
||||
hasLab: tasks.some((x) => x.taskType === 'sysz'),
|
||||
hasThirdParty: tasks.some((x) => x.taskType === 'dsfjc'),
|
||||
createTime: t.createTime || '',
|
||||
bizType: woType === 'outbound' ? t.receptionSource : undefined,
|
||||
bizTypeLabel: woType === 'outbound' ? t.receptionSourceLabel : undefined,
|
||||
hasLab: t.hasLab ?? tasks.some((x) => x.taskType === 'sysz'),
|
||||
hasThirdParty: t.hasThirdParty ?? tasks.some((x) => x.taskType === 'dsfjc'),
|
||||
createTime: t.workOrderCreateTime || t.createTime || '',
|
||||
bizType: woType === 'outbound' ? t.bizType || t.receptionSource : undefined,
|
||||
bizTypeLabel: woType === 'outbound' ? t.bizTypeLabel || t.receptionSourceLabel : undefined,
|
||||
applicationNo: t.applicationNo,
|
||||
applicationName: t.applicationName,
|
||||
projectTitle: t.projectTitle,
|
||||
customerName: t.customerName,
|
||||
channelName: t.channelName,
|
||||
patientName: t.patientName,
|
||||
motherName: t.motherName,
|
||||
newbornName: t.newbornName,
|
||||
sortNo: t.sortNo,
|
||||
totalCourseCount: t.totalCourseCount,
|
||||
infusionTime: t.infusionTime,
|
||||
products: t.products,
|
||||
cellType: t.cellType,
|
||||
specText: t.specText,
|
||||
triggerSource: t.triggerSource,
|
||||
triggerSourceLabel: t.triggerSourceLabel,
|
||||
productionWorkOrderId: t.productionWorkOrderId,
|
||||
productionWorkOrderNo: t.productionWorkOrderNo,
|
||||
protocolStatus: t.protocolStatus,
|
||||
protocolStatusLabel: t.protocolStatusLabel
|
||||
protocolStatusLabel: t.protocolStatusLabel,
|
||||
paymentSummary: t.paymentSummary,
|
||||
feeSummary: t.feeSummary,
|
||||
remark: t.workOrderRemark || t.remark
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,11 @@ function formatTempCellProductField(row: QcWorkOrder, value?: string | null) {
|
||||
return String(value || '').trim() || '—'
|
||||
}
|
||||
|
||||
function isInboundUserInfoRow(row: QcWorkOrder) {
|
||||
const source = (row.receptionSource || '').trim()
|
||||
return source === 'cell_storage' || source === 'research_donation'
|
||||
}
|
||||
|
||||
function protocolStatusLabelBySource(status: string, source: string): string {
|
||||
return source === 'research_donation' ? donationStatusLabel(status) : protocolStatusLabel(status)
|
||||
}
|
||||
@@ -99,6 +104,8 @@ const commonTailFields: CardMetaField[] = [
|
||||
]
|
||||
|
||||
const inboundGroupedFields: CardMetaField[] = [
|
||||
{ label: '母亲姓名', getValue: (row) => row.motherName || '—', visible: isInboundUserInfoRow },
|
||||
{ label: '新生儿姓名', getValue: (row) => row.newbornName || '—', visible: isInboundUserInfoRow },
|
||||
{ label: '质检对象', getValue: formatQcTarget, colSpan: 2 },
|
||||
{
|
||||
label: '关联生产工单',
|
||||
@@ -159,6 +166,8 @@ const temporaryFields: CardMetaField[] = [
|
||||
|
||||
function buildCellStorageGroupContext(row: QcWorkOrder): string[] {
|
||||
const parts: string[] = []
|
||||
if (row.motherName) parts.push(`母亲 ${row.motherName}`)
|
||||
if (row.newbornName) parts.push(`新生儿 ${row.newbornName}`)
|
||||
if (row.sampleTypeName) parts.push(row.sampleTypeName)
|
||||
if (row.protocolNo) parts.push(`协议 ${row.protocolNo}`)
|
||||
if (row.protocolStatus) {
|
||||
@@ -171,6 +180,8 @@ function buildCellStorageGroupContext(row: QcWorkOrder): string[] {
|
||||
|
||||
function buildDonationGroupContext(row: QcWorkOrder): string[] {
|
||||
const parts: string[] = []
|
||||
if (row.motherName) parts.push(`母亲 ${row.motherName}`)
|
||||
if (row.newbornName) parts.push(`新生儿 ${row.newbornName}`)
|
||||
if (row.sampleTypeName) parts.push(row.sampleTypeName)
|
||||
if (row.protocolNo) parts.push(`捐赠 ${row.protocolNo}`)
|
||||
if (row.protocolStatus) {
|
||||
|
||||
@@ -160,6 +160,49 @@ export const workOrderStatus: StatusRegistry = {
|
||||
export const workOrderStatusLabel = createLabelGetter(workOrderStatus)
|
||||
export const workOrderStatusTagType = createTagTypeGetter(workOrderStatus)
|
||||
|
||||
export const QC_WORK_ORDER_STATUS_FILTER_OPTIONS = Object.entries(workOrderStatus).map(
|
||||
([value, meta]) => ({
|
||||
value,
|
||||
label: meta.label
|
||||
})
|
||||
)
|
||||
|
||||
export function joinWorkOrderStatusFilter(statuses: string[]): string | undefined {
|
||||
const list = statuses.map((s) => s.trim()).filter(Boolean)
|
||||
return list.length ? list.join(',') : undefined
|
||||
}
|
||||
|
||||
export function matchesWorkOrderStatusFilter(
|
||||
status: string,
|
||||
filter: string | string[] | undefined
|
||||
): boolean {
|
||||
if (!filter) return true
|
||||
const list = Array.isArray(filter)
|
||||
? filter
|
||||
: filter
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
if (!list.length) return true
|
||||
return list.includes(status)
|
||||
}
|
||||
|
||||
export const productionWorkOrderStatus: StatusRegistry = {
|
||||
dpz: { label: '待配置', tagType: 'info' },
|
||||
jxz: { label: '进行中', tagType: 'warning' },
|
||||
ywc: { label: '已完成', tagType: 'success' },
|
||||
yzf: { label: '已作废', tagType: 'danger' }
|
||||
}
|
||||
export const productionWorkOrderStatusLabel = createLabelGetter(productionWorkOrderStatus)
|
||||
export const productionWorkOrderStatusTagType = createTagTypeGetter(productionWorkOrderStatus)
|
||||
|
||||
export const PRODUCTION_WORK_ORDER_STATUS_FILTER_OPTIONS = Object.entries(
|
||||
productionWorkOrderStatus
|
||||
).map(([value, meta]) => ({
|
||||
value,
|
||||
label: meta.label
|
||||
}))
|
||||
|
||||
export const taskType: StatusRegistry = {
|
||||
sysz: { label: '实验室自检', tagType: 'success' },
|
||||
dsfjc: { label: '第三方检测', tagType: 'primary' }
|
||||
|
||||
Reference in new issue
Block a user