Holter页面

This commit is contained in:
Flow 2025-07-10 16:52:37 +08:00
parent b14a8d01d0
commit a7106ced9b

View File

@ -0,0 +1,650 @@
<template>
<div class="holter-container">
<!-- 搜索条件 -->
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="患者姓名" prop="patientName">
<el-input
v-model="queryParams.patientName"
placeholder="请输入患者姓名"
clearable
@keyup.enter="handleQuery"
class="!w-140px"
/>
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-select
v-model="queryParams.gender"
placeholder="请选择性别"
clearable
class="!w-140px"
>
<el-option label="男" value="男" />
<el-option label="女" value="女" />
</el-select>
</el-form-item>
<el-form-item label="身份证号" prop="idCard">
<el-input
v-model="queryParams.idCard"
placeholder="请输入身份证号"
clearable
@keyup.enter="handleQuery"
class="!w-200px"
/>
</el-form-item>
<el-form-item label="体检编号" prop="examNumber">
<el-input
v-model="queryParams.examNumber"
placeholder="请输入体检编号"
clearable
@keyup.enter="handleQuery"
class="!w-200px"
/>
</el-form-item>
<el-form-item label="检查日期" prop="examDate">
<el-date-picker
v-model="queryParams.examDate"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery" :loading="loading">
<Icon icon="ep:search" />搜索
</el-button>
<el-button @click="resetQuery"> <Icon icon="ep:refresh" />重置 </el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 统计信息 -->
<el-row :gutter="20" class="stats-row">
<el-col :span="6">
<div class="stat-card">
<div class="stat-icon total">
<Icon icon="ep:files" />
</div>
<div class="stat-content">
<div class="stat-number">{{ stats.total }}</div>
<div class="stat-label">总检查数</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card">
<div class="stat-icon today">
<Icon icon="ep:calendar-today" />
</div>
<div class="stat-content">
<div class="stat-number">{{ stats.today }}</div>
<div class="stat-label">今日检查</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card">
<div class="stat-icon analyzed">
<Icon icon="ep:circle-check" />
</div>
<div class="stat-content">
<div class="stat-number">{{ stats.analyzed }}</div>
<div class="stat-label">已分析</div>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card">
<div class="stat-icon pending">
<Icon icon="ep:clock" />
</div>
<div class="stat-content">
<div class="stat-number">{{ stats.pending }}</div>
<div class="stat-label">待分析</div>
</div>
</div>
</el-col>
</el-row>
<!-- 数据表格 -->
<ContentWrap class="table-card">
<el-table
v-loading="loading"
:data="tableData"
@selection-change="handleSelectionChange"
stripe
border
style="width: 100%"
header-cell-class-name="table-header-bold"
>
<el-table-column type="selection" width="48" align="center" />
<el-table-column prop="examNumber" label="体检编号" width="200" align="left" />
<el-table-column prop="patientName" label="姓名" width="140" align="left" />
<el-table-column prop="gender" label="性别" width="80" align="center">
<template #default="{ row }">
<el-tag :type="row.gender === '男' ? 'primary' : 'danger'">
{{ row.gender }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="age" label="年龄" width="80" align="center" />
<el-table-column prop="examTime" label="检查时间" width="150" align="center" />
<el-table-column prop="duration" label="时长" width="100" align="center">
<template #default="{ row }">
<el-tag type="info">{{ row.duration }}</el-tag>
</template>
</el-table-column>
<el-table-column label="Holter" width="120" align="center">
<template #default="{ row }">
<el-button type="primary" size="small" plain @click="handleAnalysis(row)"
>Holter</el-button
>
</template>
</el-table-column>
<el-table-column prop="reportStatus" label="报告" width="120" align="center">
<template #default="{ row }">
<el-tag :type="row.reportStatus === '已生成' ? 'success' : 'info'">
{{ row.reportStatus }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="上传" width="120" align="center">
<template #default="{ row }">
<el-button type="primary" size="small" plain @click="handleUpload(row)">上传</el-button>
</template>
</el-table-column>
<el-table-column label="申请" width="120" align="center">
<template #default="{ row }">
<el-tag :type="row.applicationStatus === '已申请' ? 'success' : 'info'">
{{ row.applicationStatus }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="下载" width="120" align="center">
<template #default="{ row }">
<el-button type="success" size="small" plain @click="handleDownload(row)"
>下载</el-button
>
</template>
</el-table-column>
<el-table-column
prop="institution"
label="机构"
min-width="120"
align="left"
show-overflow-tooltip
/>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
defineOptions({ name: 'AnalysisHolter' })
//
interface HolterData {
id: number
examNumber: string
patientName: string
gender: string
age: number
examTime: string
duration: string
analysisStatus: string
reportStatus: string
canUpload: boolean
applicationStatus: string
institution: string
}
const loading = ref(false)
const exportLoading = ref(false)
const total = ref(0)
const tableData = ref<HolterData[]>([])
const selectedIds = ref<number[]>([])
const queryFormRef = ref()
//
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
patientName: '',
gender: '',
idCard: '',
examNumber: '',
ageMin: undefined as number | undefined,
ageMax: undefined as number | undefined,
examDate: [],
examTime: '',
institution: ''
})
//
const stats = reactive({
total: 0,
today: 0,
analyzed: 0,
pending: 0
})
//
const mockData = [
{
id: 1,
examNumber: '572506080',
patientName: '赵艳军',
gender: '男',
age: 53,
examTime: '07-08 10:07',
duration: '0时30分',
analysisStatus: '已完成',
reportStatus: '已生成',
canUpload: true,
applicationStatus: '未申请',
institution: '保定慈惠医院'
},
{
id: 2,
examNumber: '1000370587',
patientName: '郭宣宣',
gender: '女',
age: 31,
examTime: '07-08 10:30',
duration: '0时30分',
analysisStatus: '分析中',
reportStatus: '未生成',
canUpload: false,
applicationStatus: '未申请',
institution: '沧州慈惠医院'
},
{
id: 3,
examNumber: '1000381275',
patientName: '薛巧敏',
gender: '男',
age: 34,
examTime: '07-08 10:28',
duration: '0时30分',
analysisStatus: '已完成',
reportStatus: '已生成',
canUpload: true,
applicationStatus: '已申请',
institution: '沧州慈惠医院'
},
{
id: 4,
examNumber: '0440000201',
patientName: '刘鑫',
gender: '男',
age: 41,
examTime: '07-08 10:11',
duration: '0时30分',
analysisStatus: '已完成',
reportStatus: '已生成',
canUpload: true,
applicationStatus: '未申请',
institution: '包头康泰医院'
},
{
id: 5,
examNumber: '532507070',
patientName: '李艳杰',
gender: '男',
age: 49,
examTime: '07-08 09:05',
duration: '0时30分',
analysisStatus: '已完成',
reportStatus: '已生成',
canUpload: true,
applicationStatus: '未申请',
institution: '高碑店全科诊所'
}
]
/** 获取列表数据 */
const getList = async () => {
loading.value = true
try {
// API
await new Promise((resolve) => setTimeout(resolve, 300))
// API
// const response = await HolterApi.getHolterPage(queryParams)
//
let filteredData = [...mockData]
if (queryParams.patientName) {
filteredData = filteredData.filter((item) =>
item.patientName.includes(queryParams.patientName)
)
}
if (queryParams.gender) {
filteredData = filteredData.filter((item) => item.gender === queryParams.gender)
}
if (queryParams.examNumber) {
filteredData = filteredData.filter((item) => item.examNumber.includes(queryParams.examNumber))
}
tableData.value = filteredData
total.value = filteredData.length
//
stats.total = filteredData.length
stats.today = filteredData.filter((item) => item.examTime.includes('07-08')).length
stats.analyzed = filteredData.filter((item) => item.analysisStatus === '已完成').length
stats.pending = filteredData.filter((item) => item.analysisStatus !== '已完成').length
} finally {
loading.value = false
}
}
/** 搜索 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置 */
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
/** 表格选择变化 */
const handleSelectionChange = (selection) => {
selectedIds.value = selection.map((item) => item.id)
}
/** 分析 */
const handleAnalysis = (row) => {
if (row.analysisStatus === '已完成') {
ElMessage.info('该记录已完成分析')
return
}
ElMessage.success('开始分析...')
// API
}
/** 报告 */
const handleReport = (row) => {
if (row.reportStatus === '已生成') {
ElMessage.success('打开报告...')
//
} else {
ElMessage.info('报告未生成,请先完成分析')
}
}
/** 上传 */
const handleUpload = (row) => {
ElMessage.success('上传功能')
//
}
/** 申请 */
const handleApply = (row) => {
ElMessage.success('申请功能')
//
}
/** 下载 */
const handleDownload = (row) => {
if (row.analysisStatus !== '已完成') {
ElMessage.warning('请等待分析完成后再下载')
return
}
ElMessage.success('开始下载...')
//
}
/** 导出 */
const handleExport = async () => {
exportLoading.value = true
try {
ElMessage.success('导出功能')
//
} finally {
exportLoading.value = false
}
}
/** 批量下载 */
const handleBatchDownload = () => {
ElMessage.success('批量下载功能')
//
}
/** 操作分发 */
const handleCommand = (command, row) => {
switch (command) {
case 'view':
ElMessage.info('查看详情')
break
case 'edit':
ElMessage.info('编辑信息')
break
case 'delete':
handleDelete(row)
break
}
}
/** 删除 */
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确定要删除这条记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
ElMessage.success('删除成功')
getList()
} catch {
ElMessage.info('已取消删除')
}
}
/** 初始化 */
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
.holter-container {
background: #f5f7fa;
min-height: 100vh;
}
.page-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 30px;
border-radius: 12px;
margin-bottom: 20px;
color: white;
text-align: center;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
.page-title {
font-size: 28px;
font-weight: 600;
margin: 0 0 10px 0;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
.title-icon {
font-size: 32px;
}
}
.page-description {
font-size: 16px;
opacity: 0.9;
margin: 0;
}
}
.age-range {
display: flex;
align-items: center;
gap: 10px;
.range-separator {
color: #666;
font-weight: 500;
}
}
.stats-row {
margin-bottom: 20px;
.stat-card {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
display: flex;
align-items: center;
gap: 15px;
transition: transform 0.3s ease;
&:hover {
transform: translateY(-2px);
}
.stat-icon {
width: 50px;
height: 50px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: white;
&.total {
background: linear-gradient(135deg, #667eea, #764ba2);
}
&.today {
background: linear-gradient(135deg, #f093fb, #f5576c);
}
&.analyzed {
background: linear-gradient(135deg, #4facfe, #00f2fe);
}
&.pending {
background: linear-gradient(135deg, #43e97b, #38f9d7);
}
}
.stat-content {
.stat-number {
font-size: 28px;
font-weight: 700;
color: #333;
line-height: 1;
}
.stat-label {
font-size: 14px;
color: #666;
margin-top: 4px;
}
}
}
}
.table-card {
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
:deep(.el-table) {
border-radius: 12px;
overflow: hidden;
font-size: 16px; //
.el-table__header,
.el-table__row,
.el-table__cell {
font-size: 16px; //
text-align: center; //
}
.el-table__header {
background: #f8f9ff;
th {
background: #f8f9ff;
color: #333;
font-weight: 600;
border-bottom: 2px solid #e4e7ed;
}
}
.el-table__row {
&:hover {
background: #f8f9ff;
}
}
}
}
:deep(.el-input) {
.el-input__wrapper {
border-radius: 8px;
}
}
:deep(.el-select) {
.el-input__wrapper {
border-radius: 8px;
}
}
:deep(.el-date-editor) {
border-radius: 8px;
}
:deep(.el-button) {
border-radius: 8px;
font-weight: 500;
}
:deep(.el-tag) {
border-radius: 6px;
font-weight: 500;
}
.table-header-bold {
font-weight: 600;
color: #333;
background: #f8f9ff;
}
</style>