637 lines
19 KiB
Vue
637 lines
19 KiB
Vue
<template>
|
||
<div class="app-container consultation-confirmation">
|
||
<div class="page-header">
|
||
<span class="tab-title">会诊确认</span>
|
||
</div>
|
||
|
||
<div class="action-bar no-print">
|
||
<el-button type="success" @click="handlePrint">打印</el-button>
|
||
<el-button @click="handleRefresh">刷新</el-button>
|
||
<el-button
|
||
type="primary"
|
||
:disabled="!currentRow"
|
||
@click="handleConfirm"
|
||
>
|
||
{{ confirmButtonText }}
|
||
</el-button>
|
||
<el-button
|
||
type="primary"
|
||
:disabled="!canSign"
|
||
@click="handleSign"
|
||
>
|
||
签名
|
||
</el-button>
|
||
</div>
|
||
|
||
<div class="list-section no-print">
|
||
<el-table
|
||
:data="tableData"
|
||
border
|
||
stripe
|
||
highlight-current-row
|
||
@current-change="handleRowChange"
|
||
v-loading="loading"
|
||
>
|
||
<el-table-column type="index" width="60" label="序号" align="center" />
|
||
<el-table-column label="紧急" width="70" align="center">
|
||
<template #default="scope">
|
||
<el-checkbox v-model="scope.row.urgent" disabled />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="consultationId" label="申请单号" min-width="160" />
|
||
<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="invitedObject" label="邀请对象" min-width="120" />
|
||
<el-table-column prop="applyDept" label="申请科室" min-width="120" />
|
||
<el-table-column prop="applyDoctor" label="申请医师" min-width="120" />
|
||
<el-table-column prop="applyTime" label="申请时间" min-width="160">
|
||
<template #default="scope">
|
||
{{ formatDateTime(scope.row.applyTime) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="确认" width="70" align="center">
|
||
<template #default="scope">
|
||
<el-checkbox :model-value="scope.row.myInvitedStatus >= 20" disabled />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="签名" width="70" align="center">
|
||
<template #default="scope">
|
||
<el-checkbox :model-value="scope.row.myInvitedStatus >= 30" disabled />
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<div class="section-title">会诊记录单</div>
|
||
<el-form :model="formData" label-width="110px">
|
||
<el-row :gutter="16">
|
||
<el-col :span="6">
|
||
<el-form-item label="病人姓名">
|
||
<el-input v-model="formData.patientName" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="性别">
|
||
<el-input v-model="formData.genderText" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="年龄">
|
||
<el-input v-model="formData.age" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="就诊卡号">
|
||
<el-input v-model="formData.patientIdentifierNo" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="申请单号">
|
||
<el-input v-model="formData.consultationId" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="申请科室">
|
||
<el-input v-model="formData.applyDept" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="会诊时间">
|
||
<el-input :model-value="formatDateTime(formData.consultationDate)" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="紧急标志">
|
||
<el-input :model-value="formData.urgent ? '是' : '否'" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="会诊邀请对象">
|
||
<el-input v-model="formData.invitedObject" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="提交医生">
|
||
<el-input v-model="formData.submittingPhysician" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="提交时间">
|
||
<el-input :model-value="formatDateTime(formData.submittingTime)" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-form-item label="病史及目的">
|
||
<el-input v-model="formData.consultationPurpose" type="textarea" :rows="3" disabled />
|
||
</el-form-item>
|
||
|
||
<el-form-item label="门诊诊断">
|
||
<el-input v-model="formData.provisionalDiagnosis" type="textarea" :rows="2" disabled />
|
||
</el-form-item>
|
||
|
||
<el-form-item label="会诊意见" required>
|
||
<el-input
|
||
v-model="formData.consultationOpinion"
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入会诊意见"
|
||
:disabled="!canEdit"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<!-- 会诊意见列表 -->
|
||
<el-form-item label="所有会诊意见">
|
||
<div class="opinions-container">
|
||
<el-timeline v-if="opinionList.length > 0">
|
||
<el-timeline-item
|
||
v-for="opinion in opinionList"
|
||
:key="opinion.physicianId"
|
||
:timestamp="formatDateTime(opinion.confirmTime)"
|
||
placement="top"
|
||
>
|
||
<el-card shadow="hover">
|
||
<div class="opinion-header">
|
||
<span class="physician-info">
|
||
<el-tag type="info" size="small">{{ opinion.deptName }}</el-tag>
|
||
<span style="margin-left: 8px; font-weight: bold;">{{ opinion.physicianName }}</span>
|
||
</span>
|
||
<el-tag v-if="opinion.isSigned" type="success" size="small">
|
||
<el-icon><Check /></el-icon> 已签名
|
||
</el-tag>
|
||
<el-tag v-else type="warning" size="small">已确认</el-tag>
|
||
</div>
|
||
<div class="opinion-content">
|
||
{{ opinion.opinion }}
|
||
</div>
|
||
<div v-if="opinion.signatureTime" class="opinion-footer">
|
||
签名时间:{{ formatDateTime(opinion.signatureTime) }}
|
||
</div>
|
||
</el-card>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
<el-empty v-else description="暂无会诊意见" :image-size="80" />
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<el-row :gutter="16">
|
||
<el-col :span="6">
|
||
<el-form-item label="所属医生">
|
||
<el-input v-model="formData.confirmingPhysicianName" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="代表科室">
|
||
<el-input v-model="formData.confirmingDeptName" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="签名医生">
|
||
<el-input v-model="formData.signature" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="签名时间">
|
||
<el-input :model-value="formatDateTime(formData.signatureDate)" disabled />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="consultationConfirmation">
|
||
import { computed, ref, onMounted, watch } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { Check } from '@element-plus/icons-vue'
|
||
import useUserStore from '@/store/modules/user'
|
||
import {
|
||
getPendingConfirmationList,
|
||
confirmConsultation,
|
||
cancelConfirmation,
|
||
signConsultation,
|
||
getConsultationOpinions
|
||
} from './api'
|
||
|
||
const userStore = useUserStore()
|
||
|
||
const loading = ref(false)
|
||
const tableData = ref([])
|
||
const currentRow = ref(null)
|
||
const opinionList = ref([]) // 会诊意见列表
|
||
|
||
const formData = ref({
|
||
id: null,
|
||
consultationRequestId: null,
|
||
consultationId: '',
|
||
patientName: '',
|
||
genderText: '',
|
||
age: '',
|
||
patientIdentifierNo: '',
|
||
applyDept: '',
|
||
applyDoctor: '',
|
||
applyTime: null,
|
||
consultationDate: null,
|
||
urgent: false,
|
||
invitedObject: '',
|
||
consultationPurpose: '',
|
||
provisionalDiagnosis: '',
|
||
confirmingPhysician: '',
|
||
consultationOpinion: '',
|
||
confirmingPhysicianName: '',
|
||
confirmingDeptName: '',
|
||
signature: '',
|
||
signatureDate: null,
|
||
submittingPhysician: '',
|
||
submittingTime: null,
|
||
consultationStatus: 0
|
||
})
|
||
|
||
const confirmButtonText = computed(() => {
|
||
if (!currentRow.value) {
|
||
return '确认'
|
||
}
|
||
// 基于当前医生的个人状态判断(使用ConsultationStatusEnum:20=已确认)
|
||
return currentRow.value.myInvitedStatus >= 20 ? '取消确认' : '确认'
|
||
})
|
||
|
||
const canSign = computed(() => {
|
||
// 只要当前医生已确认但未签名,就可以签名(不需要等待所有医生都确认)
|
||
// 使用ConsultationStatusEnum:20=已确认,30=已签名
|
||
return !!currentRow.value &&
|
||
currentRow.value.myInvitedStatus >= 20 && // 已确认
|
||
currentRow.value.myInvitedStatus < 30 // 但还未签名
|
||
})
|
||
|
||
const canEdit = computed(() => {
|
||
// 当前医生未确认时可以编辑(使用ConsultationStatusEnum:10=已提交/待确认,20=已确认)
|
||
return !!currentRow.value && (!currentRow.value.myInvitedStatus || currentRow.value.myInvitedStatus < 20)
|
||
})
|
||
|
||
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 getDoctorName = () => userStore.nickName || userStore.name || '当前医生'
|
||
const getDoctorDept = () => userStore.orgName || '当前科室'
|
||
|
||
const applyRowToForm = (row) => {
|
||
if (!row) {
|
||
Object.keys(formData.value).forEach((key) => {
|
||
if (key === 'urgent') {
|
||
formData.value[key] = false
|
||
} else if (key === 'consultationStatus') {
|
||
formData.value[key] = 0
|
||
} else {
|
||
formData.value[key] = key === 'id' || key === 'consultationRequestId' ? null : ''
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
formData.value = {
|
||
id: row.id,
|
||
consultationRequestId: row.consultationRequestId,
|
||
consultationId: row.consultationId,
|
||
patientName: row.patientName,
|
||
genderText: row.genderText,
|
||
age: row.age,
|
||
patientIdentifierNo: row.patientIdentifierNo,
|
||
applyDept: row.applyDept,
|
||
applyDoctor: row.applyDoctor,
|
||
applyTime: row.applyTime,
|
||
consultationDate: row.consultationDate,
|
||
urgent: row.urgent,
|
||
invitedObject: row.invitedObject,
|
||
consultationPurpose: row.consultationPurpose,
|
||
provisionalDiagnosis: row.provisionalDiagnosis,
|
||
confirmingPhysician: row.confirmingPhysician || '',
|
||
consultationOpinion: '', // 先清空,后面从 opinionList 中获取
|
||
confirmingPhysicianName: row.confirmingPhysicianName || '',
|
||
confirmingDeptName: row.confirmingDeptName || '',
|
||
signature: row.signature || '',
|
||
signatureDate: row.signatureDate,
|
||
submittingPhysician: row.submittingPhysician,
|
||
submittingTime: row.submittingTime,
|
||
consultationStatus: row.consultationStatus
|
||
}
|
||
|
||
// 从会诊意见列表中获取当前医生的信息
|
||
if (opinionList.value.length > 0) {
|
||
const currentPhysicianId = userStore.practitionerId || userStore.user?.practitionerId
|
||
const myOpinion = opinionList.value.find(op => op.physicianId === currentPhysicianId)
|
||
|
||
if (myOpinion) {
|
||
// 如果当前医生已确认,回显其信息
|
||
formData.value.confirmingPhysicianName = myOpinion.physicianName
|
||
formData.value.confirmingDeptName = myOpinion.deptName
|
||
|
||
// 回显会诊意见(去掉前缀"科室-医生:")
|
||
if (myOpinion.opinion) {
|
||
// 格式:科室-医生:意见内容
|
||
const opinionText = myOpinion.opinion
|
||
const colonIndex = opinionText.indexOf(':')
|
||
if (colonIndex > 0) {
|
||
// 提取冒号后面的内容
|
||
formData.value.consultationOpinion = opinionText.substring(colonIndex + 1)
|
||
} else {
|
||
formData.value.consultationOpinion = opinionText
|
||
}
|
||
}
|
||
|
||
if (myOpinion.isSigned) {
|
||
formData.value.signature = myOpinion.physicianName
|
||
formData.value.signatureDate = myOpinion.signatureTime
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const handleRowChange = async (row) => {
|
||
currentRow.value = row
|
||
|
||
// 先加载会诊意见列表
|
||
if (row) {
|
||
await loadConsultationOpinions(row.consultationId)
|
||
} else {
|
||
opinionList.value = []
|
||
}
|
||
|
||
// 然后应用数据到表单(这样可以使用 opinionList 中的数据)
|
||
applyRowToForm(row)
|
||
}
|
||
|
||
// 加载会诊意见列表
|
||
const loadConsultationOpinions = async (consultationId) => {
|
||
try {
|
||
const res = await getConsultationOpinions(consultationId)
|
||
if (res.code === 200) {
|
||
opinionList.value = res.data || []
|
||
}
|
||
} catch (error) {
|
||
console.error('加载会诊意见失败', error)
|
||
}
|
||
}
|
||
|
||
const handleConfirm = async () => {
|
||
if (!currentRow.value) {
|
||
return
|
||
}
|
||
|
||
try {
|
||
// 如果已确认,则取消确认(使用ConsultationStatusEnum:20=已确认,30=已签名)
|
||
if (currentRow.value.myInvitedStatus >= 20) {
|
||
// 已签名不能取消确认
|
||
if (currentRow.value.myInvitedStatus >= 30) {
|
||
ElMessage.warning('已签名的会诊无法取消确认')
|
||
return
|
||
}
|
||
|
||
await ElMessageBox.confirm('确定要取消确认吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
})
|
||
|
||
loading.value = true
|
||
const res = await cancelConfirmation(currentRow.value.consultationId)
|
||
|
||
if (res.code === 200) {
|
||
ElMessage.success('取消确认成功')
|
||
await loadData()
|
||
} else {
|
||
ElMessage.error(res.msg || '取消确认失败')
|
||
}
|
||
} else {
|
||
// 确认会诊
|
||
if (!formData.value.consultationOpinion.trim()) {
|
||
ElMessage.warning('请先填写会诊意见')
|
||
return
|
||
}
|
||
|
||
loading.value = true
|
||
const data = {
|
||
consultationId: formData.value.consultationId,
|
||
consultationOpinion: formData.value.consultationOpinion,
|
||
confirmingPhysician: getDoctorName(), // 自动使用当前医生姓名
|
||
confirmingDeptName: getDoctorDept() // 自动使用当前科室
|
||
}
|
||
|
||
const res = await confirmConsultation(data)
|
||
|
||
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 handleSign = async () => {
|
||
if (!currentRow.value) {
|
||
return
|
||
}
|
||
|
||
// 检查当前医生是否已确认(使用ConsultationStatusEnum:20=已确认,30=已签名)
|
||
if (!currentRow.value.myInvitedStatus || currentRow.value.myInvitedStatus < 20) {
|
||
ElMessage.warning('请先确认会诊后再签名')
|
||
return
|
||
}
|
||
|
||
// 检查是否已签名
|
||
if (currentRow.value.myInvitedStatus >= 30) {
|
||
ElMessage.warning('您已经签名过了')
|
||
return
|
||
}
|
||
|
||
// 🎯 移除整体状态检查:只要当前医生已确认就可以签名,不需要等待所有医生都确认
|
||
|
||
try {
|
||
await ElMessageBox.confirm('确定要签名吗?签名后将无法修改会诊意见。', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
})
|
||
|
||
loading.value = true
|
||
const res = await signConsultation(currentRow.value.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 handlePrint = () => {
|
||
if (!currentRow.value) {
|
||
ElMessage.warning('请先选择会诊申请')
|
||
return
|
||
}
|
||
window.print()
|
||
}
|
||
|
||
const handleRefresh = async () => {
|
||
await loadData()
|
||
ElMessage.success('已刷新')
|
||
}
|
||
|
||
const loadData = async () => {
|
||
try {
|
||
loading.value = true
|
||
const res = await getPendingConfirmationList()
|
||
|
||
if (res.code === 200) {
|
||
tableData.value = res.data || []
|
||
|
||
// 如果当前选中的行还在列表中,重新选中
|
||
if (currentRow.value) {
|
||
const updatedRow = tableData.value.find(
|
||
item => item.consultationId === currentRow.value.consultationId
|
||
)
|
||
if (updatedRow) {
|
||
currentRow.value = updatedRow
|
||
// 重新加载会诊意见
|
||
await loadConsultationOpinions(updatedRow.consultationId)
|
||
// 然后应用数据到表单
|
||
applyRowToForm(updatedRow)
|
||
} else {
|
||
currentRow.value = null
|
||
applyRowToForm(null)
|
||
opinionList.value = []
|
||
}
|
||
}
|
||
} else {
|
||
ElMessage.error(res.msg || '加载数据失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error)
|
||
ElMessage.error('加载数据失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadData()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.consultation-confirmation .page-header {
|
||
padding-bottom: 8px;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.consultation-confirmation .tab-title {
|
||
display: inline-block;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #4a89dc;
|
||
border-bottom: 2px solid #4a89dc;
|
||
padding-bottom: 6px;
|
||
}
|
||
|
||
.action-bar {
|
||
margin: 16px 0;
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.list-section {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
@media print {
|
||
.no-print {
|
||
display: none !important;
|
||
}
|
||
.consultation-confirmation {
|
||
padding: 0;
|
||
}
|
||
}
|
||
|
||
.opinions-container {
|
||
width: 100%;
|
||
max-height: 500px;
|
||
overflow-y: auto;
|
||
padding: 10px;
|
||
background-color: #f9fafb;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.opinion-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.physician-info {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.opinion-content {
|
||
padding: 12px;
|
||
background-color: #f5f7fa;
|
||
border-radius: 4px;
|
||
line-height: 1.6;
|
||
color: #333;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.opinion-footer {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
text-align: right;
|
||
}
|
||
|
||
.el-timeline {
|
||
padding-left: 0;
|
||
}
|
||
</style>
|