SampleCollectionInfoTab.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. <!-- 样本信息管理 · 样本采集信息 Tab -->
  2. <template>
  3. <div class="sample-collection-info-tab">
  4. <div class="filter-block">
  5. <el-form :model="searchForm" class="filter-form" @submit.prevent>
  6. <div class="filter-grid">
  7. <el-form-item label="关键词">
  8. <el-input v-model="searchForm.keyword" placeholder="姓名/样本编码/入库编码" clearable />
  9. </el-form-item>
  10. <el-form-item label="采集状态">
  11. <el-select
  12. v-model="searchForm.sampleStatus"
  13. placeholder="全部"
  14. clearable
  15. style="width: 100%"
  16. >
  17. <el-option label="未采集" value="uncollected" />
  18. <el-option label="已采集" value="ycj" />
  19. <el-option label="检测中" value="jcz" />
  20. <el-option label="已入库" value="yrk" />
  21. <el-option label="已作废" value="yzf" />
  22. </el-select>
  23. </el-form-item>
  24. </div>
  25. <div class="filter-actions-row">
  26. <div class="filter-actions-end">
  27. <el-button class="btn-ghost" @click="handleReset">
  28. <el-icon><RefreshLeft /></el-icon>重置
  29. </el-button>
  30. <el-button class="btn-main" type="primary" @click="handleSearch">
  31. <el-icon><Search /></el-icon>搜索
  32. </el-button>
  33. </div>
  34. </div>
  35. </el-form>
  36. </div>
  37. <div class="list-body list-wrap">
  38. <el-table
  39. class="list-table"
  40. :data="tableData"
  41. v-loading="loading"
  42. stripe
  43. row-key="protocolSampleId"
  44. @row-dblclick="handleView"
  45. >
  46. <el-table-column
  47. type="index"
  48. label="序号"
  49. min-width="68"
  50. align="center"
  51. header-align="center"
  52. :index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1"
  53. />
  54. <el-table-column
  55. prop="protocolStatus"
  56. label="协议状态"
  57. min-width="100"
  58. align="center"
  59. header-align="center"
  60. >
  61. <template #default="{ row }">
  62. <el-tag
  63. :type="protocolStatusTagType(String(row.protocolStatus || ''))"
  64. size="small"
  65. >{{ protocolStatusDisplayLabel(String(row.protocolStatus || ''), String(row.voidType || '')) }}</el-tag
  66. >
  67. </template>
  68. </el-table-column>
  69. <el-table-column
  70. prop="expectedDeliveryDate"
  71. label="预产期"
  72. min-width="120"
  73. align="center"
  74. header-align="center"
  75. />
  76. <el-table-column
  77. prop="motherName"
  78. label="母亲姓名"
  79. min-width="100"
  80. align="center"
  81. header-align="center"
  82. />
  83. <el-table-column
  84. prop="newbornName"
  85. label="新生儿姓名"
  86. min-width="110"
  87. align="center"
  88. header-align="center"
  89. show-overflow-tooltip
  90. />
  91. <el-table-column
  92. prop="sampleCode"
  93. label="样本编码"
  94. min-width="160"
  95. align="center"
  96. header-align="center"
  97. >
  98. <template #default="{ row }">
  99. <SampleCodeWithSpecialDisplay
  100. :sample-code="row.sampleCode"
  101. :is-special="!!row.isSpecial"
  102. :special-remark="row.specialRemark"
  103. monospace
  104. />
  105. </template>
  106. </el-table-column>
  107. <el-table-column
  108. prop="labStorageCode"
  109. label="实验室入库编码"
  110. min-width="150"
  111. align="center"
  112. header-align="center"
  113. >
  114. <template #default="{ row }">{{ row.labStorageCode || '—' }}</template>
  115. </el-table-column>
  116. <el-table-column
  117. prop="sampleTypeName"
  118. label="样本类型"
  119. min-width="110"
  120. align="center"
  121. header-align="center"
  122. />
  123. <el-table-column
  124. prop="sampleStatus"
  125. label="采集状态"
  126. min-width="100"
  127. align="center"
  128. header-align="center"
  129. >
  130. <template #default="{ row }">
  131. <el-tag :type="collectionStatusTagType(String(row.sampleStatus || ''))" size="small">
  132. {{ row.sampleStatusLabel || collectionStatusLabel(String(row.sampleStatus || '')) }}
  133. </el-tag>
  134. </template>
  135. </el-table-column>
  136. <el-table-column
  137. prop="regionName"
  138. label="采集地区"
  139. min-width="120"
  140. align="center"
  141. header-align="center"
  142. show-overflow-tooltip
  143. />
  144. <el-table-column
  145. prop="hospitalName"
  146. label="采集医院"
  147. min-width="140"
  148. align="center"
  149. header-align="center"
  150. show-overflow-tooltip
  151. />
  152. <el-table-column
  153. prop="medicalStaff"
  154. label="采集医护"
  155. min-width="100"
  156. align="center"
  157. header-align="center"
  158. show-overflow-tooltip
  159. />
  160. <el-table-column
  161. prop="sampleCollectTime"
  162. label="样本采集时间"
  163. min-width="170"
  164. align="center"
  165. header-align="center"
  166. />
  167. <el-table-column
  168. prop="maternalBloodCollectTime"
  169. label="母血采集时间"
  170. min-width="170"
  171. align="center"
  172. header-align="center"
  173. />
  174. <el-table-column
  175. label="操作"
  176. min-width="120"
  177. fixed="right"
  178. align="center"
  179. header-align="center"
  180. >
  181. <template #default="{ row }">
  182. <el-button class="btn-op-item primary" @click="handleView(row)">
  183. <el-icon><View /></el-icon>查看
  184. </el-button>
  185. </template>
  186. </el-table-column>
  187. </el-table>
  188. <div class="pagination list-pager-bar">
  189. <el-pagination
  190. :current-page="pagination.page"
  191. :page-size="pagination.pageSize"
  192. :total="pagination.total"
  193. :page-sizes="[10, 20, 50]"
  194. layout="total, sizes, prev, pager, next"
  195. @size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
  196. @current-change="(val) => ((pagination.page = val), loadData())"
  197. />
  198. </div>
  199. </div>
  200. <ProtocolViewDialog
  201. v-model="viewVisible"
  202. :protocol-id="viewProtocolId"
  203. :list-row="viewRow"
  204. show-audit-history
  205. show-storage-cert
  206. show-fullmoon-insurance
  207. />
  208. </div>
  209. </template>
  210. <script setup lang="ts">
  211. import { ref, reactive, onMounted } from 'vue'
  212. import { ElMessage } from 'element-plus'
  213. import { Search, RefreshLeft, View } from '@element-plus/icons-vue'
  214. import ProtocolViewDialog from '../protocol/ProtocolViewDialog.vue'
  215. import SampleCodeWithSpecialDisplay from '@/components/business/sample-code-with-special-display/index.vue'
  216. import { fetchProtocolSampleCollectionList } from '@/api/protocol-sample-collection'
  217. import { type CollectionProtocolRow } from '@/api/collection-protocol'
  218. import {
  219. protocolStatusDisplayLabel,
  220. protocolStatusTagType,
  221. sampleStatusLabel,
  222. sampleStatusTagType
  223. } from '@/utils/status-tag-registry'
  224. defineOptions({ name: 'CellStorageSampleCollectionInfoTab' })
  225. function collectionStatusLabel(status: string) {
  226. return status ? sampleStatusLabel(status) : '未采集'
  227. }
  228. function collectionStatusTagType(status: string): 'success' | 'warning' | 'danger' | 'info' | 'primary' {
  229. return status ? sampleStatusTagType(status) : 'info'
  230. }
  231. const searchForm = reactive({ keyword: '', sampleStatus: '' })
  232. const tableData = ref<Record<string, unknown>[]>([])
  233. const loading = ref(false)
  234. const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
  235. const viewVisible = ref(false)
  236. const viewProtocolId = ref<string | null>(null)
  237. const viewRow = ref<CollectionProtocolRow | null>(null)
  238. async function loadData() {
  239. loading.value = true
  240. try {
  241. const result = await fetchProtocolSampleCollectionList({
  242. page: pagination.page,
  243. pageSize: pagination.pageSize,
  244. keyword: searchForm.keyword || undefined,
  245. sampleStatus: searchForm.sampleStatus || undefined
  246. })
  247. const res = (result as Record<string, unknown>) || {}
  248. tableData.value = (res.list as Record<string, unknown>[]) || []
  249. pagination.total = Number(res.total) || 0
  250. } finally {
  251. loading.value = false
  252. }
  253. }
  254. function handleSearch() {
  255. pagination.page = 1
  256. loadData()
  257. }
  258. function handleReset() {
  259. Object.assign(searchForm, { keyword: '', sampleStatus: '' })
  260. handleSearch()
  261. }
  262. function handleView(row: Record<string, unknown>) {
  263. const pid = String(row.collectionProtocolId || '')
  264. if (!pid) {
  265. ElMessage.warning('缺少关联协议单,无法查看')
  266. return
  267. }
  268. viewProtocolId.value = pid
  269. viewRow.value = {
  270. id: pid,
  271. motherName: row.motherName,
  272. newbornName: row.newbornName
  273. } as CollectionProtocolRow
  274. viewVisible.value = true
  275. }
  276. watchDebounced(
  277. searchForm,
  278. () => {
  279. pagination.page = 1
  280. loadData()
  281. },
  282. { debounce: 300, deep: true }
  283. )
  284. onMounted(() => {
  285. loadData()
  286. })
  287. </script>