379 lines
10 KiB
Vue
379 lines
10 KiB
Vue
<template>
|
||
<div class="cardiology-queue">
|
||
<div class="app-container">
|
||
<el-card>
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span class="title">心内科分诊排队管理</span>
|
||
<div class="header-actions">
|
||
<el-button type="primary" @click="refreshData" :loading="loading">
|
||
<el-icon><Refresh /></el-icon>
|
||
刷新
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 当前呼叫区 -->
|
||
<div class="current-call-section">
|
||
<div class="call-box">
|
||
<div class="call-text">
|
||
请 <span class="highlight">{{ currentCall?.number || '-' }}</span> 号
|
||
<span class="highlight">{{ currentCall?.name || '-' }}</span>
|
||
到 <span class="highlight">{{ currentCall?.room || '-' }}</span> 诊室就诊
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 候诊队列 -->
|
||
<div class="queue-section">
|
||
<h3 class="section-title">候诊队列</h3>
|
||
<el-table
|
||
:data="queueList"
|
||
v-loading="loading"
|
||
stripe
|
||
border
|
||
style="width: 100%"
|
||
:default-sort="{ prop: 'displayOrder', order: 'ascending' }"
|
||
>
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column prop="patientName" label="患者姓名" width="120" align="center">
|
||
<template #default="scope">
|
||
{{ formatPatientName(scope.row.patientName) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="practitionerName" label="医生" width="120" align="center" />
|
||
<el-table-column prop="room" label="诊室" width="100" align="center" />
|
||
<el-table-column prop="displayOrder" label="流水号" width="120" align="center">
|
||
<template #default="scope">
|
||
{{ formatSerialNo(scope.row) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||
<template #default="scope">
|
||
<el-tag :type="getStatusType(scope.row.status)">
|
||
{{ getStatusText(scope.row.status) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="registerTime" label="挂号时间" width="180" align="center">
|
||
<template #default="scope">
|
||
{{ formatTime(scope.row.registerTime) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||
<template #default="scope">
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
@click="handleCall(scope.row)"
|
||
:disabled="scope.row.status === 'CALLED'"
|
||
>
|
||
叫号
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 分页 -->
|
||
<div class="pagination-wrapper">
|
||
<el-pagination
|
||
v-model:current-page="queryParams.pageNo"
|
||
v-model:page-size="queryParams.pageSize"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
:total="total"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handlePageChange"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, onMounted } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { Refresh } from '@element-plus/icons-vue'
|
||
import { parseTime } from '@/utils/his'
|
||
import { getOutpatientRegistrationCurrent } from '@/views/charge/outpatientregistration/components/outpatientregistration'
|
||
|
||
// 响应式数据
|
||
const loading = ref(false)
|
||
const queueList = ref([])
|
||
const total = ref(0)
|
||
const currentCall = ref({
|
||
number: '-',
|
||
name: '-',
|
||
room: '-'
|
||
})
|
||
|
||
// 查询参数
|
||
const queryParams = reactive({
|
||
pageNo: 1,
|
||
pageSize: 20,
|
||
searchKey: '',
|
||
organizationId: null // 心内科科室ID,可以从路由参数或配置中获取
|
||
})
|
||
|
||
// 计算流水号:直接使用挂号记录表的主键ID(encounterId)
|
||
function formatSerialNo(row) {
|
||
if (!row) return '-'
|
||
|
||
// 直接使用主键ID作为流水号
|
||
if (row.encounterId != null && row.encounterId !== undefined) {
|
||
return String(row.encounterId)
|
||
} else if (row.id != null && row.id !== undefined) {
|
||
// 兼容其他可能的ID字段名
|
||
return String(row.id)
|
||
} else {
|
||
return '-'
|
||
}
|
||
}
|
||
|
||
// 格式化患者姓名(脱敏)
|
||
function formatPatientName(name) {
|
||
if (!name || typeof name !== 'string') return '-'
|
||
if (name.length <= 2) return name.charAt(0) + '*'
|
||
return name.charAt(0) + '*' + name.slice(-1)
|
||
}
|
||
|
||
// 格式化时间
|
||
function formatTime(time) {
|
||
if (!time) return '-'
|
||
return parseTime(time, '{y}-{m}-{d} {h}:{i}:{s}')
|
||
}
|
||
|
||
// 获取状态类型
|
||
function getStatusType(status) {
|
||
const statusMap = {
|
||
'WAITING': 'info',
|
||
'CALLED': 'warning',
|
||
'IN_PROGRESS': 'success',
|
||
'COMPLETED': ''
|
||
}
|
||
return statusMap[status] || ''
|
||
}
|
||
|
||
// 获取状态文本
|
||
function getStatusText(status) {
|
||
const statusMap = {
|
||
'WAITING': '等待',
|
||
'CALLED': '已叫号',
|
||
'IN_PROGRESS': '就诊中',
|
||
'COMPLETED': '已完成'
|
||
}
|
||
return statusMap[status] || '未知'
|
||
}
|
||
|
||
// 获取队列数据
|
||
async function fetchQueueData() {
|
||
try {
|
||
loading.value = true
|
||
|
||
// 调用API获取当日就诊数据
|
||
const response = await getOutpatientRegistrationCurrent({
|
||
searchKey: queryParams.searchKey,
|
||
pageNo: queryParams.pageNo,
|
||
pageSize: queryParams.pageSize
|
||
})
|
||
|
||
if (response && response.code === 200) {
|
||
// 获取记录数据
|
||
const records = response.data?.records || response.data || []
|
||
|
||
// 过滤心内科的数据(如果有organizationId)
|
||
let filteredData = records
|
||
if (queryParams.organizationId) {
|
||
filteredData = records.filter(item => item.organizationId === queryParams.organizationId)
|
||
}
|
||
|
||
// 转换为队列数据格式
|
||
queueList.value = filteredData.map(item => ({
|
||
id: item.encounterId,
|
||
patientName: item.patientName,
|
||
practitionerName: item.practitionerName || '-',
|
||
organizationName: item.organizationName,
|
||
room: item.organizationName || '-', // 使用科室名称作为诊室
|
||
displayOrder: item.displayOrder,
|
||
registerTime: item.registerTime,
|
||
status: 'WAITING' // 默认状态,实际应该从后端获取
|
||
}))
|
||
|
||
total.value = response.data?.total || filteredData.length || 0
|
||
} else {
|
||
queueList.value = []
|
||
total.value = 0
|
||
if (response && response.msg) {
|
||
ElMessage.warning('获取数据失败: ' + response.msg)
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取队列数据失败:', error)
|
||
ElMessage.error('获取队列数据失败: ' + (error.message || '未知错误'))
|
||
queueList.value = []
|
||
total.value = 0
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 刷新数据
|
||
function refreshData() {
|
||
queryParams.pageNo = 1
|
||
fetchQueueData()
|
||
}
|
||
|
||
// 叫号
|
||
async function handleCall(row) {
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
`确认叫号:${formatPatientName(row.patientName)}(${formatSerialNo(row)})?`,
|
||
'确认叫号',
|
||
{
|
||
confirmButtonText: '确认',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}
|
||
)
|
||
|
||
// TODO: 调用叫号API
|
||
// await callPatient(row.id)
|
||
|
||
// 更新当前呼叫信息
|
||
currentCall.value = {
|
||
number: row.displayOrder || '-',
|
||
name: formatPatientName(row.patientName),
|
||
room: row.room
|
||
}
|
||
|
||
// 更新状态
|
||
row.status = 'CALLED'
|
||
|
||
ElMessage.success('叫号成功')
|
||
|
||
// 刷新数据
|
||
await fetchQueueData()
|
||
} catch (error) {
|
||
if (error !== 'cancel') {
|
||
console.error('叫号失败:', error)
|
||
ElMessage.error('叫号失败: ' + (error.message || '未知错误'))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 分页大小改变
|
||
function handleSizeChange(val) {
|
||
queryParams.pageSize = val
|
||
queryParams.pageNo = 1
|
||
fetchQueueData()
|
||
}
|
||
|
||
// 页码改变
|
||
function handlePageChange(val) {
|
||
queryParams.pageNo = val
|
||
fetchQueueData()
|
||
}
|
||
|
||
// 组件挂载时获取数据
|
||
onMounted(() => {
|
||
fetchQueueData()
|
||
|
||
// 定时刷新数据(每30秒)
|
||
const refreshInterval = setInterval(() => {
|
||
fetchQueueData()
|
||
}, 30000)
|
||
|
||
// 组件卸载时清理定时器
|
||
return () => {
|
||
clearInterval(refreshInterval)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.cardiology-queue {
|
||
.app-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
.title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
}
|
||
|
||
.current-call-section {
|
||
margin: 20px 0;
|
||
text-align: center;
|
||
|
||
.call-box {
|
||
display: inline-block;
|
||
padding: 20px 40px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 12px;
|
||
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
|
||
animation: pulse 2s infinite;
|
||
|
||
.call-text {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: white;
|
||
letter-spacing: 2px;
|
||
|
||
.highlight {
|
||
color: #ffd700;
|
||
font-size: 28px;
|
||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.queue-section {
|
||
margin-top: 20px;
|
||
|
||
.section-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 15px;
|
||
padding-left: 10px;
|
||
border-left: 4px solid #667eea;
|
||
}
|
||
|
||
.pagination-wrapper {
|
||
margin-top: 20px;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% {
|
||
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.4);
|
||
}
|
||
70% {
|
||
box-shadow: 0 0 0 15px rgba(102, 126, 234, 0);
|
||
}
|
||
100% {
|
||
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0);
|
||
}
|
||
}
|
||
</style>
|
||
|