feat(qc): add outbound QC work order tab with list, filters and action buttons

This commit is contained in:
zlwu committed 2026-05-26 18:43:49 +08:00
1 parent b32196690b
commit b20c9f7bd7
1 file changed
+434 -77
@@ -4,7 +4,9 @@
<div class="page-container">
<header class="page-head">
<h1>质检工单</h1>
<p class="page-desc">管理入库与临时质检工单;入库工单由样本接收后自动生成,临时工单可独立创建并配置检测项目。</p>
<p class="page-desc"
>管理入库、临时与出库质检工单;入库工单由样本接收后自动生成,临时工单可独立创建并配置检测项目,出库工单由出库申请触发生成。</p
>
</header>
<section class="panel">
@@ -12,9 +14,14 @@
<h2>工单列表</h2>
</div>
<el-tabs v-model="activeTypeTab" class="work-order-type-tabs" @tab-change="handleTypeTabChange">
<el-tabs
v-model="activeTypeTab"
class="work-order-type-tabs"
@tab-change="handleTypeTabChange"
>
<el-tab-pane label="入库质检工单" name="inbound" />
<el-tab-pane label="临时质检工单" name="temporary" />
<el-tab-pane label="出库质检工单" name="outbound" />
<el-tab-pane label="全部质检工单" name="all" />
</el-tabs>
@@ -24,15 +31,33 @@
<el-form-item label="关键词">
<el-input
v-model="searchForm.keyword"
:placeholder="activeTypeTab === 'temporary' ? '工单号' : '工单号/协议号/样本编码'"
:placeholder="
activeTypeTab === 'temporary'
? '工单号'
: activeTypeTab === 'outbound'
? '工单号/申请单号/申请单名称'
: '工单号/协议号/样本编码'
"
clearable
/>
</el-form-item>
<el-form-item label="工单状态">
<el-select v-model="searchForm.workOrderStatus" placeholder="全部" clearable style="width: 100%">
<el-select
v-model="searchForm.workOrderStatus"
placeholder="全部"
clearable
style="width: 100%"
>
<el-option label="待配置" value="dpz" />
<el-option label="已配置" value="ypz" />
<el-option label="已完成" value="ywc" />
<el-option label="已作废" value="yzf" />
</el-select>
</el-form-item>
<el-form-item v-if="activeTypeTab === 'outbound'" label="业务类型">
<el-select v-model="searchForm.bizType" placeholder="全部" clearable style="width: 100%">
<el-option label="中试服务" value="pilot_service" />
<el-option label="临床应用" value="clinical_application" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
@@ -50,68 +75,232 @@
<div class="filter-actions-row">
<span class="filter-hint">默认展示近 30 天内创建的工单。</span>
<div class="filter-actions-end">
<el-button class="btn-ghost" @click="handleReset"><el-icon><RefreshLeft /></el-icon>重置</el-button>
<el-button class="btn-main" type="primary" @click="handleSearch"><el-icon><Search /></el-icon>搜索</el-button>
<el-button class="btn-ghost" @click="handleReset"
><el-icon><RefreshLeft /></el-icon>重置</el-button
>
<el-button class="btn-main" type="primary" @click="handleSearch"
><el-icon><Search /></el-icon>搜索</el-button
>
</div>
</div>
</el-form>
</div>
<div v-if="activeTypeTab !== 'all'" class="list-toolbar">
<div v-if="activeTypeTab !== 'all' && activeTypeTab !== 'outbound'" class="list-toolbar">
<div class="list-toolbar-row">
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>{{ activeTypeTab === 'temporary' ? '新增临时工单' : '新增入库质检工单' }}
<el-icon><Plus /></el-icon
>{{ activeTypeTab === 'temporary' ? '新增临时工单' : '新增入库质检工单' }}
</el-button>
</div>
</div>
<div class="list-body list-wrap">
<el-table class="list-table" :data="tableData" v-loading="loading" stripe @row-dblclick="handleView">
<el-table-column type="index" label="序号" min-width="68" align="center" :index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1" />
<el-table-column prop="workOrderNo" label="工单号" min-width="150" align="center" />
<el-table-column v-if="activeTypeTab === 'all'" prop="workOrderType" label="工单类型" min-width="100" align="center">
<el-table
:class="['list-table', { 'list-table--no-action-col list-table--all-tab': activeTypeTab === 'all' }]"
:data="tableData"
v-loading="loading"
stripe
@row-dblclick="handleView"
>
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
:index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1"
/>
<el-table-column
prop="workOrderNo"
label="工单号"
min-width="150"
align="center"
show-overflow-tooltip
/>
<el-table-column
v-if="showOutboundCols"
prop="bizTypeLabel"
label="业务类型"
min-width="100"
align="center"
>
<template #default="{ row }">
<el-tag :type="row.workOrderType === 'temporary' ? 'warning' : 'success'" size="small">
<el-tag
:type="
row.bizType === 'pilot_service'
? 'primary'
: row.bizType === 'clinical_application'
? 'warning'
: 'info'
"
size="small"
>
{{ row.bizTypeLabel || '—' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
v-if="showOutboundCols"
prop="applicationNo"
label="申请单号"
min-width="150"
align="center"
show-overflow-tooltip
/>
<el-table-column
v-if="showOutboundCols"
prop="applicationName"
label="申请单名称"
min-width="150"
align="center"
show-overflow-tooltip
/>
<el-table-column
v-if="showOutboundCols"
prop="channelName"
label="渠道名称"
min-width="120"
align="center"
show-overflow-tooltip
/>
<el-table-column
v-if="showOutboundCols"
prop="infusionTime"
label="输注时间"
min-width="170"
align="center"
/>
<el-table-column
v-if="activeTypeTab === 'all'"
prop="workOrderType"
label="工单类型"
min-width="100"
align="center"
>
<template #default="{ row }">
<el-tag
:type="row.workOrderType === 'temporary' ? 'warning' : 'success'"
size="small"
>
{{ row.workOrderType === 'temporary' ? '临时' : '入库' }}
</el-tag>
</template>
</el-table-column>
<el-table-column v-if="activeTypeTab !== 'inbound'" prop="qcBizTypeLabel" label="业务类型" min-width="100" align="center">
<el-table-column
v-if="activeTypeTab !== 'inbound'"
prop="qcBizTypeLabel"
label="业务类型"
min-width="100"
align="center"
>
<template #default="{ row }">
<el-tag
:type="qcBizTypeTagType(row.workOrderType === 'temporary' ? 'temporary' : (row.qcBizType || 'inbound'))"
:type="
qcBizTypeTagType(
row.workOrderType === 'temporary' ? 'temporary' : row.qcBizType || 'inbound'
)
"
size="small"
>
{{ row.qcBizTypeLabel || qcBizTypeLabel(row.workOrderType === 'temporary' ? 'temporary' : (row.qcBizType || 'inbound')) }}
{{
row.qcBizTypeLabel ||
qcBizTypeLabel(
row.workOrderType === 'temporary' ? 'temporary' : row.qcBizType || 'inbound'
)
}}
</el-tag>
</template>
</el-table-column>
<el-table-column v-if="showTempProductCols" prop="cellProductCode" label="产品编码" min-width="130" align="center">
<template #default="{ row }">{{ formatTempCellProductField(row, row.cellProductCode) }}</template>
<el-table-column
v-if="showTempProductCols"
prop="cellProductCode"
label="产品编码"
min-width="130"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">{{
formatTempCellProductField(row, row.cellProductCode)
}}</template>
</el-table-column>
<el-table-column v-if="showTempProductCols" prop="cellProductName" label="产品名称" min-width="140" align="center">
<template #default="{ row }">{{ formatTempCellProductField(row, row.cellProductName) }}</template>
<el-table-column
v-if="showTempProductCols"
prop="cellProductName"
label="产品名称"
min-width="140"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">{{
formatTempCellProductField(row, row.cellProductName)
}}</template>
</el-table-column>
<el-table-column v-if="showTempProductCols" prop="cellProductTypeName" label="产品类型" min-width="110" align="center">
<template #default="{ row }">{{ formatTempCellProductField(row, row.cellProductTypeName) }}</template>
<el-table-column
v-if="showTempProductCols"
prop="cellProductTypeName"
label="产品类型"
min-width="110"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">{{
formatTempCellProductField(row, row.cellProductTypeName)
}}</template>
</el-table-column>
<el-table-column v-if="showReceptionCols" prop="protocolNo" label="协议号" min-width="140" align="center">
<el-table-column
v-if="showReceptionCols"
prop="protocolNo"
label="协议号"
min-width="140"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">{{ formatTempEmptyField(row, row.protocolNo) }}</template>
</el-table-column>
<el-table-column v-if="showReceptionCols" prop="protocolStatus" label="协议状态" min-width="100" align="center">
<el-table-column
v-if="showReceptionCols"
prop="protocolStatus"
label="协议状态"
min-width="100"
align="center"
>
<template #default="{ row }">
<span v-if="shouldShowTempEmptyField(row, row.protocolStatus)">—</span>
<el-tag
v-else
:type="protocolStatusTagTypeBySource(String(row.protocolStatus || ''), row.receptionSource || 'cell_storage')"
:type="
protocolStatusTagTypeBySource(
String(row.protocolStatus || ''),
row.receptionSource || 'cell_storage'
)
"
size="small"
>{{ protocolStatusLabelBySource(String(row.protocolStatus || ''), row.receptionSource || 'cell_storage') }}</el-tag>
>{{
protocolStatusLabelBySource(
String(row.protocolStatus || ''),
row.receptionSource || 'cell_storage'
)
}}</el-tag
>
</template>
</el-table-column>
<el-table-column v-if="showReceptionCols" prop="sampleCode" label="样本编码" min-width="120" align="center">
<el-table-column
v-if="showReceptionCols"
prop="sampleCode"
label="样本编码"
min-width="120"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">{{ formatTempEmptyField(row, row.sampleCode) }}</template>
</el-table-column>
<el-table-column v-if="showReceptionCols" prop="receptionSourceLabel" label="来源类型" min-width="100" align="center">
<el-table-column
v-if="showReceptionCols"
prop="receptionSourceLabel"
label="来源类型"
min-width="100"
align="center"
>
<template #default="{ row }">
<span v-if="shouldShowTempEmptyField(row, row.receptionSource)">—</span>
<el-tag
@@ -123,43 +312,101 @@
</el-tag>
</template>
</el-table-column>
<el-table-column v-if="showReceptionCols" prop="sampleTypeName" label="样本类型" min-width="110" align="center">
<el-table-column
v-if="showReceptionCols"
prop="sampleTypeName"
label="样本类型"
min-width="110"
align="center"
>
<template #default="{ row }">{{ row.sampleTypeName || '—' }}</template>
</el-table-column>
<el-table-column v-if="showReceptionCols" prop="receptionTime" label="接收时间" min-width="170" align="center">
<el-table-column
v-if="showReceptionCols"
prop="receptionTime"
label="接收时间"
min-width="170"
align="center"
>
<template #default="{ row }">{{ row.receptionTime || '—' }}</template>
</el-table-column>
<el-table-column v-if="showReceptionCols" prop="handlerName" label="接收经手人" min-width="110" align="center">
<el-table-column
v-if="showReceptionCols"
prop="handlerName"
label="接收经手人"
min-width="110"
align="center"
>
<template #default="{ row }">{{ row.handlerName || '—' }}</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="140" align="center">
<el-table-column
prop="remark"
label="备注"
min-width="140"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">{{ row.remark || '—' }}</template>
</el-table-column>
<el-table-column prop="workOrderStatus" label="状态" min-width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.workOrderStatus === 'ywc' ? 'success' : row.workOrderStatus === 'ypz' ? 'warning' : 'info'" size="small">
<el-tag
:type="
row.workOrderStatus === 'ywc'
? 'success'
: row.workOrderStatus === 'ypz'
? 'warning'
: row.workOrderStatus === 'yzf'
? 'danger'
: 'info'
"
size="small"
>
{{ workOrderStatusLabel(row.workOrderStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="检测方式" min-width="160" align="center">
<template #default="{ row }">
<span>{{ formatDetectMethods(row) }}</span>
</template>
<el-table-column label="检测方式" min-width="160" align="center" show-overflow-tooltip>
<template #default="{ row }">{{ formatDetectMethods(row) }}</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="170" align="center" />
<el-table-column label="操作" min-width="380" fixed="right" align="center">
<el-table-column
v-if="activeTypeTab !== 'all'"
label="操作"
:min-width="activeTypeTab === 'outbound' ? '260' : '380'"
fixed="right"
align="center"
>
<template #default="{ row }">
<el-button class="btn-op-item" @click="handleView(row)"><el-icon><View /></el-icon>查看</el-button>
<el-button class="btn-op-item" @click="handleView(row)"
><el-icon><View /></el-icon>查看</el-button
>
<el-button
v-if="activeTypeTab === 'temporary' || (activeTypeTab === 'all' && row.workOrderType === 'temporary')"
v-if="
activeTypeTab === 'temporary' ||
(activeTypeTab === 'all' && row.workOrderType === 'temporary')
"
class="btn-op-item"
@click="handleEdit(row)"
>
<el-icon><EditPen /></el-icon>编辑
</el-button>
<el-button
v-if="activeTypeTab === 'inbound' || (activeTypeTab === 'all' && row.workOrderType !== 'temporary')"
v-if="
activeTypeTab === 'inbound' ||
(activeTypeTab === 'all' && row.workOrderType !== 'temporary')
"
class="btn-op-item"
@click="handleEditRemark(row)"
>
<el-icon><EditPen /></el-icon>编辑
</el-button>
<el-button
v-if="
activeTypeTab === 'outbound' &&
row.workOrderStatus !== 'ywc' &&
row.workOrderStatus !== 'yzf'
"
class="btn-op-item"
@click="handleEditRemark(row)"
>
@@ -170,9 +417,15 @@
class="btn-op-item primary"
@click="handleConfigure(row)"
>
<el-icon><Setting /></el-icon>{{ row.workOrderStatus === 'ypz' ? '调整检测' : '配置检测' }}
<el-icon><Setting /></el-icon
>{{ row.workOrderStatus === 'ypz' ? '调整检测' : '配置检测' }}
</el-button>
<el-button v-if="row.workOrderStatus !== 'ywc'" class="btn-op-item" @click="handleDelete(row)">删除</el-button>
<el-button
v-if="activeTypeTab !== 'outbound' && activeTypeTab !== 'all' && row.workOrderStatus !== 'ywc'"
class="btn-op-item"
@click="handleDelete(row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
@@ -200,21 +453,34 @@
<el-form label-position="top" class="biz-dialog-form">
<el-form-item label="检测方式">
<el-checkbox v-model="configureForm.hasLab">实验室自检</el-checkbox>
<el-checkbox v-model="configureForm.hasThirdParty" style="margin-left: 16px">第三方检测</el-checkbox>
<el-checkbox v-model="configureForm.hasThirdParty" style="margin-left: 16px"
>第三方检测</el-checkbox
>
</el-form-item>
<el-form-item v-if="configureForm.hasLab" label="实验室自检项目">
<el-checkbox-group v-model="configureForm.labItemIds" class="qc-config-check-group">
<el-checkbox v-for="item in labOptions" :key="item.id" :value="item.id">{{ item.itemName }}({{ item.itemCode }})</el-checkbox>
<el-checkbox v-for="item in labOptions" :key="item.id" :value="item.id"
>{{ item.itemName }}({{ item.itemCode }})</el-checkbox
>
</el-checkbox-group>
<p v-if="!labOptions.length" class="hint-muted">暂无启用的实验室项目</p>
</el-form-item>
<el-form-item v-if="configureForm.hasThirdParty" label="第三方检测项目">
<el-checkbox-group v-model="configureForm.thirdPartyItemIds" class="qc-config-check-group">
<div v-for="group in thirdOptionsGrouped" :key="group.companyId" class="qc-company-group">
<el-checkbox-group
v-model="configureForm.thirdPartyItemIds"
class="qc-config-check-group"
>
<div
v-for="group in thirdOptionsGrouped"
:key="group.companyId"
class="qc-company-group"
>
<div class="qc-company-group-title">{{ group.companyName }}</div>
<div class="qc-company-group-items">
<el-checkbox v-for="item in group.items" :key="item.id" :value="item.id">
{{ item.itemName }}({{ item.itemCode }})¥{{ formatMoneyDisplay(item.feeAmount) }}
{{ item.itemName }}({{ item.itemCode }})¥{{
formatMoneyDisplay(item.feeAmount)
}}
</el-checkbox>
</div>
</div>
@@ -223,8 +489,16 @@
</el-form-item>
</el-form>
<template #footer>
<el-button class="btn-ghost" @click="configureVisible = false"><el-icon><Close /></el-icon>取消</el-button>
<el-button class="btn-main" type="primary" :loading="configureLoading" @click="submitConfigure"><el-icon><Check /></el-icon>确认</el-button>
<el-button class="btn-ghost" @click="configureVisible = false"
><el-icon><Close /></el-icon>取消</el-button
>
<el-button
class="btn-main"
type="primary"
:loading="configureLoading"
@click="submitConfigure"
><el-icon><Check /></el-icon>确认</el-button
>
</template>
</el-dialog>
@@ -275,7 +549,9 @@
:close-on-click-modal="false"
>
<el-form ref="addFormRef" :model="addForm" :rules="addRules" label-position="top">
<p v-if="addMode === 'temporary' && !editingId" class="hint-muted">临时工单需选择质检对象(细胞产品),创建后请配置检测项目。</p>
<p v-if="addMode === 'temporary' && !editingId" class="hint-muted"
>临时工单需选择质检对象(细胞产品),创建后请配置检测项目。</p
>
<div v-if="addMode === 'temporary'" class="form-grid-2">
<el-form-item v-if="editingId" label="工单号" class="form-col-span-2">
<el-input :model-value="editingWorkOrderNo" disabled />
@@ -300,7 +576,13 @@
</el-select>
</el-form-item>
<el-form-item label="备注" class="form-col-span-2">
<el-input v-model="addForm.remark" type="textarea" :rows="3" placeholder="选填" maxlength="512" />
<el-input
v-model="addForm.remark"
type="textarea"
:rows="3"
placeholder="选填"
maxlength="512"
/>
</el-form-item>
</div>
<div v-else class="form-grid-2">
@@ -308,7 +590,11 @@
<el-input v-model="addForm.protocolNo" placeholder="请输入协议号(可选)" clearable />
</el-form-item>
<el-form-item label="样本编码">
<el-input v-model="addForm.sampleCode" placeholder="请输入样本编码(可选)" clearable />
<el-input
v-model="addForm.sampleCode"
placeholder="请输入样本编码(可选)"
clearable
/>
</el-form-item>
<el-form-item label="来源类型" prop="receptionSource">
<el-select v-model="addForm.receptionSource" style="width: 100%">
@@ -322,7 +608,13 @@
</el-select>
</el-form-item>
<el-form-item label="备注" class="form-col-span-2">
<el-input v-model="addForm.remark" type="textarea" :rows="3" placeholder="选填" maxlength="512" />
<el-input
v-model="addForm.remark"
type="textarea"
:rows="3"
placeholder="选填"
maxlength="512"
/>
</el-form-item>
</div>
</el-form>
@@ -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<WorkOrderTypeTab>('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<QcWorkOrder, 'cellProductId' | 'cellProductCode' | 'cellProductName'>) {
function ensureCellProductOption(
row?: Pick<QcWorkOrder, 'cellProductId' | 'cellProductCode' | 'cellProductName'>
) {
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<QcWorkOrder | null>(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<QcWorkOrder[]>([])
const loading = ref(false)
@@ -615,7 +927,10 @@
const labOptions = ref<QcLabTestItem[]>([])
const thirdOptions = ref<QcThirdPartyItem[]>([])
const thirdOptionsGrouped = computed(() => {
const map = new Map<string, { companyId: string; companyName: string; items: QcThirdPartyItem[] }>()
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 @@
</script>
<style scoped lang="scss">
/* 全部工单 Tab 无操作列,略增高行高便于阅读多列信息 */
:deep(.list-table.list-table--all-tab) {
td.el-table__cell > .cell {
min-height: 36px;
line-height: 36px;
}
}
.work-order-type-tabs {
padding: 0 16px;
margin-bottom: 4px;