refactor(ui): 优化按钮样式和数据加载逻辑

- 将多个按钮组件从 type="text" 改为 link 属性,提升界面美观性
- 修复 PatientList 组件中姓名显示的文本截断功能
- 在住院记录模板中添加对 patientInfo 变化的监听,自动更新表单数据
- 优化打印机列表获取逻辑,添加连接状态检查和警告信息
- 移除不必要的防抖和重复请求防护逻辑,简化代码实现
- 修复多处组件中对 patientInfo 属性访问的安全性问题
- 优化病历数据加载时机,移除防抖包装直接调用加载函数
- 改进数据设置逻辑,避免覆盖未传入字段的原有值
- 调整组件属性定义,使 patientInfo 参数变为可选并设置默认值
- 优化患者切换时的组件重置和数据加载流程
This commit is contained in:
2026-01-27 17:32:03 +08:00
parent 0f0dc70c7e
commit 4f0cc1a0c4
21 changed files with 232 additions and 387 deletions

View File

@@ -12,7 +12,7 @@
@keyup.enter="handleSearch" @keyup.enter="handleSearch"
:prefix-icon="Search" :prefix-icon="Search"
/> />
<el-button class="icon-btn" circle @click="handleRefresh" type="text" plain> <el-button class="icon-btn" circle @click="handleRefresh" link plain>
<el-icon icon-class="Refresh" size="24" :class="{ 'is-rotating': refreshing }"> <el-icon icon-class="Refresh" size="24" :class="{ 'is-rotating': refreshing }">
<Refresh /> <Refresh />
</el-icon> </el-icon>
@@ -36,7 +36,7 @@
<!-- 第1行姓名 --> <!-- 第1行姓名 -->
<div class="info-row name-row"> <div class="info-row name-row">
<div class="name"> <div class="name">
<el-text :text="item.patientName" tclass="name" width="auto"> <el-text :text="item.patientName" class="name" :truncated="true">
{{ item.patientName || '-' }} {{ item.patientName || '-' }}
</el-text> </el-text>
</div> </div>

View File

@@ -23,7 +23,7 @@
<el-form-item v-if="showDefaultButtons" style="margin-left: 20px"> <el-form-item v-if="showDefaultButtons" style="margin-left: 20px">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button v-if="needCollapse" type="text" @click="toggleExpand" style="margin-left: 16px"> <el-button v-if="needCollapse" link @click="toggleExpand" style="margin-left: 16px">
{{ isExpanded ? '收起' : '展开' }} {{ isExpanded ? '收起' : '展开' }}
<el-icon class="el-icon--right"> <el-icon class="el-icon--right">
<DArrowLeft v-if="isExpanded" class="collapse-arrow collapse-arrow--up" /> <DArrowLeft v-if="isExpanded" class="collapse-arrow collapse-arrow--up" />

View File

@@ -565,6 +565,26 @@ const rules = reactive({
chiefComplaint: [{ required: true, message: '请填写主诉', trigger: ['blur', 'submit'] }], chiefComplaint: [{ required: true, message: '请填写主诉', trigger: ['blur', 'submit'] }],
}); });
// 监听 patientInfo 变化,更新表单数据
watch(
() => props.patientInfo,
(newPatientInfo) => {
if (newPatientInfo) {
// 更新基础信息
formData.patientName = newPatientInfo.patientName || newPatientInfo.name || '';
formData.hospitalNo = newPatientInfo.busNo || newPatientInfo.hospitalNo || '';
formData.gender = newPatientInfo.genderEnum_enumText || newPatientInfo.gender || '';
formData.age = newPatientInfo.age || '';
formData.nation = newPatientInfo.nation || '';
formData.occupation = newPatientInfo.profession || '';
formData.marriage = newPatientInfo.maritalStatus || '';
formData.birthplace = newPatientInfo.birthPlace || '';
// 可以根据需要更新更多字段
}
},
{ deep: true, immediate: true }
);
// 生命周期 // 生命周期
onMounted(() => { onMounted(() => {
// 初始化记录时间为当前时间 // 初始化记录时间为当前时间
@@ -578,16 +598,16 @@ onMounted(() => {
formData.signDate = formatDateTime(new Date()); formData.signDate = formatDateTime(new Date());
} }
if (!formData.patientName) { if (!formData.patientName) {
formData.patientName = patient?.patientName || ''; formData.patientName = patient?.patientName || patient?.name || '';
} }
if (!formData.gender) { if (!formData.gender) {
formData.gender = patient?.genderEnum_enumText || ''; formData.gender = patient?.genderEnum_enumText || patient?.gender || '';
} }
if (!formData.age) { if (!formData.age) {
formData.age = patient?.age || ''; formData.age = patient?.age || '';
} }
if (!formData.hospitalNo) { if (!formData.hospitalNo) {
formData.hospitalNo = patient?.busNo || ''; formData.hospitalNo = patient?.busNo || patient?.hospitalNo || '';
} }
}); });

View File

