Files
his/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue
2026-04-07 10:33:04 +08:00

428 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div @keyup="handleKeyDown" tabindex="0" ref="tableWrapper">
<el-table
ref="adviceBaseRef"
v-loading="loading"
height="400"
:data="adviceBaseList"
highlight-current-row
@current-change="handleCurrentChange"
row-key="adviceDefinitionId"
@cell-click="clickRow"
>
<el-table-column label="名称" align="center" prop="adviceName" width="200" show-overflow-tooltip />
<el-table-column label="包装单位" align="center" prop="unitCode_dictText" width="100" />
<el-table-column label="最小单位" align="center" prop="minUnitCode_dictText" width="100" />
<el-table-column label="单次剂量" align="center" width="120">
<template #default="scope">
<span>
{{
!scope.row.dose || isNaN(parseFloat(scope.row.dose))
? '-'
: parseFloat(scope.row.dose).toFixed(2) === '0.00'
? '-'
: parseFloat(scope.row.dose).toFixed(2) + scope.row.doseUnitCode_dictText
}}
</span>
</template>
</el-table-column>
<el-table-column label="规格" align="center" prop="volume" width="120" show-overflow-tooltip />
<el-table-column label="用法" align="center" prop="methodCode_dictText" width="120" show-overflow-tooltip />
<!-- 修改价格列从inventoryList中获取价格 -->
<el-table-column label="价格" align="center" width="100">
<template #default="scope">
<span>
{{ getPriceFromInventory(scope.row) }}
</span>
</template>
</el-table-column>
<el-table-column label="库存数量" align="center" width="100">
<template #default="scope">{{ handleQuantity(scope.row) }}</template>
</el-table-column>
<el-table-column label="频次" align="center" prop="rateCode_dictText" width="100" show-overflow-tooltip />
<el-table-column label="注射药品" align="center" prop="injectFlag_enumText" width="100" />
<el-table-column label="皮试" align="center" prop="skinTestFlag_enumText" width="100" />
<el-table-column label="医保等级" min-width="100" align="center">
<template #default="scope">
{{ getMedicalInsuranceLevel(scope.row) }}
</template>
</el-table-column>
<el-table-column label="医保码" align="center" prop="ybNo" width="100" show-overflow-tooltip />
<!-- <el-table-column label="限制使用标志" align="center" prop="useLimitFlag" /> -->
<el-table-column
label="限制使用范围"
align="center"
:show-overflow-tooltip="true"
prop="useScope"
width="120"
>
<template #default="scope">
<span v-if="scope.row.useLimitFlag === 1">{{ scope.row.useScope }}</span>
<span v-else>{{ '-' }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { nextTick, ref, watch, computed } from 'vue';
import { getAdviceBaseInfo, getDeviceList, getConsultationActivities } from './api';
import { throttle } from 'lodash-es';
// 获取医保等级 - 简单直接的实现
function getMedicalInsuranceLevel(record) {
// 非常简单直接的实现:直接返回值或转换为中文
const value = record.chrgitmLv || record.insuranceClass || '';
if (!value) return '无';
// 简单映射
const map = {
'1': '甲类',
'2': '乙类',
'3': '自费',
'甲': '甲类',
'乙': '乙类',
'自': '自费',
'甲类': '甲类',
'乙类': '乙类',
'自费': '自费'
};
return map[value] || value;
}
const props = defineProps({
adviceQueryParams: {
type: Object,
required: true
},
patientInfo: {
type: Object,
required: true,
},
// 预加载的数据
preloadedData: {
type: Array,
default: () => []
},
// 预加载是否完成
preloadedLoaded: {
type: Boolean,
default: false
},
});
const emit = defineEmits(['selectAdviceBase']);
const adviceBaseList = ref([]);
const loading = ref(false);
const currentSelectRow = ref({});
const currentIndex = ref(0);
const tableWrapper = ref();
const adviceBaseRef = ref();
// 查询参数 - 与后端API参数名一致
const queryParams = ref({
pageSize: 100,
pageNo: 1,
adviceTypes: '1,2,3',
});
// 节流函数 - 与V1.3一致300ms首次立即响应
const throttledGetList = throttle(
() => {
getList();
},
300,
{ leading: true, trailing: true }
);
// 监听参数变化 - 优化:优先使用预加载数据
watch(
() => props.adviceQueryParams,
(newValue) => {
queryParams.value.searchKey = newValue.searchKey;
if (newValue.adviceTypes) {
queryParams.value.adviceTypes = [newValue.adviceTypes].join(',');
} else {
queryParams.value.adviceTypes = '1,2,3';
}
throttledGetList();
},
{ deep: true }
);
// 获取数据 - 优化:优先使用预加载数据
function getList() {
const searchKey = queryParams.value.searchKey || '';
const adviceTypes = queryParams.value.adviceTypes || '1,2,3';
// 判断是否可以使用预加载数据
// 条件:预加载数据已就绪,且是普通药品/诊疗类型(adviceTypes包含1,2,3),且没有搜索关键词或搜索关键词很短
const canUsePreloaded = props.preloadedLoaded &&
props.preloadedData &&
props.preloadedData.length > 0 &&
(adviceTypes === '1,2,3' || adviceTypes === '1' || adviceTypes === '2' || adviceTypes === '3');
if (canUsePreloaded) {
// 使用预加载数据
console.log('[adviceBaseList] 使用预加载数据,共', props.preloadedData.length, '条');
let filteredData = [...props.preloadedData]; // 创建副本避免修改原数据
// 根据 adviceTypes 过滤
if (adviceTypes !== '1,2,3') {
const types = adviceTypes.split(',').map(t => parseInt(t));
filteredData = filteredData.filter(item => types.includes(item.adviceType));
}
// 根据搜索关键词过滤
if (searchKey && searchKey.length >= 1) {
const lowerSearchKey = searchKey.toLowerCase();
filteredData = filteredData.filter(item =>
(item.adviceName && item.adviceName.toLowerCase().includes(lowerSearchKey)) ||
(item.adviceDefinitionId && item.adviceDefinitionId.toString().includes(searchKey))
);
}
adviceBaseList.value = filteredData;
nextTick(() => {
currentIndex.value = 0;
});
// 如果有搜索关键词,还需要后台请求更精确的结果
if (searchKey && searchKey.length >= 2) {
fetchFromApi(searchKey);
}
return;
}
// 不能使用预加载数据直接请求API
fetchFromApi(searchKey);
}
// 从API获取数据
function fetchFromApi(searchKey) {
loading.value = true;
queryParams.value.organizationId = props.patientInfo.orgId;
const isConsumables = queryParams.value.adviceTypes === '2';
const isConsultation = queryParams.value.adviceTypes === '5';
if (isConsultation) {
// 会诊类型
getConsultationActivities().then((res) => {
if (res.data && Array.isArray(res.data)) {
adviceBaseList.value = res.data.map((item) => ({
adviceName: item.name || item.activityName,
adviceType: 5,
unitCode: '111',
unitCode_dictText: '次',
minUnitCode: '111',
minUnitCode_dictText: '次',
volume: '',
partPercent: 1,
priceList: item.price ? [{ price: item.price }] : [],
inventoryList: [],
adviceDefinitionId: item.id || item.activityId,
chargeItemDefinitionId: item.id || item.activityId,
positionId: '',
positionName: '',
dose: 0,
doseUnitCode: '111',
doseUnitCode_dictText: '次',
injectFlag: 0,
injectFlag_enumText: '否',
skinTestFlag: 0,
skinTestFlag_enumText: '否',
categoryCode: 31,
unitPrice: item.price || 0,
...item,
}));
nextTick(() => {
currentIndex.value = 0;
});
}
}).finally(() => {
loading.value = false;
});
return;
}
if (isConsumables) {
// 耗材类型
getDeviceList({
pageNo: 1,
pageSize: 100,
searchKey: searchKey || '',
statusEnum: 2,
}).then((res) => {
console.log('[BugFix] 耗材列表返回数据:', res.data);
if (res.data && res.data.records) {
adviceBaseList.value = res.data.records.map((item) => {
console.log('[BugFix] 耗材项:', item.name, 'price:', item.price, 'retailPrice:', item.retailPrice);
const mappedItem = {
...item,
// 🔧 Bug Fix: 强制覆盖后端返回的字段,确保数据正确
adviceName: item.name || item.busNo,
adviceType: 4, // 强制设置为前端耗材类型
adviceType_dictText: '耗材', // 🔧 Bug Fix: 设置医嘱类型显示文本
adviceTableName: 'adm_device_definition',
unitCode: item.unitCode || '',
unitCode_dictText: item.unitCode_dictText || '',
minUnitCode: item.minUnitCode || item.unitCode || '',
minUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '',
volume: item.size || item.totalVolume || '',
partPercent: item.partPercent || 1,
// 🔧 Bug Fix: 如果后端提供了inventoryList则使用否则为空数组
inventoryList: item.inventoryList || [],
// 🔧 Bug Fix: 构造stockList用于库存显示
stockList: item.inventoryList || [],
adviceDefinitionId: item.id,
chargeItemDefinitionId: item.id,
positionId: item.locationId,
positionName: item.locationId_dictText || '',
dose: 0,
doseUnitCode: item.minUnitCode || item.unitCode || '',
doseUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '',
injectFlag: 0,
injectFlag_enumText: '否',
skinTestFlag: 0,
skinTestFlag_enumText: '否',
categoryCode: item.categoryCode || '',
deviceId: item.id,
deviceName: item.name,
// 🔧 Bug #220 修复正确处理耗材价格支持price或retailPrice字段
// 价格字段优先使用retailPrice
priceList: (item.retailPrice !== undefined && item.retailPrice !== null)
? [{ price: item.retailPrice }]
: ((item.price !== undefined && item.price !== null)
? [{ price: item.price }]
: []),
};
console.log('[BugFix] 映射后的耗材项:', mappedItem.adviceName, 'adviceType:', mappedItem.adviceType, 'adviceType_dictText:', mappedItem.adviceType_dictText);
return mappedItem;
});
nextTick(() => {
currentIndex.value = 0;
});
}
}).finally(() => {
loading.value = false;
});
return;
}
// 普通药品/诊疗 - 与V1.3一致的处理方式
getAdviceBaseInfo(queryParams.value).then((res) => {
if (res.data && res.data.records && res.data.records.length > 0) {
// 与V1.3一致:在获取数据后直接过滤无库存的药品
adviceBaseList.value = res.data.records.filter((item) => {
if (item.adviceType == 1 || item.adviceType == 2) {
return handleQuantity(item) != 0;
} else {
return true;
}
});
nextTick(() => {
currentIndex.value = 0;
});
}
}).finally(() => {
loading.value = false;
});
}
// 从priceList列表中获取价格 - 与V1.3一致
// 🔧 Bug #220 修复:增强价格获取逻辑,支持耗材直接价格字段
function getPriceFromInventory(row) {
// 优先从priceList获取药品/诊疗)
if (row.priceList && row.priceList.length > 0) {
const price = row.priceList[0].price || 0;
return Number(price).toFixed(2) + ' 元';
}
// 耗材类型直接从retailPrice或price字段获取
if (row.adviceType === 4) {
const price = row.retailPrice !== undefined && row.retailPrice !== null
? row.retailPrice
: (row.price !== undefined && row.price !== null ? row.price : null);
if (price !== null && price !== '') {
return Number(price).toFixed(2) + ' 元';
}
}
return '-';
}
function handleQuantity(row) {
if (row.inventoryList && row.inventoryList.length > 0) {
const totalQuantity = row.inventoryList.reduce((sum, item) => sum + (item.quantity || 0), 0);
return totalQuantity.toString() + row.minUnitCode_dictText;
}
// 🔧 Bug Fix: 耗材类型可能没有inventoryList但可能有stockList
if (row.stockList && row.stockList.length > 0) {
const totalQuantity = row.stockList.reduce((sum, item) => sum + (item.quantity || 0), 0);
return totalQuantity.toString() + row.minUnitCode_dictText;
}
return 0;
}
// 处理键盘事件 - 与V1.3一致
const handleKeyDown = (event) => {
const key = event.key;
const data = adviceBaseList.value;
switch (key) {
case 'ArrowUp': // 上箭头
event.preventDefault();
if (currentIndex.value > 0) {
currentIndex.value--;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'ArrowDown': // 下箭头
event.preventDefault();
if (currentIndex.value < data.length - 1) {
currentIndex.value++;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'Enter': // 回车键
event.preventDefault();
if (currentSelectRow.value) {
emit('selectAdviceBase', currentSelectRow.value);
}
break;
}
};
// 设置选中行(带滚动)- 与V1.3一致
const setCurrentRow = (row) => {
adviceBaseRef.value.setCurrentRow(row);
const tableBody = adviceBaseRef.value.$el.querySelector('.el-table__body-wrapper');
const currentRowEl = adviceBaseRef.value.$el.querySelector('.current-row');
if (tableBody && currentRowEl) {
currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
};
// 当前行变化时更新索引 - 与V1.3一致
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
};
// 点击行
function clickRow(row) {
emit('selectAdviceBase', row);
}
defineExpose({
handleKeyDown,
});
</script>
<style scoped>
/* 保留原有的表格样式 */
</style>