Files
zkxw/web-admin/src/views/cell-storage/protocol-audit/SampleQcInfoTab.vue
T
2026-06-02 09:39:50 +08:00

365 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 样本信息管理 · 样本质检信息 Tab -->
<template>
<div class="sample-qc-info-tab">
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="协议号/姓名/样本编码" clearable />
</el-form-item>
<el-form-item label="质检状态">
<el-select
v-model="searchForm.qcStatus"
placeholder="全部"
clearable
style="width: 100%"
>
<el-option label="待质检" value="dzj" />
<el-option label="质检中" value="zjz" />
<el-option label="通过" value="tg" />
<el-option label="不通过" value="btg" />
<el-option label="已作废" value="yzf" />
</el-select>
</el-form-item>
<el-form-item label="通知状态">
<el-select
v-model="searchForm.smsNotified"
placeholder="全部"
clearable
style="width: 100%"
>
<el-option label="已通知" value="1" />
<el-option label="未通知" value="0" />
</el-select>
</el-form-item>
<el-form-item label="样本时间">
<el-date-picker
v-model="sampleTimeRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</div>
<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>
</div>
</div>
</el-form>
</div>
<div class="list-body list-wrap">
<el-table
class="list-table"
:data="tableData"
v-loading="loading"
stripe
row-key="id"
@row-dblclick="handleView"
>
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1"
/>
<el-table-column
prop="protocolNo"
label="协议号"
min-width="140"
align="center"
header-align="center"
/>
<el-table-column
prop="protocolStatus"
label="协议状态"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag
:type="protocolStatusTagType(String(row.protocolStatus || ''))"
size="small"
>{{ protocolStatusLabel(String(row.protocolStatus || '')) }}</el-tag
>
</template>
</el-table-column>
<el-table-column
prop="motherName"
label="母亲姓名"
min-width="100"
align="center"
header-align="center"
/>
<el-table-column
prop="newbornName"
label="新生儿姓名"
min-width="110"
align="center"
header-align="center"
show-overflow-tooltip
/>
<el-table-column
prop="sampleCode"
label="样本编码"
min-width="130"
align="center"
header-align="center"
/>
<el-table-column
prop="sampleTypeName"
label="样本类型"
min-width="110"
align="center"
header-align="center"
/>
<el-table-column
prop="qcCompleteTime"
label="质检完成时间"
min-width="170"
align="center"
header-align="center"
/>
<el-table-column
prop="qcStatusLabel"
label="质检状态"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag :type="qcStatusTagType(String(row.qcStatus || ''))" size="small">
{{ row.qcStatusLabel || '待质检' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="smsNotified"
label="通知状态"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.smsNotified == 1 ? 'success' : 'info'" size="small">
{{ row.smsNotified == 1 ? '已通知' : '未通知' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作"
min-width="360"
fixed="right"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-button class="btn-op-item primary" @click="handleView(row)">
<el-icon><View /></el-icon>查看
</el-button>
<el-button class="btn-op-item" @click="handleExtractSms(row)">
<el-icon><ChatDotRound /></el-icon>提取短信
</el-button>
<el-button class="btn-op-item" @click="handleToggleNotified(row)">
<el-icon><Bell /></el-icon>{{ row.smsNotified == 1 ? '标记未通知' : '标记已通知' }}
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination list-pager-bar">
<el-pagination
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
/>
</div>
</div>
<ProtocolViewDialog
v-model="viewVisible"
:protocol-id="viewProtocolId"
:list-row="viewRow"
show-audit-history
show-storage-cert
show-fullmoon-insurance
/>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Search, RefreshLeft, View, ChatDotRound, Bell } from '@element-plus/icons-vue'
import { useClipboard } from '@vueuse/core'
import ProtocolViewDialog from '../protocol/ProtocolViewDialog.vue'
import {
fetchQcSmsList,
fetchToggleQcSmsNotified,
fetchGenerateQcSms
} from '@/api/qc-sms-notification'
import { type CollectionProtocolRow } from '@/api/collection-protocol'
import {
protocolStatusLabel,
protocolStatusTagType
} from '@/utils/status-tag-registry'
defineOptions({ name: 'CellStorageSampleQcInfoTab' })
function qcStatusTagType(status: string) {
const map: Record<string, 'success' | 'warning' | 'danger' | 'info'> = {
dzj: 'info',
zjz: 'warning',
tg: 'success',
btg: 'danger',
yzf: 'danger'
}
return map[status] || 'info'
}
function isQcCompleted(row: Record<string, unknown>) {
return row.qcStatus === 'tg' || row.qcStatus === 'btg'
}
function formatLocalDate(d: Date): string {
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${y}-${m}-${day}`
}
function getDefaultSampleTimeRange(): [string, string] {
const end = new Date()
const start = new Date()
start.setDate(start.getDate() - 30)
return [formatLocalDate(start), formatLocalDate(end)]
}
const searchForm = reactive({ keyword: '', qcStatus: '', smsNotified: '' })
const sampleTimeRange = ref<[string, string] | null>(getDefaultSampleTimeRange())
const tableData = ref<any[]>([])
const loading = ref(false)
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
const viewVisible = ref(false)
const viewProtocolId = ref<string | null>(null)
const viewRow = ref<CollectionProtocolRow | null>(null)
const { copy } = useClipboard()
async function loadData() {
loading.value = true
try {
const result = await fetchQcSmsList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined,
qcStatus: searchForm.qcStatus || undefined,
smsNotified: searchForm.smsNotified || undefined,
qcCompleteTimeStart: sampleTimeRange.value?.[0] || undefined,
qcCompleteTimeEnd: sampleTimeRange.value?.[1] || undefined
})
const res = (result as any) || {}
tableData.value = res.list || []
pagination.total = res.total || 0
} finally {
loading.value = false
}
}
function handleSearch() {
pagination.page = 1
loadData()
}
function handleReset() {
Object.assign(searchForm, { keyword: '', qcStatus: '', smsNotified: '' })
sampleTimeRange.value = getDefaultSampleTimeRange()
handleSearch()
}
function handleView(row: Record<string, unknown>) {
const pid = String(row.collectionProtocolId || row.protocolId || '')
if (!pid) {
ElMessage.warning('缺少关联协议单,无法查看')
return
}
viewProtocolId.value = pid
viewRow.value = {
id: pid,
protocolNo: row.protocolNo,
motherName: row.motherName,
newbornName: row.newbornName
}
viewVisible.value = true
}
async function handleExtractSms(row: any) {
try {
const result = await fetchGenerateQcSms(row.id, {
qcWorkOrderId: row.qcWorkOrderId,
protocolSampleId: row.protocolSampleId
})
const content = (result as any)?.smsContent || ''
if (!content || content.startsWith('暂无短信内容')) {
ElMessage.warning(content || '短信内容为空')
return
}
await copy(content)
ElMessage.success('短信内容已复制到剪贴板')
} catch {
ElMessage.error('提取短信失败')
}
}
async function handleToggleNotified(row: any) {
if (!isQcCompleted(row)) {
ElMessage.warning('质检尚未完成,暂不可标记通知状态')
return
}
const newVal = row.smsNotified == 1 ? 0 : 1
try {
await fetchToggleQcSmsNotified(row.id, {
smsNotified: newVal,
qcWorkOrderId: row.qcWorkOrderId,
protocolSampleId: row.protocolSampleId
})
ElMessage.success(newVal === 1 ? '已标记为已通知' : '已标记为未通知')
loadData()
} catch {
ElMessage.error('操作失败')
}
}
watchDebounced(
[searchForm, sampleTimeRange],
() => {
pagination.page = 1
loadData()
},
{ debounce: 300, deep: true }
)
onMounted(() => {
loadData()
})
</script>