A8000
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

690 lines
17 KiB

<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>
</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" />
</div>
<!-- 功能 -->
<div class="history-function">
<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')">导出</el-button>
</div>
<!-- 确认操作弹框 -->
<HistoryWarn v-if="showModal" :icon="currentAction.icon" :message="currentAction.message"
:confirmText="currentAction.confirmText" :cancelText="currentAction.cancelText" @confirm="handleConfirm"
@cancel="handleCancel" />
<!-- 通知提示框 -->
<HistoryWarn v-if="showWarn" :message="warnMessage" :icon="warnIcon" @close="showWarn = false" />
<!-- 通用通知组件 -->
<HistoryWarn v-if="showWarn" :message="warnMessage" :icon="warnIcon" :confirmText="'关闭'" :showButtons="false"
@confirm="handleWarnClose" />
<HistoryMessage :isVisible="isVisible" @update:isVisible="isVisible = $event">
<div class="detail-container">
<div class="detail-section">
<div class="detail-item">
<span class="label">日期:</span>
<span class="value">{{ rowData && formatDate(rowData.creatDate) }}</span>
</div>
<div class="divider"></div>
<div class="detail-item">
<span class="label">样本id:</span>
<span class="value">{{ rowData && rowData.id }}</span>
</div>
<div class="divider"></div>
<div class="detail-item">
<span class="label">项目名称:</span>
<span class="value">{{ rowData && rowData.id }}</span>
</div>
<div class="detail-item">
<span class="label">Result:</span>
<span class="value">{{ rowData && JSON.stringify(rowData.results) }}</span>
</div>
</div>
<div class="detail-section">
<div class="detail-item">
<span class="label">样本种类:</span>
<span class="value">{{ rowData && rowData.sampleBloodType }}</span>
</div>
<div class="detail-item">
<span class="label">操次:</span>
<span class="value">{{ rowData && rowData.lotId }}</span>
</div>
<div class="detail-item">
<span class="label">Rec:</span>
<span class="value">Record 1</span>
</div>
<div class="detail-item">
<span class="label">有效期:</span>
<span class="value">2024-12-31</span>
</div>
<div class="divider"></div>
<div class="detail-item">
<span class="label">操作人:</span>
<span class="value">John Doe</span>
</div>
<div class="detail-item">
<span class="label">序列号:</span>
<span class="value">SN12345</span>
</div>
<div class="detail-item">
<span class="label">App Ver:</span>
<span class="value">1.0.0</span>
</div>
<div class="detail-item">
<span class="label">F/W Ver:</span>
<span class="value">2.0.0</span>
</div>
</div>
<div class="detail-footer">
<button class="confirm-btn" @click="handleClose">确认</button>
</div>
</div>
</HistoryMessage>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import dayjs from 'dayjs'
import { HistoryTable, HistoryWarn } from './components/index'
import {
getHistoryInfo,
deleteHistoryInfo,
printHistoryInfo,
} from '../../services/Index/index'
import HistoryMessage from './components/History/HistoryMessage.vue'
import type { 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'
import { eMessage } from './utils'
// 添加表格引用
const historyTableRef = ref()
//选中弹出框
const isVisible = ref<boolean>(false)
const handleClose = () => {
isVisible.value = false
}
// 绑定输入框的值
// const inputValue = ref<string>('')
// 选中的项目
const selectedItems = ref<TableItem[]>([])
// 控制弹框和通知的显示
const showModal = ref<boolean>(false)
const showWarn = ref<boolean>(false)
// 当前操作的图标和提示信息
const currentAction = ref<{
type: string
icon: string
message: string
confirmText: string
cancelText: string
}>({
type: '',
icon: '',
message: '',
confirmText: '',
cancelText: '',
})
// 通知消息
const warnMessage = ref<string>('')
// 未选择项目通知的图标
// 定义不同操作的图标和提示信息
const actions: Record<
string,
{
type: string
icon: string
message: string
confirmText: string
cancelText: string
}
> = {
delete: {
type: 'delete',
icon: WarnSvg,
message: '请确认是否删除所选项目',
confirmText: '确认删除',
cancelText: '取消',
},
print: {
type: 'print',
icon: PrintSvg,
message: '请确认是否打印所选项目',
confirmText: '确认打印',
cancelText: '取消',
},
export: {
type: 'export',
icon: WarnSvg,
message: '请确认是否导出所选项目',
confirmText: '确认导出',
cancelText: '取消',
},
}
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) => {
isVisible.value = true
rowData.value = item
}
const formatDate = (date: string | number | Date) => {
return dayjs(date).format('YYYY-MM-DD')
}
// 根据操作类型显示不同的确认弹框或通知
const showActionConfirm = (actionType: string) => {
// 判断是否有选中的项目
if (selectedItems.value.length === 0) {
// 如果没有选中项目,弹出通知框
warnMessage.value = '请先选择项目'
showWarn.value = true
return
}
// 如果是删除操作,确认删除的数量
if (actionType === 'delete') {
currentAction.value = {
...actions[actionType],
message: `是否删除选中的 ${selectedItems.value.length} 条记录?`
}
} else {
currentAction.value = actions[actionType]
}
showModal.value = true
}
//控制骨架屏
const loading = ref(false)
// 获取表格数据
const tableData = ref<TableItem[]>([])
const currentPage = ref(1)
const pageSize = ref(20)
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 getTableData = async (isReset: boolean = false) => {
if (isReset) {
// 重置数据和页码
tableData.value = []
currentPage.value = 1
hasMore.value = true
}
if (loading.value || !hasMore.value) return
loading.value = true
try {
const res = await getHistoryInfoApi()
if (currentPage.value > totalPage.value) {
hasMore.value = false
loadingText.value = "没有更多数据了"
} else {
tableData.value = [...tableData.value, ...res.data.list]
currentPage.value++;
loadingText.value = "加载中..."
}
} catch (error) {
console.error('获取数据失败', error)
loadingText.value = "加载失败,请重试"
} finally {
setTimeout(() => {
loading.value = false
}, 1000)
}
}
const getHistoryInfoApi = async () => {
const params = {
pageNum: currentPage.value,
pageSize: pageSize.value,
}
try {
const res = await getHistoryInfo(params)
total.value = res.data.total
totalPage.value = res.data.totalPage
return res
} catch (error) {
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 () => {
showModal.value = false
showWarn.value = false
const actionType = currentAction.value.type
if (actionType === 'delete') {
await handleConfirmDelete()
} else if (actionType === 'print') {
// 执行打印操作
handlePrint()
} else if (actionType === 'export') {
// 执行导出操作
handleExport()
}
}
const handleWarnClose = () => {
getTableData()
showWarn.value = false
}
// 取消操作时的回调函数
const handleCancel = () => {
showModal.value = false
showWarn.value = false
}
// 处理删除确认
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('删除成功')
} catch (error) {
eMessage.error('删除失败')
} finally {
showModal.value = false
}
}
let warnIcon: any
// 打印功能
const handlePrint = async () => {
try {
if (selectedItems.value.length > 10) {
warnMessage.value = '一次最多只能打印 10 条记录'
warnIcon = new URL('@/assets/Index/History/warn.svg', import.meta.url).href
showWarn.value = true
return
}
const idsToPrint = selectedItems.value.map((item) => item.id)
for (const item of idsToPrint) {
const res = await printHistoryInfo(item)
if (res.success && res.ecode === "SUC") {
warnMessage.value = '打印成功'
warnIcon = new URL('@/assets/Index/History/success.svg', import.meta.url).href
showWarn.value = true
// 清空选中状态
selectedItems.value = []
selectedIds.value = []
// 手动清除表格的选中状态
if (historyTableRef.value?.clearSelection) {
historyTableRef.value.clearSelection()
}
} else {
throw new Error(res.message || '打印失败')
}
}
} catch (error) {
console.error('打印失败', error)
warnMessage.value = '打印失败,请重试'
warnIcon = ErrorSvg
showWarn.value = true
}
}
// 导出功能
const handleExport = () => {
// 执行导出操作
// 根据实际需求实现导出功能
console.log('导出项目:', selectedItems.value)
warnMessage.value = '导出成功'
showWarn.value = true
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="less">
:global(.custom-message) {
min-width: 380px !important;
padding: 16px 24px !important;
.el-message__content {
font-size: 24px !important;
line-height: 1.5 !important;
}
.el-message__icon {
font-size: 24px !important;
margin-right: 12px !important;
}
}
#history-container {
width: 100%;
height: 90vh;
display: flex;
flex-direction: column;
background-color: #f5f7fa;
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;
}
}
}
}
.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 {
width: 100%;
height: 84px;
margin-top: 20px;
background-color: #fff;
box-shadow: 0 -2px 12px 0 rgba(0, 0, 0, 0.05);
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
.el-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);
}
&:nth-child(1) {
width: 215px;
}
&:nth-child(2),
&:nth-child(3) {
width: 392px;
}
&.is-plain {
background-color: #fff;
&:hover {
background-color: #ecf5ff;
}
}
}
}
}
.detail-container {
.detail-section {
margin-bottom: 24px;
}
.detail-item {
display: flex;
align-items: center;
padding: 16px 0;
font-size: 28px;
.label {
color: #606266;
min-width: 200px;
font-weight: 500;
}
.value {
color: #303133;
flex: 1;
word-break: break-all;
}
}
.divider {
height: 1px;
background-color: #ebeef5;
margin: 16px 0;
}
.detail-footer {
margin-top: 40px;
text-align: center;
.confirm-btn {
width: 90%;
height: 88px;
background-color: #409eff;
border: none;
border-radius: 44px;
color: white;
font-size: 32px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #66b1ff;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
&:active {
transform: translateY(0);
}
}
}
}
</style>