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

@@ -116,4 +116,12 @@ public interface IDoctorStationAdviceAppService {
* @return 检查url相关参数 * @return 检查url相关参数
*/ */
R<?> getTestResult(Long encounterId); R<?> getTestResult(Long encounterId);
/**
* 获取当前科室已配置的药品类别列表
*
* @param organizationId 科室id
* @return 已配置的药品类别编码列表
*/
R<?> getConfiguredCategories(Long organizationId);
} }

View File

@@ -155,6 +155,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
String safePricingFlag = pricingFlag != null ? pricingFlag.toString() : ""; String safePricingFlag = pricingFlag != null ? pricingFlag.toString() : "";
String safePageNo = pageNo != null ? pageNo.toString() : ""; String safePageNo = pageNo != null ? pageNo.toString() : "";
String safePageSize = pageSize != null ? pageSize.toString() : ""; String safePageSize = pageSize != null ? pageSize.toString() : "";
String safeCategoryCode = categoryCode != null ? categoryCode : "";
// 设置默认科室:仅当前端/调用方未传 organizationId 时才回退到登录人科室 // 设置默认科室:仅当前端/调用方未传 organizationId 时才回退到登录人科室
// 否则会导致门诊划价等场景(按患者挂号科室查询)返回空 // 否则会导致门诊划价等场景(按患者挂号科室查询)返回空
@@ -169,7 +170,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
String cacheKey = null; String cacheKey = null;
if (useCache) { if (useCache) {
// 生成缓存 key无搜索关键字时按科室缓存 // 生成缓存 key无搜索关键字时按科室缓存
cacheKey = ADVICE_BASE_INFO_CACHE_PREFIX + organizationId + ":" + safeAdviceTypesStr + ":" + safePageNo + ":" + safePageSize; cacheKey = ADVICE_BASE_INFO_CACHE_PREFIX + organizationId + ":" + safeAdviceTypesStr + ":" + safeCategoryCode + ":" + safePageNo + ":" + safePageSize;
// 先清除可能存在的无效缓存JSONObject类型 // 先清除可能存在的无效缓存JSONObject类型
if (redisCache.hasKey(cacheKey)) { if (redisCache.hasKey(cacheKey)) {
@@ -281,6 +282,8 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
} }
String unitCode = ""; // 包装单位 String unitCode = ""; // 包装单位
Long chargeItemDefinitionId; // 费用定价主表ID Long chargeItemDefinitionId; // 费用定价主表ID
// 检查是否有取药科室配置(用于药品类型)
boolean hasPharmacyConfig = medLocationConfig != null && !medLocationConfig.isEmpty();
for (AdviceBaseDto baseDto : adviceBaseDtoList) { for (AdviceBaseDto baseDto : adviceBaseDtoList) {
String tableName = baseDto.getAdviceTableName(); String tableName = baseDto.getAdviceTableName();
if (CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(tableName)) { // 药品 if (CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(tableName)) { // 药品
@@ -290,6 +293,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 是否为注射药物 // 是否为注射药物
baseDto.setInjectFlag_enumText(EnumUtils.getInfoByValue(Whether.class, baseDto.getInjectFlag())); baseDto.setInjectFlag_enumText(EnumUtils.getInfoByValue(Whether.class, baseDto.getInjectFlag()));
// 设置是否缺少取药科室配置标志
baseDto.setPharmacyConfigMissing(!hasPharmacyConfig);
// fallthrough to 耗材处理逻辑(保持原有逻辑) // fallthrough to 耗材处理逻辑(保持原有逻辑)
// 每一条医嘱的库存集合信息 , 包装单位库存前端计算 // 每一条医嘱的库存集合信息 , 包装单位库存前端计算
List<AdviceInventoryDto> inventoryList = adviceInventory List<AdviceInventoryDto> inventoryList = adviceInventory
@@ -2148,4 +2154,23 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
return searchKey.matches("[a-zA-Z]+"); return searchKey.matches("[a-zA-Z]+");
} }
/**
* 获取当前科室已配置的药品类别列表
*
* @param organizationId 科室id
* @return 已配置的药品类别编码列表
*/
@Override
public R<?> getConfiguredCategories(Long organizationId) {
// 查询取药科室配置
List<AdviceInventoryDto> medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
// 提取不重复的 categoryCode
List<String> categoryCodes = medLocationConfig.stream()
.map(AdviceInventoryDto::getCategoryCode)
.filter(code -> code != null && !code.isEmpty())
.distinct()
.collect(Collectors.toList());
return R.ok(categoryCodes);
}
} }

View File

@@ -50,9 +50,10 @@ public class DoctorStationAdviceController {
@RequestParam(value = "organizationId", required = false) Long organizationId, @RequestParam(value = "organizationId", required = false) Long organizationId,
@RequestParam(value = "adviceTypes", defaultValue = "1,2,3") List<Integer> adviceTypes, @RequestParam(value = "adviceTypes", defaultValue = "1,2,3") List<Integer> adviceTypes,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "categoryCode", required = false) String categoryCode) {
return R.ok(iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, return R.ok(iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId,
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, Whether.NO.getValue(), adviceTypes, null, null)); adviceDefinitionIdParamList, organizationId, pageNo, pageSize, Whether.NO.getValue(), adviceTypes, null, categoryCode));
} }
/** /**
@@ -186,4 +187,15 @@ public class DoctorStationAdviceController {
return iDoctorStationAdviceAppService.getTestResult(encounterId); return iDoctorStationAdviceAppService.getTestResult(encounterId);
} }
/**
* 获取当前科室已配置的药品类别列表
*
* @param organizationId 科室id
* @return 已配置的药品类别编码列表
*/
@GetMapping(value = "/configured-categories")
public R<?> getConfiguredCategories(@RequestParam(value = "organizationId", required = false) Long organizationId) {
return iDoctorStationAdviceAppService.getConfiguredCategories(organizationId);
}
} }

