Files
his/openhis-ui-vue3/src/views/consultationmanagement/consultationapplication/index.vue
chenqi b65841c0cc fix(common): 统一异常处理并迁移打印功能到hiprint
- 替换所有System.out.println和printStackTrace为slf4j日志记录
- 在BeanUtils、AuditFieldUtil、DateUtils、ServletUtils等工具类中添加Logger实例
- 在Flowable相关控制器和服务中统一错误日志记录格式
- 在代码生成器中添加日志记录功能
- 将前端打印组件从Lodop迁移到hiprint打印方案
- 更新体温单打印功能使用hiprint预览打印
- 移除调试用的console.log语句
- 修复打印模板中线条元素类型定义
2026-03-06 22:16:44 +08:00

762 lines
21 KiB
Vue

<template>
<div class="consultation-application-container">
<!-- 页面标题 -->
<div class="page-header">
<span class="tab-title">门诊会诊申请管理</span>
</div>
<!-- 查询条件 -->
<div class="search-section">
<el-form :model="queryParams" inline>
<el-form-item label="时间类型">
<el-select v-model="queryParams.timeType" placeholder="请选择" style="width: 120px">
<el-option label="会诊时间" value="consultation" />
<el-option label="开始时间" value="start" />
<el-option label="结束时间" value="end" />
</el-select>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker
v-model="queryParams.startTime"
type="datetime"
placeholder="年/月/日 --:--"
format="YYYY/MM/DD HH:mm"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 180px"
/>
</el-form-item>
<el-form-item label="结束时间">
<el-date-picker
v-model="queryParams.endTime"
type="datetime"
placeholder="年/月/日 --:--"
format="YYYY/MM/DD HH:mm"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 180px"
/>
</el-form-item>
<el-form-item label="申请科室">
<el-input
v-model="queryParams.applyDept"
placeholder="请输入或输入科室"
clearable
style="width: 150px"
/>
</el-form-item>
<el-form-item label="申请医生">
<el-input
v-model="queryParams.applyDoctor"
placeholder="请输入或输入医生"
clearable
style="width: 150px"
/>
</el-form-item>
<el-form-item label="紧急程度">
<el-select v-model="queryParams.urgency" placeholder="全部" style="width: 120px">
<el-option label="全部" value="" />
<el-option label="一般" value="1" />
<el-option label="紧急" value="2" />
</el-select>
</el-form-item>
<el-form-item label="会诊状态">
<el-select v-model="queryParams.consultationStatus" placeholder="全部" style="width: 120px">
<el-option label="全部" value="" />
<el-option label="未提交" value="0" />
<el-option label="提交" value="10" />
<el-option label="结束" value="40" />
</el-select>
</el-form-item>
<el-form-item label="病人姓名">
<el-input
v-model="queryParams.patientName"
placeholder="请输入病人姓名"
clearable
style="width: 150px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<el-icon><Search /></el-icon> 查询
</el-button>
<el-button @click="handleReset">
<el-icon><Refresh /></el-icon> 重置
</el-button>
<el-button type="success" @click="handlePrint">
<el-icon><Printer /></el-icon> 打印
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 数据表格 -->
<div class="table-section">
<el-table
:data="tableData"
border
stripe
highlight-current-row
@current-change="handleRowChange"
v-loading="loading"
height="400"
>
<el-table-column type="index" label="ID" width="60" align="center" />
<el-table-column label="急" width="60" align="center">
<template #default="scope">
<el-checkbox :model-value="scope.row.consultationUrgency === '2'" disabled />
</template>
</el-table-column>
<el-table-column prop="patientName" label="病人姓名" min-width="100" />
<el-table-column prop="consultationDate" label="会诊时间" min-width="160">
<template #default="scope">
{{ formatDateTime(scope.row.consultationDate) }}
</template>
</el-table-column>
<el-table-column prop="department" label="申请科室" min-width="120" />
<el-table-column prop="invitedObject" label="邀请对象" min-width="150" show-overflow-tooltip />
<el-table-column prop="consultationRequestDate" label="申请时间" min-width="160">
<template #default="scope">
{{ formatDateTime(scope.row.consultationRequestDate) }}
</template>
</el-table-column>
<el-table-column prop="requestingPhysician" label="申请医师" min-width="100" />
<el-table-column label="提交" width="70" align="center">
<template #default="scope">
<el-checkbox :model-value="scope.row.consultationStatus >= 10" disabled />
</template>
</el-table-column>
<el-table-column label="结束" width="70" align="center">
<template #default="scope">
<el-checkbox :model-value="scope.row.consultationStatus >= 40" disabled />
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right" align="center">
<template #default="scope">
<el-button
type="primary"
size="small"
:icon="Edit"
@click="handleEdit(scope.row)"
:disabled="scope.row.consultationStatus >= 40"
title="编辑"
/>
<el-button
type="info"
size="small"
:icon="View"
@click="handleView(scope.row)"
title="查看"
/>
<el-button
type="danger"
size="small"
:icon="Delete"
@click="handleDelete(scope.row)"
:disabled="scope.row.consultationStatus >= 40"
title="作废"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 30, 50, 100]"
:total="pagination.total"
:disabled="loading"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<!-- 编辑/查看弹窗 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="800px"
:close-on-click-modal="false"
@close="handleDialogClose"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
:disabled="isViewMode"
>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="申请单号">
<el-input v-model="formData.consultationId" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="申请时间">
<el-input :model-value="formatDateTime(formData.consultationRequestDate)" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="病人姓名">
<el-input v-model="formData.patientName" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别">
<el-input :model-value="formData.genderEnum === 1 ? '男' : '女'" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="年龄">
<el-input v-model="formData.age" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="就诊卡号">
<el-input v-model="formData.patientIdentifierNo" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="申请医师" prop="requestingPhysician">
<el-input v-model="formData.requestingPhysician" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="会诊时间" prop="consultationDate">
<el-date-picker
v-model="formData.consultationDate"
type="datetime"
placeholder="// --:--"
format="YYYY/MM/DD HH:mm"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="申请科室">
<el-input v-model="formData.department" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="门诊诊断">
<el-input v-model="formData.provisionalDiagnosis" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="患者病史及会诊目的" prop="consultationPurpose">
<el-input
v-model="formData.consultationPurpose"
type="textarea"
:rows="4"
placeholder="请输入患者病史及会诊目的"
/>
</el-form-item>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="会诊邀请对象">
<el-input v-model="formData.invitedObject" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="会诊邀请参加医师">
<el-input placeholder="请输入参加医师姓名" disabled />
</el-form-item>
</el-col>
</el-row>
<div class="section-title">会诊记录</div>
<el-form-item label="会诊意见">
<el-input
v-model="formData.consultationOpinion"
type="textarea"
:rows="4"
placeholder="会诊意见"
disabled
/>
</el-form-item>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="所属医生">
<el-input v-model="formData.attendingPhysician" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="代表科室">
<el-input v-model="formData.representDepartment" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="签名医生">
<el-input v-model="formData.signPhysician" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="签名时间">
<el-date-picker
v-model="formData.signTime"
type="datetime"
placeholder="// --:--"
format="YYYY/MM/DD HH:mm"
disabled
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">{{ isViewMode ? '关闭' : '取消' }}</el-button>
<el-button v-if="!isViewMode" type="primary" @click="handleSave" :loading="saving">
保存
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="ConsultationApplication">
import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Refresh, Printer, Edit, View, Delete } from '@element-plus/icons-vue'
import { queryConsultationListPage, cancelConsultation, saveConsultation } from './api'
// 迁移到 hiprint
import { simplePrint, PRINT_TEMPLATE } from '@/utils/printUtils.js'
const loading = ref(false)
const saving = ref(false)
const tableData = ref([])
const currentRow = ref(null)
const dialogVisible = ref(false)
const isViewMode = ref(false)
const formRef = ref(null)
// 用于取消请求的控制器
let abortController = null
// 分页参数
const pagination = reactive({
currentPage: 1,
pageSize: 10,
total: 0
})
const queryParams = reactive({
timeType: 'consultation',
startTime: '',
endTime: '',
applyDept: '',
applyDoctor: '',
urgency: '',
consultationStatus: '',
patientName: ''
})
const formData = ref({
id: null,
consultationId: '',
patientName: '',
genderEnum: null,
age: null,
patientIdentifierNo: '',
requestingPhysician: '',
consultationDate: '',
department: '',
provisionalDiagnosis: '',
consultationPurpose: '',
invitedObject: '',
consultationOpinion: '',
attendingPhysician: '',
representDepartment: '',
signPhysician: '',
signTime: null,
consultationRequestDate: null
})
const formRules = {
requestingPhysician: [
{ required: true, message: '请输入申请医师', trigger: 'blur' }
],
consultationDate: [
{ required: true, message: '请选择会诊时间', trigger: 'change' }
],
consultationPurpose: [
{ required: true, message: '请输入患者病史及会诊目的', trigger: 'blur' }
]
}
const dialogTitle = computed(() => {
return isViewMode.value ? '会诊申请查看' : '会诊申请编辑'
})
const formatDateTime = (date) => {
if (!date) return ''
const d = new Date(date)
if (isNaN(d.getTime())) return ''
const pad = (value) => String(value).padStart(2, '0')
const yyyy = d.getFullYear()
const mm = pad(d.getMonth() + 1)
const dd = pad(d.getDate())
const hh = pad(d.getHours())
const mi = pad(d.getMinutes())
const ss = pad(d.getSeconds())
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
}
const handleQuery = async () => {
// 验证时间范围
if (queryParams.startTime && queryParams.endTime) {
const start = new Date(queryParams.startTime)
const end = new Date(queryParams.endTime)
if (end < start) {
ElMessage.warning('结束时间不能早于开始时间')
return
}
}
// 重置到第一页
pagination.currentPage = 1
await loadData()
}
const handleReset = () => {
Object.keys(queryParams).forEach(key => {
if (key === 'timeType') {
queryParams[key] = 'consultation'
} else {
queryParams[key] = ''
}
})
pagination.currentPage = 1
loadData()
}
const handleSizeChange = (val) => {
pagination.pageSize = val
pagination.currentPage = 1
loadData()
}
const handleCurrentChange = (val) => {
// 如果正在加载,忽略此次操作
if (loading.value) {
return
}
pagination.currentPage = val
loadData()
}
const handlePrint = async () => {
if (!currentRow.value) {
ElMessage.warning('请先选择一条记录')
return
}
try {
// 构建打印数据
const printData = {
patientName: currentRow.value.patientName || '',
gender: currentRow.value.genderText || '',
age: currentRow.value.age || '',
deptName: currentRow.value.department || '',
diagnosis: currentRow.value.provisionalDiagnosis || '',
consultationReason: currentRow.value.consultationPurpose || '',
applyTime: currentRow.value.applyTime || '',
applyDoctor: currentRow.value.requestingPhysician || ''
}
await simplePrint(PRINT_TEMPLATE.CONSULTATION, printData)
} catch (error) {
console.error('会诊申请单打印失败:', error)
ElMessage.error('打印失败')
}
}
const handleRowChange = (row) => {
currentRow.value = row
}
const handleEdit = (row) => {
// 检查是否已结束
if (row.consultationStatus >= 40) {
ElMessage.warning('已结束的会诊申请不可编辑')
return
}
isViewMode.value = false
formData.value = { ...row }
dialogVisible.value = true
}
const handleView = (row) => {
isViewMode.value = true
formData.value = { ...row }
dialogVisible.value = true
}
const handleDialogClose = () => {
formRef.value?.clearValidate()
formData.value = {
id: null,
consultationId: '',
patientName: '',
genderEnum: null,
age: null,
patientIdentifierNo: '',
requestingPhysician: '',
consultationDate: '',
department: '',
provisionalDiagnosis: '',
consultationPurpose: '',
invitedObject: '',
consultationOpinion: '',
attendingPhysician: '',
representDepartment: '',
signPhysician: '',
signTime: null,
consultationRequestDate: null
}
}
const handleSave = async () => {
try {
// 表单验证
await formRef.value.validate()
saving.value = true
const res = await saveConsultation(formData.value)
if (res.code === 200) {
ElMessage.success('保存成功')
dialogVisible.value = false
await loadData()
} else {
ElMessage.error(res.msg || '保存失败,请重试')
}
} catch (error) {
if (error !== 'cancel') {
console.error('保存失败:', error)
ElMessage.error('保存失败,请重试')
}
} finally {
saving.value = false
}
}
const handleDelete = async (row) => {
// 检查是否已结束
if (row.consultationStatus >= 40) {
ElMessage.warning('已结束的会诊申请不可作废')
return
}
try {
await ElMessageBox.confirm('确定要作废该会诊申请吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
loading.value = true
const res = await cancelConsultation(row.consultationId, '用户作废')
if (res.code === 200) {
ElMessage.success('作废成功')
await loadData()
} else {
ElMessage.error(res.msg || '作废失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('作废失败:', error)
ElMessage.error('作废失败')
}
} finally {
loading.value = false
}
}
const loadData = async () => {
// 如果正在加载,取消上一个请求
if (loading.value && abortController) {
abortController.abort()
}
try {
loading.value = true
// 创建新的 AbortController
abortController = new AbortController()
// 构建查询参数
const queryData = {
department: queryParams.applyDept,
requestingPhysician: queryParams.applyDoctor,
consultationStatus: queryParams.consultationStatus,
patientName: queryParams.patientName,
consultationRequestDate: queryParams.startTime,
consultationUrgency: queryParams.urgency
}
console.log('查询参数:', queryData, '页码:', pagination.currentPage, '每页:', pagination.pageSize)
const res = await queryConsultationListPage(queryData, pagination.currentPage, pagination.pageSize)
console.log('查询结果:', res)
if (res.code === 200) {
tableData.value = res.data.records || []
pagination.total = res.data.total || 0
// 如果没有查询结果,显示提示
if (tableData.value.length === 0 && pagination.currentPage === 1) {
ElMessage.info('暂无查询结果')
}
} else {
// 忽略"数据正在处理"的错误提示
if (!res.msg || !res.msg.includes('数据正在处理')) {
ElMessage.error(res.msg || '加载数据失败')
}
tableData.value = []
pagination.total = 0
}
} catch (error) {
// 忽略取消请求的错误
if (error.name === 'AbortError' || error.name === 'CanceledError') {
console.log('请求已取消')
return
}
console.error('加载数据失败:', error)
// 忽略"数据正在处理"的错误提示
const errorMsg = error.message || error.msg || '网络错误'
if (!errorMsg.includes('数据正在处理')) {
ElMessage.error('加载数据失败: ' + errorMsg)
}
tableData.value = []
pagination.total = 0
} finally {
loading.value = false
abortController = null
}
}
onMounted(() => {
loadData()
})
</script>
<style scoped>
.consultation-application-container {
padding: 20px;
background: #fff;
min-height: calc(100vh - 84px);
}
.page-header {
padding-bottom: 12px;
border-bottom: 2px solid #e5e7eb;
margin-bottom: 20px;
}
.tab-title {
display: inline-block;
font-size: 18px;
font-weight: 700;
color: #2563eb;
border-bottom: 3px solid #2563eb;
padding-bottom: 8px;
}
.search-section {
margin-bottom: 20px;
padding: 16px;
background: #f9fafb;
border-radius: 8px;
}
.table-section {
margin-top: 20px;
}
.pagination-container {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 20px 0 16px 0;
padding-bottom: 8px;
border-bottom: 2px solid #e5e7eb;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
@media print {
.search-section,
.pagination-container,
.el-table__column--selection,
.el-table-column--action {
display: none !important;
}
}
/* 表单样式优化 */
:deep(.el-form-item__label) {
font-weight: 500;
color: #606266;
}
:deep(.el-input.is-disabled .el-input__inner) {
background-color: #f5f7fa;
color: #909399;
}
:deep(.el-textarea.is-disabled .el-textarea__inner) {
background-color: #f5f7fa;
color: #909399;
}
</style>