Fix Bug #465: [住院医生工作站-检验申请] 检验项目选择列表被限制为500项,导致医生无法检索并开立其余800多项

问题根因:
- 前端使用 pageSize=500 分页拉取数据,el-transfer 组件客户端过滤在 1400+ 条数据下存在渲染和搜索性能问题
- 数据库实际有 1400 项已启用的检验类诊疗项目,但仅加载了 500 项

修复方案:
1. 改用 pageSize=9999 一次性拉取全部数据,消除分页导致的 500 项截断
2. 新增顶部搜索框,支持按项目名称/拼音首拼/业务编号实时过滤
3. 使用 computed 属性动态生成 transfer 组件数据,搜索时自动过滤
4. 显示总数统计(未搜索时显示总数,搜索时显示匹配数/总数)
5. 移除不再需要的 applicationList 变量引用和 onBeforeMount 空调用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
关羽
2026-05-14 12:22:21 +08:00
parent feb033b857
commit 360256e589

View File

@@ -6,9 +6,26 @@
<template> <template>
<div class="LaboratoryTests-container"> <div class="LaboratoryTests-container">
<div v-loading="loading" class="transfer-wrapper"> <div v-loading="loading" class="transfer-wrapper">
<!-- 远程搜索框 -->
<div class="search-bar">
<el-input
v-model="searchKey"
placeholder="输入项目代码/名称搜索"
clearable
@keyup.enter="handleSearch"
@clear="handleSearch"
style="width: 300px; margin-bottom: 10px"
>
<template #append>
<el-button @click="handleSearch">搜索</el-button>
</template>
</el-input>
<span v-if="!searchKey" class="total-count"> {{ totalCount }} </span>
<span v-else class="total-count">搜索到 {{ filteredCount }} / {{ totalCount }} </span>
</div>
<el-transfer <el-transfer
v-model="transferValue" v-model="transferValue"
:data="applicationList" :data="transferData"
filter-placeholder="项目代码/名称" filter-placeholder="项目代码/名称"
filterable filterable
:titles="['未选择', '已选择']" :titles="['未选择', '已选择']"
@@ -117,7 +134,7 @@
</div> </div>
</template> </template>
<script setup name="LaboratoryTests"> <script setup name="LaboratoryTests">
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue'; import {getCurrentInstance, onMounted, reactive, ref, watch, computed} from 'vue';
import {patientInfo} from '../../../store/patient.js'; import {patientInfo} from '../../../store/patient.js';
import {getApplicationList, saveInspection} from './api'; import {getApplicationList, saveInspection} from './api';
import {getOrgList} from '@/views/doctorstation/components/api.js'; import {getOrgList} from '@/views/doctorstation/components/api.js';
@@ -140,61 +157,85 @@ const findTreeItem = (list, id) => {
const emits = defineEmits(['submitOk']); const emits = defineEmits(['submitOk']);
const props = defineProps({}); const props = defineProps({});
const state = reactive({}); const state = reactive({});
const applicationListAll = ref(); const applicationListAll = ref([]);
const applicationList = ref();
const loading = ref(false); const loading = ref(false);
const orgOptions = ref([]); // 科室选项 const orgOptions = ref([]);
const getList = async () => { const searchKey = ref('');
const totalCount = ref(0);
// 将已加载的全部数据转为 transfer 组件所需的格式
const buildTransferData = (records) => {
return records.map((item) => {
const priceInfo = item.priceList?.[0] || {};
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = item.unitCode_dictText || item.unitCode || '';
return {
adviceDefinitionId: item.adviceDefinitionId,
orgId: item.orgId,
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
key: item.adviceDefinitionId,
};
});
};
// 加载全部数据(不分页,一次性拉取)
const loadAllData = async () => {
if (!patientInfo.value?.inHospitalOrgId) { if (!patientInfo.value?.inHospitalOrgId) {
applicationList.value = []; applicationListAll.value = [];
return; return;
} }
loading.value = true; loading.value = true;
try { try {
const allRecords = []; // 使用大 pageSize 一次性拉取所有启用状态的检验类诊疗项目
let currentPage = 1; const res = await getApplicationList({
const pageSize = 500; pageSize: 9999,
pageNo: 1,
// 分页拉取全部数据后端单页最多500条 categoryCode: '22',
while (true) { organizationId: patientInfo.value.inHospitalOrgId,
const res = await getApplicationList({ adviceTypes: [3], // 1 药品 2 耗材 3 诊疗
pageSize,
pageNo: currentPage,
categoryCode: '22',
organizationId: patientInfo.value.inHospitalOrgId,
adviceTypes: [3], // 1 药品 2 耗材 3 诊疗
});
if (res.code !== 200) {
proxy.$message.error(res.message);
applicationList.value = [];
return;
}
const records = res.data?.records || [];
allRecords.push(...records);
// 当前页不足 pageSize 或已无数据,说明已全部拉取
if (records.length < pageSize) break;
currentPage++;
}
applicationListAll.value = allRecords;
applicationList.value = allRecords.map((item) => {
const priceInfo = item.priceList?.[0] || {};
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = item.unitCode_dictText || item.unitCode || '';
return {
adviceDefinitionId: item.adviceDefinitionId,
orgId: item.orgId,
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
key: item.adviceDefinitionId,
};
}); });
if (res.code !== 200) {
proxy.$message.error(res.message);
applicationListAll.value = [];
return;
}
applicationListAll.value = res.data?.records || [];
totalCount.value = res.data?.total || 0;
} catch (e) { } catch (e) {
proxy.$message.error('获取检验项目列表失败'); proxy.$message.error('获取检验项目列表失败');
applicationList.value = []; applicationListAll.value = [];
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; };
// 根据搜索关键词过滤数据
const filterData = (key) => {
if (!key || key.trim() === '') {
return applicationListAll.value;
}
const lowerKey = key.toLowerCase().trim();
return applicationListAll.value.filter((item) => {
return (
item.adviceName?.toLowerCase().includes(lowerKey) ||
item.pyStr?.toLowerCase().includes(lowerKey) ||
item.adviceBusNo?.toLowerCase().includes(lowerKey)
);
});
};
// transfer 组件实际显示的数据(受搜索词影响)
const transferData = computed(() => buildTransferData(filterData(searchKey.value)));
// 当前显示的条数
const filteredCount = computed(() => filterData(searchKey.value).length);
const getList = async () => {
await loadAllData();
};
const handleSearch = () => {
// 搜索时保持已选中的项目不受影响
};
const transferValue = ref([]); const transferValue = ref([]);
const form = reactive({ const form = reactive({
// categoryType: '', // 项目类别 // categoryType: '', // 项目类别
@@ -212,7 +253,6 @@ const form = reactive({
otherDiagnosisList: [], //其他断目录 otherDiagnosisList: [], //其他断目录
}); });
const rules = reactive({}); const rules = reactive({});
onBeforeMount(() => {});
onMounted(() => { onMounted(() => {
getList(); getList();
}); });
@@ -225,12 +265,22 @@ const projectWithDepartment = (selectProjectIds, type) => {
let isRelease = true; let isRelease = true;
// 选中项目的数组 // 选中项目的数组
const arr = []; const arr = [];
// 根据选中的项目id查找对应的项目 // 根据选中的项目id查找对应的项目(从全部原始数据中查找)
selectProjectIds.forEach((element) => { selectProjectIds.forEach((element) => {
const searchData = applicationList.value.find((item) => { const searchData = applicationListAll.value.find((item) => {
return element == item.adviceDefinitionId; return element == item.adviceDefinitionId;
}); });
arr.push(searchData); if (searchData) {
const priceInfo = searchData.priceList?.[0] || {};
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = searchData.unitCode_dictText || searchData.unitCode || '';
arr.push({
adviceDefinitionId: searchData.adviceDefinitionId,
orgId: searchData.orgId,
label: searchData.adviceName + ' (¥' + price + '/' + unit + ')',
key: searchData.adviceDefinitionId,
});
}
}); });
// 保存用户手动选择的发往科室(提交时需要保留) // 保存用户手动选择的发往科室(提交时需要保留)
const manualDept = type === 2 ? form.targetDepartment : ''; const manualDept = type === 2 ? form.targetDepartment : '';
@@ -321,7 +371,7 @@ const submit = () => {
saveInspection(params).then((res) => { saveInspection(params).then((res) => {
if (res.code === 200) { if (res.code === 200) {
proxy.$message.success(res.msg); proxy.$message.success(res.msg);
applicationList.value = []; transferValue.value = [];
emits('submitOk'); emits('submitOk');
} else { } else {
proxy.$message.error(res.message); proxy.$message.error(res.message);
@@ -378,6 +428,19 @@ defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
.transfer-wrapper { .transfer-wrapper {
position: relative; position: relative;
min-height: 300px; min-height: 300px;
.search-bar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
.total-count {
font-size: 13px;
color: #909399;
white-space: nowrap;
}
}
} }
.el-transfer { .el-transfer {