370【住院护士站站-》进入“三测单”模块报错

371 【业务配置】住院医生站-“呼吸内科病房”未在西药房取药规则中维护配置
This commit is contained in:
2026-04-21 13:05:46 +08:00
parent 88d9e19cc5
commit 14333f47ea
10 changed files with 403 additions and 120 deletions

View File

@@ -23,26 +23,20 @@
</template>
<script setup lang="ts">
import {computed, nextTick, ref, watch} from 'vue';
import {computed, nextTick, onMounted, ref} from 'vue';
import {throttle} from 'lodash-es';
import Table from '@/components/TableLayout/Table.vue';
import {getAdviceBaseInfo} from './api';
import type {TableColumn} from '@/components/types/TableLayout.d';
import type {TableColumn} from '@/components/types/TableLayout';
interface Props {
adviceQueryParams?: {
searchKey?: string;
adviceType?: string;
};
patientInfo: {
inHospitalOrgId?: string;
[key: string]: any;
};
}
const props = withDefaults(defineProps<Props>(), {
adviceQueryParams: () => ({}),
});
const props = defineProps<Props>();
const emit = defineEmits<{
selectAdviceBase: [row: any];
@@ -56,14 +50,14 @@ const currentIndex = ref<number>(0);
const currentSelectRow = ref<any>({});
const queryParams = ref({
pageSize: 100,
pageNum: 1,
adviceTypes: '1,3',
pageNo: 1,
adviceTypes: '1,2,3,6',
searchKey: '',
organizationId: '',
categoryCode: '',
});
const adviceBaseList = ref<any[]>([]);
// 表格列配置
const tableColumns = computed<TableColumn[]>(() => [
{ label: '名称', prop: 'adviceName', align: 'center', width: 200 },
{ label: '类型', prop: 'activityType_enumText', align: 'center' },
@@ -84,50 +78,53 @@ const tableColumns = computed<TableColumn[]>(() => [
slot: 'useScope',
},
]);
// 节流函数
const throttledGetList = throttle(
() => {
getList();
},
300,
{ leading: true, trailing: true }
);
watch(
() => props.adviceQueryParams,
(newValue) => {
queryParams.value.searchKey = newValue.searchKey;
// queryParams.value.adviceType = newValue.adviceType;
queryParams.value.adviceTypes = [newValue.adviceType].join(',');
throttledGetList();
},
{ deep: true }
);
getList();
/**
* 父组件主动调用此方法刷新列表
* @param adviceType 医嘱类型1=药品, 3=诊疗, 6=手术, ''=全部)
* @param categoryCode 药品分类编码('1'=中成药, '2'=西药, '4'=中草药, ''=不限)
* @param searchKey 搜索关键词
*/
function refresh(adviceType: any, categoryCode: string, searchKey: string) {
queryParams.value.adviceTypes =
adviceType !== undefined && adviceType !== '' ? String(adviceType) : '1,2,3,6';
queryParams.value.categoryCode = categoryCode || '';
queryParams.value.searchKey = searchKey || '';
getList();
}
function getList() {
loading.value = true;
queryParams.value.organizationId = props.patientInfo?.inHospitalOrgId || '';
// organizationId 必须为有效数字或 undefined空字符串会导致后端 Long 类型转换失败(400)
const orgId = props.patientInfo?.inHospitalOrgId;
queryParams.value.organizationId = orgId ? orgId : undefined;
// 空字符串参数不发送,避免后端类型转换错误
if (!queryParams.value.searchKey) {
queryParams.value.searchKey = undefined;
}
if (!queryParams.value.categoryCode) {
queryParams.value.categoryCode = undefined;
}
getAdviceBaseInfo(queryParams.value)
.then((res) => {
console.log(res.data.records);
if (res.data.records.length > 0) {
adviceBaseList.value = res.data.records.filter((item) => {
if (item.adviceType == 1 || item.adviceType == 2) {
return handleQuantity(item) != 0;
} else {
return true;
}
});
total.value = res.data.total;
nextTick(() => {
if (adviceBaseList.value.length > 0) {
currentIndex.value = 0;
setCurrentRow(adviceBaseList.value[0]);
}
});
} else {
adviceBaseList.value = [];
}
const records = res.data?.records || [];
// 药品/耗材需要有库存才显示,诊疗/手术直接显示
adviceBaseList.value = records.filter((item: any) => {
if (item.adviceType == 1 || item.adviceType == 2) {
return handleQuantity(item) !== '0';
}
return true;
});
total.value = res.data?.total || 0;
nextTick(() => {
if (adviceBaseList.value.length > 0) {
currentIndex.value = 0;
setCurrentRow(adviceBaseList.value[0]);
}
});
})
.catch(() => {
adviceBaseList.value = [];
@@ -141,23 +138,22 @@ function getList() {
const handleKeyDown = (event: KeyboardEvent): void => {
const key = event.key;
const data = adviceBaseList.value;
switch (key) {
case 'ArrowUp': // 上箭头
case 'ArrowUp':
event.preventDefault();
if (currentIndex.value > 0) {
currentIndex.value--;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'ArrowDown': // 下箭头
case 'ArrowDown':
event.preventDefault();
if (currentIndex.value < data.length - 1) {
currentIndex.value++;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'Enter': // 回车键
case 'Enter':
event.preventDefault();
if (currentSelectRow.value && Object.keys(currentSelectRow.value).length > 0) {
emit('selectAdviceBase', currentSelectRow.value);
@@ -177,17 +173,14 @@ function handleQuantity(row: any): string {
return '0';
}
// 设置选中行(带滚动)
const setCurrentRow = (row: any) => {
if (adviceBaseRef.value?.tableRef) {
adviceBaseRef.value.tableRef.setCurrentRow(row);
// 滚动到选中行
nextTick(() => {
const tableEl = adviceBaseRef.value?.tableRef?.$el;
if (tableEl) {
const tableBody = tableEl.querySelector('.el-table__body-wrapper');
const currentRowEl = tableEl.querySelector('.current-row');
if (tableBody && currentRowEl) {
if (currentRowEl) {
currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}
@@ -195,31 +188,20 @@ const setCurrentRow = (row: any) => {
}
};
// 行点击事件
const handleRowClick = (row: any): void => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === row);
currentSelectRow.value = row;
emit('selectAdviceBase', row);
};
// 监听表格当前行变化(通过 el-table 的 current-change 事件)
watch(
() => adviceBaseRef.value?.tableRef,
(tableRef) => {
if (tableRef) {
// 通过 $el 访问原生 el-table 并监听 current-change
const elTable = tableRef.$el?.querySelector('.el-table');
if (elTable) {
// 使用 MutationObserver 或直接监听 DOM 变化来检测当前行变化
// 或者通过 watch 监听 currentSelectRow 的变化
}
}
},
{ immediate: true }
);
defineExpose({
handleKeyDown,
refresh,
});
// 组件挂载时自动加载数据el-popover 懒渲染,父组件 refresh 可能因时序问题未生效onMounted 最可靠)
onMounted(() => {
getList();
});
</script>
@@ -234,8 +216,4 @@ defineExpose({
outline: 2px solid #409eff;
}
}
.popover-table-wrapper:focus {
outline: 2px solid #409eff;
}
</style>

View File

@@ -170,6 +170,22 @@ export function getAdviceBaseInfo(queryParams) {
});
}
/**
* 获取当前科室已配置的药品类别列表
*/
export function getConfiguredCategories(organizationId) {
// organizationId 为空时不发送该参数,避免后端 Long 类型转换 400 错误
const params = {};
if (organizationId !== undefined && organizationId !== null && organizationId !== '') {
params.organizationId = organizationId;
}
return request({
url: '/doctor-station/advice/configured-categories',
method: 'get',
params: params,
});
}
/**
* 保存处方(单条)
*/

View File

@@ -145,28 +145,40 @@
<template v-if="getRowDisabled(scope.row)">
<el-select
style="width: 35%; margin-right: 8px"
v-model="scope.row.adviceType"
:model-value="getRowSelectValue(scope.row)"
:ref="'adviceTypeRef' + scope.$index"
:disabled="!isCategoryLoaded"
@change="
(value) => {
expandOrder = [];
filterPrescriptionList[scope.$index].adviceName = undefined;
adviceQueryParams.adviceType = value;
// 根据选中的医嘱类型,设置对应的 categoryCode
const selectedItem = adviceTypeList.find(item => item.value === value);
const newCategoryCode = selectedItem ? selectedItem.categoryCode : '';
const newAdviceType = selectedItem ? selectedItem.adviceType : value;
// 更新行的 adviceType数字和 categoryCode
filterPrescriptionList[scope.$index].adviceType = newAdviceType;
filterPrescriptionList[scope.$index].categoryCode = newCategoryCode;
// 更新 adviceQueryParams备用
adviceQueryParams.value = {
adviceType: newAdviceType,
categoryCode: newCategoryCode,
searchKey: adviceQueryParams.value.searchKey || '',
};
// 直接调用子组件 refresh 方法,立即刷新列表(用 scope.$index 找到当前行的子组件)
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[scope.$index] : adviceTableRef.value;
if (tableRef && tableRef.refresh) {
tableRef.refresh(newAdviceType, newCategoryCode, '');
}
}
"
clearable
>
<el-option
v-for="item in adviceTypeList"
:key="item.value"
:key="item.label + '-' + item.value"
:label="item.label"
:value="item.value"
@click="
() => {
filterPrescriptionList[scope.$index].adviceType = item.value;
filterPrescriptionList[scope.$index].adviceType_dictText = item.label;
}
"
/>
</el-select>
<el-popover
@@ -177,8 +189,6 @@
>
<adviceBaseList
ref="adviceTableRef"
:popoverVisible="scope.row.showPopover"
:adviceQueryParams="adviceQueryParams"
:patientInfo="patientInfo"
@selectAdviceBase="(row) => selectAdviceBase(scope.row.uniqueKey, row)"
/>
@@ -198,7 +208,8 @@
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
e.preventDefault();
// 传递事件到弹窗容器
adviceTableRef.handleKeyDown(e);
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[scope.$index] : adviceTableRef.value;
if (tableRef && tableRef.handleKeyDown) tableRef.handleKeyDown(e);
}
}
"
@@ -346,6 +357,7 @@ import {
singOut,
stopAdvice,
updateGroupId,
getConfiguredCategories,
} from '../api';
import adviceBaseList from '../adviceBaseList';
import {calculateQuantityByDays} from '@/utils/his';
@@ -360,7 +372,7 @@ import LeaveHospitalDialog from './applicationForm/leaveHospitalDialog.vue';
import TransferOrganizationDialog from './applicationForm/transferOrganizationDialog.vue';
import NursingStatus from '@/views/inpatientDoctor/home/components/applicationShow/nursingStatus.vue';
import OrderForm from './OrderForm.vue';
import {computed, watch} from 'vue';
// Vue API (ref, computed, watch, nextTick, onMounted, etc.) are auto-imported by unplugin-auto-import
const emit = defineEmits(['selectDiagnosis']);
const total = ref(0);
@@ -369,7 +381,11 @@ const prescriptionList = ref([]);
const form = ref({
prescriptionList: prescriptionList.value,
});
const adviceQueryParams = ref({});
const adviceQueryParams = ref({
adviceType: 1,
categoryCode: '', // 初始为空,等待加载配置后动态设置
searchKey: '',
});
const rowIndex = ref(-1);
const groupIndex = ref(1);
const groupIndexList = ref([]);
@@ -436,24 +452,65 @@ const { method_code, unit_code, rate_code, distribution_category_code, drord_doc
const openDrawer = ref(false);
const orderClassCode = ref('');
const orderStatus = ref('');
// 医嘱类型 - 使用 drord_doctor_type 字典
// 已配置的药品类别列表
const configuredCategoryCodes = ref([]);
// 当前选择的药品 categoryCode用于后端过滤
const currentCategoryCode = ref('');
// 标记是否已显示过配置警告(整个生命周期只提示一次)
const hasShownPharmacyConfigWarning = ref(false);
// 标记配置是否已加载完成
const isCategoryLoaded = ref(false);
// 医嘱类型 - 根据取药科室配置动态过滤
const adviceTypeList = computed(() => {
// 如果字典已加载,使用字典数据(过滤掉全部选项
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
const list = drord_doctor_type.value.map(item => ({
label: item.label,
value: parseInt(item.value) || item.value
}));
// 添加全部选项
return [...list, { label: '全部', value: '' }];
// 如果配置还未加载完成,返回空列表(等待配置加载
if (!isCategoryLoaded.value) {
return [];
}
// 默认返回值
return [
{ label: '西药中成药', value: 1 },
{ label: '诊疗', value: 3 },
{ label: '手术', value: 6 },
{ label: '全部', value: '' },
];
// 如果没有配置类别,只显示诊疗和手术,隐藏所有药品分类
if (configuredCategoryCodes.value.length === 0) {
// 只显示警告(整个生命周期只提示一次)
if (!hasShownPharmacyConfigWarning.value) {
ElMessage.warning('当前科室并未在取药配置室配置');
hasShownPharmacyConfigWarning.value = true;
}
// 只返回不需要取药科室配置的类别(诊疗、手术)
return [
{ label: '诊疗', value: 3, adviceType: 3, categoryCode: '' },
{ label: '手术', value: 6, adviceType: 6, categoryCode: '' },
{ label: '全部', value: '', adviceType: '', categoryCode: '' },
];
}
// 根据已配置的 categoryCode 动态构建医嘱类型列表
// 重要:药品子分类使用复合值 'adviceType-categoryCode'(如 '1-2'),确保 el-select 能区分同 adviceType 的不同分类
const typeList = [];
// 如果配置了西药(categoryCode='2'),添加西药选项
if (configuredCategoryCodes.value.includes('2')) {
typeList.push({ label: '西药', value: '1-2', adviceType: 1, categoryCode: '2' });
}
// 如果配置了中成药(categoryCode='1'),添加中成药选项
if (configuredCategoryCodes.value.includes('1')) {
typeList.push({ label: '中成药', value: '1-1', adviceType: 1, categoryCode: '1' });
}
// 如果配置了中草药(categoryCode='4'),添加中草药选项
if (configuredCategoryCodes.value.includes('4')) {
typeList.push({ label: '中草药', value: '1-4', adviceType: 1, categoryCode: '4' });
}
// 始终添加诊疗和手术(它们不受取药配置限制)
typeList.push({ label: '诊疗', value: 3, adviceType: 3, categoryCode: '' });
typeList.push({ label: '手术', value: 6, adviceType: 6, categoryCode: '' });
// 添加全部选项
typeList.push({ label: '全部', value: '', adviceType: '', categoryCode: '' });
return typeList;
});
// 医嘱状态
const statusOption = [
@@ -564,6 +621,75 @@ function getListInfo(addNewRow) {
contractList.value = res.data;
});
accountId.value = patientInfo.value.accountId;
// 加载已配置的药品类别
loadConfiguredCategories();
}
/**
* 加载已配置的药品类别
*/
function loadConfiguredCategories() {
const orgId = patientInfo.value?.inHospitalOrgId;
if (!orgId) {
isCategoryLoaded.value = true; // 标记已加载即使没有科室ID
return;
}
getConfiguredCategories(orgId).then((res) => {
if (res.data && Array.isArray(res.data)) {
configuredCategoryCodes.value = res.data;
// 如果没有配置任何类别,只显示警告并隐藏药品分类
if (configuredCategoryCodes.value.length === 0) {
if (!hasShownPharmacyConfigWarning.value) {
ElMessage.warning('当前科室并未在取药配置室配置');
hasShownPharmacyConfigWarning.value = true;
}
}
// 根据配置动态设置默认的 categoryCode
// 优先级:西药(2) > 中成药(1) > 中草药(4) > 第一个配置的类别
const priorityOrder = ['2', '1', '4'];
let defaultCategoryCode = '';
for (const code of priorityOrder) {
if (configuredCategoryCodes.value.includes(code)) {
defaultCategoryCode = code;
break;
}
}
// 如果没有匹配到优先级类别,使用第一个配置的类别
if (!defaultCategoryCode && configuredCategoryCodes.value.length > 0) {
defaultCategoryCode = configuredCategoryCodes.value[0];
}
// 设置默认的 categoryCode
if (defaultCategoryCode) {
// 使用 nextTick 确保配置更新后触发 watch
nextTick(() => {
// 创建新对象触发响应式更新
adviceQueryParams.value = {
adviceType: 1,
categoryCode: defaultCategoryCode,
searchKey: '',
};
});
} else if (configuredCategoryCodes.value.length === 0) {
// 如果没有配置任何类别,清空 categoryCode
nextTick(() => {
adviceQueryParams.value = {
adviceType: '',
categoryCode: '',
searchKey: '',
};
});
}
}
isCategoryLoaded.value = true; // 标记已加载完成
}).catch((err) => {
isCategoryLoaded.value = true; // 即使失败也标记已加载
});
}
// 数据过滤
const filterPrescriptionList = computed(() => {
@@ -595,8 +721,24 @@ function getRowDisabled(row) {
return row.isEdit;
}
/**
* 将行的 adviceType + categoryCode 映射为 el-select 的选中值
* 药品子分类使用复合值如 '1-2'adviceType-categoryCode诊疗/手术/全部使用原始值
*/
function getRowSelectValue(row) {
if (row.adviceType == 1 && row.categoryCode) {
return '1-' + row.categoryCode;
}
return row.adviceType;
}
// 新增医嘱
function handleAddPrescription() {
// 校验是否选中患者
if (!patientInfo.value || !patientInfo.value.encounterId) {
proxy.$modal.msgWarning('请先选择患者');
return;
}
if (isAdding.value) {
proxy.$modal.msgWarning('请先保存当前医嘱');
return;
@@ -697,6 +839,32 @@ function handleDiagnosisChange(item) {
function handleFocus(row, index) {
rowIndex.value = index;
row.showPopover = true;
// el-popover 懒渲染,需要等两帧组件才会挂载
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
// 用 adviceType + categoryCode 组合查找匹配的选项
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);
// 诊疗(3)和手术(6)没有categoryCode传空字符串给refresh由子组件转为undefined不发送
// 药品(1)才有categoryCode如'1'=中成药,'2'=西药,'4'=中草药)
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
const searchKey = row.adviceName || '';
nextTick(() => {
nextTick(() => {
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
if (tableRef && tableRef.refresh) {
tableRef.refresh(adviceType, categoryCode, searchKey);
} else {
// fallback: 如果双重 nextTick 仍未挂载,延迟 100ms 再试
setTimeout(() => {
const tableRef2 = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
if (tableRef2 && tableRef2.refresh) {
tableRef2.refresh(adviceType, categoryCode, searchKey);
}
}, 100);
}
});
});
}
function handleBlur(row) {
@@ -705,6 +873,21 @@ function handleBlur(row) {
function handleChange(value) {
adviceQueryParams.value.searchKey = value;
// 搜索词变化时,调用当前行子组件的 refresh 方法
const index = rowIndex.value;
if (index >= 0) {
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
if (tableRef && tableRef.refresh) {
const row = filterPrescriptionList.value[index];
const adviceType = row?.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
// 用 adviceType + categoryCode 组合查找匹配的选项
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为空字符串由子组件转为undefined不发送
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
tableRef.refresh(adviceType, categoryCode, value);
}
}
}
/**

View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"types": ["vite/client"]
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"exclude": ["node_modules", "dist"]
}