|
|
@@ -0,0 +1,576 @@
|
|
|
+<template>
|
|
|
+ <div class="unified-sample-info-page">
|
|
|
+ <div v-if="visibleTabs.length > 1" class="panel-pill-tabs panel-pill-tabs--lg">
|
|
|
+ <div class="panel-pill-tabs__track" role="tablist" aria-label="来源">
|
|
|
+ <button
|
|
|
+ v-for="item in visibleTabs"
|
|
|
+ :key="item.key"
|
|
|
+ type="button"
|
|
|
+ role="tab"
|
|
|
+ class="panel-pill-tabs__btn"
|
|
|
+ :class="{ 'is-active': activeSource === item.key }"
|
|
|
+ :aria-selected="activeSource === item.key"
|
|
|
+ @click="switchSource(item.key)"
|
|
|
+ >
|
|
|
+ {{ item.title }}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="filter-block">
|
|
|
+ <el-form
|
|
|
+ :model="searchForm"
|
|
|
+ class="filter-form sample-info-filter-form"
|
|
|
+ label-width="auto"
|
|
|
+ @submit.prevent
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="filter-grid sample-info-filter-grid"
|
|
|
+ :class="{ 'is-cell-storage': isCellStorage }"
|
|
|
+ >
|
|
|
+ <el-form-item label="关键词">
|
|
|
+ <el-input v-model="searchForm.keyword" placeholder="姓名/样本编码/入库编码" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="采集">
|
|
|
+ <el-select v-model="searchForm.collectionFlag" placeholder="全部" clearable style="width: 100%">
|
|
|
+ <el-option label="未采集" value="uncollected" />
|
|
|
+ <el-option label="已采集" value="collected" />
|
|
|
+ <el-option label="已作废" value="voided" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="接收">
|
|
|
+ <el-select v-model="searchForm.receptionFlag" placeholder="全部" clearable style="width: 100%">
|
|
|
+ <el-option label="未接收" value="none" />
|
|
|
+ <el-option label="已接收" value="js" />
|
|
|
+ <el-option label="拒收" value="bj" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="质检">
|
|
|
+ <el-select v-model="searchForm.qcFlag" placeholder="全部" clearable style="width: 100%">
|
|
|
+ <el-option label="未完成" value="incomplete" />
|
|
|
+ <el-option label="已完成" value="complete" />
|
|
|
+ <el-option label="已作废" value="voided" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="入库">
|
|
|
+ <el-select v-model="searchForm.storageFlag" placeholder="全部" clearable style="width: 100%">
|
|
|
+ <el-option label="未入库" value="none" />
|
|
|
+ <el-option label="已入库" value="yrk" />
|
|
|
+ <el-option label="拒绝入库" value="rejected" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCellStorage" label="储存证">
|
|
|
+ <el-select
|
|
|
+ v-model="searchForm.storageCertHandled"
|
|
|
+ placeholder="全部"
|
|
|
+ clearable
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option label="已办理" value="1" />
|
|
|
+ <el-option label="未办理" value="0" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCellStorage" label="保险">
|
|
|
+ <el-select
|
|
|
+ v-model="searchForm.insuranceHandled"
|
|
|
+ placeholder="全部"
|
|
|
+ clearable
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option label="已办理" value="1" />
|
|
|
+ <el-option label="未办理" value="0" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <div class="filter-actions-row">
|
|
|
+ <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-toolbar">
|
|
|
+ <div class="list-toolbar-row">
|
|
|
+ <div class="list-toolbar-selected">
|
|
|
+ 已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
|
|
|
+ </div>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ class="btn-main"
|
|
|
+ :disabled="!selectedRows.length"
|
|
|
+ @click="openQuickRegister('storage')"
|
|
|
+ >
|
|
|
+ <el-icon><Box /></el-icon>
|
|
|
+ 登记入库
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="isCellStorage"
|
|
|
+ class="btn-ghost"
|
|
|
+ :disabled="!selectedRows.length"
|
|
|
+ @click="openQuickRegister('storage_cert')"
|
|
|
+ >
|
|
|
+ <el-icon><Ticket /></el-icon>
|
|
|
+ 登记储存证
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="isCellStorage"
|
|
|
+ class="btn-ghost"
|
|
|
+ :disabled="!selectedRows.length"
|
|
|
+ @click="openQuickRegister('insurance')"
|
|
|
+ >
|
|
|
+ <el-icon><Collection /></el-icon>
|
|
|
+ 登记保险
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="list-body list-wrap">
|
|
|
+ <el-table
|
|
|
+ ref="tableRef"
|
|
|
+ class="list-table"
|
|
|
+ :data="tableData"
|
|
|
+ v-loading="loading"
|
|
|
+ stripe
|
|
|
+ row-key="rowKey"
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ @row-dblclick="handleView"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" min-width="55" align="center" header-align="center" />
|
|
|
+ <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 label="协议状态" min-width="100" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="statusTagType(row)" size="small">{{ statusLabel(row) }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="expectedDeliveryDate" label="预产期" min-width="120" align="center" header-align="center" />
|
|
|
+ <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="160" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <SampleCodeWithSpecialDisplay
|
|
|
+ :sample-code="row.sampleCode"
|
|
|
+ :is-special="!!row.isSpecial"
|
|
|
+ :special-remark="row.specialRemark"
|
|
|
+ monospace
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="labStorageCode" label="实验室入库编码" min-width="150" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">{{ row.labStorageCode || '—' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="sampleTypeName" label="样本类型" min-width="110" align="center" header-align="center" />
|
|
|
+ <el-table-column label="采集" min-width="90" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="sampleCollectionFlagTagType(String(row.collectionFlag || ''))" size="small">
|
|
|
+ {{ row.collectionFlagLabel || sampleCollectionFlagLabel(String(row.collectionFlag || '')) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="接收" min-width="90" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="sampleReceptionFlagTagType(String(row.receptionFlag || ''))" size="small">
|
|
|
+ {{ row.receptionFlagLabel || sampleReceptionFlagLabel(String(row.receptionFlag || '')) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="质检" min-width="90" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="sampleQcFlagTagType(String(row.qcFlag || ''))" size="small">
|
|
|
+ {{ row.qcFlagLabel || sampleQcFlagLabel(String(row.qcFlag || '')) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="入库" min-width="100" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="sampleStorageFlagTagType(String(row.storageFlag || ''))" size="small">
|
|
|
+ {{ row.storageFlagLabel || sampleStorageFlagLabel(String(row.storageFlag || '')) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ v-if="isCellStorage"
|
|
|
+ label="储存证"
|
|
|
+ min-width="90"
|
|
|
+ align="center"
|
|
|
+ header-align="center"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="handleDoneFlagTagType(row.storageCertHandled ? '1' : '0')" size="small">
|
|
|
+ {{ handleDoneFlagLabel(row.storageCertHandled ? '1' : '0') }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ v-if="isCellStorage"
|
|
|
+ label="保险"
|
|
|
+ min-width="90"
|
|
|
+ align="center"
|
|
|
+ header-align="center"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="handleDoneFlagTagType(row.insuranceHandled ? '1' : '0')" size="small">
|
|
|
+ {{ handleDoneFlagLabel(row.insuranceHandled ? '1' : '0') }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" min-width="280" 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
|
|
|
+ v-if="isCellStorage && supportsStorageCertQc(row.sampleTypeName)"
|
|
|
+ class="btn-op-item"
|
|
|
+ @click="openStorageCertQcView(row)"
|
|
|
+ >
|
|
|
+ <el-icon><Document /></el-icon>查看储存证的质检信息
|
|
|
+ </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-if="isCellStorage"
|
|
|
+ v-model="protocolViewVisible"
|
|
|
+ :protocol-id="viewProtocolId"
|
|
|
+ :list-row="viewProtocolRow"
|
|
|
+ :highlight-sample-code="highlightSampleCode"
|
|
|
+ show-audit-history
|
|
|
+ show-handle-panels
|
|
|
+ />
|
|
|
+ <DonationViewDialog
|
|
|
+ v-else
|
|
|
+ v-model="donationViewVisible"
|
|
|
+ :donation-id="viewDonationId"
|
|
|
+ :list-row="viewDonationRow"
|
|
|
+ :highlight-sample-code="highlightSampleCode"
|
|
|
+ show-audit-history
|
|
|
+ />
|
|
|
+ <SampleInfoQuickRegisterDialog
|
|
|
+ v-model="quickRegisterVisible"
|
|
|
+ :kind="quickRegisterKind"
|
|
|
+ :reception-source="activeSource"
|
|
|
+ :rows="quickRegisterRows"
|
|
|
+ @saved="onQuickRegisterSaved"
|
|
|
+ />
|
|
|
+ <StorageCertQcDialog
|
|
|
+ v-model:visible="storageCertQcVisible"
|
|
|
+ mode="view"
|
|
|
+ :sample-code="storageCertQcSampleCode"
|
|
|
+ :sample-type-name="storageCertQcSampleTypeName"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+ import { Box, Collection, Document, RefreshLeft, Search, Ticket, View } from '@element-plus/icons-vue'
|
|
|
+ import { ElMessage } from 'element-plus'
|
|
|
+ import StorageCertQcDialog from '@/components/business/storage-cert-qc-dialog/index.vue'
|
|
|
+ import { supportsStorageCertQc } from '@/utils/storage-cert-qc-fields'
|
|
|
+ import { fetchProtocolSampleCollectionList } from '@/api/protocol-sample-collection'
|
|
|
+ import { fetchResearchDonationSampleCollectionList } from '@/api/research-donation-sample-collection'
|
|
|
+ import type { CollectionProtocolRow } from '@/api/collection-protocol'
|
|
|
+ import type { ResearchDonationRow } from '@/api/research-donation'
|
|
|
+ import ProtocolViewDialog from '@/views/cell-storage/protocol/ProtocolViewDialog.vue'
|
|
|
+ import DonationViewDialog from '@/views/research-donation/list/DonationViewDialog.vue'
|
|
|
+ import SampleCodeWithSpecialDisplay from '@/components/business/sample-code-with-special-display/index.vue'
|
|
|
+ import SampleInfoQuickRegisterDialog, {
|
|
|
+ type QuickRegisterKind
|
|
|
+ } from './SampleInfoQuickRegisterDialog.vue'
|
|
|
+ import { useMenuPerm } from '@/hooks/core/useMenuPerm'
|
|
|
+ import {
|
|
|
+ donationStatusDisplayLabel,
|
|
|
+ donationStatusTagType,
|
|
|
+ handleDoneFlagLabel,
|
|
|
+ handleDoneFlagTagType,
|
|
|
+ protocolStatusDisplayLabel,
|
|
|
+ protocolStatusTagType,
|
|
|
+ sampleCollectionFlagLabel,
|
|
|
+ sampleCollectionFlagTagType,
|
|
|
+ sampleQcFlagLabel,
|
|
|
+ sampleQcFlagTagType,
|
|
|
+ sampleReceptionFlagLabel,
|
|
|
+ sampleReceptionFlagTagType,
|
|
|
+ sampleStorageFlagLabel,
|
|
|
+ sampleStorageFlagTagType
|
|
|
+ } from '@/utils/status-tag-registry'
|
|
|
+
|
|
|
+ defineOptions({ name: 'UnifiedSampleInfoPage' })
|
|
|
+
|
|
|
+ const props = defineProps<{
|
|
|
+ defaultSource: 'cell_storage' | 'research_donation'
|
|
|
+ }>()
|
|
|
+
|
|
|
+ const { hasMenuPerm } = useMenuPerm()
|
|
|
+ const route = useRoute()
|
|
|
+ const router = useRouter()
|
|
|
+
|
|
|
+ type SourceKey = 'cell_storage' | 'research_donation'
|
|
|
+
|
|
|
+ const allTabs: Array<{ key: SourceKey; title: string; perm: string }> = [
|
|
|
+ { key: 'cell_storage', title: '细胞存储', perm: 'cell-storage:sample-info:list' },
|
|
|
+ {
|
|
|
+ key: 'research_donation',
|
|
|
+ title: '科研捐赠',
|
|
|
+ perm: 'research-donation:sample-info:list'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ const visibleTabs = computed(() => allTabs.filter((t) => hasMenuPerm(t.perm)))
|
|
|
+ const activeSource = ref<SourceKey>(props.defaultSource)
|
|
|
+ const isCellStorage = computed(() => activeSource.value === 'cell_storage')
|
|
|
+
|
|
|
+ const searchForm = reactive({
|
|
|
+ keyword: '',
|
|
|
+ collectionFlag: '',
|
|
|
+ receptionFlag: '',
|
|
|
+ qcFlag: '',
|
|
|
+ storageFlag: '',
|
|
|
+ storageCertHandled: '',
|
|
|
+ insuranceHandled: ''
|
|
|
+ })
|
|
|
+ const tableData = ref<Record<string, unknown>[]>([])
|
|
|
+ const loading = ref(false)
|
|
|
+ const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
|
|
|
+ const selectedRows = ref<Record<string, unknown>[]>([])
|
|
|
+ const tableRef = ref<{ clearSelection?: () => void } | null>(null)
|
|
|
+ const quickRegisterVisible = ref(false)
|
|
|
+ const quickRegisterKind = ref<QuickRegisterKind>('storage')
|
|
|
+ const quickRegisterRows = ref<Record<string, unknown>[]>([])
|
|
|
+
|
|
|
+ const protocolViewVisible = ref(false)
|
|
|
+ const donationViewVisible = ref(false)
|
|
|
+ const viewProtocolId = ref<string | null>(null)
|
|
|
+ const viewDonationId = ref<string | null>(null)
|
|
|
+ const viewProtocolRow = ref<CollectionProtocolRow | null>(null)
|
|
|
+ const viewDonationRow = ref<ResearchDonationRow | null>(null)
|
|
|
+ const highlightSampleCode = ref('')
|
|
|
+ const storageCertQcVisible = ref(false)
|
|
|
+ const storageCertQcSampleCode = ref('')
|
|
|
+ const storageCertQcSampleTypeName = ref('')
|
|
|
+
|
|
|
+ function openStorageCertQcView(row: Record<string, unknown>) {
|
|
|
+ storageCertQcSampleCode.value = String(row.sampleCode || '')
|
|
|
+ storageCertQcSampleTypeName.value = String(row.sampleTypeName || '')
|
|
|
+ if (!storageCertQcSampleCode.value) {
|
|
|
+ ElMessage.warning('缺少样本编码,无法查看')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ storageCertQcVisible.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ function statusLabel(row: Record<string, unknown>) {
|
|
|
+ if (isCellStorage.value) {
|
|
|
+ return protocolStatusDisplayLabel(String(row.protocolStatus || ''), String(row.voidType || ''))
|
|
|
+ }
|
|
|
+ return donationStatusDisplayLabel(String(row.donationStatus || ''), String(row.voidType || ''))
|
|
|
+ }
|
|
|
+
|
|
|
+ function statusTagType(row: Record<string, unknown>) {
|
|
|
+ if (isCellStorage.value) {
|
|
|
+ return protocolStatusTagType(String(row.protocolStatus || ''))
|
|
|
+ }
|
|
|
+ return donationStatusTagType(String(row.donationStatus || ''))
|
|
|
+ }
|
|
|
+
|
|
|
+ function switchSource(key: SourceKey) {
|
|
|
+ activeSource.value = key
|
|
|
+ protocolViewVisible.value = false
|
|
|
+ donationViewVisible.value = false
|
|
|
+ selectedRows.value = []
|
|
|
+ router.replace({ query: { ...route.query, tab: key } })
|
|
|
+ pagination.page = 1
|
|
|
+ loadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ async function loadData() {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const params: Record<string, unknown> = {
|
|
|
+ page: pagination.page,
|
|
|
+ pageSize: pagination.pageSize,
|
|
|
+ keyword: searchForm.keyword || undefined,
|
|
|
+ collectionFlag: searchForm.collectionFlag || undefined,
|
|
|
+ receptionFlag: searchForm.receptionFlag || undefined,
|
|
|
+ qcFlag: searchForm.qcFlag || undefined,
|
|
|
+ storageFlag: searchForm.storageFlag || undefined
|
|
|
+ }
|
|
|
+ if (isCellStorage.value) {
|
|
|
+ params.storageCertHandled = searchForm.storageCertHandled || undefined
|
|
|
+ params.insuranceHandled = searchForm.insuranceHandled || undefined
|
|
|
+ const res = (await fetchProtocolSampleCollectionList(params as any)) as Record<string, unknown>
|
|
|
+ const list = ((res.list as Record<string, unknown>[]) || []).map((row) => ({
|
|
|
+ ...row,
|
|
|
+ rowKey: String(row.protocolSampleId || row.sampleCode || Math.random())
|
|
|
+ }))
|
|
|
+ tableData.value = list
|
|
|
+ pagination.total = Number(res.total) || 0
|
|
|
+ } else {
|
|
|
+ const res = (await fetchResearchDonationSampleCollectionList(
|
|
|
+ params as any
|
|
|
+ )) as Record<string, unknown>
|
|
|
+ const list = ((res.list as Record<string, unknown>[]) || []).map((row) => ({
|
|
|
+ ...row,
|
|
|
+ rowKey: String(row.donationSampleId || row.sampleCode || Math.random())
|
|
|
+ }))
|
|
|
+ tableData.value = list
|
|
|
+ pagination.total = Number(res.total) || 0
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleSearch() {
|
|
|
+ pagination.page = 1
|
|
|
+ loadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleReset() {
|
|
|
+ Object.assign(searchForm, {
|
|
|
+ keyword: '',
|
|
|
+ collectionFlag: '',
|
|
|
+ receptionFlag: '',
|
|
|
+ qcFlag: '',
|
|
|
+ storageFlag: '',
|
|
|
+ storageCertHandled: '',
|
|
|
+ insuranceHandled: ''
|
|
|
+ })
|
|
|
+ handleSearch()
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleSelectionChange(rows: Record<string, unknown>[]) {
|
|
|
+ selectedRows.value = rows
|
|
|
+ }
|
|
|
+
|
|
|
+ function openQuickRegister(kind: QuickRegisterKind) {
|
|
|
+ if (!selectedRows.value.length) {
|
|
|
+ ElMessage.warning('请先选择样本')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if ((kind === 'storage_cert' || kind === 'insurance') && !isCellStorage.value) {
|
|
|
+ ElMessage.warning('储存证与保险仅支持细胞存储样本')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ quickRegisterKind.value = kind
|
|
|
+ quickRegisterRows.value = [...selectedRows.value]
|
|
|
+ quickRegisterVisible.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ function onQuickRegisterSaved() {
|
|
|
+ tableRef.value?.clearSelection?.()
|
|
|
+ selectedRows.value = []
|
|
|
+ loadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ function handleView(row: Record<string, unknown>) {
|
|
|
+ highlightSampleCode.value = String(row.sampleCode || '')
|
|
|
+ if (isCellStorage.value) {
|
|
|
+ const pid = String(row.collectionProtocolId || '')
|
|
|
+ if (!pid) {
|
|
|
+ ElMessage.warning('缺少关联协议单,无法查看')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ viewProtocolId.value = pid
|
|
|
+ viewProtocolRow.value = {
|
|
|
+ id: pid,
|
|
|
+ motherName: row.motherName,
|
|
|
+ newbornName: row.newbornName
|
|
|
+ } as CollectionProtocolRow
|
|
|
+ protocolViewVisible.value = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const did = String(row.researchDonationId || '')
|
|
|
+ if (!did) {
|
|
|
+ ElMessage.warning('缺少关联捐赠协议单,无法查看')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ viewDonationId.value = did
|
|
|
+ viewDonationRow.value = {
|
|
|
+ id: did,
|
|
|
+ motherName: row.motherName,
|
|
|
+ newbornName: row.newbornName
|
|
|
+ } as ResearchDonationRow
|
|
|
+ donationViewVisible.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ watchDebounced(
|
|
|
+ searchForm,
|
|
|
+ () => {
|
|
|
+ pagination.page = 1
|
|
|
+ loadData()
|
|
|
+ },
|
|
|
+ { debounce: 300, deep: true }
|
|
|
+ )
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ const tab = String(route.query.tab || '')
|
|
|
+ const allowed = visibleTabs.value.map((t) => t.key)
|
|
|
+ if (allowed.includes(tab as SourceKey)) {
|
|
|
+ activeSource.value = tab as SourceKey
|
|
|
+ } else if (allowed.includes(props.defaultSource)) {
|
|
|
+ activeSource.value = props.defaultSource
|
|
|
+ } else if (allowed.length) {
|
|
|
+ activeSource.value = allowed[0]
|
|
|
+ }
|
|
|
+ loadData()
|
|
|
+ })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ .sample-info-filter-form {
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .unified-sample-info-page .filter-grid.sample-info-filter-grid {
|
|
|
+ margin-bottom: 0;
|
|
|
+ align-items: center;
|
|
|
+ grid-template-columns: minmax(0, 1.5fr) repeat(4, minmax(0, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .unified-sample-info-page .filter-grid.sample-info-filter-grid.is-cell-storage {
|
|
|
+ grid-template-columns: minmax(0, 1.5fr) repeat(6, minmax(0, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .sample-info-filter-form :deep(.el-form-item) {
|
|
|
+ margin-bottom: 0;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sample-info-filter-form :deep(.el-select),
|
|
|
+ .sample-info-filter-form :deep(.el-input) {
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+</style>
|