View File

@@ -242,4 +242,9 @@ public class AdviceBaseDto {
@Dict(dictCode = "chrgitm_lv") @Dict(dictCode = "chrgitm_lv")
private String chrgitmLv; private String chrgitmLv;
private String chrgitmLv_dictText; private String chrgitmLv_dictText;
/**
* 是否缺少取药科室配置(仅药品类型使用)
*/
private Boolean pharmacyConfigMissing;
} }

View File

@@ -46,7 +46,7 @@
abi.chrgitm_lv abi.chrgitm_lv
FROM ( FROM (
<!-- 确保至少有一个查询被执行以避免语法错误 --> <!-- 确保至少有一个查询被执行以避免语法错误 -->
<if test="adviceTypes != null and !adviceTypes.isEmpty() and (adviceTypes.contains(1) or adviceTypes.contains(2) or adviceTypes.contains(3))"> <if test="adviceTypes != null and !adviceTypes.isEmpty() and (adviceTypes.contains(1) or adviceTypes.contains(2) or adviceTypes.contains(3) or adviceTypes.contains(6))">
<!-- 如果有有效的adviceTypes则执行对应的查询 --> <!-- 如果有有效的adviceTypes则执行对应的查询 -->
<if test="adviceTypes.contains(1)"> <if test="adviceTypes.contains(1)">
(SELECT (SELECT
@@ -95,14 +95,29 @@
AND T2.delete_flag = '0' AND T2.status_enum = #{statusEnum} AND T2.delete_flag = '0' AND T2.status_enum = #{statusEnum}
LEFT JOIN adm_supplier AS T3 ON T3.ID = t1.supply_id AND T3.delete_flag = '0' LEFT JOIN adm_supplier AS T3 ON T3.ID = t1.supply_id AND T3.delete_flag = '0'
LEFT JOIN adm_charge_item_definition AS T5 ON T5.instance_id = t1.ID AND T5.delete_flag = '0' AND T5.status_enum = #{statusEnum} LEFT JOIN adm_charge_item_definition AS T5 ON T5.instance_id = t1.ID AND T5.delete_flag = '0' AND T5.status_enum = #{statusEnum}
LEFT JOIN adm_organization_location AS T6 ON T6.distribution_category_code = t1.category_code AND T6.delete_flag = '0' AND T6.item_code = '1' AND T6.organization_id = #{organizationId} AND (CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time) INNER JOIN adm_organization_location AS T6 ON T6.distribution_category_code = t1.category_code AND T6.delete_flag = '0' AND T6.item_code = '1' AND T6.organization_id = #{organizationId} AND (CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time)
WHERE t1.delete_flag = '0' WHERE t1.delete_flag = '0'
AND T2.status_enum = #{statusEnum} AND T2.status_enum = #{statusEnum}
<if test="pricingFlag == 1"> <if test="pricingFlag == 1">
AND 1 = 2 AND 1 = 2
</if> </if>
<if test="categoryCode != null and categoryCode != ''"> <if test="categoryCode != null and categoryCode != ''">
AND t1.category_code = #{categoryCode} <!-- 🔧 BugFix: 支持两种匹配方式 -->
<!-- 1. 直接匹配distribution_category_code = category_code都是数字代码 -->
<!-- 2. 字典转换匹配:通过 sys_dict_data 表将 distribution_category_code中文转换为 category_code数字代码 -->
AND (
-- 方式1直接匹配
t1.category_code = #{categoryCode}
OR
-- 方式2通过字典转换匹配当 distribution_category_code 存储的是中文时)
EXISTS (
SELECT 1 FROM sys_dict_data sdd
WHERE sdd.dict_type = 'med_category_code'
AND sdd.status = '0'
AND sdd.dict_label = T6.distribution_category_code
AND sdd.dict_value = #{categoryCode}
)
)
</if> </if>
<if test="searchKey != null and searchKey != ''"> <if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%') AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
@@ -185,11 +200,15 @@
<if test="adviceTypes.contains(3)">UNION ALL</if> <if test="adviceTypes.contains(3)">UNION ALL</if>
</if> </if>
<if test="adviceTypes.contains(3)"> <if test="adviceTypes.contains(3) or adviceTypes.contains(6)">
(SELECT (SELECT
DISTINCT ON (T1.ID) DISTINCT ON (T1.ID)
T1.tenant_id, T1.tenant_id,
3 AS advice_type, <choose>
<when test="adviceTypes.contains(3) and adviceTypes.contains(6)">CASE T1.category_code WHEN '手术' THEN 6 WHEN '24' THEN 6 ELSE 3 END</when>
<when test="adviceTypes.contains(6)">6</when>
<otherwise>3</otherwise>
</choose> AS advice_type,
T1.bus_no AS bus_no, T1.bus_no AS bus_no,
T1.category_code AS category_code, T1.category_code AS category_code,
'' AS pharmacology_category_code, '' AS pharmacology_category_code,
@@ -213,7 +232,7 @@
WHEN '检验' THEN 1 WHEN '检验' THEN 1
WHEN '检查' THEN 2 WHEN '检查' THEN 2
WHEN '护理' THEN 3 WHEN '护理' THEN 3
WHEN '手术' THEN 4 WHEN '手术' THEN 4 WHEN '24' THEN 4
WHEN '其他' THEN 5 WHEN '其他' THEN 5
ELSE 0 ELSE 0
END AS activity_type, END AS activity_type,
@@ -250,6 +269,14 @@
<if test="pricingFlag != null"> <if test="pricingFlag != null">
AND (t1.pricing_flag = #{pricingFlag} OR t1.pricing_flag IS NULL) AND (t1.pricing_flag = #{pricingFlag} OR t1.pricing_flag IS NULL)
</if> </if>
<!-- 如果只选择手术(adviceType=6),过滤 category_code = '手术' 或 '24' -->
<if test="adviceTypes.contains(6) and !adviceTypes.contains(3)">
AND (T1.category_code = '手术' OR T1.category_code = '24')
</if>
<!-- 如果只选择诊疗(adviceType=3),排除手术 -->
<if test="adviceTypes.contains(3) and !adviceTypes.contains(6)">
AND T1.category_code != '手术' AND T1.category_code != '24'
</if>
<if test="searchKey != null and searchKey != ''"> <if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%') AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
</if> </if>
@@ -263,7 +290,7 @@
</if> </if>
</if> </if>
<!-- 如果没有有效的adviceTypes提供一个空的默认查询以避免语法错误 --> <!-- 如果没有有效的adviceTypes提供一个空的默认查询以避免语法错误 -->
<if test="adviceTypes == null or adviceTypes.isEmpty() or (!adviceTypes.contains(1) and !adviceTypes.contains(2) and !adviceTypes.contains(3))"> <if test="adviceTypes == null or adviceTypes.isEmpty() or (!adviceTypes.contains(1) and !adviceTypes.contains(2) and !adviceTypes.contains(3) and !adviceTypes.contains(6))">
SELECT SELECT
mmd.tenant_id, mmd.tenant_id,
CAST(0 AS INTEGER) AS advice_type, CAST(0 AS INTEGER) AS advice_type,

View File

@@ -27,6 +27,7 @@
T2.start_time AS admissionDate, --入院日期 T2.start_time AS admissionDate, --入院日期
T3.ward_admission_date AS wardAdmissionDate, --入科日期 T3.ward_admission_date AS wardAdmissionDate, --入科日期
T3.location_id AS ward_location_id, --病区 T3.location_id AS ward_location_id, --病区
T3.ward_admission_date AS wardAdmissionDate, --病区入院日期
T4.location_id AS bed_location_id --床号 T4.location_id AS bed_location_id --床号
FROM adm_patient AS T1 FROM adm_patient AS T1
INNER JOIN adm_encounter AS T2 INNER JOIN adm_encounter AS T2

View File

@@ -23,26 +23,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, nextTick, ref, watch} from 'vue'; import {computed, nextTick, onMounted, ref} from 'vue';
import {throttle} from 'lodash-es'; import {throttle} from 'lodash-es';
import Table from '@/components/TableLayout/Table.vue'; import Table from '@/components/TableLayout/Table.vue';
import {getAdviceBaseInfo} from './api'; import {getAdviceBaseInfo} from './api';
import type {TableColumn} from '@/components/types/TableLayout.d'; import type {TableColumn} from '@/components/types/TableLayout';
interface Props { interface Props {
adviceQueryParams?: {
searchKey?: string;
adviceType?: string;
};
patientInfo: { patientInfo: {
inHospitalOrgId?: string; inHospitalOrgId?: string;
[key: string]: any; [key: string]: any;
}; };
} }
const props = withDefaults(defineProps<Props>(), { const props = defineProps<Props>();
adviceQueryParams: () => ({}),
});
const emit = defineEmits<{ const emit = defineEmits<{
selectAdviceBase: [row: any]; selectAdviceBase: [row: any];
@@ -56,14 +50,14 @@ const currentIndex = ref<number>(0);
const currentSelectRow = ref<any>({}); const currentSelectRow = ref<any>({});
const queryParams = ref({ const queryParams = ref({
pageSize: 100, pageSize: 100,
pageNum: 1, pageNo: 1,
adviceTypes: '1,3', adviceTypes: '1,2,3,6',
searchKey: '', searchKey: '',
organizationId: '', organizationId: '',
categoryCode: '',
}); });
const adviceBaseList = ref<any[]>([]); const adviceBaseList = ref<any[]>([]);
// 表格列配置
const tableColumns = computed<TableColumn[]>(() => [ const tableColumns = computed<TableColumn[]>(() => [
{ label: '名称', prop: 'adviceName', align: 'center', width: 200 }, { label: '名称', prop: 'adviceName', align: 'center', width: 200 },
{ label: '类型', prop: 'activityType_enumText', align: 'center' }, { label: '类型', prop: 'activityType_enumText', align: 'center' },
@@ -84,50 +78,53 @@ const tableColumns = computed<TableColumn[]>(() => [
slot: 'useScope', 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() { function getList() {
loading.value = true; 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) getAdviceBaseInfo(queryParams.value)
.then((res) => { .then((res) => {
console.log(res.data.records); const records = res.data?.records || [];
if (res.data.records.length > 0) {
adviceBaseList.value = res.data.records.filter((item) => { // 药品/耗材需要有库存才显示,诊疗/手术直接显示
adviceBaseList.value = records.filter((item: any) => {
if (item.adviceType == 1 || item.adviceType == 2) { if (item.adviceType == 1 || item.adviceType == 2) {
return handleQuantity(item) != 0; return handleQuantity(item) !== '0';
} else {
return true;
} }
return true;
}); });
total.value = res.data.total;
total.value = res.data?.total || 0;
nextTick(() => { nextTick(() => {
if (adviceBaseList.value.length > 0) { if (adviceBaseList.value.length > 0) {
currentIndex.value = 0; currentIndex.value = 0;
setCurrentRow(adviceBaseList.value[0]); setCurrentRow(adviceBaseList.value[0]);
} }
}); });
} else {
adviceBaseList.value = [];
}
}) })
.catch(() => { .catch(() => {
adviceBaseList.value = []; adviceBaseList.value = [];
@@ -141,23 +138,22 @@ function getList() {
const handleKeyDown = (event: KeyboardEvent): void => { const handleKeyDown = (event: KeyboardEvent): void => {
const key = event.key; const key = event.key;
const data = adviceBaseList.value; const data = adviceBaseList.value;
switch (key) { switch (key) {
case 'ArrowUp': // 上箭头 case 'ArrowUp':
event.preventDefault(); event.preventDefault();
if (currentIndex.value > 0) { if (currentIndex.value > 0) {
currentIndex.value--; currentIndex.value--;
setCurrentRow(data[currentIndex.value]); setCurrentRow(data[currentIndex.value]);
} }
break; break;
case 'ArrowDown': // 下箭头 case 'ArrowDown':
event.preventDefault(); event.preventDefault();
if (currentIndex.value < data.length - 1) { if (currentIndex.value < data.length - 1) {
currentIndex.value++; currentIndex.value++;
setCurrentRow(data[currentIndex.value]); setCurrentRow(data[currentIndex.value]);
} }
break; break;
case 'Enter': // 回车键 case 'Enter':
event.preventDefault(); event.preventDefault();
if (currentSelectRow.value && Object.keys(currentSelectRow.value).length > 0) { if (currentSelectRow.value && Object.keys(currentSelectRow.value).length > 0) {
emit('selectAdviceBase', currentSelectRow.value); emit('selectAdviceBase', currentSelectRow.value);
@@ -177,17 +173,14 @@ function handleQuantity(row: any): string {
return '0'; return '0';
} }
// 设置选中行(带滚动)
const setCurrentRow = (row: any) => { const setCurrentRow = (row: any) => {
if (adviceBaseRef.value?.tableRef) { if (adviceBaseRef.value?.tableRef) {
adviceBaseRef.value.tableRef.setCurrentRow(row); adviceBaseRef.value.tableRef.setCurrentRow(row);
// 滚动到选中行
nextTick(() => { nextTick(() => {
const tableEl = adviceBaseRef.value?.tableRef?.$el; const tableEl = adviceBaseRef.value?.tableRef?.$el;
if (tableEl) { if (tableEl) {
const tableBody = tableEl.querySelector('.el-table__body-wrapper');
const currentRowEl = tableEl.querySelector('.current-row'); const currentRowEl = tableEl.querySelector('.current-row');
if (tableBody && currentRowEl) { if (currentRowEl) {
currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} }
} }
@@ -195,31 +188,20 @@ const setCurrentRow = (row: any) => {
} }
}; };
// 行点击事件
const handleRowClick = (row: any): void => { const handleRowClick = (row: any): void => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === row); currentIndex.value = adviceBaseList.value.findIndex((item) => item === row);
currentSelectRow.value = row; currentSelectRow.value = row;
emit('selectAdviceBase', 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({ defineExpose({
handleKeyDown, handleKeyDown,
refresh,
});
// 组件挂载时自动加载数据el-popover 懒渲染,父组件 refresh 可能因时序问题未生效onMounted 最可靠)
onMounted(() => {
getList();
}); });
</script> </script>
@@ -234,8 +216,4 @@ defineExpose({
outline: 2px solid #409eff; outline: 2px solid #409eff;
} }
} }
.popover-table-wrapper:focus {
outline: 2px solid #409eff;
}
</style> </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)"> <template v-if="getRowDisabled(scope.row)">
<el-select <el-select
style="width: 35%; margin-right: 8px" style="width: 35%; margin-right: 8px"
v-model="scope.row.adviceType" :model-value="getRowSelectValue(scope.row)"
:ref="'adviceTypeRef' + scope.$index" :ref="'adviceTypeRef' + scope.$index"
:disabled="!isCategoryLoaded"
@change=" @change="
(value) => { (value) => {
expandOrder = []; expandOrder = [];
filterPrescriptionList[scope.$index].adviceName = undefined; 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 clearable
> >
<el-option <el-option
v-for="item in adviceTypeList" v-for="item in adviceTypeList"
:key="item.value" :key="item.label + '-' + item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
@click="
() => {
filterPrescriptionList[scope.$index].adviceType = item.value;
filterPrescriptionList[scope.$index].adviceType_dictText = item.label;
}
"
/> />
</el-select> </el-select>
<el-popover <el-popover
@@ -177,8 +189,6 @@
> >
<adviceBaseList <adviceBaseList
ref="adviceTableRef" ref="adviceTableRef"
:popoverVisible="scope.row.showPopover"
:adviceQueryParams="adviceQueryParams"
:patientInfo="patientInfo" :patientInfo="patientInfo"
@selectAdviceBase="(row) => selectAdviceBase(scope.row.uniqueKey, row)" @selectAdviceBase="(row) => selectAdviceBase(scope.row.uniqueKey, row)"
/> />
@@ -198,7 +208,8 @@
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) { if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
e.preventDefault(); 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, singOut,
stopAdvice, stopAdvice,
updateGroupId, updateGroupId,
getConfiguredCategories,
} from '../api'; } from '../api';
import adviceBaseList from '../adviceBaseList'; import adviceBaseList from '../adviceBaseList';
import {calculateQuantityByDays} from '@/utils/his'; import {calculateQuantityByDays} from '@/utils/his';
@@ -360,7 +372,7 @@ import LeaveHospitalDialog from './applicationForm/leaveHospitalDialog.vue';
import TransferOrganizationDialog from './applicationForm/transferOrganizationDialog.vue'; import TransferOrganizationDialog from './applicationForm/transferOrganizationDialog.vue';
import NursingStatus from '@/views/inpatientDoctor/home/components/applicationShow/nursingStatus.vue'; import NursingStatus from '@/views/inpatientDoctor/home/components/applicationShow/nursingStatus.vue';
import OrderForm from './OrderForm.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 emit = defineEmits(['selectDiagnosis']);
const total = ref(0); const total = ref(0);
@@ -369,7 +381,11 @@ const prescriptionList = ref([]);
const form = ref({ const form = ref({
prescriptionList: prescriptionList.value, prescriptionList: prescriptionList.value,
}); });
const adviceQueryParams = ref({}); const adviceQueryParams = ref({
adviceType: 1,
categoryCode: '', // 初始为空,等待加载配置后动态设置
searchKey: '',
});
const rowIndex = ref(-1); const rowIndex = ref(-1);
const groupIndex = ref(1); const groupIndex = ref(1);
const groupIndexList = ref([]); const groupIndexList = ref([]);
@@ -436,24 +452,65 @@ const { method_code, unit_code, rate_code, distribution_category_code, drord_doc
const openDrawer = ref(false); const openDrawer = ref(false);
const orderClassCode = ref(''); const orderClassCode = ref('');
const orderStatus = 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(() => { const adviceTypeList = computed(() => {
// 如果字典已加载,使用字典数据(过滤掉全部选项 // 如果配置还未加载完成,返回空列表(等待配置加载
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) { if (!isCategoryLoaded.value) {
const list = drord_doctor_type.value.map(item => ({ return [];
label: item.label,
value: parseInt(item.value) || item.value
}));
// 添加全部选项
return [...list, { label: '全部', value: '' }];
} }
// 默认返回值
// 如果没有配置类别,只显示诊疗和手术,隐藏所有药品分类
if (configuredCategoryCodes.value.length === 0) {
// 只显示警告(整个生命周期只提示一次)
if (!hasShownPharmacyConfigWarning.value) {
ElMessage.warning('当前科室并未在取药配置室配置');
hasShownPharmacyConfigWarning.value = true;
}
// 只返回不需要取药科室配置的类别(诊疗、手术)
return [ return [
{ label: '西药中成药', value: 1 }, { label: '诊疗', value: 3, adviceType: 3, categoryCode: '' },
{ label: '诊疗', value: 3 }, { label: '手术', value: 6, adviceType: 6, categoryCode: '' },
{ label: '手术', value: 6 }, { label: '全部', value: '', adviceType: '', categoryCode: '' },
{ label: '全部', value: '' },
]; ];
}
// 根据已配置的 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 = [ const statusOption = [
@@ -564,6 +621,75 @@ function getListInfo(addNewRow) {
contractList.value = res.data; contractList.value = res.data;
}); });
accountId.value = patientInfo.value.accountId; 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(() => { const filterPrescriptionList = computed(() => {
@@ -595,8 +721,24 @@ function getRowDisabled(row) {
return row.isEdit; 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() { function handleAddPrescription() {
// 校验是否选中患者
if (!patientInfo.value || !patientInfo.value.encounterId) {
proxy.$modal.msgWarning('请先选择患者');
return;
}
if (isAdding.value) { if (isAdding.value) {
proxy.$modal.msgWarning('请先保存当前医嘱'); proxy.$modal.msgWarning('请先保存当前医嘱');
return; return;
@@ -697,6 +839,32 @@ function handleDiagnosisChange(item) {
function handleFocus(row, index) { function handleFocus(row, index) {
rowIndex.value = index; rowIndex.value = index;
row.showPopover = true; 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) { function handleBlur(row) {
@@ -705,6 +873,21 @@ function handleBlur(row) {
function handleChange(value) { function handleChange(value) {
adviceQueryParams.value.searchKey = 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"]
}