@@ -334,11 +334,13 @@ export function handleColor(a, b, status) {
*/ */
export function getPrinterList() { export function getPrinterList() {
try { try {
const printerList = if (window.hiprint && window.hiprint.hiwebSocket && window.hiprint.hiwebSocket.connected) {
window.hiprint && window.hiprint.hiwebSocket const printerList = window.hiprint.hiwebSocket.getPrinterList();
? window.hiprint.hiwebSocket.getPrinterList()
: [];
return printerList || []; return printerList || [];
} else {
console.warn('打印服务未连接,返回空打印机列表');
return [];
}
} catch (error) { } catch (error) {
console.error('获取打印机列表失败:', error); console.error('获取打印机列表失败:', error);
return []; return [];

View File

@@ -151,11 +151,13 @@ export const PRINT_TEMPLATE = {
*/ */
export function getPrinterList() { export function getPrinterList() {
try { try {
const printerList = if (window.hiprint && window.hiprint.hiwebSocket && window.hiprint.hiwebSocket.connected) {
window.hiprint && window.hiprint.hiwebSocket const printerList = window.hiprint.hiwebSocket.getPrinterList();
? window.hiprint.hiwebSocket.getPrinterList()
: [];
return printerList || []; return printerList || [];
} else {
console.warn('打印服务未连接,返回空打印机列表');
return [];
}
} catch (error) { } catch (error) {
console.error('获取打印机列表失败:', error); console.error('获取打印机列表失败:', error);
return []; return [];

View File

@@ -52,8 +52,8 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" width="100" fixed="right" align="center"> <el-table-column label="操作" width="100" fixed="right" align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-button type="text" @click="editTemplate(row)">编辑</el-button> <el-button link @click="editTemplate(row)">编辑</el-button>
<el-button type="text" @click="deleteTemplate(row.id)" style="color: #f56c6c"> <el-button link @click="deleteTemplate(row.id)" style="color: #f56c6c">
删除 删除
</el-button> </el-button>
</template> </template>

View File

@@ -42,7 +42,6 @@ import {defineEmits, ref, unref} from 'vue';
import {deleteRecord, getRecordByEncounterIdList} from '../api'; import {deleteRecord, getRecordByEncounterIdList} from '../api';
import {ElMessage} from 'element-plus'; import {ElMessage} from 'element-plus';
import {patientInfo} from '../../store/patient.js'; import {patientInfo} from '../../store/patient.js';
import apiRequestManager from '@/utils/apiRequestManager.js';
const emits = defineEmits(['historyClick']); const emits = defineEmits(['historyClick']);
const props = defineProps({ const props = defineProps({
@@ -68,30 +67,15 @@ const queryParams = ref({
isPage: 0, isPage: 0,
}); });
const historyData = ref([]); const historyData = ref([]);
// 防止重复加载的标志
let isLoadingHistory = false;
const queryList = async () => { const queryList = async () => {
// 防止重复加载
if (isLoadingHistory) {
console.log('History data is already loading, skipping duplicate call');
return;
}
isLoadingHistory = true;
try { try {
if (patientInfo.value.encounterId && unref(definitionId) && unref(definitionId) !== '') { if (patientInfo.value.encounterId && unref(definitionId) && unref(definitionId) !== '') {
const res = await apiRequestManager.execute( const res = await getRecordByEncounterIdList({
getRecordByEncounterIdList, ...queryParams.value,
'/document/record/getRecordByEncounterIdList',
{
isPage: 0, // 确保参数一致,便于去重
encounterId: patientInfo.value.encounterId, encounterId: patientInfo.value.encounterId,
patientId: patientInfo.value.patientId, patientId: patientInfo.value.patientId,
definitionId: unref(definitionId), definitionId: unref(definitionId),
} });
);
historyData.value = res.data || []; historyData.value = res.data || [];
} else { } else {
historyData.value = []; historyData.value = [];
@@ -99,8 +83,6 @@ const queryList = async () => {
} catch (error) { } catch (error) {
// ElMessage.error(' 获取模板树失败 '); // ElMessage.error(' 获取模板树失败 ');
historyData.value = []; historyData.value = [];
} finally {
isLoadingHistory = false; // 重置加载标志
} }
}; };
const handleNodeClick = (data) => { const handleNodeClick = (data) => {

View File

@@ -103,7 +103,6 @@ import dayjs from 'dayjs';
// 打印工具 // 打印工具
import {PRINT_TEMPLATE, simplePrint} from '@/utils/printUtils.js'; import {PRINT_TEMPLATE, simplePrint} from '@/utils/printUtils.js';
import {getEncounterDiagnosis} from '../api'; import {getEncounterDiagnosis} from '../api';
import apiRequestManager from '@/utils/apiRequestManager.js';
import History from './components/history'; import History from './components/history';
import Template from './components/template'; import Template from './components/template';
import TemplateEdit from './components/templateEdit.vue'; import TemplateEdit from './components/templateEdit.vue';
@@ -206,7 +205,7 @@ const handleNodeClick = (data, node) => {
// 选择任何病历模板后,都加载该病历类型的最新历史记录 // 选择任何病历模板后,都加载该病历类型的最新历史记录
if (node.isLeaf && props.patientInfo && props.patientInfo.patientId) { if (node.isLeaf && props.patientInfo && props.patientInfo.patientId) {
debouncedLoadLatestMedicalRecord(); loadLatestMedicalRecord();
} }
}, 100); }, 100);
}); });
@@ -280,7 +279,7 @@ const handleSubmitOk = async (data) => {
// 等待历史记录列表更新后,重新加载最新病历并更新选中状态 // 等待历史记录列表更新后,重新加载最新病历并更新选中状态
setTimeout(() => { setTimeout(() => {
debouncedLoadLatestMedicalRecord(); loadLatestMedicalRecord();
}, 100); }, 100);
} catch (error) { } catch (error) {
ElMessage.error('提交失败'); ElMessage.error('提交失败');
@@ -411,7 +410,7 @@ const selectOutpatientMedicalRecordTemplate = async () => {
// 等待模板加载完成,然后获取并回显最新病历数据 // 等待模板加载完成,然后获取并回显最新病历数据
setTimeout(() => { setTimeout(() => {
historyRef.value?.queryList(); historyRef.value?.queryList();
debouncedLoadLatestMedicalRecord(); loadLatestMedicalRecord();
}, 500); }, 500);
}); });
} else { } else {
@@ -422,36 +421,19 @@ const selectOutpatientMedicalRecordTemplate = async () => {
// 当前选中的历史病历ID用于在History组件中高亮显示 // 当前选中的历史病历ID用于在History组件中高亮显示
const selectedHistoryRecordId = ref(''); const selectedHistoryRecordId = ref('');
import { debounce } from 'lodash-es';
// 防止重复加载的标志
let isLoadingLatestRecord = false;
// 加载最新的病历数据并回显 // 加载最新的病历数据并回显
const loadLatestMedicalRecord = async () => { const loadLatestMedicalRecord = async () => {
if (!patientInfo.value.encounterId || !currentSelectTemplate.value.id) return; if (!patientInfo.value.encounterId || !currentSelectTemplate.value.id) return;
// 防止重复加载
if (isLoadingLatestRecord) {
console.log('Latest medical record is already loading, skipping duplicate call');
return;
}
isLoadingLatestRecord = true;
loading.value = true; loading.value = true;
try { try {
// 获取患者的历史病历记录 // 获取患者的历史病历记录
const res = await apiRequestManager.execute( const res = await getRecordByEncounterIdList({
getRecordByEncounterIdList,
'/document/record/getRecordByEncounterIdList',
{
isPage: 0, isPage: 0,
encounterId: patientInfo.value.encounterId, encounterId: patientInfo.value.encounterId,
patientId: patientInfo.value.patientId, patientId: patientInfo.value.patientId,
definitionId: currentSelectTemplate.value.id, definitionId: currentSelectTemplate.value.id,
} });
);
const historyRecords = res.data || []; const historyRecords = res.data || [];
if (historyRecords.length > 0) { if (historyRecords.length > 0) {
@@ -537,12 +519,8 @@ const loadLatestMedicalRecord = async () => {
}); });
} finally { } finally {
loading.value = false; loading.value = false;
isLoadingLatestRecord = false; // 重置加载标志
} }
}; };
// 防抖版本的加载最新病历数据函数
const debouncedLoadLatestMedicalRecord = debounce(loadLatestMedicalRecord, 300);
const templateRef = ref(null); const templateRef = ref(null);
const handleTemplateClick = (data) => { const handleTemplateClick = (data) => {
@@ -772,7 +750,7 @@ const selectDefaultTemplate = () => {
// 直接加载最新病历数据不再使用额外的setTimeout延迟 // 直接加载最新病历数据不再使用额外的setTimeout延迟
// 因为handleNodeClick中已经有nextTick和setTimeout处理组件渲染 // 因为handleNodeClick中已经有nextTick和setTimeout处理组件渲染
debouncedLoadLatestMedicalRecord(); loadLatestMedicalRecord();
}); });
} else { } else {
console.log('未找到门诊病历模板'); console.log('未找到门诊病历模板');

View File

@@ -59,8 +59,8 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120" align="center"> <el-table-column label="操作" width="120" align="center">
<template #default="scope"> <template #default="scope">
<el-button type="text" size="small" @click="handlePrint(scope.row)">打印</el-button> <el-button link size="small" @click="handlePrint(scope.row)">打印</el-button>
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button> <el-button link size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -379,7 +379,7 @@
<!-- 标题栏 --> <!-- 标题栏 -->
<div class="selected-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee;"> <div class="selected-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee;">
<span style="font-weight: bold; color: #1a2b6d">已选择</span> <span style="font-weight: bold; color: #1a2b6d">已选择</span>
<el-button type="text" @click="clearAllSelected" style="color: #f56c6c">清空</el-button> <el-button link @click="clearAllSelected" style="color: #f56c6c">清空</el-button>
</div> </div>
<!-- 已选项目列表 --> <!-- 已选项目列表 -->
@@ -393,7 +393,7 @@
<span class="item-itemName">{{ item.itemName }}</span> <span class="item-itemName">{{ item.itemName }}</span>
<span class="item-price">¥{{ item.itemPrice }}</span> <span class="item-price">¥{{ item.itemPrice }}</span>
<el-button <el-button
type="text" link
size="small" size="small"
style="color: #f56c6c; margin-left: auto" style="color: #f56c6c; margin-left: auto"
@click="removeInspectionItem(item)" @click="removeInspectionItem(item)"

View File

@@ -213,7 +213,6 @@ import useUserStore from '@/store/modules/user';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { updatePatientInfo } from './components/store/patient.js'; import { updatePatientInfo } from './components/store/patient.js';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { debounce } from 'lodash-es';
// // 监听路由离开事件 // // 监听路由离开事件
// onBeforeRouteLeave((to, from, next) => { // onBeforeRouteLeave((to, from, next) => {
@@ -493,8 +492,7 @@ function handleOpen() {
patientDrawerRef.value.refreshList(); patientDrawerRef.value.refreshList();
} }
// 原始的handleCardClick函数 function handleCardClick(item, index) {
function handleCardClickOriginal(item, index) {
console.log('handleCardClick 被调用'); console.log('handleCardClick 被调用');
console.log('点击的患者项目:', item); console.log('点击的患者项目:', item);
console.log('患者项目中的encounterId:', item.encounterId); console.log('患者项目中的encounterId:', item.encounterId);
@@ -560,9 +558,6 @@ function handleCardClickOriginal(item, index) {
}); });
} }
// 使用防抖的handleCardClick函数防止短时间内多次点击
const handleCardClick = debounce(handleCardClickOriginal, 500);
function handleLeave(encounterId) { function handleLeave(encounterId) {
leaveEncounter(encounterId).then((res) => { leaveEncounter(encounterId).then((res) => {
if (res.code == 200) { if (res.code == 200) {
@@ -615,7 +610,7 @@ function handleHospitalizationClick() {
// 接诊回调 // 接诊回调
function handleReceive(row) { function handleReceive(row) {
handleCardClickOriginal(row); handleCardClick(row);
currentEncounterId.value = row.encounterId; currentEncounterId.value = row.encounterId;
drawer.value = false; drawer.value = false;
getPatientList(); getPatientList();
@@ -802,7 +797,7 @@ const markSeen = async () => {
currentCallPatient.value = {}; currentCallPatient.value = {};
}; };
const callThis = (row) => { const callThis = (row) => {
handleCardClickOriginal(row); handleCardClick(row);
currentCallPatient.value = row; currentCallPatient.value = row;
dialogVisible.value = false; dialogVisible.value = false;
// 刷新患者列表和候诊列表 // 刷新患者列表和候诊列表

View File

@@ -699,7 +699,12 @@ const getDom = () => {
}; };
const setData = (data) => { const setData = (data) => {
console.log('设置数据=========>', JSON.stringify(data)); console.log('设置数据=========>', JSON.stringify(data));
Object.assign(formData, data); // 仅更新传入的数据,保留未传入字段的原有值
Object.keys(data).forEach(key => {
if (formData.hasOwnProperty(key)) {
formData[key] = data[key];
}
});
}; };
defineExpose({ defineExpose({
setData, setData,

View File

@@ -106,7 +106,7 @@ watch(
getList(); getList();
function getList() { function getList() {
loading.value = true; loading.value = true;
queryParams.value.organizationId = props.patientInfo.inHospitalOrgId; queryParams.value.organizationId = props.patientInfo?.inHospitalOrgId || '';
getAdviceBaseInfo(queryParams.value) getAdviceBaseInfo(queryParams.value)
.then((res) => { .then((res) => {
console.log(res.data.records); console.log(res.data.records);

View File

@@ -203,7 +203,8 @@ const form = ref({
const props = defineProps({ const props = defineProps({
patientInfo: { patientInfo: {
type: Object, type: Object,
required: true, required: false,
default: () => ({}),
}, },
}); });
const emits = defineEmits(['diagnosisSave']); const emits = defineEmits(['diagnosisSave']);

View File

@@ -10,25 +10,25 @@
<el-button <el-button
type="primary" type="primary"
@click="showApplicationFormDialog('LaboratoryTests')" @click="showApplicationFormDialog('LaboratoryTests')"
:disabled="!patientInfo?.inHospitalOrgId" :disabled="!props.patientInfo?.inHospitalOrgId"
>检验</el-button >检验</el-button
> >
<el-button <el-button
type="primary" type="primary"
@click="showApplicationFormDialog('MedicalExaminations')" @click="showApplicationFormDialog('MedicalExaminations')"
:disabled="!patientInfo?.inHospitalOrgId" :disabled="!props.patientInfo?.inHospitalOrgId"
>检查</el-button >检查</el-button
> >
<el-button <el-button
type="primary" type="primary"
@click="showApplicationFormDialog('BloodTransfusion')" @click="showApplicationFormDialog('BloodTransfusion')"
:disabled="!patientInfo?.inHospitalOrgId" :disabled="!props.patientInfo?.inHospitalOrgId"
>输血</el-button >输血</el-button
> >
<el-button <el-button
type="primary" type="primary"
@click="showApplicationFormDialog('Surgery')" @click="showApplicationFormDialog('Surgery')"
:disabled="!patientInfo?.inHospitalOrgId" :disabled="!props.patientInfo?.inHospitalOrgId"
>手术</el-button >手术</el-button
> >
</el-button-group> </el-button-group>
@@ -58,14 +58,19 @@
<script setup> <script setup>
import {computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, reactive, ref,} from 'vue'; import {computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, reactive, ref,} from 'vue';
import BloodTransfusion from './bloodTransfusion.vue'; import BloodTransfusion from './bloodTransfusion.vue';
import {patientInfo} from '../../../store/patient.js';
import Surgery from './surgery.vue'; import Surgery from './surgery.vue';
import LaboratoryTests from './laboratoryTests.vue'; import LaboratoryTests from './laboratoryTests.vue';
import MedicalExaminations from './medicalExaminations.vue'; import MedicalExaminations from './medicalExaminations.vue';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const emits = defineEmits(['refResh']); const emits = defineEmits(['refResh']);
const props = defineProps({}); const props = defineProps({
patientInfo: {
type: Object,
required: false,
default: () => ({}),
},
});
const state = reactive({}); const state = reactive({});
const components = ref({ const components = ref({
BloodTransfusion, BloodTransfusion,

View File

@@ -297,16 +297,17 @@
</el-table> </el-table>
</div> </div>
<!-- // 底部按钮 --> <!-- // 底部按钮 -->
<application-form-bottom-btn @refResh="refresh" /> <application-form-bottom-btn :patientInfo="patientInfo" @refResh="refresh" />
<OrderGroupDrawer <OrderGroupDrawer
ref="orderFroupRef" ref="orderFroupRef"
:diagnosis="diagnosisInfo" :diagnosis="diagnosisInfo"
:organizationId="patientInfo?.orgId || ''"
@useOrderGroup="handleSaveGroup" @useOrderGroup="handleSaveGroup"
/> />
<PrescriptionHistory <PrescriptionHistory
ref="prescriptionHistoryRef" ref="prescriptionHistoryRef"
:diagnosis="diagnosisInfo" :diagnosis="diagnosisInfo"
:patientInfo="patientInfo" :patientInfo="patientInfo || {}"
@userPrescriptionHistory="handleSaveHistory" @userPrescriptionHistory="handleSaveHistory"
/> />
<LeaveHospitalDialog <LeaveHospitalDialog
@@ -405,7 +406,8 @@ const buttonDisabled = computed(() => {
const props = defineProps({ const props = defineProps({
patientInfo: { patientInfo: {
type: Object, type: Object,
required: true, required: false,
default: () => ({}),
}, },
activeTab: { activeTab: {
type: String, type: String,
@@ -525,33 +527,19 @@ function getList() {
function refresh() { function refresh() {
getListInfo(false); getListInfo(false);
} }
// 防止重复请求的标志
let listInfoRequestPromise = null;
// 获取列表信息 // 获取列表信息
function getListInfo(addNewRow) { function getListInfo(addNewRow) {
// 如果已经有正在进行的请求则返回该请求的Promise
if (listInfoRequestPromise) {
return listInfoRequestPromise;
}
loadingInstance = ElLoading.service({ fullscreen: true }); loadingInstance = ElLoading.service({ fullscreen: true });
setTimeout(() => { setTimeout(() => {
if (loadingInstance) {
loadingInstance.close(); loadingInstance.close();
}
}, 180); }, 180);
isAdding.value = false; isAdding.value = false;
expandOrder.value = []; expandOrder.value = [];
getPrescriptionList(patientInfo.value.encounterId).then((res) => {
console.log('getListInfo==========>', JSON.stringify(res.data));
// 并行请求两个API并将结果合并处理 loadingInstance.close();
listInfoRequestPromise = Promise.all([ prescriptionList.value = res.data
getPrescriptionList(patientInfo.value.encounterId),
getContract({ encounterId: patientInfo.value.encounterId })
])
.then(([prescriptionRes, contractRes]) => {
// 处理处方列表
prescriptionList.value = prescriptionRes.data
.map((item) => { .map((item) => {
return { return {
...JSON.parse(item.contentJson), ...JSON.parse(item.contentJson),
@@ -563,35 +551,15 @@ function getListInfo(addNewRow) {
.sort((a, b) => { .sort((a, b) => {
return new Date(b.requestTime) - new Date(a.requestTime); return new Date(b.requestTime) - new Date(a.requestTime);
}); });
getGroupMarkers(); // 更新标记
// 处理合同列表
contractList.value = contractRes.data;
// 更新账户ID
accountId.value = patientInfo.value.accountId;
// 更新标记
getGroupMarkers();
if (props.activeTab == 'prescription' && addNewRow) { if (props.activeTab == 'prescription' && addNewRow) {
handleAddPrescription(); handleAddPrescription();
} }
console.log('getListInfo==========>', JSON.stringify(prescriptionRes.data));
})
.catch(error => {
console.error('获取列表信息失败:', error);
ElMessage.error('获取列表信息失败');
})
.finally(() => {
if (loadingInstance) {
loadingInstance.close();
}
// 请求完成后清除Promise引用
listInfoRequestPromise = null;
}); });
getContract({ encounterId: patientInfo.value.encounterId }).then((res) => {
return listInfoRequestPromise; contractList.value = res.data;
});
accountId.value = patientInfo.value.accountId;
} }
// 数据过滤 // 数据过滤
const filterPrescriptionList = computed(() => { const filterPrescriptionList = computed(() => {
@@ -605,17 +573,8 @@ const filterPrescriptionList = computed(() => {
return pList; return pList;
}); });
// 防止诊断信息重复请求的标志
let diagnosisInfoRequestPromise = null;
function getDiagnosisInfo() { function getDiagnosisInfo() {
// 如果已经有正在进行的请求则返回该请求的Promise getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => {
if (diagnosisInfoRequestPromise) {
return diagnosisInfoRequestPromise;
}
diagnosisInfoRequestPromise = getEncounterDiagnosis(patientInfo.value.encounterId)
.then((res) => {
diagnosisList.value = res.data; diagnosisList.value = res.data;
let diagnosisInfo = diagnosisList.value.filter((item) => { let diagnosisInfo = diagnosisList.value.filter((item) => {
return item.maindiseFlag == 1; return item.maindiseFlag == 1;
@@ -625,17 +584,7 @@ function getDiagnosisInfo() {
conditionId.value = diagnosisInfo[0].conditionId; conditionId.value = diagnosisInfo[0].conditionId;
encounterDiagnosisId.value = diagnosisInfo[0].encounterDiagnosisId; encounterDiagnosisId.value = diagnosisInfo[0].encounterDiagnosisId;
diagnosisName.value = diagnosisInfo[0].name; diagnosisName.value = diagnosisInfo[0].name;
})
.catch(error => {
console.error('获取诊断信息失败:', error);
ElMessage.error('获取诊断信息失败');
})
.finally(() => {
// 请求完成后清除Promise引用
diagnosisInfoRequestPromise = null;
}); });
return diagnosisInfoRequestPromise;
} }
function getRowDisabled(row) { function getRowDisabled(row) {

View File

@@ -65,40 +65,22 @@ const queryParams = ref({
isPage: 0, isPage: 0,
}); });
const historyData = ref([]); const historyData = ref([]);
// 防止重复请求的标志
let queryListPromise = null;
const queryList = async () => { const queryList = async () => {
// 如果已经有正在进行的请求则返回该请求的Promise
if (queryListPromise) {
return queryListPromise;
}
try { try {
if (patientInfo.value.encounterId && unref(definitionId) && unref(definitionId) !== '') { if (patientInfo.value.encounterId && unref(definitionId) && unref(definitionId) !== '') {
queryListPromise = getRecordByEncounterIdList({ const res = await getRecordByEncounterIdList({
...queryParams.value, ...queryParams.value,
encounterId: patientInfo.value.encounterId, encounterId: patientInfo.value.encounterId,
patientId: patientInfo.value.patientId, patientId: patientInfo.value.patientId,
definitionId: unref(definitionId), definitionId: unref(definitionId),
})
.then(res => {
historyData.value = res.data || [];
})
.finally(() => {
// 请求完成后清除Promise引用
queryListPromise = null;
}); });
historyData.value = res.data || [];
return queryListPromise;
} else { } else {
historyData.value = []; historyData.value = [];
} }
} catch (error) { } catch (error) {
// 不显示错误消息,避免干扰用户体验 // 不显示错误消息,避免干扰用户体验
historyData.value = []; historyData.value = [];
// 请求完成后清除Promise引用
queryListPromise = null;
} }
}; };

View File

@@ -211,11 +211,11 @@ const handleNodeClick = (data, node) => {
}; };
// 先清空当前组件,再设置新组件,确保组件完全重新渲染 // 先清空当前组件,再设置新组件,确保组件完全重新渲染
currentComponent.value = ''; currentComponent.value = undefined;
// 使用 nextTick 确保 DOM 更新后再设置新组件 // 使用 nextTick 确保 DOM 更新后再设置新组件
nextTick(() => { nextTick(() => {
currentComponent.value = currentSelectTemplate.value.vueRouter || ''; currentComponent.value = currentSelectTemplate.value.vueRouter;
}); });
} else { } else {
currentSelectTemplate.value = { currentSelectTemplate.value = {
@@ -241,7 +241,7 @@ const handleNodeClick = (data, node) => {
const newEmr = () => { const newEmr = () => {
if (currentSelectTemplate.value) { if (currentSelectTemplate.value) {
currentComponent.value = currentSelectTemplate.value.vueRouter || ''; currentComponent.value = currentSelectTemplate.value.vueRouter;
return; return;
} }
ElMessage.error('请选择模版!'); ElMessage.error('请选择模版!');
@@ -305,10 +305,9 @@ const handleSubmitOk = async (data) => {
// templateRef.value?.queryList(); // templateRef.value?.queryList();
// 等待历史记录列表更新后,重新加载最新病历并更新选中状态 // 等待历史记录列表更新后,重新加载最新病历并更新选中状态
// 增加延迟时间以确保数据库更新完成
setTimeout(() => { setTimeout(() => {
loadLatestMedicalRecord(); loadLatestMedicalRecord();
}, 300); }, 100);
ElMessage.success('保存成功'); ElMessage.success('保存成功');
} catch (error) { } catch (error) {
ElMessage.error('提交失败'); ElMessage.error('提交失败');
@@ -504,7 +503,7 @@ const resetForm = async () => {
// 先将组件设置为空,强制卸载 // 先将组件设置为空,强制卸载
const currentComponentName = currentComponent.value; const currentComponentName = currentComponent.value;
if (currentComponentName) { if (currentComponentName) {
currentComponent.value = ''; currentComponent.value = undefined;
// 等待DOM更新 // 等待DOM更新
await nextTick(); await nextTick();
@@ -554,22 +553,11 @@ const selectOutpatientMedicalRecordTemplate = async () => {
selectDefaultTemplate(); selectDefaultTemplate();
}; };
// 防止重复请求的标志
let loadLatestMedicalRecordPromise = null;
// 加载最新的病历数据并回显 // 加载最新的病历数据并回显
const loadLatestMedicalRecord = async () => { const loadLatestMedicalRecord = async () => {
// 如果已经有正在进行的请求则返回该请求的Promise
if (loadLatestMedicalRecordPromise) {
return loadLatestMedicalRecordPromise;
}
if (!patientInfo.value?.encounterId || !currentSelectTemplate.value.id) return; if (!patientInfo.value?.encounterId || !currentSelectTemplate.value.id) return;
editForm.value.id = ''; editForm.value.id = '';
loading.value = true; loading.value = true;
// 创建一个新的Promise来处理请求
loadLatestMedicalRecordPromise = new Promise(async (resolve, reject) => {
try { try {
// 获取患者的历史病历记录 // 获取患者的历史病历记录
const res = await getRecordByEncounterIdList({ const res = await getRecordByEncounterIdList({
@@ -608,8 +596,6 @@ const loadLatestMedicalRecord = async () => {
if (historyRef.value && typeof historyRef.value.updateSelectedRecord === 'function') { if (historyRef.value && typeof historyRef.value.updateSelectedRecord === 'function') {
historyRef.value.updateSelectedRecord(latestRecord.id); historyRef.value.updateSelectedRecord(latestRecord.id);
} }
resolve(); // 成功完成
}); });
} else { } else {
// 清空选中状态 // 清空选中状态
@@ -633,7 +619,6 @@ const loadLatestMedicalRecord = async () => {
if (emrComponentRef.value) { if (emrComponentRef.value) {
emrComponentRef.value.setFormData({}); emrComponentRef.value.setFormData({});
} }
resolve(); // 成功完成
}); });
loading.value = false; loading.value = false;
} }
@@ -660,17 +645,11 @@ const loadLatestMedicalRecord = async () => {
if (emrComponentRef.value) { if (emrComponentRef.value) {
emrComponentRef.value.setFormData({}); emrComponentRef.value.setFormData({});
} }
reject(error); // 错误完成
}); });
loading.value = false; loading.value = false;
} finally { } finally {
loading.value = false; loading.value = false;
// 请求完成后清除Promise引用
loadLatestMedicalRecordPromise = null;
} }
});
return loadLatestMedicalRecordPromise;
}; };
// 选择默认模板 - 获取住院病历分类下的第一个模板 // 选择默认模板 - 获取住院病历分类下的第一个模板
@@ -830,7 +809,7 @@ watch(
} }
} else { } else {
// 如果没有患者信息,也要重置组件和表单数据 // 如果没有患者信息,也要重置组件和表单数据
currentComponent.value = ''; currentComponent.value = undefined;
editForm.value = { editForm.value = {
id: '', id: '',
definitionId: '', definitionId: '',
@@ -856,41 +835,6 @@ watch(
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
// 合并两个监听器,避免重复触发
let patientChangeProcessing = false; // 防止重复处理
watch(
() => [patientInfo.value?.encounterId, currentSelectTemplate.value?.id],
([newEncounterId, newTemplateId]) => {
// 当患者就诊ID或模板ID变化时加载最新病历数据
if (newEncounterId && newTemplateId && !patientChangeProcessing) {
patientChangeProcessing = true;
// 添加延迟以确保模板数据已更新
nextTick(() => {
loadLatestMedicalRecord().finally(() => {
// 重置处理标志
patientChangeProcessing = false;
});
});
}
},
{ immediate: true }
);
// 监听模板选择变化,当模板选择变化时加载最新病历数据
watch(
() => currentSelectTemplate.value.id,
(newTemplateId) => {
// 当模板选择变化时,加载该模板的最新病历数据
if (newTemplateId) {
// 只要有模板ID就尝试加载数据不管之前是否有患者信息
// 因为可能是在切换患者后才选择模板
loadLatestMedicalRecord();
}
}
);
onMounted(async () => { onMounted(async () => {
// 移除日志 // 移除日志
await queryTemplateTree(); await queryTemplateTree();
@@ -914,7 +858,43 @@ const onPrint = () => {
} }
}; };
defineExpose({ state }); // 添加一个方法供父组件调用,处理患者切换
const handlePatientChange = (patient) => {
// 更新患者信息
// 注意这里我们不直接修改patientInfo.value因为它是由父组件通过watch管理的
// 我们只需要确保当前组件响应patientInfo的变化
// 重置当前组件,确保在切换患者时重新加载
// 但我们需要确保在组件重新加载后能正确设置患者信息
const selectedTemplateId = currentSelectTemplate.value?.id;
if (selectedTemplateId) {
// 先清空当前组件
currentComponent.value = undefined;
// 使用nextTick确保DOM更新后再重新加载组件和数据
nextTick(() => {
// 重新设置组件
const template = templateData.value?.find(t => t.document && t.document.id === selectedTemplateId);
if (template && template.document?.vueRouter) {
currentComponent.value = template.document.vueRouter;
// 再次使用nextTick确保组件已加载后再加载数据
nextTick(() => {
loadLatestMedicalRecord();
});
} else {
// 如果找不到对应模板,尝试选择默认模板
selectDefaultTemplate();
}
});
} else {
// 如果没有选中模板,尝试选择默认模板
selectDefaultTemplate();
}
};
defineExpose({ state, handlePatientChange });
const disNode = () => { const disNode = () => {
leftShow.value = !leftShow.value; leftShow.value = !leftShow.value;

View File

@@ -109,25 +109,16 @@ const getList = () => {
}); });
}; };
// 标记是否已经手动选择过患者,防止后续自动选择
const hasManuallySelectedPatient = ref(false);
// 添加一个变量来跟踪当前期望的患者ID
let expectedPatientId = null;
watch( watch(
() => filteredCardData.value, () => filteredCardData.value,
(newData) => { (newData) => {
// 如果有数据且当前没有选中患者,且是首次加载,默认选择第一条 // 如果有数据且当前没有选中患者,且是首次加载,默认选择第一条
// 只有在从未手动选择过患者的情况下才自动选择
// 并且确保当前没有正在处理的患者切换操作
if ( if (
newData && newData &&
newData.length > 0 && newData.length > 0 &&
!cardId.value && !cardId.value &&
isFirstLoad.value && isFirstLoad.value &&
!patientInfo.value?.encounterId && !patientInfo.value?.encounterId
!hasManuallySelectedPatient.value
) { ) {
const firstPatient = newData[0]; const firstPatient = newData[0];
if (firstPatient?.encounterId) { if (firstPatient?.encounterId) {
@@ -139,81 +130,34 @@ watch(
debounceTimer = setTimeout(() => { debounceTimer = setTimeout(() => {
handleItemClick(firstPatient); handleItemClick(firstPatient);
isFirstLoad.value = false; isFirstLoad.value = false;
hasManuallySelectedPatient.value = true; // 标记已手动选择过
}, 100); }, 100);
} }
} else if (expectedPatientId && cardId.value && cardId.value !== expectedPatientId) {
// 如果当前cardId与期望的不一致且不是初始状态这可能意味着发生了意外的重置
// 这种情况下,我们不希望自动选择第一个患者
console.debug(`期望的患者ID: ${expectedPatientId}, 当前cardId: ${cardId.value}`);
} }
}, },
{ immediate: true } { immediate: true }
); );
// 更新handleItemClick函数设置期望的患者ID // 防抖函数,防止快速点击导致状态冲突
let debounceTimer = null;
const handleItemClick = (node) => { const handleItemClick = (node) => {
// 设置期望的患者ID
expectedPatientId = node.encounterId;
// 清除之前的计时器 // 清除之前的计时器
if (debounceTimer) { if (debounceTimer) {
clearTimeout(debounceTimer); clearTimeout(debounceTimer);
} }
// 取消之前未完成的患者加载操作
if (currentPatientPromise) {
// 注意这里无法真正取消Promise但我们可以标记当前操作已过期
currentPatientPromise.cancelled = true;
}
// 设置新的计时器 // 设置新的计时器
debounceTimer = setTimeout(async () => { debounceTimer = setTimeout(() => {
// 检查是否已被取消
if (currentPatientPromise?.cancelled) {
return;
}
cardId.value = node.encounterId; cardId.value = node.encounterId;
// 同时更新本地和全局状态,确保模块内组件和跨模块组件都能正确响应 // 同时更新本地和全局状态,确保模块内组件和跨模块组件都能正确响应
updatePatientInfo(node); updatePatientInfo(node);
updateLocalPatientInfo(node); updateLocalPatientInfo(node);
// 标记已手动选择患者,防止自动选择第一条 diagnosisRef.value?.getList();
hasManuallySelectedPatient.value = true; adviceRef.value?.getListInfo();
adviceRef.value?.getDiagnosisInfo();
// 创建一个新的Promise来追踪这次加载操作
currentPatientPromise = Promise.all([
// 并行调用医嘱相关的API避免重复请求
adviceRef.value?.getListInfo().catch(error => {
console.error('获取医嘱信息失败:', error);
return null;
}),
adviceRef.value?.getDiagnosisInfo().catch(error => {
console.error('获取诊断信息失败:', error);
return null;
}),
// 获取诊断信息
diagnosisRef.value?.getList?.().catch(error => {
console.error('获取诊断信息失败:', error);
return null;
})
]);
try {
await currentPatientPromise;
// 检查在此期间是否选择了其他患者
if (currentPatientPromise?.cancelled) {
return;
}
} catch (error) {
console.error('加载患者信息时出错:', error);
}
}, 100); // 100ms 防抖延迟 }, 100); // 100ms 防抖延迟
}; };
// 防抖函数,防止快速点击导致状态冲突
const handleSearch = (keyword) => { const handleSearch = (keyword) => {
searchData.keyword = keyword; searchData.keyword = keyword;
getList(); getList();

View File

@@ -206,7 +206,7 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" width="100" align="center"> <el-table-column label="操作" width="100" align="center">
<template #default="scope"> <template #default="scope">
<el-button type="text" size="small" @click="feeItemsList.splice(scope.$index, 1)"> <el-button link size="small" @click="feeItemsList.splice(scope.$index, 1)">
删除 删除
</el-button> </el-button>
</template> </template>

View File

@@ -200,7 +200,7 @@
<el-table-column label="金额" prop="amount" width="100" align="center" /> <el-table-column label="金额" prop="amount" width="100" align="center" />
<el-table-column label="退费审核" width="80" align="center"> <el-table-column label="退费审核" width="80" align="center">
<template #default="scope"> <template #default="scope">
<el-button type="text" style="color: #409eff">退费申请</el-button> <el-button link style="color: #409eff">退费申请</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

View File

@@ -74,7 +74,7 @@
</span> </span>
</div> </div>
<!-- <div> <!-- <div>
<el-button size="small" type="text" @click="exportData"> <el-button size="small" link @click="exportData">
<el-icon><Download /></el-icon> 导出数据 <el-icon><Download /></el-icon> 导出数据
</el-button> </el-button>
</div> --> </div> -->