|
|
@@ -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;
|