diff --git a/web-admin/src/views/quality-inspection/qc-work-order/index.vue b/web-admin/src/views/quality-inspection/qc-work-order/index.vue index de70e407..757541c0 100644 --- a/web-admin/src/views/quality-inspection/qc-work-order/index.vue +++ b/web-admin/src/views/quality-inspection/qc-work-order/index.vue @@ -4,7 +4,9 @@

质检工单

-

管理入库与临时质检工单;入库工单由样本接收后自动生成,临时工单可独立创建并配置检测项目。

+

管理入库、临时与出库质检工单;入库工单由样本接收后自动生成,临时工单可独立创建并配置检测项目,出库工单由出库申请触发生成。

@@ -12,9 +14,14 @@

工单列表

- + + @@ -24,15 +31,33 @@ - + + + + + + + + @@ -50,68 +75,232 @@
默认展示近 30 天内创建的工单。
- 重置 - 搜索 + 重置 + 搜索
-
+
- {{ activeTypeTab === 'temporary' ? '新增临时工单' : '新增入库质检工单' }} + {{ activeTypeTab === 'temporary' ? '新增临时工单' : '新增入库质检工单' }}
- - - - + + + + + + + + + + + - + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - - + + - + @@ -200,21 +453,34 @@ 实验室自检 - 第三方检测 + 第三方检测 - {{ item.itemName }}({{ item.itemCode }}) + {{ item.itemName }}({{ item.itemCode }})

暂无启用的实验室项目

- -
+ +
{{ group.companyName }}
- {{ item.itemName }}({{ item.itemCode }})¥{{ formatMoneyDisplay(item.feeAmount) }} + {{ item.itemName }}({{ item.itemCode }})¥{{ + formatMoneyDisplay(item.feeAmount) + }}
@@ -223,8 +489,16 @@ @@ -275,7 +549,9 @@ :close-on-click-modal="false" > -

临时工单需选择质检对象(细胞产品),创建后请配置检测项目。

+

临时工单需选择质检对象(细胞产品),创建后请配置检测项目。

