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:
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user