Merge remote-tracking branch 'origin/develop' into zhaoyun
This commit is contained in:
@@ -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: {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 = [];
|
||||
})
|
||||
|
||||
@@ -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-op(false→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 等字段可能为 null,mergedDetail 已做 ?? 兜底
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
// 等待组件挂载完成、模板ref(prescriptionRefs)填充后再触发数据加载
|
||||
// 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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
// 等待组件挂载完成、模板ref(prescriptionRefs)填充后再触发数据加载
|
||||
// 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>
|
||||
|
||||
Reference in New Issue
Block a user