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

453 lines
14 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.

<template>
<div style="margin-bottom: 15px">
<el-button type="primary" @click="addEmr()">保存</el-button>
<el-button type="primary" plain @click="handleEmrTemplate()" style="margin-left: 20px">
模板
</el-button>
<el-button type="primary" plain @click="handleSaveTemplate()" style="margin-left: 20px">
另存模板
</el-button>
<el-button type="primary" plain @click="handleEmrHistory()" style="margin-left: 20px">
历史病历
</el-button>
<!-- 可选添加打印按钮 -->
<!-- <el-button type="primary" plain @click="printEmr" style="margin-left: 20px">
打印病历
</el-button> -->
</div>
<div
ref="printArea"
style="max-height: 650px; overflow-y: auto; overflow-x: hidden; font-family: 'SimSun', '宋体', sans-serif;"
>
<!-- 标题 -->
<div style="text-align: center; font-size: 18px; font-weight: bold; margin-bottom: 10px;">
{{ visitType === 'FIRST' ? '门诊初诊病历' : '门诊复诊病历' }}
</div>
<!-- 患者基本信息 -->
<div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>就诊卡号:</strong> {{ patientInfo.patientId || '' }}
</div>
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>姓名:</strong> {{ patientInfo.patientName || '' }}
</div>
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>性别:</strong> {{ patientInfo.genderEnum_enumText || '' }}
</div>
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>年龄:</strong> {{ patientInfo.age || '' }}
</div>
</div>
<!-- 就诊信息 -->
<div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
<div style="flex: 1; min-width: 200px; font-size: 14px; line-height: 24px;">
<strong>就诊日期:</strong> {{ currentVisitDate }}
</div>
<div style="flex: 1; min-width: 200px; font-size: 14px; line-height: 24px;">
<strong>就诊科室:</strong> {{ patientInfo.organizationName || '' }}
</div>
</div>
<!-- 生命体征 -->
<div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>身高:</strong>
<el-input
v-model="form.height"
style="width: 80px; margin-left: 5px;"
/> cm
</div>
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>体重:</strong>
<el-input
v-model="form.weight"
style="width: 80px; margin-left: 5px;"
/> kg
</div>
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>体温:</strong>
<el-input
v-model="form.temperature"
style="width: 80px; margin-left: 5px;"
:step="0.1"
/>
</div>
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>脉搏:</strong>
<el-input
v-model="form.pulse"
style="width: 80px; margin-left: 5px;"
/> /
</div>
</div>
<!-- 主诉 + 发病日期 -->
<div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
<div style="flex: 1; min-width: 300px; font-size: 14px; line-height: 24px;">
<strong>主诉:</strong>
<el-input
v-model="form.chiefComplaint"
style="width: calc(100% - 50px); margin-left: 5px;"
/>
</div>
<div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<strong>发病日期:</strong>
<el-date-picker
v-model="form.onsetDate"
type="date"
style="width: calc(100% - 80px); margin-left: 5px;"
value-format="YYYY-MM-DD"
/>
</div>
</div>
<!-- 多行文本字段 -->
<div v-for="field in textFieldList" :key="field.key" style="padding: 10px 0;">
<div style="font-size: 14px; margin-bottom: 5px;">
<strong>{{ field.label }}:</strong>
</div>
<el-input
v-model="form[field.key]"
type="textarea"
:rows="4"
style="width: 100%; font-size: 14px; line-height: 1.5;"
/>
</div>
<!-- 弹窗 -->
<el-dialog
:title="emrTitle"
v-model="openEmrTemplate"
:width="showDialog === 'emrHistory' ? '800px' : '600px'"
append-to-body
destroy-on-close
>
<emrTemplate v-if="showDialog === 'emrTemplate'" @selectRow="templateSelect" />
<emrhistory v-if="showDialog === 'emrHistory'" @selectRow="emrHistorySelect" />
<div v-if="showDialog === 'saveTemplate'">
<span>模板名称</span>
<el-input
v-model="templateName"
style="width: 260px; margin-top: 10px; margin-right: 20px"
/>
<el-radio-group v-model="radio">
<el-radio-button :label="1">个人</el-radio-button>
<el-radio-button :label="2">科室</el-radio-button>
<el-radio-button :label="3">全院</el-radio-button>
</el-radio-group>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submit"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {getEmrDetail, saveEmr, saveEmrTemplate} from '../api';
import emrTemplate from '../emr/emrtemplate.vue';
import emrhistory from '../emr/emrhistory.vue';
import {computed, getCurrentInstance, ref, watch} from 'vue';
import {formatDate as formatDateUtil} from '@/utils/index';
// Props
const props = defineProps({
patientInfo: {
type: Object,
required: true,
},
visitType: {
type: String,
default: '',
},
firstVisitDate: {
type: String,
default: '',
},
});
// Computed: 当前就诊日期
const currentVisitDate = computed(() => {
if (props.visitType === 'FOLLOW_UP' && props.firstVisitDate) {
return props.firstVisitDate;
}
return formatDateUtil(props.patientInfo?.registerTime || new Date());
});
// 表单数据
const form = ref({
height: '',
weight: '',
temperature: '',
pulse: '',
chiefComplaint: '',
onsetDate: '',
currentIllnessHistory: '',
pastMedicalHistory: '',
menstrualHistory: '',
allergyHistory: '',
physicalExamination: '',
treatment: '',
auxiliaryExamination: '',
});
// 文本字段配置(用于循环渲染)
const textFieldList = [
{ key: 'currentIllnessHistory', label: '现病史' },
{ key: 'pastMedicalHistory', label: '既往史' },
{ key: 'menstrualHistory', label: '个人史' },
{ key: 'allergyHistory', label: '过敏史' },
{ key: 'physicalExamination', label: '查体' },
{ key: 'treatment', label: '处理' },
{ key: 'auxiliaryExamination', label: '辅助检查' },
];
// 弹窗控制
const emrTitle = ref('');
const radio = ref(1);
const showDialog = ref('');
const openEmrTemplate = ref(false);
const templateName = ref('');
// 事件
const emits = defineEmits(['save']);
const { proxy } = getCurrentInstance();
console.log('EMR组件初始化proxy:', proxy);
// 监听表单变化
watch(
() => form.value,
() => {
emits('save', false);
},
{ deep: true }
);
// 格式化日期(用于 onsetDate
function formatDate(date) {
if (!date) return '';
return formatDateUtil(date);
}
// 保存病历
function addEmr() {
// 简单校验主诉
if (!form.value.chiefComplaint) {
proxy.$message.warning('请输入主诉');
return;
}
saveEmr({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
contextJson: form.value,
}).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('病历已保存');
emits('save', true);
}
});
}
// 打开模板
function handleEmrTemplate() {
emrTitle.value = '病历模板';
showDialog.value = 'emrTemplate';
openEmrTemplate.value = true;
}
// 打开历史
function handleEmrHistory() {
emrTitle.value = '历史病历';
showDialog.value = 'emrHistory';
openEmrTemplate.value = true;
sessionStorage.setItem('patientId', props.patientInfo.patientId);
}
// 保存为模板
function handleSaveTemplate() {
emrTitle.value = '保存模板';
showDialog.value = 'saveTemplate';
openEmrTemplate.value = true;
}
// 选择模板/历史
function templateSelect(row) {
form.value = { ...row };
openEmrTemplate.value = false;
}
function emrHistorySelect(row) {
form.value = { ...row };
openEmrTemplate.value = false;
}
// 弹窗确认
function submit() {
if (showDialog.value === 'saveTemplate') {
if (!templateName.value.trim()) {
proxy.$message.warning('请输入模板名称');
return;
}
saveEmrTemplate({
templateName: templateName.value,
useScopeCode: radio.value,
contextJson: form.value,
}).then((res) => {
if (res.code === 200) {
openEmrTemplate.value = false;
proxy.$modal.msgSuccess('保存成功');
}
});
} else {
openEmrTemplate.value = false;
}
}
function cancel() {
openEmrTemplate.value = false;
}
// 可选:打印功能
// function printEmr() {
// const printContent = proxy.$refs.printArea.innerHTML;
// const originalContent = document.body.innerHTML;
// document.body.innerHTML = printContent;
// window.print();
// document.body.innerHTML = originalContent;
// window.location.reload(); // 恢复组件状态
// }
// 暴露方法给父组件
defineExpose({
getDetail(encounterId) {
console.log('开始获取病历详情encounterId:', encounterId);
// 立即初始化form.value为空对象确保页面有内容显示
form.value = {};
// 检查API函数是否存在
if (typeof getEmrDetail !== 'function') {
console.error('getEmrDetail函数未定义');
if (proxy) {
proxy.$modal.msgError('获取病历详情失败API函数未定义');
}
return;
}
// 添加超时处理,防止请求一直挂起
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('获取病历详情请求超时'));
}, 10000); // 10秒超时
});
// 使用Promise.race处理正常请求和超时
Promise.race([getEmrDetail(encounterId), timeoutPromise])
.then((res) => {
console.log('获取病历详情成功,返回完整数据:', JSON.stringify(res, null, 2));
// 检查响应是否有效
if (!res) {
console.error('API返回结果为空');
if (proxy) {
proxy.$modal.msgError('获取病历详情失败API返回结果为空');
}
return;
}
console.log('res存在类型:', typeof res);
console.log('res的属性:', Object.keys(res));
// 检查响应码
if (res.code !== 200) {
console.error('API返回错误代码:', res.code);
if (proxy) {
proxy.$modal.msgError('获取病历详情失败:' + (res.msg || '未知错误'));
}
return;
}
// 检查数据是否存在
if (!res.data) {
console.log('res.data为空使用默认值');
return;
}
console.log('res.data存在类型:', typeof res.data);
console.log('res.data的属性:', Object.keys(res.data));
// 处理contextJson
try {
if (res.data.contextJson) {
console.log('contextJson存在值:', res.data.contextJson);
console.log('contextJson类型:', typeof res.data.contextJson);
// 检查contextJson类型
if (typeof res.data.contextJson === 'string') {
// 尝试解析JSON字符串
const parsedData = JSON.parse(res.data.contextJson);
// 确保解析结果是对象
if (typeof parsedData === 'object' && parsedData !== null) {
form.value = parsedData;
console.log('解析后的病历数据:', JSON.stringify(form.value, null, 2));
} else {
form.value = {};
console.error('contextJson解析结果不是有效对象:', parsedData);
}
} else if (typeof res.data.contextJson === 'object') {
// 如果已经是对象,直接使用
form.value = res.data.contextJson;
console.log('直接使用contextJson作为对象:', JSON.stringify(form.value, null, 2));
} else {
form.value = {};
console.error('contextJson类型无效:', typeof res.data.contextJson);
}
} else {
console.log('contextJson为空使用默认值');
}
} catch (e) {
form.value = {};
console.error('病历数据解析失败:', e);
console.error('解析失败的contextJson值:', res.data.contextJson);
if (proxy) {
proxy.$modal.msgError('病历数据解析失败');
}
}
emits('save', true);
})
.catch((error) => {
// 处理API调用失败或超时的情况
console.error('获取病历详情失败:', error);
if (proxy) {
proxy.$modal.msgError('获取病历详情失败:' + (error.message || '未知错误'));
}
});
},
addEmr
});
</script>
<style scoped>
/* 可选:增强打印样式 */
@media print {
body * {
visibility: hidden;
}
#printArea, #printArea * {
visibility: visible;
}
#printArea {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
}
</style>