|
|
@ -1,51 +1,47 @@ |
|
|
|
<template> |
|
|
|
<!-- 历史页面--> |
|
|
|
<div id="history-container"> |
|
|
|
<!-- 筛选 --> |
|
|
|
<!-- <div class="history-filter"> |
|
|
|
<div class="filter-input"> |
|
|
|
<el-input v-model="inputValue" placeholder="输入信息"> |
|
|
|
<template #prefix> |
|
|
|
<el-icon class="el-input__icon"> |
|
|
|
<search /> |
|
|
|
</el-icon> |
|
|
|
<div class="table-box"> |
|
|
|
<el-table |
|
|
|
style="height: 100%" |
|
|
|
v-loading="loading" |
|
|
|
v-el-table-infinite-scroll="load" |
|
|
|
:infinite-scroll-disabled="disabled" |
|
|
|
:data="tableData" |
|
|
|
show-overflow-tooltip |
|
|
|
@row-click="rowClickHandle" |
|
|
|
@selection-change="handleSelection" |
|
|
|
header-cell-class-name="table-header" |
|
|
|
row-class-name="table-row"> |
|
|
|
<el-table-column type="selection"></el-table-column> |
|
|
|
<el-table-column prop="id" label="序号" width="50"></el-table-column> |
|
|
|
<el-table-column prop="creatDate" label="日期" width="150"> |
|
|
|
<template #default="scope"> |
|
|
|
{{ formatDate(scope.row.creatDate) }} |
|
|
|
</template> |
|
|
|
</el-input> |
|
|
|
</div> |
|
|
|
<div class="filter-button"> |
|
|
|
<el-button type="primary" class="search-button" @click="handleSearch" disabled>搜索</el-button> |
|
|
|
<el-button class="reload-button" @click="handleReset">重置</el-button> |
|
|
|
</div> |
|
|
|
</div> --> |
|
|
|
|
|
|
|
<!-- 表格 --> |
|
|
|
<div class="history-table" @scroll="onScroll" ref="tableContainer"> |
|
|
|
<HistoryTable |
|
|
|
ref="historyTableRef" |
|
|
|
@selectItems="handleSelection" |
|
|
|
@selectIds="handleSelectIds" |
|
|
|
@select-row="handleSelectRow" |
|
|
|
:tableData="tableData" |
|
|
|
:loading="loading" |
|
|
|
:key="tableKey" |
|
|
|
:loadingText="loadingText" |
|
|
|
/> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column prop="sampleId" label="患者ID" ></el-table-column> |
|
|
|
<el-table-column prop="sampleBloodType" label="样本类型"> |
|
|
|
<template #default="scope"> |
|
|
|
{{ |
|
|
|
settingTubeStore.bloodTypeKeyMap[scope.row.sampleBloodType].name |
|
|
|
}} |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column prop="lotId" label="批次" ></el-table-column> |
|
|
|
<el-table-column prop="results" label="结果"> |
|
|
|
<template #default="scope"> |
|
|
|
<div v-for="(r, idx) in scope.row.results" :key="idx"> |
|
|
|
{{ showResult(r) }} |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
</el-table> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 功能 --> |
|
|
|
<div class="history-function"> |
|
|
|
<el-button type="primary" plain @click="showActionConfirm('refresh')" |
|
|
|
>刷新</el-button |
|
|
|
> |
|
|
|
<el-button type="primary" plain @click="showActionConfirm('delete')" |
|
|
|
>删除</el-button |
|
|
|
> |
|
|
|
<el-button type="primary" plain @click="showActionConfirm('print')" |
|
|
|
>打印</el-button |
|
|
|
> |
|
|
|
<el-button type="primary" plain @click="showActionConfirm('export')" |
|
|
|
>导出LIS</el-button |
|
|
|
> |
|
|
|
<div class="history-table-footer"> |
|
|
|
<el-button type="danger" size="large" :disabled="!selectedItems.length" @click="showActionConfirm('delete')">删除</el-button> |
|
|
|
<el-button type="primary" size="large" :disabled="!selectedItems.length" @click="showActionConfirm('print')">打印</el-button> |
|
|
|
<el-button type="primary" size="large" :disabled="!selectedItems.length" @click="showActionConfirm('export')">导出LIS</el-button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 确认操作弹框 --> |
|
|
@ -140,17 +136,17 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
import { onMounted, ref } from 'vue' |
|
|
|
import { ref } from 'vue' |
|
|
|
|
|
|
|
import { HistoryTable, HistoryWarn } from './components/index' |
|
|
|
import { HistoryWarn } from './components/index' |
|
|
|
import { |
|
|
|
getHistoryInfo, |
|
|
|
deleteHistoryInfo, |
|
|
|
printHistoryInfo, |
|
|
|
exportRecordsToLIS, |
|
|
|
} from '../../services/Index/index' |
|
|
|
} from '@/services' |
|
|
|
import HistoryMessage from './components/History/HistoryMessage.vue' |
|
|
|
import type { TableItem } from '../../types/Index' |
|
|
|
import type { ResultItem, TableItem } from '@/types/Index' |
|
|
|
import WarnSvg from '@/assets/Index/History/warn.svg' |
|
|
|
import PrintSvg from '@/assets/Index/History/print.svg' |
|
|
|
import ErrorSvg from '@/assets/Warn.svg' |
|
|
@ -163,8 +159,16 @@ defineOptions({ |
|
|
|
}) |
|
|
|
const settingTubeStore = useSettingTestTubeStore() |
|
|
|
|
|
|
|
// 添加表格引用 |
|
|
|
const historyTableRef = ref() |
|
|
|
const showResult = (t: ResultItem) => { |
|
|
|
if (t.status !== 'SUCCESS') { |
|
|
|
return '错误' |
|
|
|
} |
|
|
|
const unit1 = t.resultConverters[0] |
|
|
|
return ( |
|
|
|
t.subProjName + ' ' + (t.result * unit1.A + unit1.B).toFixed(2) + ' ' + unit1.uintstr |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//选中弹出框 |
|
|
|
const isVisible = ref<boolean>(false) |
|
|
@ -235,14 +239,11 @@ const actions: Record< |
|
|
|
const selectedIds = ref<number[]>([]) |
|
|
|
// 处理表格选中的项目 |
|
|
|
const handleSelection = (items: TableItem[]) => { |
|
|
|
console.log(items) |
|
|
|
selectedItems.value = items |
|
|
|
} |
|
|
|
const handleSelectIds = (ids: number[]) => { |
|
|
|
selectedIds.value = ids |
|
|
|
} |
|
|
|
|
|
|
|
const rowData = ref<TableItem>() |
|
|
|
const handleSelectRow = (item: TableItem) => { |
|
|
|
const rowClickHandle = (item: TableItem) => { |
|
|
|
isVisible.value = true |
|
|
|
rowData.value = item |
|
|
|
} |
|
|
@ -252,18 +253,7 @@ const formatDate = (date: string | number | Date) => { |
|
|
|
|
|
|
|
// 根据操作类型显示不同的确认弹框或通知 |
|
|
|
const showActionConfirm = (actionType: string) => { |
|
|
|
if (actionType === 'refresh') { |
|
|
|
getTableData(true) |
|
|
|
return |
|
|
|
} |
|
|
|
// 判断是否有选中的项目 |
|
|
|
if (selectedItems.value.length === 0) { |
|
|
|
// 如果没有选中项目,弹出通知框 |
|
|
|
warnMessage.value = '请先选择项目' |
|
|
|
warnIcon = WarnSvg |
|
|
|
showWarn.value = true |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 如果是删除操作,确认删除的数量 |
|
|
|
if (actionType === 'delete') { |
|
|
|
currentAction.value = { |
|
|
@ -281,13 +271,36 @@ const loading = ref(false) |
|
|
|
// 获取表格数据 |
|
|
|
const tableData = ref<TableItem[]>([]) |
|
|
|
const currentPage = ref(1) |
|
|
|
const pageSize = ref(20) |
|
|
|
const pageSize = ref(25) |
|
|
|
const total = ref(0) |
|
|
|
const totalPage = ref(0) |
|
|
|
const tableKey = ref(0) //控制重新渲染 |
|
|
|
const tableContainer = ref(null as HTMLElement | null) |
|
|
|
const hasMore = ref(true) |
|
|
|
const loadingText = ref('加载中...') |
|
|
|
|
|
|
|
const disabled = ref(false) |
|
|
|
const load = () => { |
|
|
|
if (disabled.value) return; |
|
|
|
loading.value = true |
|
|
|
|
|
|
|
getHistoryInfo({ |
|
|
|
pageNum: currentPage.value, |
|
|
|
pageSize: pageSize.value, |
|
|
|
}).then(async (res) => { |
|
|
|
currentPage.value++; |
|
|
|
total.value = res.data.total |
|
|
|
totalPage.value = res.data.totalPage |
|
|
|
tableData.value = tableData.value.concat(res.data.list) |
|
|
|
if (currentPage.value > totalPage.value) { |
|
|
|
disabled.value = true |
|
|
|
} |
|
|
|
}).catch(() => { |
|
|
|
eMessage.error("获取数据失败") |
|
|
|
}).finally(() => { |
|
|
|
loading.value = false |
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const getTableData = async (isReset: boolean = false) => { |
|
|
|
if (isReset) { |
|
|
|
// 重置数据和页码 |
|
|
@ -332,30 +345,8 @@ const getHistoryInfoApi = async () => { |
|
|
|
console.log('获取数据失败', error) |
|
|
|
} |
|
|
|
} |
|
|
|
const onScroll = (event: any) => { |
|
|
|
const container = event.target |
|
|
|
const isNearBottom = |
|
|
|
container.scrollHeight - container.scrollTop <= container.clientHeight + 100 |
|
|
|
if (isNearBottom && !loading.value) { |
|
|
|
getTableData() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 搜索功能 暂时注释 |
|
|
|
// const handleSearch = async () => { |
|
|
|
// console.log('搜索内容:', inputValue.value) |
|
|
|
// try { |
|
|
|
// const res = await searchHistoryInfo(inputValue.value) |
|
|
|
// console.log(res.data.list) |
|
|
|
// // 直接替换数据,不进行累加 |
|
|
|
// tableData.value = res.data.list as TableItem[] |
|
|
|
// // 重置分页相关状态 |
|
|
|
// currentPage.value = 1 |
|
|
|
// hasMore.value = false |
|
|
|
// } catch (error) { |
|
|
|
// console.error('搜索失败', error) |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// 确认操作时的回调函数 |
|
|
|
const handleConfirm = async () => { |
|
|
@ -366,10 +357,10 @@ const handleConfirm = async () => { |
|
|
|
await handleConfirmDelete() |
|
|
|
} else if (actionType === 'print') { |
|
|
|
// 执行打印操作 |
|
|
|
handlePrint() |
|
|
|
await handlePrint() |
|
|
|
} else if (actionType === 'export') { |
|
|
|
// 执行导出操作 |
|
|
|
handleExport() |
|
|
|
await handleExport() |
|
|
|
} |
|
|
|
} |
|
|
|
const handleWarnClose = () => { |
|
|
@ -385,34 +376,18 @@ const handleCancel = () => { |
|
|
|
|
|
|
|
// 处理删除确认 |
|
|
|
const handleConfirmDelete = async () => { |
|
|
|
try { |
|
|
|
// 一次删除一条记录 |
|
|
|
for (const item of selectedItems.value) { |
|
|
|
const res = await deleteHistoryInfo(item.id) |
|
|
|
if (!res.success) { |
|
|
|
throw new Error('删除失败') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 从表格数据中移除被删除的项目 |
|
|
|
const deleteIds = selectedItems.value.map((item) => item.id) |
|
|
|
tableData.value = tableData.value.filter( |
|
|
|
(item) => !deleteIds.includes(item.id), |
|
|
|
) |
|
|
|
|
|
|
|
// 通过表格组件清空选择状态 |
|
|
|
if (historyTableRef.value) { |
|
|
|
historyTableRef.value.clearSelection() |
|
|
|
} |
|
|
|
|
|
|
|
eMessage.success('删除成功') |
|
|
|
//刷新 |
|
|
|
getTableData() |
|
|
|
} catch (error) { |
|
|
|
eMessage.error('删除失败') |
|
|
|
} finally { |
|
|
|
showModal.value = false |
|
|
|
} |
|
|
|
deleteHistoryInfo(deleteIds.join(',')).then((res) => { |
|
|
|
if (res.success && res.ecode === 'SUC') { |
|
|
|
eMessage.success('删除成功') |
|
|
|
tableData.value = tableData.value.filter( |
|
|
|
(item) => !deleteIds.includes(item.id), |
|
|
|
) |
|
|
|
}else { |
|
|
|
eMessage.error('删除失败') |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
let warnIcon: any |
|
|
|
// 打印功能 |
|
|
@ -429,10 +404,6 @@ const handlePrint = async () => { |
|
|
|
// 清空选中状态 |
|
|
|
selectedItems.value = [] |
|
|
|
selectedIds.value = [] |
|
|
|
// 手动清除表格的选中状态 |
|
|
|
if (historyTableRef.value?.clearSelection) { |
|
|
|
historyTableRef.value.clearSelection() |
|
|
|
} |
|
|
|
} else { |
|
|
|
eMessage.error(res.message || '打印失败') |
|
|
|
} |
|
|
@ -457,10 +428,6 @@ const handleExport = async () => { |
|
|
|
// 清空选中状态 |
|
|
|
selectedItems.value = [] |
|
|
|
selectedIds.value = [] |
|
|
|
// 手动清除表格的选中状态 |
|
|
|
if (historyTableRef.value?.clearSelection) { |
|
|
|
historyTableRef.value.clearSelection() |
|
|
|
} |
|
|
|
} else { |
|
|
|
eMessage.error(res.message || '导出失败') |
|
|
|
} |
|
|
@ -471,10 +438,6 @@ const handleExport = async () => { |
|
|
|
showWarn.value = true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
getTableData() |
|
|
|
}) |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped lang="less"> |
|
|
@ -498,176 +461,23 @@ onMounted(() => { |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
width: 100%; |
|
|
|
height: 90vh; |
|
|
|
height: 100%; |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
background-color: #f5f7fa; |
|
|
|
background-color: #fff; |
|
|
|
padding: 20px 20px 0 20px; |
|
|
|
box-sizing: border-box; |
|
|
|
|
|
|
|
.history-filter { |
|
|
|
width: 100%; |
|
|
|
height: 70px; |
|
|
|
margin-bottom: 20px; |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
background-color: #fff; |
|
|
|
border-radius: 10px; |
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
|
|
|
|
|
|
|
.filter-input { |
|
|
|
flex: 1; |
|
|
|
max-width: 800px; |
|
|
|
|
|
|
|
.el-input { |
|
|
|
height: 50px; |
|
|
|
font-size: 26px; |
|
|
|
|
|
|
|
.el-input__wrapper { |
|
|
|
border-radius: 25px; |
|
|
|
padding: 0 20px; |
|
|
|
box-shadow: 0 0 0 1px #dcdfe6; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
box-shadow: 0 0 0 1px #409eff; |
|
|
|
} |
|
|
|
|
|
|
|
&.is-focus { |
|
|
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.el-input__icon { |
|
|
|
font-size: 26px; |
|
|
|
color: #909399; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.filter-button { |
|
|
|
display: flex; |
|
|
|
|
|
|
|
.search-button, |
|
|
|
.reload-button { |
|
|
|
height: 50px; |
|
|
|
border-radius: 25px; |
|
|
|
font-size: 26px; |
|
|
|
font-weight: 500; |
|
|
|
transition: all 0.3s; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
transform: translateY(-2px); |
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.search-button { |
|
|
|
width: 200px; |
|
|
|
|
|
|
|
&:disabled { |
|
|
|
background-color: #a0cfff; |
|
|
|
border-color: #a0cfff; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
transform: none; |
|
|
|
box-shadow: none; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.reload-button { |
|
|
|
width: 90px; |
|
|
|
color: #606266; |
|
|
|
border: 1px solid #dcdfe6; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
color: #409eff; |
|
|
|
border-color: #c6e2ff; |
|
|
|
background-color: #ecf5ff; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
.table-box { |
|
|
|
height: calc(100% - 80px); |
|
|
|
} |
|
|
|
|
|
|
|
.history-table { |
|
|
|
flex: 1; |
|
|
|
overflow-y: auto; // 只允许垂直滚动 |
|
|
|
overflow-x: hidden; // 禁止水平滚动 |
|
|
|
background-color: #fff; |
|
|
|
border-radius: 10px; |
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
|
|
|
margin: 0 20px; |
|
|
|
// padding: 20px; |
|
|
|
position: relative; |
|
|
|
|
|
|
|
// 优化滚动条样式 |
|
|
|
&::-webkit-scrollbar { |
|
|
|
width: 6px; |
|
|
|
height: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
&::-webkit-scrollbar-thumb { |
|
|
|
background: #c0c4cc; |
|
|
|
border-radius: 3px; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
background: #909399; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
&::-webkit-scrollbar-track { |
|
|
|
background: #f5f7fa; |
|
|
|
} |
|
|
|
|
|
|
|
//// 添加表格容器阴影效果 |
|
|
|
//&::after { |
|
|
|
// content: ''; |
|
|
|
// position: absolute; |
|
|
|
// left: 0; |
|
|
|
// right: 0; |
|
|
|
// bottom: 0; |
|
|
|
// height: 30px; |
|
|
|
// background: linear-gradient( |
|
|
|
// to top, |
|
|
|
// rgba(255, 255, 255, 0.9), |
|
|
|
// transparent |
|
|
|
// ); |
|
|
|
// pointer-events: none; |
|
|
|
//} |
|
|
|
} |
|
|
|
|
|
|
|
.history-function { |
|
|
|
padding: 0 20px; |
|
|
|
width: 100%; |
|
|
|
height: 84px; |
|
|
|
margin-top: 20px; |
|
|
|
background-color: #fff; |
|
|
|
box-shadow: 0 -2px 12px 0 rgba(0, 0, 0, 0.05); |
|
|
|
.history-table-footer { |
|
|
|
height: 80px; |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
gap: 20px; |
|
|
|
|
|
|
|
justify-content: space-around; |
|
|
|
.el-button { |
|
|
|
flex: 1 1 auto; |
|
|
|
height: 50px; |
|
|
|
border-radius: 25px; |
|
|
|
font-size: 26px; |
|
|
|
font-weight: 500; |
|
|
|
transition: all 0.3s; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
transform: translateY(-2px); |
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
&.is-plain { |
|
|
|
background-color: #fff; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
background-color: #ecf5ff; |
|
|
|
} |
|
|
|
} |
|
|
|
width: 100px; |
|
|
|
font-size: 18px; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -690,8 +500,6 @@ onMounted(() => { |
|
|
|
.result-success { |
|
|
|
display: flex; |
|
|
|
gap: 12px; |
|
|
|
.res-unit { |
|
|
|
} |
|
|
|
} |
|
|
|
.result-error { |
|
|
|
color: red; |
|
|
@ -727,4 +535,10 @@ onMounted(() => { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
:deep(.table-header) { |
|
|
|
height: 60px; |
|
|
|
} |
|
|
|
:deep(.table-row) { |
|
|
|
height: 60px; |
|
|
|
} |
|
|
|
</style> |