@@ -300,7 +576,13 @@ - +
@@ -308,7 +590,11 @@ - + @@ -322,7 +608,13 @@ - +
@@ -351,7 +643,17 @@ import { formatMoneyDisplay } from '@/utils/money-format' import { ref, reactive, computed, onMounted } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' - import { Close, Check, Search, RefreshLeft, View, Setting, Document, Plus, EditPen } from '@element-plus/icons-vue' + import { + Close, + Check, + Search, + RefreshLeft, + View, + Setting, + Document, + Plus, + EditPen + } from '@element-plus/icons-vue' import { fetchQcLabTestItemsActive, type QcLabTestItem } from '@/api/qc-lab-test-item' import { fetchQcThirdPartyItemsActive, type QcThirdPartyItem } from '@/api/qc-third-party-item' import { @@ -381,16 +683,20 @@ donationStatusLabel, donationStatusTagType, qcBizTypeLabel, - qcBizTypeTagType, + qcBizTypeTagType } from '@/utils/status-tag-registry' import type { TagType } from '@/utils/status-tag-registry' /** 根据来源类型返回协议状态标签 */ function protocolStatusLabelBySource(status: string, source: string): string { - return source === 'research_donation' ? donationStatusLabel(status) : protocolStatusLabel(status) + return source === 'research_donation' + ? donationStatusLabel(status) + : protocolStatusLabel(status) } function protocolStatusTagTypeBySource(status: string, source: string): TagType { - return source === 'research_donation' ? donationStatusTagType(status) : protocolStatusTagType(status) + return source === 'research_donation' + ? donationStatusTagType(status) + : protocolStatusTagType(status) } /** 临时工单无关联协议/样本时,空字段展示为 — */ @@ -408,10 +714,11 @@ defineOptions({ name: 'QualityInspectionQcWorkOrder' }) - type WorkOrderTypeTab = 'inbound' | 'temporary' | 'all' + type WorkOrderTypeTab = 'inbound' | 'temporary' | 'outbound' | 'all' const activeTypeTab = ref('inbound') const showReceptionCols = computed(() => activeTypeTab.value !== 'temporary') const showTempProductCols = computed(() => activeTypeTab.value !== 'inbound') + const showOutboundCols = computed(() => activeTypeTab.value === 'outbound') const addMode = ref<'inbound' | 'temporary'>('inbound') const editingId = ref('') const editingWorkOrderNo = ref('') @@ -444,7 +751,9 @@ return code ? `${name}(${code})` : name } - function ensureCellProductOption(row?: Pick) { + function ensureCellProductOption( + row?: Pick + ) { if (!row?.cellProductId) return if (cellProductOptions.value.some((o) => o.id === row.cellProductId)) return cellProductOptions.value = [ @@ -493,7 +802,9 @@ const addRules = computed(() => { if (addMode.value === 'temporary') { return { - cellProductId: [{ required: true, message: '请选择质检对象(细胞产品)', trigger: 'change' }] + cellProductId: [ + { required: true, message: '请选择质检对象(细胞产品)', trigger: 'change' } + ] } } return { @@ -533,7 +844,8 @@ const remarkEditLoading = ref(false) const remarkEditRow = ref(null) const remarkEditTitle = computed( - () => `编辑备注${remarkEditRow.value?.workOrderNo ? ` — ${remarkEditRow.value.workOrderNo}` : ''}` + () => + `编辑备注${remarkEditRow.value?.workOrderNo ? ` — ${remarkEditRow.value.workOrderNo}` : ''}` ) const remarkEditInitialRemark = computed(() => remarkEditRow.value?.remark || '') @@ -602,7 +914,7 @@ return [formatLocalDate(start), formatLocalDate(end)] } - const searchForm = reactive({ keyword: '', workOrderStatus: '' }) + const searchForm = reactive({ keyword: '', workOrderStatus: '', bizType: '' }) const createTimeRange = ref<[string, string] | null>(getDefaultLast30DaysRange()) const tableData = ref([]) const loading = ref(false) @@ -615,7 +927,10 @@ const labOptions = ref([]) const thirdOptions = ref([]) const thirdOptionsGrouped = computed(() => { - const map = new Map() + const map = new Map< + string, + { companyId: string; companyName: string; items: QcThirdPartyItem[] } + >() for (const item of thirdOptions.value) { const cid = item.companyId if (!map.has(cid)) { @@ -719,7 +1034,10 @@ function formatConfiguredThirdItems(d: QcWorkOrderDetail) { return ( d.thirdPartyItems - ?.map((li) => `${li.companyName || ''} — ${li.itemName || ''}(${li.feeAmount != null ? '¥' + formatMoneyDisplay(li.feeAmount) : '—'})`) + ?.map( + (li) => + `${li.companyName || ''} — ${li.itemName || ''}(${li.feeAmount != null ? '¥' + formatMoneyDisplay(li.feeAmount) : '—'})` + ) .join('、') || '无' ) } @@ -733,8 +1051,7 @@ if (!filtered.length) return [] return filtered.map((task, index) => { - const title = - filtered.length > 1 ? `${sectionTitle}(${index + 1})` : sectionTitle + const title = filtered.length > 1 ? `${sectionTitle}(${index + 1})` : sectionTitle const fields: ViewField[] = [ { label: '检测项目', value: task.itemName || '—' }, { label: '项目编码', value: task.itemCode || '—' }, @@ -813,9 +1130,7 @@ return String(v) } - function formatCheckResults( - items?: Array<{ itemName?: string; checked?: boolean }> - ) { + function formatCheckResults(items?: Array<{ itemName?: string; checked?: boolean }>) { if (!items?.length) return '—' return items.map((x) => `${x.itemName || ''}${x.checked ? '(✓)' : '(×)'}`).join('、') } @@ -880,18 +1195,36 @@ { label: '协议号', value: d.protocolNo }, { label: '协议状态', - value: protocolStatusLabelBySource(String(d.protocolStatus || ''), d.receptionSource || 'cell_storage'), - tagType: protocolStatusTagTypeBySource(String(d.protocolStatus || ''), d.receptionSource || 'cell_storage') + value: protocolStatusLabelBySource( + String(d.protocolStatus || ''), + d.receptionSource || 'cell_storage' + ), + tagType: protocolStatusTagTypeBySource( + String(d.protocolStatus || ''), + d.receptionSource || 'cell_storage' + ) }, { label: '样本编码', value: d.sampleCode }, { label: '来源类型', - value: r.sourceTypeLabel || d.receptionSourceLabel || receptionSourceLabel(d.receptionSource || ''), + value: + r.sourceTypeLabel || + d.receptionSourceLabel || + receptionSourceLabel(d.receptionSource || ''), tagType: 'primary' as const }, { label: '样本类型', value: r.sampleTypeName || d.sampleTypeName || '—' } ]), - { label: '状态', value: workOrderStatusLabel(d.workOrderStatus), tagType: d.workOrderStatus === 'ywc' ? 'success' : d.workOrderStatus === 'ypz' ? 'warning' : 'info' }, + { + label: '状态', + value: workOrderStatusLabel(d.workOrderStatus), + tagType: + d.workOrderStatus === 'ywc' + ? 'success' + : d.workOrderStatus === 'ypz' + ? 'warning' + : 'info' + }, { label: '备注', value: d.remark || '—', span: true }, { label: '创建时间', value: d.createTime || '—' } ] @@ -914,8 +1247,15 @@ { label: '净重(g)', value: formatMetric(r.netWeightG) }, { label: '结论', - value: r.acceptConclusionLabel || acceptConclusionLabel(String(r.acceptConclusion || '')), - tagType: r.acceptConclusion === 'js' ? 'success' : r.acceptConclusion === 'jj' ? 'danger' : 'info' + value: + r.acceptConclusionLabel || + acceptConclusionLabel(String(r.acceptConclusion || '')), + tagType: + r.acceptConclusion === 'js' + ? 'success' + : r.acceptConclusion === 'jj' + ? 'danger' + : 'info' }, { label: '检查项', value: formatCheckResults(r.checkResults), span: true }, { label: '备注', value: r.remark || '—', span: true } @@ -925,7 +1265,14 @@ ...(viewTasks.value.some((t) => t.taskType === 'sysz') ? buildTaskTypeSections(viewTasks.value, 'sysz', '实验室项目') : d.hasLab - ? [buildConfiguredOnlySection('实验室项目', '已配置项目', formatConfiguredLabItems(d), '检测附件')] + ? [ + buildConfiguredOnlySection( + '实验室项目', + '已配置项目', + formatConfiguredLabItems(d), + '检测附件' + ) + ] : []), ...(viewTasks.value.some((t) => t.taskType === 'dsfjc') ? buildTaskTypeSections(viewTasks.value, 'dsfjc', '第三方项目') @@ -958,6 +1305,7 @@ keyword: searchForm.keyword || undefined, workOrderStatus: searchForm.workOrderStatus || undefined, workOrderType: activeTypeTab.value === 'all' ? undefined : activeTypeTab.value, + bizType: searchForm.bizType || undefined, createTimeStart: createTimeRange.value?.[0] || undefined, createTimeEnd: createTimeRange.value?.[1] || undefined }) @@ -975,6 +1323,7 @@ function handleReset() { searchForm.keyword = '' searchForm.workOrderStatus = '' + searchForm.bizType = '' createTimeRange.value = getDefaultLast30DaysRange() pagination.page = 1 loadData() @@ -1058,6 +1407,14 @@