Files
his/openhis-ui-vue3/src/views/doctorstation/components/surgery/surgeryApplication.vue

1198 lines
42 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
* @Description: 门诊手术申请
-->
<template>
<div class="surgery-application-container">
<!-- 顶部操作栏 -->
<el-row :gutter="10" class="mb8 top-operation-bar">
<el-col :span="1.5">
<el-button type="primary" icon="Plus" @click="handleAdd" class="add-button">新增手术申请</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Refresh" @click="handleRefresh" class="refresh-button">刷新</el-button>
</el-col>
</el-row>
<!-- 手术申请记录表格 -->
<el-table
v-loading="loading"
:data="surgeryList"
border
row-key="id"
:row-class-name="getRowClassName"
height="calc(100vh - 250px)"
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<!-- 申请日期 -->
<el-table-column label="申请日期" align="center" prop="createTime" width="180">
<template #default="scope">
{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
</template>
</el-table-column>
<!-- 手术单号 -->
<el-table-column label="手术单号" align="center" prop="surgeryNo" width="150" show-overflow-tooltip />
<!-- 患者姓名 -->
<el-table-column label="患者姓名" align="center" prop="patientName" width="100" />
<!-- 申请医生 -->
<el-table-column label="申请医生" align="center" prop="applyDoctorName" width="100" />
<!-- 申请科室 -->
<el-table-column label="申请科室" align="center" prop="applyDeptName" width="120" show-overflow-tooltip />
<!-- 手术名称 -->
<el-table-column label="手术名称" align="center" prop="surgeryName" min-width="150" show-overflow-tooltip />
<!-- 手术等级 -->
<el-table-column label="手术等级" align="center" prop="surgeryLevel_dictText" width="100" />
<!-- 手术室确认时间 -->
<el-table-column label="手术室确认时间" align="center" prop="operatingRoomConfirmTime" width="180">
<template #default="scope">
{{ scope.row.operatingRoomConfirmTime ? parseTime(scope.row.operatingRoomConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
</template>
</el-table-column>
<!-- 手术室确认人 -->
<el-table-column label="手术室确认人" align="center" prop="operatingRoomConfirmUser" width="100" />
<!-- 状态 -->
<el-table-column label="状态" align="center" prop="statusEnum_dictText" width="100">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.statusEnum)">
{{ scope.row.statusEnum_dictText }}
</el-tag>
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column label="操作" align="center" width="200" fixed="right">
<template #default="scope">
<!-- 查看显示手术申请详情只读模式 -->
<el-button link type="primary" icon="View" @click="handleView(scope.row)">查看</el-button>
<!-- 编辑修改手术申请信息只有状态为新开的能修改 -->
<el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)" v-if="scope.row.statusEnum === 0">编辑</el-button>
<!-- 删除取消手术申请作废 -->
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-if="scope.row.statusEnum === 0 || scope.row.statusEnum === 1">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增或修改手术申请对话框 -->
<el-dialog
:title="title"
v-model="open"
width="900px"
@close="cancel"
append-to-body
:close-on-click-modal="false"
>
<el-form ref="surgeryRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="id" prop="id" v-show="false">
<el-input v-model="form.id" />
</el-form-item>
<!-- 患者基本信息区 -->
<el-divider content-position="left">患者基本信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="手术单号" prop="surgeryNo">
<el-input v-model="form.surgeryNo" disabled placeholder="系统自动生成" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="患者姓名" prop="patientName">
<el-input v-model="form.patientName" disabled placeholder="系统自动获取" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="就诊卡号" prop="encounterNo">
<el-input v-model="form.encounterNo" disabled placeholder="系统自动获取" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别" prop="patientGender">
<el-select v-model="form.patientGender" disabled placeholder="系统自动获取" style="width: 100%">
<el-option label="男" value="1" />
<el-option label="女" value="2" />
<el-option label="其他" value="9" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="年龄" prop="patientAge">
<el-input-number v-model="form.patientAge" disabled placeholder="系统自动获取" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<!-- 手术信息区 -->
<el-divider content-position="left">手术信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="手术类型" prop="surgeryTypeEnum">
<el-select v-model="form.surgeryTypeEnum" placeholder="请选择手术类型" style="width: 100%">
<el-option
v-for="item in surgery_type"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手术名称" prop="surgeryName">
<el-select
v-model="form.surgeryName"
filterable
remote
reserve-keyword
placeholder="请输入关键词搜索手术"
:remote-method="remoteSearchSurgery"
:loading="surgeryLoading"
style="width: 100%"
@change="handleSurgeryChange"
@focus="() => remoteSearchSurgery('')"
>
<el-option
v-for="item in surgeryNameList"
:key="item.id"
:label="item.name"
:value="item.name"
>
<span>{{ item.retailPrice || item.retail_price || item.salePrice || item.price || 0 }} - {{ item.name }}</span>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="拟实施手术日期" prop="plannedTime">
<el-date-picker
v-model="form.plannedTime"
type="datetime"
placeholder="选择日期时间"
value-format="YYYY-MM-DDTHH:mm:ss"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="添加次要手术" prop="appendSurgery">
<el-button type="primary" plain icon="Plus" @click="addAppendSurgery" style="width: 100%">添加次要手术</el-button>
</el-form-item>
</el-col>
</el-row>
<!-- 次要手术表格 -->
<el-row v-if="form.secondarySurgeries && form.secondarySurgeries.length > 0">
<el-col :span="24" style="margin-bottom: 20px;">
<el-table :data="form.secondarySurgeries" border stripe size="small">
<el-table-column label="手术名称" min-width="200">
<template #default="scope">
<el-select
v-model="scope.row.surgeryName"
filterable
remote
reserve-keyword
placeholder="搜索次要手术"
:remote-method="remoteSearchSurgery"
:loading="surgeryLoading"
style="width: 100%"
@change="(val) => handleSecondarySurgeryChange(val, scope.row)"
@focus="() => remoteSearchSurgery('')"
>
<el-option
v-for="item in surgeryNameList"
:key="item.id"
:label="item.name"
:value="item.name"
>
<span>{{ item.retailPrice || item.retail_price || item.salePrice || item.price || 0 }} - {{ item.name }}</span>
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="手术等级" width="130">
<template #default="scope">
<el-select v-model="scope.row.surgeryLevel" placeholder="选择等级">
<el-option label="一级手术" :value="1" />
<el-option label="二级手术" :value="2" />
<el-option label="三级手术" :value="3" />
<el-option label="四级手术" :value="4" />
</el-select>
</template>
</el-table-column>
<el-table-column label="切口类型" width="130">
<template #default="scope">
<el-select v-model="scope.row.incisionLevel" placeholder="选择切口">
<el-option label="I类切口" :value="1" />
<el-option label="II类切口" :value="2" />
<el-option label="III类切口" :value="3" />
<el-option label="IV类切口" :value="4" />
</el-select>
</template>
</el-table-column>
<el-table-column label="麻醉方式" width="180">
<template #default="scope">
<el-select
v-model="scope.row.anesthesiaName"
filterable
remote
reserve-keyword
placeholder="搜索麻醉"
:remote-method="remoteSearchAnesthesia"
:loading="anesthesiaLoading"
style="width: 100%"
@change="(val) => handleSecondaryAnesthesiaChange(val, scope.row)"
@focus="() => remoteSearchAnesthesia('')"
>
<el-option
v-for="item in anesthesiaNameList"
:key="item.id"
:label="item.name"
:value="item.name"
>
<span>{{ item.retailPrice || item.retail_price || item.salePrice || item.price || 0 }} - {{ item.name }}</span>
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="scope">
<el-button link type="danger" icon="Delete" @click="removeSecondarySurgery(scope.$index)"></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<!-- 医疗信息区 -->
<el-divider content-position="left">医疗信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="手术等级" prop="surgeryLevel">
<el-select v-model="form.surgeryLevel" placeholder="请选择手术等级" style="width: 100%">
<el-option
v-for="item in surgery_level"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="切口类型" prop="incisionLevel">
<el-select v-model="form.incisionLevel" placeholder="请选择切口类型" style="width: 100%">
<el-option
v-for="item in incision_level"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="麻醉方式" prop="anesthesiaTypeEnum">
<el-select v-model="form.anesthesiaTypeEnum" placeholder="请选择麻醉方式" style="width: 100%">
<el-option
v-for="item in anesthesia_type"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 人员信息区 -->
<el-divider content-position="left">人员信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请医生" prop="applyDoctorName">
<el-input v-model="form.applyDoctorName" disabled placeholder="系统自动获取" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="主刀医生" prop="mainSurgeonId">
<el-select v-model="form.mainSurgeonId" filterable placeholder="请选择主刀医生" style="width: 100%">
<el-option v-for="item in doctorList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="第一助手" prop="assistant1Id">
<el-select v-model="form.assistant1Id" filterable clearable placeholder="请选择第一助手" style="width: 100%">
<el-option v-for="item in doctorList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="第二助手" prop="assistant2Id">
<el-select v-model="form.assistant2Id" filterable clearable placeholder="请选择第二助手" style="width: 100%">
<el-option v-for="item in doctorList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请科室" prop="applyDeptName">
<el-input v-model="form.applyDeptName" disabled placeholder="系统自动获取" />
</el-form-item>
</el-col>
</el-row>
<!-- 其他信息区 -->
<el-divider content-position="left">其他信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="急诊标志" prop="emergencyFlag">
<el-switch
v-model="form.emergencyFlag"
:active-value="1"
:inactive-value="0"
active-text=""
inactive-text=""
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="植入高值耗材" prop="implantFlag">
<el-switch
v-model="form.implantFlag"
:active-value="1"
:inactive-value="0"
active-text=""
inactive-text=""
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="术前诊断" prop="preoperativeDiagnosis">
<el-input v-model="form.preoperativeDiagnosis" disabled placeholder="自动获取门诊诊断的主要诊断名称" type="textarea" :rows="3" />
</el-form-item>
<el-form-item label="手术指征" prop="surgeryIndication">
<el-input v-model="form.surgeryIndication" placeholder="请输入手术指征" type="textarea" :rows="3" />
</el-form-item>
<!-- 费用信息区 -->
<el-divider content-position="left">费用信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="手术费用">
<el-input v-model="form.surgeryFee" disabled placeholder="0.00">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="麻醉费用">
<el-input v-model="form.anesthesiaFee" disabled placeholder="0.00">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="总计费用">
<el-input :value="totalCalculatedFee" disabled placeholder="0.00">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button icon="Close" @click="cancel"> </el-button>
<el-button type="primary" icon="Check" @click="submitForm">提交申请</el-button>
</div>
</template>
</el-dialog>
<!-- 查看手术详情对话框 -->
<el-dialog title="手术申请详情" v-model="viewOpen" width="900px" append-to-body>
<el-descriptions :column="2" border>
<el-descriptions-item label="手术单号">{{ viewData.surgeryNo }}</el-descriptions-item>
<el-descriptions-item label="申请日期">{{ parseTime(viewData.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
<el-descriptions-item label="患者姓名">{{ viewData.patientName }}</el-descriptions-item>
<el-descriptions-item label="患者信息">{{ viewData.patientGender }} / {{ viewData.patientAge }}</el-descriptions-item>
<el-descriptions-item label="就诊流水号">{{ viewData.encounterNo }}</el-descriptions-item>
<el-descriptions-item label="手术状态">
<el-tag :type="getStatusType(viewData.statusEnum)">{{ viewData.statusEnum_dictText }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="手术名称">{{ viewData.surgeryName }}</el-descriptions-item>
<el-descriptions-item label="手术类型">{{ viewData.surgeryTypeEnum_dictText }}</el-descriptions-item>
<el-descriptions-item label="手术等级">{{ viewData.surgeryLevel_dictText }}</el-descriptions-item>
<el-descriptions-item label="麻醉方式">{{ viewData.anesthesiaTypeEnum_dictText }}</el-descriptions-item>
<el-descriptions-item label="计划时间">{{ viewData.plannedTime }}</el-descriptions-item>
<el-descriptions-item label="主刀医生">{{ viewData.mainSurgeonName }}</el-descriptions-item>
<el-descriptions-item label="申请医生">{{ viewData.applyDoctorName }}</el-descriptions-item>
<el-descriptions-item label="申请科室">{{ viewData.applyDeptName }}</el-descriptions-item>
<el-descriptions-item label="术前诊断" :span="2">{{ viewData.preoperativeDiagnosis }}</el-descriptions-item>
<el-descriptions-item label="手术指征" :span="2">{{ viewData.surgeryIndication }}</el-descriptions-item>
<!-- 次要手术详情展示 -->
<el-descriptions-item label="次要手术" :span="2" v-if="viewData.secondarySurgeries && viewData.secondarySurgeries.length > 0">
<el-table :data="viewData.secondarySurgeries" border size="small">
<el-table-column prop="surgeryName" label="手术名称" />
<el-table-column prop="surgeryCode" label="手术编码" width="120" />
<el-table-column label="等级" width="100">
<template #default="scope">
{{ scope.row.surgeryLevel === 1 ? '一级' : scope.row.surgeryLevel === 2 ? '二级' : scope.row.surgeryLevel === 3 ? '三级' : scope.row.surgeryLevel === 4 ? '四级' : '-' }}
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script setup name="SurgeryApplication">
import { getCurrentInstance, ref, computed, watch, onMounted } from 'vue'
import { getSurgeryPage, addSurgery, updateSurgery, deleteSurgery, getSurgeryDetail, updateSurgeryStatus } from '@/api/surgerymanage'
import { getEncounterDiagnosis, getDiagnosisTreatmentList, queryParticipantList } from '../api'
import { listUser } from '@/api/system/user'
import useUserStore from '@/store/modules/user'
import { useDict } from '@/utils/dict'
//字典数据 手术类型、手术等级、切口等级、麻醉方式
const { surgery_type, surgery_level, incision_level, anesthesia_type } = useDict('surgery_type', 'surgery_level', 'incision_level', 'anesthesia_type')
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
const props = defineProps({
patientInfo: {
type: Object,
default: () => ({})
},
activeTab: {
type: String,
default: ''
}
})
const loading = ref(true)
const surgeryList = ref([])
const open = ref(false)
const viewOpen = ref(false)
const isEditMode = ref(false)
const form = ref({
id: undefined,
patientId: undefined,
encounterId: undefined,
encounterNo: undefined,
patientName: undefined,
patientGender: undefined,
patientAge: undefined,
applyDoctorName: undefined,
applyDeptName: undefined,
surgeryNo: undefined,
surgeryName: undefined,
surgeryCode: undefined,
surgeryTypeEnum: undefined,
surgeryLevel: undefined,
plannedTime: undefined,
mainSurgeonId: undefined,
assistant1Id: undefined,
assistant2Id: undefined,
anesthesiaTypeEnum: undefined,
anesthesiaName: undefined,
anesthesiaFee: undefined,
surgeryFee: undefined,
totalFee: undefined,
incisionLevel: undefined,
preoperativeDiagnosis: undefined,
surgeryIndication: undefined,
emergencyFlag: 0, // 急诊标志,默认为否
implantFlag: 0, // 植入高值耗材标志,默认为否
secondarySurgeries: [] // 次要手术列表
})
const surgeryRef = ref()
const viewData = ref({})
const title = ref('')
const doctorList = ref([])
const surgeryNameList = ref([])
const anesthesiaNameList = ref([])
const surgeryLoading = ref(false)
const anesthesiaLoading = ref(false)
// 计算总费用
const totalCalculatedFee = computed(() => {
const surgeryFee = parseFloat(form.value.surgeryFee || 0)
const anesthesiaFee = parseFloat(form.value.anesthesiaFee || 0)
// 加上次要手术费用
let secondaryFee = 0
if (form.value.secondarySurgeries && form.value.secondarySurgeries.length > 0) {
secondaryFee = form.value.secondarySurgeries.reduce((acc, curr) => {
// 累加次要手术的手术费和麻醉费
const sFee = parseFloat(curr.retailPrice || curr.retail_price || curr.surgeryFee || 0)
const aFee = parseFloat(curr.anesthesiaFee || 0)
return acc + sFee + aFee
}, 0)
}
return (surgeryFee + anesthesiaFee + secondaryFee).toFixed(2)
})
// 监听总费用变化,同步到表单对象
watch(totalCalculatedFee, (newVal) => {
form.value.totalFee = newVal
})
// 字典选项
const surgeryStatusOptions = ref([
{ value: 0, label: '新开' },
{ value: 1, label: '已安排' },
{ value: 2, label: '手术中' },
{ value: 3, label: '已完成' },
{ value: 4, label: '已取消' },
{ value: 5, label: '暂停' }
])
const surgeryTypeOptions = ref([
{ value: 1, label: '门诊手术' },
{ value: 2, label: '日间手术' },
{ value: 3, label: '急诊手术' }
])
const rules = ref({
surgeryName: [{ required: true, message: '请输入手术名称', trigger: 'blur' }],
surgeryTypeEnum: [{ required: true, message: '请选择手术类型', trigger: 'change' }],
surgeryLevel: [{ required: true, message: '请选择手术等级', trigger: 'change' }],
plannedTime: [{ required: true, message: '请选择计划手术时间', trigger: 'change' }],
mainSurgeonId: [{ required: true, message: '请选择主刀医生', trigger: 'change' }],
anesthesiaName: [{ required: true, message: '请选择麻醉方式', trigger: 'change' }],
applyDoctorName: [{ required: true, message: '申请医生不能为空', trigger: 'blur' }],
applyDeptName: [{ required: true, message: '申请科室不能为空', trigger: 'blur' }]
})
// 监听患者信息变化
watch(() => props.patientInfo, (newVal) => {
if (newVal && newVal.encounterId) {
getList()
loadDiagnosisInfo()
loadDoctorList()
}
}, { immediate: true })
// 挂载时加载医生列表
onMounted(() => {
loadDoctorList()
})
// 获取手术申请列表
function getList() {
if (!props.patientInfo?.encounterId) {
surgeryList.value = []
loading.value = false
return
}
loading.value = true
getSurgeryPage({
pageNo: 1,
pageSize: 100,
encounterId: props.patientInfo.encounterId
}).then((res) => {
surgeryList.value = res.data.records || []
}).catch(error => {
console.error('获取手术列表失败:', error)
proxy.$message.error('数据加载失败,请稍后重试')
surgeryList.value = []
}).finally(() => {
loading.value = false
})
}
// 加载诊断信息
function loadDiagnosisInfo() {
if (!props.patientInfo?.encounterId) return
getEncounterDiagnosis(props.patientInfo.encounterId).then((res) => {
if (res.code == 200) {
const datas = res.data || []
const mainDiagnosis = datas.find(item => item?.maindiseFlag == 1)
if (mainDiagnosis) {
form.value.preoperativeDiagnosis = mainDiagnosis.name
}
}
}).catch(error => {
console.error('获取诊断信息失败:', error)
})
}
// 加载医生列表
function loadDoctorList() {
console.log('开始加载医生列表...')
// 同时尝试两个接口,确保能获取到数据
const p1 = queryParticipantList({ searchKey: '' }).catch(() => ({ code: 500 }))
const p2 = listUser({ pageNo: 1, pageSize: 1000 }).catch(() => ({ code: 500 }))
Promise.all([p1, p2]).then(([res1, res2]) => {
let list = []
// 方案1从参与者接口获取
if (res1.code === 200 && res1.data && Array.isArray(res1.data) && res1.data.length > 0) {
list = res1.data.map(item => ({
id: item.practitionerId || item.practitioner_id || item.id,
name: item.practitionerName || item.practitioner_name || item.nickName || item.name,
orgName: item.organizationName || item.organization_name || item.deptName
}))
console.log('从参与者接口获取到医生:', list.length)
}
// 方案2如果方案1没数据从用户接口获取
if (list.length === 0 && res2.code === 200) {
const data = res2.data?.records || res2.rows || res2.data || []
if (Array.isArray(data)) {
list = data.map(item => ({
id: item.practitionerId || item.id || item.userId,
name: item.nickName || item.name || item.userName,
orgName: item.deptName || item.organizationName || item.orgId_dictText
}))
console.log('从用户接口获取到医生:', list.length)
}
}
if (list.length > 0) {
// 去重处理 (根据ID)
const uniqueList = []
const idMap = new Set()
for (const item of list) {
if (item.id && !idMap.has(String(item.id))) {
idMap.add(String(item.id))
uniqueList.push(item)
}
}
doctorList.value = uniqueList
} else {
console.warn('两个医生接口均未返回有效数据')
}
}).catch(err => {
console.error('加载医生列表失败:', err)
})
}
// 新增
function handleAdd() {
if (!props.patientInfo?.encounterId) {
proxy.$message.warning('请先选择患者')
return
}
title.value = '新增手术申请'
open.value = true
reset()
// 自动填充患者信息
form.value.patientId = props.patientInfo.patientId
form.value.encounterId = props.patientInfo.encounterId
form.value.encounterNo = props.patientInfo.busNo
form.value.patientName = props.patientInfo.patientName
form.value.patientGender = props.patientInfo.genderEnum_enumText
form.value.patientAge = props.patientInfo.age
form.value.applyDoctorName = userStore.nickName
form.value.applyDeptName = userStore.orgName || props.patientInfo.deptName || ''
// 确保医生列表已加载
if (doctorList.value.length === 0) {
loadDoctorList()
}
// 加载诊断信息
loadDiagnosisInfo()
}
// 编辑
function handleEdit(row) {
if (row.statusEnum !== 0) {
proxy.$modal.msgWarning('当前状态不允许编辑手术,仅新开状态可编辑')
return
}
title.value = '编辑手术申请'
open.value = true
isEditMode.value = true
// 确保医生列表已加载
if (doctorList.value.length === 0) {
loadDoctorList()
}
getSurgeryDetail(row.id).then(res => {
if (res.code === 200) {
console.log('【编辑手术】完整返回数据:', res.data)
console.log('【编辑手术】手术指征字段值:', res.data.surgeryIndication)
Object.assign(form.value, res.data)
console.log('【编辑手术】赋值后form.surgeryIndication:', form.value.surgeryIndication)
// 处理麻醉名称回显
if (res.data.anesthesiaTypeEnum_dictText) {
form.value.anesthesiaName = res.data.anesthesiaTypeEnum_dictText
anesthesiaNameList.value = [{
name: res.data.anesthesiaTypeEnum_dictText,
id: 'default'
}]
}
// 处理回显
if (res.data.surgeryName) {
surgeryNameList.value = [{
name: res.data.surgeryName,
busNo: res.data.surgeryCode
}]
}
// 确保 secondarySurgeries 是数组
if (!form.value.secondarySurgeries) {
form.value.secondarySurgeries = []
}
}
}).catch(error => {
console.error('获取手术信息失败:', error)
proxy.$modal.msgError('获取手术信息失败')
})
}
// 查看
function handleView(row) {
viewOpen.value = true
getSurgeryDetail(row.id).then(res => {
if (res.code === 200) {
console.log('【手术详情】完整返回数据:', res.data)
console.log('【手术详情】手术指征字段值:', res.data.surgeryIndication)
console.log('【手术详情】手术指征字段类型:', typeof res.data.surgeryIndication)
console.log('【手术详情】手术指征是否为空:', res.data.surgeryIndication === null || res.data.surgeryIndication === undefined || res.data.surgeryIndication === '')
viewData.value = res.data
if (!viewData.value.secondarySurgeries) {
viewData.value.secondarySurgeries = []
}
console.log('【手术详情】赋值后viewData:', viewData.value)
console.log('【手术详情】赋值后viewData.surgeryIndication:', viewData.value.surgeryIndication)
}
}).catch(error => {
console.error('获取手术信息失败:', error)
proxy.$modal.msgError('获取手术信息失败')
})
}
// 删除/取消
function handleDelete(row) {
if (row.statusEnum === 0) {
// 新开状态 - 直接删除
proxy.$modal.confirm('是否确认删除手术"' + row.surgeryName + '"?').then(() => {
return deleteSurgery(row.id)
}).then(() => {
getList()
proxy.$modal.msgSuccess('删除成功')
}).catch(error => {
console.error('删除手术失败:', error)
proxy.$modal.msgError('删除失败')
})
} else if (row.statusEnum === 1) {
// 已安排状态 - 更新为已取消
proxy.$modal.confirm('是否确认取消手术"' + row.surgeryName + '"?').then(() => {
return updateSurgeryStatus(row.id, 4) // 4 = 已取消
}).then(() => {
getList()
proxy.$modal.msgSuccess('手术已取消')
}).catch(error => {
console.error('取消手术失败:', error)
proxy.$modal.msgError('取消失败')
})
} else {
// 其他状态 - 不允许操作
proxy.$modal.msgWarning('当前状态不允许取消手术')
}
}
// 刷新
function handleRefresh() {
getList()
proxy.$modal.msgSuccess('刷新成功')
}
// 远程搜索手术项目
function remoteSearchSurgery(query) {
surgeryLoading.value = true
getDiagnosisTreatmentList({
searchKey: query || '',
pageNo: 1,
pageSize: 100
}).then(res => {
// 处理不同的数据返回格式
let data = []
if (res.data && res.data.records) {
data = res.data.records
} else if (res.data && Array.isArray(res.data)) {
data = res.data
} else if (res.records && Array.isArray(res.records)) {
data = res.records
}
// 在前端进行手术分类过滤 (categoryCode 为 '24' 是手术)
// 同时过滤掉停用的项 (statusEnum 为 2 是启用)
surgeryNameList.value = data.filter(item =>
(item.categoryCode === '24' || item.categoryCode_dictText === '手术') &&
(item.statusEnum === 2 || item.statusEnum_enumText === '启用' || !item.statusEnum)
)
surgeryLoading.value = false
}).catch(error => {
console.error('搜索手术项目失败:', error)
surgeryLoading.value = false
})
}
// 远程搜索麻醉项目
function remoteSearchAnesthesia(query) {
anesthesiaLoading.value = true
getDiagnosisTreatmentList({
searchKey: query || '',
pageNo: 1,
pageSize: 100
}).then(res => {
let data = []
if (res.data && res.data.records) {
data = res.data.records
} else if (res.data && Array.isArray(res.data)) {
data = res.data
} else if (res.records && Array.isArray(res.records)) {
data = res.records
}
// 过滤麻醉项 (categoryCode 为 '25' 是麻醉)
anesthesiaNameList.value = data.filter(item =>
(item.categoryCode === '25' || item.categoryCode_dictText === '麻醉') &&
(item.statusEnum === 2 || item.statusEnum_enumText === '启用' || !item.statusEnum)
)
anesthesiaLoading.value = false
}).catch(error => {
console.error('搜索麻醉项目失败:', error)
anesthesiaLoading.value = false
})
}
// 手术项目选择变更
function handleSurgeryChange(val) {
const selected = surgeryNameList.value.find(item => item.name === val)
if (selected) {
form.value.surgeryCode = selected.busNo
// 设置手术费用 (增加对多种字段名和类型的兼容)
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
form.value.surgeryFee = parseFloat(price)
// 如果手术等级为空,可以尝试自动填充
if (!form.value.surgeryLevel && selected.level) {
form.value.surgeryLevel = selected.level
}
}
}
// 麻醉项目选择变更
function handleAnesthesiaChange(val) {
const selected = anesthesiaNameList.value.find(item => item.name === val)
if (selected) {
// 设置麻醉费用 (增加对多种字段名和类型的兼容)
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
form.value.anesthesiaFee = parseFloat(price)
// 尝试根据名称映射麻醉方式枚举
form.value.anesthesiaTypeEnum = mapAnesthesiaNameToEnum(selected.name)
}
}
// 次要手术麻醉项目选择变更
function handleSecondaryAnesthesiaChange(val, row) {
const selected = anesthesiaNameList.value.find(item => item.name === val)
if (selected) {
// 设置该次要手术的麻醉费用
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
row.anesthesiaFee = parseFloat(price)
row.anesthesiaTypeEnum = mapAnesthesiaNameToEnum(selected.name)
row.anesthesiaName = selected.name
}
}
// 辅助方法:将麻醉名称映射为枚举值
function mapAnesthesiaNameToEnum(name) {
if (!name) return undefined
if (name.includes('局部')) return 1
if (name.includes('区域')) return 2
if (name.includes('全身') || name.includes('全麻')) return 3
if (name.includes('脊椎')) return 4
if (name.includes('硬膜外')) return 5
if (name.includes('表面')) return 6
return undefined
}
// 次要手术项目选择变更
function handleSecondarySurgeryChange(val, row) {
const selected = surgeryNameList.value.find(item => item.name === val)
if (selected) {
row.surgeryCode = selected.busNo
row.surgeryName = selected.name
// 设置次要手术费用
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
row.surgeryFee = parseFloat(price)
// 自动填充等级信息(如果后端返回了)
if (selected.level) row.surgeryLevel = selected.level
}
}
// 添加次要手术
function addAppendSurgery() {
form.value.secondarySurgeries.push({
surgeryName: '',
surgeryCode: '',
surgeryLevel: undefined,
incisionLevel: undefined,
anesthesiaTypeEnum: undefined
})
}
// 移除次要手术
function removeSecondarySurgery(index) {
form.value.secondarySurgeries.splice(index, 1)
}
// 提交表单
function submitForm() {
proxy.$refs['surgeryRef'].validate((valid) => {
if (valid) {
console.log('【提交表单】完整表单数据:', JSON.parse(JSON.stringify(form.value)))
console.log('【提交表单】手术指征字段值:', form.value.surgeryIndication)
console.log('【提交表单】手术指征字段类型:', typeof form.value.surgeryIndication)
if (form.value.id == undefined) {
// 新增手术
addSurgery(form.value).then((res) => {
proxy.$modal.msgSuccess('新增成功')
open.value = false
getList()
}).catch(error => {
console.error('新增手术失败:', error)
proxy.$message.error('新增手术失败,请检查表单信息')
})
} else {
// 修改手术
updateSurgery(form.value).then((res) => {
proxy.$modal.msgSuccess('修改成功')
open.value = false
getList()
}).catch(error => {
console.error('更新手术失败:', error)
proxy.$message.error('更新手术失败,请检查表单信息')
})
}
} else {
proxy.$message.error('请检查表单信息,标红字段为必填项')
}
})
}
// 取消
function cancel() {
open.value = false
reset()
}
// 重置表单
function reset() {
form.value = {
id: undefined,
patientId: undefined,
encounterId: undefined,
encounterNo: undefined,
patientName: undefined,
patientGender: undefined,
patientAge: undefined,
applyDoctorName: undefined,
applyDeptName: undefined,
surgeryNo: undefined,
surgeryName: undefined,
surgeryCode: undefined,
surgeryTypeEnum: undefined,
surgeryLevel: undefined,
plannedTime: undefined,
mainSurgeonId: undefined,
assistant1Id: undefined,
assistant2Id: undefined,
anesthesiaTypeEnum: undefined,
anesthesiaName: undefined,
anesthesiaFee: undefined,
surgeryFee: undefined,
totalFee: undefined,
incisionLevel: undefined,
preoperativeDiagnosis: undefined,
surgeryIndication: undefined,
emergencyFlag: 0,
implantFlag: 0,
secondarySurgeries: []
}
if (surgeryRef.value) {
surgeryRef.value.resetFields()
}
}
// 获取状态标签类型
function getStatusType(status) {
const typeMap = {
0: 'info',
1: 'warning',
2: 'primary',
3: 'success',
4: 'danger',
5: 'info'
}
return typeMap[status] || 'info'
}
// 获取表格行样式
function getRowClassName({ row }) {
return row.statusEnum === 4 ? 'cancelled-row' : ''
}
// 时间格式化函数
function parseTime(time, pattern) {
if (!time) return ''
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '')
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
return value.toString().padStart(2, '0')
})
return time_str
}
defineExpose({
getList
})
</script>
<style scoped lang="scss">
.surgery-application-container {
height: 100%;
width: 100%;
padding: 10px;
/* 顶部操作栏样式 */
.top-operation-bar {
height: 60px;
display: flex;
align-items: center;
margin-bottom: 16px;
.add-button {
background-color: #5b8fb9;
color: white;
border-radius: 8px;
padding: 0 20px;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(91, 143, 185, 0.3);
}
}
.refresh-button {
background-color: transparent;
border: 1px solid #dcdfe6;
color: #606266;
border-radius: 8px;
padding: 0 20px;
&:hover {
background-color: #f5f7fa;
}
}
}
/* 表格样式 */
.el-table {
::v-deep(.cancelled-row) {
background-color: #f5f5f5;
color: #999;
text-decoration: line-through;
::v-deep(.cell) {
opacity: 0.6;
}
}
}
}
/* 对话框样式 */
.el-dialog {
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.dialog-footer {
text-align: center;
padding: 20px 0;
}
</style>