|
|
<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, searchHistoryInfo, printHistoryInfo, } from '../../services/Index/index' import HistoryMessage from './components/History/HistoryMessage.vue' import type { TableItem } from '../../types/Index' import { ElMessage } from 'element-plus' import WarnSvg from '@/assets/Index/History/warn.svg' import PrintSvg from '@/assets/Index/History/print.svg' import ErrorSvg from '@/assets/Warn.svg' // 添加表格引用
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 jsonResult = (result: any) => { if (!result) return if (result.errorInfo != "") { return result.errorInfo } else { return result.result1 + " " + result.result2 + " " + result.result3 } } // 根据操作类型显示不同的确认弹框或通知
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 handleReset = () => { inputValue.value = '' getTableData(true) // 传入 true 表示需要重置
}
// 确认操作时的回调函数
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 }
// 自定义 ElMessage 样式
const showCustomMessage = (message: string, type: 'success' | 'error' = 'success') => { ElMessage({ message, type, customClass: 'custom-message', duration: 2000, }) }
// 处理删除确认
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() }
showCustomMessage('删除成功') } catch (error) { showCustomMessage('删除失败', '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>
|