进度条

This commit is contained in:
Flow 2025-07-02 17:17:31 +08:00
parent d52158bb11
commit d6b6c40d74
2 changed files with 213 additions and 74 deletions

View File

@ -26,7 +26,7 @@
</el-button>
<el-button
type="success"
:disabled="selectedRows.length === 0"
:disabled="selectedRows.length === 0 || progressVisible"
@click="handleBatchReview"
>
<Icon icon="ep:check" class="mr-5px" /> 批量审核
@ -61,6 +61,34 @@
@pagination="handleQuery"
/>
</ContentWrap>
<!-- 批量处理进度对话框 -->
<el-dialog
v-model="progressVisible"
title="批量审核进度"
width="500px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
>
<div class="progress-container">
<div class="progress-info">
<div class="progress-stats">
<span>进度{{ processedCount }} / {{ totalCount }}</span>
<span class="estimated-time">预计剩余{{ formatTime(estimatedTimeRemaining) }}</span>
</div>
<el-progress
:percentage="progressPercentage"
:stroke-width="8"
:format="progressFormat"
class="progress-bar"
/>
<div class="current-processing">
<span v-if="currentProcessing">当前处理{{ currentProcessing }}</span>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
@ -71,7 +99,6 @@ import Pagination from '@/components/Pagination/index.vue'
import dayjs from 'dayjs'
import { InspectOrgApi } from '@/api/inspect/inspectorg/index'
import { getUserProfile } from '@/api/system/user/profile'
import { ElLoading } from 'element-plus'
import { PacsDataApi } from '@/api/inspect/inspectpacsdata'
import { PatientitemsApi } from '@/api/inspect/inspectpatientitems'
import { DoctorApi } from '@/api/inspect/inspectdoctor'
@ -89,6 +116,49 @@ const queryParams = reactive({
})
const selectedRows = ref<PatientVO[]>([])
//
const progressVisible = ref(false)
const progressPercentage = ref(0)
const currentProcessing = ref('')
const processedCount = ref(0)
const totalCount = ref(0)
const estimatedTimeRemaining = ref(0)
const startTime = ref(0)
//
const updateProgress = (current: number, total: number, currentItem: string) => {
processedCount.value = current
totalCount.value = total
progressPercentage.value = Math.round((current / total) * 100)
currentProcessing.value = currentItem
//
if (current > 0) {
const elapsed = Date.now() - startTime.value
const avgTimePerItem = elapsed / current
const remaining = total - current
estimatedTimeRemaining.value = Math.round((remaining * avgTimePerItem) / 1000) //
}
}
const progressFormat = (percentage: number) => {
return `${percentage}%`
}
const formatTime = (seconds: number) => {
if (seconds < 60) {
return `${seconds}`
} else if (seconds < 3600) {
const minutes = Math.floor(seconds / 60)
const remainingSeconds = seconds % 60
return `${minutes}${remainingSeconds}`
} else {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
return `${hours}小时${minutes}分钟`
}
}
const handleDateChange = (val: [string, string] | null) => {
if (val) {
// 00:00:00
@ -144,11 +214,17 @@ const handleBatchReview = async () => {
}
try {
const loading = ElLoading.service({
lock: true,
text: '批量同步中...',
background: 'rgba(255, 255, 255, 0.7)'
})
//
progressVisible.value = true
progressPercentage.value = 0
currentProcessing.value = '准备开始处理...'
processedCount.value = 0
totalCount.value = selectedRows.value.length
estimatedTimeRemaining.value = 0
startTime.value = Date.now()
//
updateProgress(0, selectedRows.value.length, '准备开始处理...')
let successCount = 0
let failCount = 0
@ -157,16 +233,20 @@ const handleBatchReview = async () => {
const userProfile = await getUserProfile()
if (!userProfile || !userProfile.nickname) {
ElMessage.error('获取用户信息失败,请重新登录')
loading.close()
progressVisible.value = false
return
}
const currentTimestamp = Date.now()
//
for (const row of selectedRows.value) {
for (let index = 0; index < selectedRows.value.length; index++) {
const row = selectedRows.value[index]
const medicalSn = row.medicalSn
const cardId = row.cardId
//
updateProgress(index, selectedRows.value.length, `${row.pname} (${medicalSn})`)
console.log('正在处理体检编号:', medicalSn)
try {
@ -515,8 +595,6 @@ const handleBatchReview = async () => {
const pacsRes = await PacsDataApi.getPacsDataDetail(medicalSn)
const pacsDuration = Date.now() - pacsStartTime
console.log(`${medicalSn} 获取PACS数据完成耗时: ${pacsDuration}ms`)
console.log('PACS数据明细', pacsRes);
console.log('typeGroups:', pacsRes);
if (pacsRes && pacsRes.length > 0) {
const typeGroups: Record<string, any[]> = {}
pacsRes.forEach((item) => {
@ -526,14 +604,7 @@ const handleBatchReview = async () => {
}
typeGroups[type].push(item)
})
console.log('typeGroups:', typeGroups);
if (typeGroups['us']) {
typeGroups['us'].forEach(item => {
console.log('us item:', item.data);
});
}
//
const typeToTabMapping = {
cbc: 'blood',
@ -792,9 +863,19 @@ const handleBatchReview = async () => {
console.error(`处理体检编号 ${medicalSn} 失败:`, error)
failCount++
}
//
updateProgress(index + 1, selectedRows.value.length,
index + 1 === selectedRows.value.length ? '全部处理完成' : `已完成 ${index + 1}`)
}
loading.close()
// 100%
updateProgress(selectedRows.value.length, selectedRows.value.length, '处理完成')
//
setTimeout(() => {
progressVisible.value = false
}, 1500)
if (failCount === 0) {
ElMessage.success(`批量同步完成,共处理 ${successCount}`)
@ -809,6 +890,7 @@ const handleBatchReview = async () => {
} catch (error) {
console.error('批量同步失败:', error)
ElMessage.error('批量同步失败,请重试')
progressVisible.value = false
}
}
@ -870,4 +952,47 @@ const handleSelectionChange = (rows: PatientVO[]) => {
</script>
<style scoped>
.progress-container {
padding: 20px 0;
}
.progress-info {
display: flex;
flex-direction: column;
gap: 16px;
}
.progress-stats {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
color: #606266;
}
.estimated-time {
color: #409eff;
font-weight: 500;
}
.progress-bar {
margin: 10px 0;
}
.current-processing {
text-align: center;
font-size: 13px;
color: #909399;
min-height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.current-processing span {
background: #f5f7fa;
padding: 4px 12px;
border-radius: 12px;
border: 1px solid #e4e7ed;
}
</style>

View File

@ -73,58 +73,60 @@
</div>
</div>
<div class="list-content">
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<template v-else>
<div
v-for="patient in filteredPatients"
:key="patient.id"
class="patient-item"
@click="handlePatientSelect(patient)"
:class="{ active: selectedPatient?.id === patient.id }"
>
<div class="patient-info">
<div class="info-row">
<span class="name">{{ patient.pname }}</span>
<span class="medical-sn">{{ truncateMedicalSn(patient.medicalSn) }}</span>
<el-tag
size="small"
:type="getStatusInfo(patient).color"
class="status-tag"
>
{{ getStatusInfo(patient).text }}
</el-tag>
<el-button
v-if="getStatusInfo(patient).showButton"
type="primary"
size="small"
@click.stop="resetPatientStatus(patient)"
class="reset-status-btn"
>
撤回
</el-button>
<div class="patient-items-wrapper">
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<template v-else>
<div
v-for="patient in filteredPatients"
:key="patient.id"
class="patient-item"
@click="handlePatientSelect(patient)"
:class="{ active: selectedPatient?.id === patient.id }"
>
<div class="patient-info">
<div class="info-row">
<span class="name">{{ patient.pname }}</span>
<span class="medical-sn">{{ truncateMedicalSn(patient.medicalSn) }}</span>
<el-tag
size="small"
:type="getStatusInfo(patient).color"
class="status-tag"
>
{{ getStatusInfo(patient).text }}
</el-tag>
<el-button
v-if="getStatusInfo(patient).showButton"
type="primary"
size="small"
@click.stop="resetPatientStatus(patient)"
class="reset-status-btn"
>
撤回
</el-button>
</div>
</div>
</div>
</div>
</template>
<!-- 将分页组件移到list-content外部底部 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pageNo"
:page-size="pageSize"
small
:total="total"
:pager-count="3"
layout="total, prev, pager, next"
@current-change="handleCurrentChange"
>
<template #total>
<span> {{ total }} </span>
</template>
</el-pagination>
</template>
</div>
</div>
<!-- 将分页组件移到list-content外部固定在底部 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pageNo"
:page-size="pageSize"
small
:total="total"
:pager-count="3"
layout="total, prev, pager, next"
@current-change="handleCurrentChange"
>
<template #total>
<span> {{ total }} </span>
</template>
</el-pagination>
</div>
</div>
<!-- 右侧内容区域 -->
<div class="right-content">
@ -2997,8 +2999,8 @@ const examhosInfo = ref({
<style scoped>
.medical-report {
display: flex;
height: calc(100vh - 120px); /* 使用视口高度减去头部和其他元素的高度 */
min-height: 600px;
height: calc(100vh - 160px); /* 使用视口高度减去头部和其他元素的高度 */
min-height: 580px;
overflow: hidden;
width: 100%;
}
@ -3381,17 +3383,20 @@ const examhosInfo = ref({
}
/* 美化滚动条 */
.table-body::-webkit-scrollbar {
.table-body::-webkit-scrollbar,
.patient-items-wrapper::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.table-body::-webkit-scrollbar-thumb {
.table-body::-webkit-scrollbar-thumb,
.patient-items-wrapper::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
}
.table-body::-webkit-scrollbar-track {
.table-body::-webkit-scrollbar-track,
.patient-items-wrapper::-webkit-scrollbar-track {
background: #f5f7fa;
}
@ -3553,12 +3558,18 @@ const examhosInfo = ref({
.list-content {
flex: 1; /* 让列表内容区域占据剩余空间 */
overflow-y: auto;
overflow: hidden; /* 防止内容溢出 */
position: relative;
display: flex;
flex-direction: column;
}
.patient-items-wrapper {
flex: 1;
overflow-y: auto; /* 只有患者列表可以滚动 */
overflow-x: hidden;
}
/* 添加空状态样式 */
.empty-content {
flex: 1;
@ -3604,6 +3615,9 @@ const examhosInfo = ref({
.pagination-container {
padding: 10px 15px;
text-align: right;
border-top: 1px solid #e6e6e6; /* 添加顶部边框 */
background: #fff; /* 确保背景是白色 */
flex-shrink: 0; /* 防止被压缩 */
}
/* 自定义分页组件样式 */