Merge remote-tracking branch 'origin/develop' into zhaoyun

This commit is contained in:
2026-06-17 11:13:43 +08:00
8 changed files with 211 additions and 81 deletions

View File

@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
import globals from "globals";
import pluginVue from "eslint-plugin-vue";
import parserVue from "vue-eslint-parser";
import parserTs from "@typescript-eslint/parser";
import importPlugin, { createNodeResolver } from "eslint-plugin-import-x";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -20,7 +21,7 @@ export default [
},
...pluginVue.configs["flat/recommended"],
{
languageOptions: {
globals: {
@@ -30,6 +31,9 @@ export default [
parser: parserVue,
ecmaVersion: "latest",
sourceType: "module",
parserOptions: {
parser: parserTs,
},
},
plugins: {

View File

@@ -72,6 +72,7 @@
"devDependencies": {
"@playwright/test": "^1.60.0",
"@types/node": "^25.0.1",
"@typescript-eslint/parser": "^8.61.1",
"@vitejs/plugin-vue": "^5.2.4",
"@vue/test-utils": "^2.4.6",
"eslint": "^10.4.1",

View File

@@ -8,7 +8,7 @@
:table-height="400"
:max-height="400"
:loading="loading"
:row-config="{ keyField: 'patientId' }"
:row-config="{ keyField: 'adviceDefinitionId' }"
@cell-click="handleRowClick"
>
<template #quantity="{ row }">
@@ -26,15 +26,16 @@
import {computed, nextTick, ref} from 'vue';
import {throttle} from 'lodash-es';
import Table from '@/components/TableLayout/Table.vue';
// @ts-ignore: api.js 无类型声明
import {getAdviceBaseInfo} from './api';
import type {TableColumn} from '@/components/types/TableLayout';
import {TableColumn} from '@/components/types/TableLayout';
interface Props {
type Props = {
patientInfo: {
inHospitalOrgId?: string;
[key: string]: any;
};
}
};
const props = defineProps<Props>();
@@ -42,13 +43,23 @@ const emit = defineEmits<{
selectAdviceBase: [row: any];
}>();
interface QueryParams {
pageSize: number;
pageNo: number;
adviceTypes: number[];
searchKey?: string;
organizationId?: string;
categoryCode?: string;
dischargeFlag?: number;
}
const total = ref<number>(0);
const loading = ref<boolean>(false);
const adviceBaseRef = ref<InstanceType<typeof Table> | null>(null);
const tableWrapper = ref<HTMLDivElement | null>(null);
const currentIndex = ref<number>(0);
const currentSelectRow = ref<any>({});
const queryParams = ref({
const queryParams = ref<QueryParams>({
pageSize: 30,
pageNo: 1,
adviceTypes: [1, 2, 3, 6],
@@ -86,6 +97,9 @@ const tableColumns = computed<TableColumn[]>(() => [
* @param searchKey 搜索关键词
*/
function refresh(adviceType: any, categoryCode: string, searchKey: string) {
// 每次刷新时重置选中状态,防止上次选中的药品在新列表中残留
currentIndex.value = 0;
currentSelectRow.value = {};
// 有搜索词时跨类型搜索,避免用户输入"级护理"但因当前adviceType为药品而搜不到诊疗类护理项目
if (searchKey) {
queryParams.value.adviceTypes = [1, 2, 3, 6];
@@ -118,7 +132,7 @@ function getList() {
}
getAdviceBaseInfo(queryParams.value)
.then((res) => {
.then((res: any) => {
const records = res.data?.records || [];
// 药品/耗材需要有库存才显示,诊疗/手术直接显示
@@ -144,7 +158,7 @@ function getList() {
}
});
})
.catch((err) => {
.catch((err: any) => {
console.warn('医嘱基础信息加载失败:', err);
adviceBaseList.value = [];
})

View File

@@ -174,6 +174,11 @@
:disabled="!isCategoryLoaded"
@change="
(value) => {
// 切换类型时强制收起编辑区,防止残留字段(如药品→诊疗时 dose/rateCode 未清理)
// 注意:模板内联表达式中 ref 自动解包expandOrder 即数组,不要加 .value
if (expandOrder.length > 0) {
collapseAllExpanded();
}
filterPrescriptionList[scope.rowIndex].adviceName = undefined;
// 根据选中的医嘱类型,设置对应的 categoryCode
const selectedItem = adviceTypeList.find(item => item.value === value);
@@ -196,7 +201,7 @@
searchKey: adviceQueryParams.value?.searchKey || '',
};
// 直接调用子组件 refresh 方法,立即刷新列表(用 scope.rowIndex 找到当前行的子组件)
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[scope.rowIndex] : adviceTableRef.value;
const tableRef = getAdviceTableRef();
if (tableRef && tableRef.refresh) {
tableRef.refresh(newAdviceType, newCategoryCode, '');
}
@@ -221,7 +226,7 @@
popper-class="order-advice-popper"
:offset="0"
:visible="scope.row.showPopover"
:width="1200"
:width="advicePopperWidth"
:teleported="true"
:popper-options="advicePopperOptions"
>
@@ -246,7 +251,7 @@
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
e.preventDefault();
// 传递事件到弹窗容器
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[scope.rowIndex] : adviceTableRef.value;
const tableRef = getAdviceTableRef();
if (tableRef && tableRef.handleKeyDown) tableRef.handleKeyDown(e);
}
}
@@ -461,7 +466,7 @@ import {
} from '../api';
import adviceBaseList from '../adviceBaseList';
import {calculateQuantityByDays} from '@/utils/his';
import {localPatientInfo as patientInfo} from '../../store/localPatient.js';
import {localPatientInfo as localPatient} from '../../store/localPatient.js';
import OrderGroupDrawer from '@/views/doctorstation/components/prescription/orderGroupDrawer.vue';
import PrescriptionHistory from '@/views/doctorstation/components/prescription/prescriptionHistory.vue';
import Decimal from 'decimal.js';
@@ -492,7 +497,7 @@ const groupIndexList = ref([]);
const diagnosisList = ref([]);
const nextId = ref(1);
const unitCodeList = ref([]);
const adviceTableRef = ref([]);
const adviceTableRef = ref(null);
const organization = ref([]);
const conditionDefinitionId = ref('');
const encounterDiagnosisId = ref('');
@@ -505,6 +510,11 @@ const popoverJustClosedByKey = ref(null);
// 医嘱检索下拉浮框对齐:跟踪表格水平滚动偏移与主体区域边界限制
const tableScrollLeft = ref(0);
const mainBoundary = ref(null);
const advicePopperWidth = computed(() => {
// 取主体区域宽度与 900px 中较小值,避免小屏幕溢出;下限 500px
const mainWidth = mainBoundary.value?.clientWidth || window.innerWidth - 300;
return Math.max(500, Math.min(mainWidth - 24, 900));
});
const advicePopperStyle = computed(() => ({
padding: '0',
marginLeft: `-${tableScrollLeft.value}px`,
@@ -549,10 +559,10 @@ const unitMap = ref({
unit: 'unit',
});
const buttonDisabled = computed(() => {
return !patientInfo.value;
return !localPatient.value;
});
const isSaveDisabled = computed(() => {
return !patientInfo.value || prescriptionList.value.length === 0;
return !localPatient.value || prescriptionList.value.length === 0;
});
const props = defineProps({
patientInfo: {
@@ -763,7 +773,7 @@ function getListInfo(addNewRow) {
const orgTreePromise = getOrgTree().then((res) => {
organization.value = res?.data?.records ?? res?.data ?? [];
});
getPrescriptionList(patientInfo.value.encounterId).then((res) => {
getPrescriptionList(localPatient.value.encounterId).then((res) => {
// 等待科室树加载完成后再处理处方数据,确保 resolveOrgId 能正确匹配
orgTreePromise.then(() => {
loading.value = false;
@@ -830,10 +840,10 @@ function getListInfo(addNewRow) {
}
});
}).catch(() => { loading.value = false; });
getContract({ encounterId: patientInfo.value.encounterId }).then((res) => {
getContract({ encounterId: localPatient.value.encounterId }).then((res) => {
contractList.value = res.data;
});
accountId.value = patientInfo.value.accountId;
accountId.value = localPatient.value.accountId;
// 加载已配置的药品类别
loadConfiguredCategories();
@@ -843,7 +853,7 @@ function getListInfo(addNewRow) {
* 加载已配置的药品类别
*/
function loadConfiguredCategories() {
const orgId = patientInfo.value?.inHospitalOrgId;
const orgId = localPatient.value?.inHospitalOrgId;
if (!orgId) {
isCategoryLoaded.value = true; // 标记已加载即使没有科室ID
return;
@@ -917,7 +927,7 @@ const filterPrescriptionList = computed(() => {
});
function getDiagnosisInfo() {
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => {
getEncounterDiagnosis(localPatient.value.encounterId).then((res) => {
diagnosisList.value = res.data;
let diagnosisInfo = diagnosisList.value.filter((item) => {
return item.maindiseFlag == 1;
@@ -937,6 +947,7 @@ function getRowDisabled(row) {
/**
* 将行的 adviceType + categoryCode 映射为 el-select 的选中值
* 药品子分类使用复合值如 '1-2'adviceType-categoryCode诊疗/手术/全部使用原始值
* 注意el-select 使用严格匹配,返回值类型需与 adviceTypeList option value 一致
*/
function getRowSelectValue(row) {
if (row.adviceType == 1 && row.categoryCode) {
@@ -945,13 +956,17 @@ function getRowSelectValue(row) {
if (row.adviceType == 7) {
return 7;
}
// adviceType == 1 但没有 categoryCode 时无匹配项,返回 undefined 让 select 显示空
if (row.adviceType == 1) {
return undefined;
}
return row.adviceType;
}
// 新增医嘱
function handleAddPrescription() {
// 校验是否选中患者
if (!patientInfo.value || !patientInfo.value.encounterId) {
if (!localPatient.value || !localPatient.value.encounterId) {
proxy.$modal.msgWarning('请先选择患者');
return;
}
@@ -1002,9 +1017,9 @@ function checkUnit(item, row) {
* @returns {boolean} true=通过, false=不通过
*/
function validateStartTime(startTime) {
if (!startTime || !patientInfo.value?.inHospitalTime) return true;
if (!startTime || !localPatient.value?.inHospitalTime) return true;
const startDate = new Date(startTime);
const inHospitalDate = new Date(patientInfo.value.inHospitalTime);
const inHospitalDate = new Date(localPatient.value.inHospitalTime);
if (startDate < inHospitalDate) {
const pad = (n) => String(n).padStart(2, '0');
const d = inHospitalDate;
@@ -1168,25 +1183,40 @@ function handleFocus(row, index) {
return;
}
row.showPopover = true;
// Bug #555: handleFocus 初始化查询参数并加载初始数据searchKey 为空,无竞态风险
let categoryCode = '';
if (row.adviceType !== undefined) {
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
categoryCode = selectedItem ? selectedItem.categoryCode : (row.categoryCode || '');
}
adviceQueryParams.value = { adviceType, categoryCode, searchKey: '' };
// 弹窗首次打开时加载初始数据
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
// 初始化查询参数searchKey 使用当前输入框已有文本,避免 @input 二次触发导致竞态
const searchKey = row.adviceName || '';
adviceQueryParams.value = { adviceType, categoryCode: resolveCategoryCode(row, adviceType), searchKey };
const tableRef = getAdviceTableRef();
if (tableRef && tableRef.refresh) {
tableRef.refresh(adviceType, categoryCode, '');
tableRef.refresh(adviceType, adviceQueryParams.value.categoryCode, searchKey);
}
}
/** 从行数据和 adviceType 解析 categoryCode */
function resolveCategoryCode(row, adviceType) {
if (row.adviceType !== undefined) {
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
return selectedItem ? selectedItem.categoryCode : (row.categoryCode || '');
}
return '';
}
/** 获取 adviceTableRef单实例不在 v-for 中) */
function getAdviceTableRef() {
return adviceTableRef.value;
}
function handleBlur(row) {
row.showPopover = false;
// Bug #587: 标记弹窗刚关闭,防止点击空白处时触发行展开
popoverJustClosedByKey.value = row.uniqueKey;
// 延迟关闭弹窗,等待 click 事件在弹出层内容上正常触发
// 原因:浏览器 blur 事件先于 click 触发;同步关闭会移除 DOM导致 click 丢失
// 如果用户在弹出层内点击药品行selectAdviceBase 会在 150ms 内设 showPopover=false
// 此处的赋值变为 no-opfalse→false弹窗正常关闭且数据正确填充
setTimeout(() => {
row.showPopover = false;
// Bug #587: 标记弹窗刚关闭,防止点击空白处时触发行展开
popoverJustClosedByKey.value = row.uniqueKey;
}, 150);
}
function handleChange(value, row, index) {
@@ -1197,21 +1227,13 @@ function handleChange(value, row, index) {
return;
}
adviceQueryParams.value.searchKey = value;
// @focus 已先于 @input 执行rowIndex 必定有效
// popover 被 blur 关闭后,用户继续输入时自行打开
if (!row.showPopover) {
row.showPopover = true;
}
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
const tableRef = getAdviceTableRef();
if (tableRef && tableRef.refresh) {
const adviceType = row?.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
let categoryCode = '';
if (row?.adviceType !== undefined) {
const selectValue = (adviceType == 1 && row?.categoryCode) ? '1-' + row.categoryCode : adviceType;
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
}
tableRef.refresh(adviceType, categoryCode, value);
tableRef.refresh(adviceType, resolveCategoryCode(row, adviceType), value);
}
}
@@ -1279,9 +1301,10 @@ function selectAdviceBase(key, row) {
prescriptionList.value[rowIndex.value].skinTestFlag = 1;
prescriptionList.value[rowIndex.value].skinTestFlag_enumText = '是';
expandOrder.value = [currentUniqueKey];
const expandRow = filterPrescriptionList.value.find(item => item.uniqueKey === currentUniqueKey);
if (expandRow && prescriptionRef.value?.setRowExpand) {
prescriptionRef.value.setRowExpand([expandRow], true);
// 使用 prescriptionList 而非 filterPrescriptionList避免过滤后找不到行导致展开失败
const rowObj = prescriptionList.value.find(item => item.uniqueKey === currentUniqueKey);
if (rowObj && prescriptionRef.value?.setRowExpand) {
prescriptionRef.value.setRowExpand([rowObj], true);
}
expandOrderAndFocus(currentUniqueKey, row);
})
@@ -1290,17 +1313,18 @@ function selectAdviceBase(key, row) {
prescriptionList.value[rowIndex.value].skinTestFlag = 0;
prescriptionList.value[rowIndex.value].skinTestFlag_enumText = '否';
expandOrder.value = [currentUniqueKey];
const expandRow = filterPrescriptionList.value.find(item => item.uniqueKey === currentUniqueKey);
if (expandRow && prescriptionRef.value?.setRowExpand) {
prescriptionRef.value.setRowExpand([expandRow], true);
// 使用 prescriptionList 而非 filterPrescriptionList避免过滤后找不到行导致展开失败
const rowObj = prescriptionList.value.find(item => item.uniqueKey === currentUniqueKey);
if (rowObj && prescriptionRef.value?.setRowExpand) {
prescriptionRef.value.setRowExpand([rowObj], true);
}
expandOrderAndFocus(currentUniqueKey, row);
});
} else {
// 选完药品后展开编辑区域,供医生编辑剂量等字段
expandOrder.value = [currentUniqueKey];
// 直接调 vxe-table 实例方法展开expandRowKeys 仅在初始化时生效)
const rowObj = filterPrescriptionList.value.find(item => item.uniqueKey === currentUniqueKey);
// 使用 prescriptionList 而非 filterPrescriptionList避免过滤后找不到行导致展开失败
const rowObj = prescriptionList.value.find(item => item.uniqueKey === currentUniqueKey);
if (rowObj && prescriptionRef.value?.setRowExpand) {
prescriptionRef.value.setRowExpand([rowObj], true);
}
@@ -1539,9 +1563,9 @@ function handleSave() {
// 签发处理逻辑
function executeSaveLogic() {
saveList.forEach((item) => {
item.patientId = patientInfo.value.patientId;
item.encounterId = patientInfo.value.encounterId;
item.accountId = patientInfo.value.accountId;
item.patientId = localPatient.value.patientId;
item.encounterId = localPatient.value.encounterId;
item.accountId = localPatient.value.accountId;
item.dbOpType = '1';
// Bug #589: 出院带药保存时转为药品类型
if (item.adviceType == 7) {
@@ -1570,11 +1594,11 @@ function handleSave() {
// 保存签发按钮
isSaving.value = true;
console.log('签发处方参数:', {
organizationId: patientInfo.value.inHospitalOrgId,
organizationId: localPatient.value.inHospitalOrgId,
adviceSaveList: list,
});
savePrescriptionSign({
organizationId: patientInfo.value.inHospitalOrgId,
organizationId: localPatient.value.inHospitalOrgId,
regAdviceSaveList: list,
})
.then((res) => {
@@ -1636,8 +1660,8 @@ function handleOrderBindInfo(bindIdInfo) {
const newRow = {
...prescriptionList.value[rowIndex.value],
uniqueKey: nextId.value++,
patientId: patientInfo.value.patientId,
encounterId: patientInfo.value.encounterId,
patientId: localPatient.value.patientId,
encounterId: localPatient.value.encounterId,
accountId: accountId.value,
quantity: item.quantity,
methodCode: item.methodCode,
@@ -1742,8 +1766,8 @@ function handleSaveSign(row, index) {
// 执行保存
row.contentJson = undefined;
row.patientId = patientInfo.value.patientId;
row.encounterId = patientInfo.value.encounterId;
row.patientId = localPatient.value.patientId;
row.encounterId = localPatient.value.encounterId;
row.accountId = accountId.value;
// 🔧 文字医嘱(type=8)跳过计费逻辑总金额为0
@@ -1867,6 +1891,11 @@ function handleSaveBatch() {
const therapyEnum = item.therapyEnum || '1';
const result = {
...item,
// 确保 patientId/encounterId/accountId 始终存在,防止新增行未保存时这些字段为 null
// (新增行由 handleAddPrescription 创建时不携带患者信息,与其他保存路径保持一致)
patientId: item.patientId || localPatient.value?.patientId,
encounterId: item.encounterId || localPatient.value?.encounterId,
accountId: item.accountId || accountId.value,
therapyEnum: therapyEnum,
dbOpType: item.requestId ? '2' : '1',
// 确保 skinTestFlag 是数字类型1 或 0
@@ -1992,15 +2021,17 @@ function setValue(row) {
// 2. 诊疗类型优先使用项目维护的所属科室(row.orgId)其次positionId
// 3. 如果都为空,回退到患者当前所在科室(patientInfo.orgId)
// 4. 使用 resolveOrgId 从组织树中匹配正确的 String id解决大 Long 精度丢失问题
orgId: row.adviceType != 3 ? undefined : (resolveOrgId(row.orgId || row.positionId || patientInfo.value?.inHospitalOrgId) || ''),
orgId: row.adviceType != 3 ? undefined : (resolveOrgId(row.orgId || row.positionId || localPatient.value?.inHospitalOrgId) || ''),
// 🔧 修复:同时保存 orgName当 orgId 在科室树中匹配不到时作为兜底显示
orgName: row.adviceType != 3 ? undefined : (findOrgName(row.orgId || row.positionId || patientInfo.value?.inHospitalOrgId) || row.orgName || patientInfo.value?.inHospitalOrgName || ''),
orgName: row.adviceType != 3 ? undefined : (findOrgName(row.orgId || row.positionId || localPatient.value?.inHospitalOrgId) || row.orgName || localPatient.value?.inHospitalOrgName || ''),
// dose: undefined, Removed to preserve dose value from group package
unitCodeList: unitCodeList.value,
doseUnitCode: row.doseUnitCode,
minUnitCode: String(row.minUnitCode),
unitCode: row.partAttributeEnum == 1 ? String(row.minUnitCode) : String(row.unitCode),
categoryEnum: row.categoryCode,
// 显式保留 categoryCode防止 API 未返回时类型下拉框显示空白
categoryCode: row.categoryCode || baseRow.categoryCode || prevRow.categoryCode || '',
categoryEnum: row.categoryCode || baseRow.categoryCode || prevRow.categoryEnum || '',
// 确保 skinTestFlag 是数字类型1 或 0如果未定义则默认为 0
skinTestFlag: row.skinTestFlag !== undefined && row.skinTestFlag !== null
? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag : (row.skinTestFlag ? 1 : 0))
@@ -2100,8 +2131,8 @@ function handleSaveGroup(orderGroupList) {
...prescriptionList.value[tempIndex],
requesterId_dictText: userStore.nickName,
requesterId: userStore.id,
patientId: patientInfo.value.patientId,
encounterId: patientInfo.value.encounterId,
patientId: localPatient.value.patientId,
encounterId: localPatient.value.encounterId,
accountId: accountId.value,
// 🔧 修复 Bug #403从 mergedDetail 读取明细字段,而非直接从 item 取
// item.dose 等字段可能为 nullmergedDetail 已做 ?? 兜底
@@ -2115,9 +2146,9 @@ function handleSaveGroup(orderGroupList) {
unitCode: mergedDetail.unitCode ?? item.unitCode,
unitCode_dictText: item.unitCodeName || mergedDetail.unitCodeName || '',
statusEnum: 1,
orgId: resolveOrgId(mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || '',
orgId: resolveOrgId(mergedDetail.orgId || localPatient.value?.inHospitalOrgId) || '',
// 🔧 修复:同时存储 orgName确保树匹配不到时仍有中文名称可显示
orgName: findOrgName(mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || mergedDetail.orgName || patientInfo.value?.inHospitalOrgName || '',
orgName: findOrgName(mergedDetail.orgId || localPatient.value?.inHospitalOrgId) || mergedDetail.orgName || localPatient.value?.inHospitalOrgName || '',
startTime: mergedDetail.startTime || defaultStartTimeFn(),
dbOpType: prescriptionList.value[tempIndex].requestId ? '2' : '1',
conditionId: conditionId.value,
@@ -2161,8 +2192,8 @@ function handleSaveHistory(value) {
...value,
requesterId_dictText: userStore.nickName,
requesterId: userStore.id,
patientId: patientInfo.value.patientId,
encounterId: patientInfo.value.encounterId,
patientId: localPatient.value.patientId,
encounterId: localPatient.value.encounterId,
accountId: accountId.value,
uniqueKey: undefined,
startTime: defaultStartTimeFn(),
@@ -2386,9 +2417,9 @@ function confirmStopAdvice() {
return;
}
// 校验:停嘱时间不能早于患者入院时间
if (patientInfo.value?.inHospitalTime) {
if (localPatient.value?.inHospitalTime) {
const stopDate = new Date(stopForm.stopTime);
const inHospitalDate = new Date(patientInfo.value.inHospitalTime);
const inHospitalDate = new Date(localPatient.value.inHospitalTime);
if (stopDate < inHospitalDate) {
const pad = (n) => String(n).padStart(2, '0');
const d = inHospitalDate;
@@ -2903,7 +2934,7 @@ function sortPrescriptionList() {
}
function handleLeaveHospital() {
if (!patientInfo.value) {
if (!localPatient.value) {
proxy.$modal.msgWarning('请先选择患者');
return;
}
@@ -3053,7 +3084,7 @@ defineExpose({ getListInfo, getDiagnosisInfo });
.inpatientDoctor-order-table {
flex: auto;
width: 100%;
min-height: 0;
min-height: 300px;
overflow: auto;
}
.applicationForm-bottom-btn {

View File

@@ -451,6 +451,17 @@ function handleGetPrescription(skipAutoSelectAll = false) {
.map((x) => x.trim())
.filter((x) => x !== '')
.map((x) => normalizeDayTimeHm(x));
// 如果频次表(adm_frequency)中 day_times 为空,降级使用 executeNum 生成默认时间点
// 避免长期医嘱因缺失 dayTimes 被静默过滤(不显示在待执行列表)
if (!rate || rate.length === 0) {
const execCount = prescription.executeNum || 1;
// 生成默认时间点:从 08:00 开始,按 executeNum 均分到 20:00
rate = Array.from({ length: execCount }, (_, i) => {
const h = 8 + Math.floor((12 / execCount) * i);
const m = Math.round(((12 / execCount) * i) % 1 * 60);
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
});
}
// 用截止时间和医嘱签发时间算出全部执行日期
times = getDateRange(prescription.requestTime, deadline.value);
} else {

View File

@@ -63,9 +63,10 @@
</template>
<script setup>
import {getCurrentInstance, ref, nextTick, provide} from 'vue';
import {getCurrentInstance, ref, nextTick, provide, watch, onActivated} from 'vue';
import PatientList from '../components/patientList.vue';
import PrescriptionList from './components/prescriptionList.vue';
import { patientInfoList } from '../components/store/patient.js';
import { RequestStatus } from '@/utils/medicalConstants';
const activeName = ref('preparation');
@@ -129,6 +130,31 @@ function handleClick(tabName) {
provide('handleGetPrescription', (value) => {
prescriptionRefs.value[activeName.value].handleGetPrescription();
});
// 监听患者列表变化自动触发当前tab数据加载
// 解决从校对页面切换过来时lazy tab 未挂载导致数据不刷新的问题)
const autoFetchDone = ref(false);
watch(patientInfoList, async (newVal) => {
if (!autoFetchDone.value && newVal.length > 0) {
autoFetchDone.value = true;
// 等待组件挂载完成、模板refprescriptionRefs填充后再触发数据加载
// immediate 回调在 setup 阶段同步执行此时模板尚未渲染prescriptionRefs 为空
await nextTick();
nextTick(() => {
handleClick(activeName.value);
});
}
}, { immediate: true });
// keep-alive 缓存后重新激活时,强制刷新当前 tab 数据
// 校对通过长期医嘱后切换到执行页面autoFetchDone 已为 true 不会再触发)
onActivated(() => {
if (patientInfoList.value.length > 0) {
nextTick(() => {
handleClick(activeName.value);
});
}
});
</script>
<style scoped>

View File

@@ -207,7 +207,7 @@
>
<template #default="scope">
<el-tag
:type="getStatusType(scope.row.requestStatus)"
:type="getStatusType(scope.row)"
size="small"
>
{{ getStatusDisplayText(scope.row) }}
@@ -430,6 +430,11 @@ const getStatusDisplayText = (row) => {
if (DISPENSE_STATUS_DISPLAY[dispenseCode]) {
return DISPENSE_STATUS_DISPLAY[dispenseCode];
}
// 2.5 兼容后端未更新 dispenseStatus 的情况:通过执行记录列表判断是否已执行
const exeRecords = row?.exePerformRecordList;
if (exeRecords && Array.isArray(exeRecords) && exeRecords.length > 0) {
return '已执行';
}
// 3. 最后回退到其他请求状态
if (REQUEST_STATUS_DISPLAY[requestCode]) {
return REQUEST_STATUS_DISPLAY[requestCode];
@@ -439,7 +444,13 @@ const getStatusDisplayText = (row) => {
};
const getStatusType = (status) => {
const getStatusType = (row) => {
// '已执行'状态优先用 success 色,与 getStatusDisplayText 保持一致
const displayText = getStatusDisplayText(row);
if (displayText === '已执行') {
return 'success';
}
const status = row?.requestStatus;
const map = {
[RequestStatus.DRAFT]: 'info',
[RequestStatus.ACTIVE]: 'primary',
@@ -452,9 +463,10 @@ const getStatusType = (status) => {
};
return map[status] || 'info';
};
/** 选中医嘱中是否包含已执行或已发药记录 — 用于禁用退回按钮 */
const hasDispensedSelected = computed(() => {
selectionTrigger.value;
return getSelectRows().some(item => item.dispenseStatus === 4);
return getSelectRows().some(item => item.dispenseStatus === 11 || item.dispenseStatus === 4);
});
const props = defineProps({
requestStatus: {
@@ -591,6 +603,12 @@ function handleCheck() {
function handleCancel() {
let list = getSelectRows();
if (list.length > 0) {
// 校验已执行的医嘱不允许直接退回
let executedItems = list.filter(item => item.dispenseStatus === 11);
if (executedItems.length > 0) {
proxy.$message.error('选中医嘱已执行,请先在"医嘱执行"界面取消执行后再执行退回操作');
return;
}
// 校验已发药的医嘱不允许退回
let dispensedItems = list.filter(item => item.dispenseStatus === 4);
if (dispensedItems.length > 0) {

View File

@@ -66,6 +66,7 @@
import PatientList from '../components/patientList.vue';
import PrescriptionList from './components/prescriptionList.vue';
import { RequestStatus } from '@/utils/medicalConstants';
import { patientInfoList } from '../components/store/patient.js';
const activeName = ref('unverified');
const active = ref('first');
@@ -123,6 +124,30 @@ function handleTabClick(tabName) {
provide('handleGetPrescription', (value) => {
prescriptionRefs.value[activeName.value].handleGetPrescription();
});
// 监听患者列表变化自动触发当前tab数据加载
// (解决切换医嘱校对/医嘱执行tab后组件重建时数据不刷新的问题
const autoFetchDone = ref(false);
watch(patientInfoList, async (newVal) => {
if (!autoFetchDone.value && newVal.length > 0) {
autoFetchDone.value = true;
// 等待组件挂载完成、模板refprescriptionRefs填充后再触发数据加载
// immediate 回调在 setup 阶段同步执行此时模板尚未渲染prescriptionRefs 为空
await nextTick();
nextTick(() => {
handleTabClick(activeName.value);
});
}
}, { immediate: true });
// keep-alive 缓存后重新激活时,强制刷新当前 tab 数据
onActivated(() => {
if (patientInfoList.value.length > 0) {
nextTick(() => {
handleTabClick(activeName.value);
});
}
});
</script>
<style scoped>