From 3a242074ff7fd4e81e1d38a67f714bd4ef8b6490 Mon Sep 17 00:00:00 2001 From: duzhongxu <15039018447@163.com> Date: Fri, 27 Mar 2026 11:48:47 +0800 Subject: [PATCH] =?UTF-8?q?209=20=E6=A3=80=E9=AA=8C=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE-=E3=80=8B=E5=A5=97=E9=A4=90=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=EF=BC=9A=E5=A1=AB=E5=86=99=E5=A5=97=E9=A4=90=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E4=BF=A1=E6=81=AF/=E6=98=8E=E7=BB=86=E6=9C=AA?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=A3=80=E9=AA=8C=E5=A5=97=E9=A4=90=E7=9A=84?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=8A=9F=E8=83=BD=20290=20=E6=A3=80=E9=AA=8C?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E8=AE=BE=E7=BD=AE-=E3=80=8B=E5=A5=97?= =?UTF-8?q?=E9=A4=90=E8=AE=BE=E7=BD=AE=EF=BC=9A=E9=A1=B9=E7=9B=AE=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E4=B8=8D=E8=83=BD=E5=BF=AB=E9=80=9F=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E5=88=B0=E6=89=80=E6=9C=89=E7=9A=84=E9=A1=B9=E7=9B=AE=20291=20?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4=E7=B3=BB=E7=BB=9F=20-=20=E3=80=8B=E6=A3=80?= =?UTF-8?q?=E9=AA=8C=E9=A1=B9=E7=9B=AE=E8=AE=BE=E7=BD=AE=20-=20=E3=80=8B?= =?UTF-8?q?=E5=A5=97=E9=A4=90=E8=AE=BE=E7=BD=AE=EF=BC=88=E5=A5=97=E9=A4=90?= =?UTF-8?q?=E6=98=8E=E7=BB=86=E8=A1=A8=E5=8D=95=EF=BC=89=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E6=A3=80=E7=B4=A2=E4=B8=8D=E5=A4=9F=E4=BA=BA?= =?UTF-8?q?=E6=80=A7=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/maintainSystem/Inspection/index.vue | 535 +++--------------- 1 file changed, 87 insertions(+), 448 deletions(-) diff --git a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue index b93a58e1..f1c310ef 100644 --- a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue +++ b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue @@ -602,117 +602,31 @@ @@ -1752,18 +1668,16 @@ const calculateAmounts = () => { // 重新分配所有项目的服务费 redistributeServiceFee(); - // 计算套餐总金额(基于项目的折扣后金额) - const totalAmount = packageItems.value.reduce((sum, item) => sum + (item.amount || 0), 0); - - // 更新套餐金额 - packageAmount.value = parseFloat(totalAmount.toFixed(2)); - // 计算套餐总服务费 if (generateServiceFee.value) { serviceFee.value = parseFloat(packageItems.value.reduce((sum, item) => sum + (item.serviceFee || 0), 0).toFixed(2)); } else { serviceFee.value = 0; } + + // 计算套餐总金额(折扣后金额 + 服务费) + const totalAmount = packageItems.value.reduce((sum, item) => sum + (item.amount || 0), 0); + packageAmount.value = parseFloat((totalAmount + serviceFee.value).toFixed(2)); }; const itemNameRefs = ref([]); @@ -2275,7 +2189,7 @@ const handleSave = () => { packageLevel: packageLevel.value, packageName: packageName.value.trim(), department: department.value, - discount: discount.value || 0, + discount: parseFloat(String(discount.value).replace('%', '').trim()) || 0, isDisabled: isDisabled.value, showPackageName: showPackageName.value, generateServiceFee: generateServiceFee.value, @@ -2285,7 +2199,7 @@ const handleSave = () => { lisGroup: selectedLisGroup.value, // 从下拉框获取 bloodVolume: bloodVolume.value, remarks: remarks.value, - orgName: userStore.orgName || '测试机构', // 卫生机构 + orgName: (tenantOptions.value.find(t => t.value === selectedTenantId.value)?.label) || userStore.orgName || '', // 卫生机构 createBy: userStore.nickName, // 制单人 createTime: new Date().toISOString(), updateTime: new Date().toISOString() @@ -2382,273 +2296,46 @@ const fetchTenantList = async () => { loadingTenant.value = false; } }; -const showProjectPopover = ref(false); -const categoryList = ref([]); -const currentCategoryCode = ref(''); -const projectTableData = ref([]); -const tableLoading = ref(false); -const popoverSearchKey = ref(''); const currentEditingRow = ref(null); -const isSelectingProject = ref(false); -const tableKey = ref(0); -// 弹窗内项目列表分页 -const popoverPageNo = ref(1); -const popoverPageSize = ref(10); -const popoverTotal = ref(0); + /** - * 点击输入框时:记录当前行并打开弹窗 + * el-autocomplete 行内搜索回调 + * query: 用户输入的关键词;cb: 回调函数,将结果数组传入即可 */ -const openProjectSelector = (row) => { - if (isSelectingProject.value) return; - +const handleProjectInlineSearch = async (query, cb, row) => { currentEditingRow.value = row; - showProjectPopover.value = true; - - if (categoryList.value.length === 0) { - initDiagnosisCategories(); - } else { - fetchProjectsByCategory(currentCategoryCode.value, true); + try { + const params = { pageNo: 1, pageSize: 20, statusEnum: 2, searchKey: query || undefined }; + const response = await getDiagnosisTreatmentList(params); + if (response.code === 200) { + let list = []; + if (response.data && response.data.records) { + list = response.data.records; + } else if (Array.isArray(response.data)) { + list = response.data; + } + // el-autocomplete 要求每项必须有 value 字段(显示在输入框中) + cb(list.map(item => ({ ...item, value: item.name }))); + } else { + cb([]); + } + } catch { + cb([]); } }; -// 监听 popover 开关,动态注册/注销 document 级监听 -watch(showProjectPopover, (val) => { - if (val) { - // nextTick 后注册,避免打开时的点击事件立即触发关闭 - nextTick(() => { - document.addEventListener('mousedown', _captureMousedown, true); - document.addEventListener('mousedown', onDocumentMousedown); - }); - } else { - document.removeEventListener('mousedown', _captureMousedown, true); - document.removeEventListener('mousedown', onDocumentMousedown); - } -}); - -const handleSelectProject = (selectedItem) => { - if (!currentEditingRow.value) { - ElMessage.warning('未检测到编辑行,请重新点击输入框'); - return; - } - - const row = currentEditingRow.value; - - // --- 数据回填逻辑 --- +/** + * el-autocomplete 选中某条记录后回填数据 + */ +const handleProjectInlineSelect = (selectedItem, row) => { row.name = selectedItem.name; row.spec = selectedItem.spec || ''; row.unitPrice = parseFloat(selectedItem.retailPrice || 0); row.unit = selectedItem.permittedUnitCode_dictText || selectedItem.unit || '次'; if (!row.quantity) row.quantity = 1; - row.amount = row.unitPrice * row.quantity; - row.totalAmount = row.amount + (row.serviceFee || 0); - - // 1. 开启“防抖”锁,阻止 openProjectSelector 执行 - isSelectingProject.value = true; - - // 2. 关闭弹窗 - showProjectPopover.value = false; - - // 3. 延迟一小段时间后解锁并清空当前行 - // 100ms 足够让浏览器的 click/focus 事件处理完毕 - setTimeout(() => { - currentEditingRow.value = null; - isSelectingProject.value = false; - }, 100); - - - ElMessage.success(`已选择:${row.name}`); -}; - - - -const initDiagnosisCategories = async () => { - if (categoryList.value.length > 0) return; - try { - const res = await getDiseaseTreatmentInit(); - if (res.code === 200 && res.data?.diagnosisCategoryOptions) { - categoryList.value = res.data.diagnosisCategoryOptions.map(item => ({ - value: item.value, - label: item.info - })); - if (categoryList.value.length > 0) { - currentCategoryCode.value = categoryList.value[0].value; - fetchProjectsByCategory(currentCategoryCode.value, true); - } - } - } catch (e) { - ElMessage.error('加载分类失败,请稍后重试'); - } -}; - -/** - * 根据分类代码加载项目列表(支持分页) - */ -// 分页数据缓存:key = `${categoryCode}_${pageNo}_${pageSize}_${searchKey}` -const _pageCache = new Map(); - -const _cacheKey = (code, pageNo, pageSize, searchKey) => - `${code}_${pageNo}_${pageSize}_${searchKey || ''}`; - -/** 静默预加载单页,结果存入缓存 */ -const _prefetchPage = (code, pageNo, pageSize, searchKey) => { - const key = _cacheKey(code, pageNo, pageSize, searchKey); - if (_pageCache.has(key)) return; - const params = { pageNo, pageSize, statusEnum: 2, categoryCode: code, searchKey: searchKey || undefined }; - getDiagnosisTreatmentList(params).then(response => { - if (response.code === 200) _pageCache.set(key, response.data); - }).catch(() => {}); -}; - -/** 静默并行预加载多页:当前页前后各2页 + 后4页,覆盖常用跳转范围 */ -const _prefetchPages = (code, currentPage, pageSize, searchKey, totalPages) => { - const pagesToFetch = new Set(); - for (let i = Math.max(1, currentPage - 2); i <= Math.min(totalPages || 999, currentPage + 4); i++) { - pagesToFetch.add(i); - } - pagesToFetch.forEach(pageNo => _prefetchPage(code, pageNo, pageSize, searchKey)); -}; - -/** 第1页加载完成后,并行预取所有页(上限20页),覆盖任意跳转 */ -const _prefetchAllPages = (code, pageSize, searchKey, total) => { - const totalPages = Math.min(Math.ceil(total / pageSize), 80); - for (let i = 2; i <= totalPages; i++) { - _prefetchPage(code, i, pageSize, searchKey); - } -}; - -const fetchProjectsByCategory = async (code, resetPage = false) => { - currentCategoryCode.value = code; - if (resetPage) { - popoverPageNo.value = 1; - // 切换分类/搜索时清空缓存 - _pageCache.clear(); - } - - const pageNo = popoverPageNo.value; - const pageSize = popoverPageSize.value; - const searchKey = popoverSearchKey.value || ''; - const key = _cacheKey(code, pageNo, pageSize, searchKey); - - if (_pageCache.has(key)) { - // 命中缓存,直接渲染,无需 loading - const cached = _pageCache.get(key); - if (cached && cached.records) { - projectTableData.value = cached.records; - popoverTotal.value = cached.total != null ? cached.total : cached.records.length; - } else if (Array.isArray(cached)) { - projectTableData.value = cached; - popoverTotal.value = cached.length; - } - // 预加载周边页 - _prefetchPages(code, pageNo, pageSize, searchKey, Math.ceil(popoverTotal.value / pageSize)); - return; - } - - tableLoading.value = true; - try { - const params = { - pageNo, - pageSize, - statusEnum: 2, - categoryCode: code, - searchKey: searchKey || undefined - }; - - const response = await getDiagnosisTreatmentList(params); - - if (response.code === 200) { - let list = []; - if (response.data && response.data.records) { - list = response.data.records; - popoverTotal.value = (response.data.total != null ? response.data.total : list.length); - } else if (Array.isArray(response.data)) { - list = response.data; - popoverTotal.value = list.length; - } - projectTableData.value = list; - // 写入缓存 - _pageCache.set(key, response.data); - // 预加载周边页 - const totalPages = Math.ceil(popoverTotal.value / pageSize); - _prefetchPages(code, pageNo, pageSize, searchKey, totalPages); - } else { - projectTableData.value = []; - popoverTotal.value = 0; - ElMessage.warning(response.msg || '暂无数据'); - } - } catch (error) { - ElMessage.error('查询失败'); - projectTableData.value = []; - popoverTotal.value = 0; - } finally { - tableLoading.value = false; - } -}; - -/** - * 弹窗内搜索触发(重置页码) - */ -const handlePopoverSearch = () => { - if (currentCategoryCode.value) { - fetchProjectsByCategory(currentCategoryCode.value, true); - } -}; - -// 捕获阶段记录真实点击目标(在 DOM 变化前) -let _capturedTarget = null; -const _captureMousedown = (e) => { _capturedTarget = e.target; }; - -// 分页操作时暂时屏蔽 document mousedown 关闭逻辑 -let _ignoreMaskOnce = false; - -/** - * 弹窗内分页页码变化 - */ -const handlePopoverPageChange = (page) => { - _ignoreMaskOnce = true; - popoverPageNo.value = page; - fetchProjectsByCategory(currentCategoryCode.value); -}; - -/** - * 弹窗内每页条数变化 - */ -const handlePopoverSizeChange = (size) => { - _ignoreMaskOnce = true; - popoverPageSize.value = size; - popoverPageNo.value = 1; - fetchProjectsByCategory(currentCategoryCode.value); -}; - -/** - * document 级 mousedown 监听:点击 popover 面板及所有浮层之外时关闭 - * 使用捕获阶段记录的真实目标,避免 DOM 销毁后 contains 检测失效 - */ -const onDocumentMousedown = (e) => { - if (!showProjectPopover.value) return; - - // 分页操作触发时,忽略本次关闭 - if (_ignoreMaskOnce) { - _ignoreMaskOnce = false; - return; - } - - // 使用捕获阶段记录的目标(DOM 变化前的真实元素) - const target = _capturedTarget || e.target; - - // 判断点击目标是否在 popover 面板内 - const popoverEl = document.querySelector('.diagnosis-project-popover'); - if (popoverEl && popoverEl.contains(target)) return; - - // 判断点击目标是否在任意浮层内(el-select-dropdown / el-popper 等) - const floatEls = document.querySelectorAll('.el-select-dropdown, .el-popper, .el-picker-panel'); - for (const el of floatEls) { - if (el.contains(target)) return; - } - // 用坐标兜底:浮层可能已销毁,检查元素是否曾属于浮层类名 - if (target && target.closest && target.closest('.el-select-dropdown, .el-popper, .el-picker-panel')) return; - - showProjectPopover.value = false; + row.amount = parseFloat((row.unitPrice * row.quantity).toFixed(2)); + row.totalAmount = parseFloat((row.amount + (row.serviceFee || 0)).toFixed(2)); + calculateAmounts(); }; @@ -3022,10 +2709,7 @@ onMounted(async () => { } }); -onUnmounted(() => { - document.removeEventListener('mousedown', _captureMousedown, true); - document.removeEventListener('mousedown', onDocumentMousedown); -}); +onUnmounted(() => {}); @@ -3294,83 +2978,38 @@ onUnmounted(() => { flex-wrap: nowrap; } -/* 诊疗项目选择器单元格 */ -.project-selector-cell { - position: relative; - width: 100%; +/* 行内搜索下拉项样式(挂载到 body 的 popper) */ +.project-inline-dropdown .el-autocomplete-suggestion__list li { + padding: 0; } - -/* 全屏遮罩,确保点击弹窗外任意位置关闭 */ -.popover-mask { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 999; /* 低于 popover 的 z-index */ - background: transparent; -} - -/* Popover 内部容器布局 */ -.popover-container { +.project-inline-item { display: flex; - border: 1px solid #e4e7ed; - border-radius: 4px; - overflow: hidden; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - background: #fff; + align-items: center; + gap: 8px; + padding: 6px 12px; + font-size: 13px; + cursor: pointer; } - -/* 左侧分类栏 */ -.category-sidebar { - width: 120px; - background-color: #f5f7fa; - border-right: 1px solid #e4e7ed; - max-height: 400px; - overflow-y: auto; +.project-inline-item .item-name { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.project-inline-item .item-code { + color: #409EFF; + font-size: 12px; flex-shrink: 0; } - -.category-item { - padding: 12px 15px; - cursor: pointer; - font-size: 14px; - color: #606266; - transition: all 0.2s; - border-left: 3px solid transparent; -} - -.category-item:hover { - background-color: #ecf5ff; - color: #409EFF; -} - -.category-item.active { - background-color: #ecf5ff; - color: #409EFF; - border-left-color: #409EFF; +.project-inline-item .item-price { + color: #f56c6c; + font-size: 12px; font-weight: 600; + flex-shrink: 0; } - -/* 右侧列表区域 */ -.project-list-area { - flex: 1; - padding: 10px; - max-height: 400px; - overflow-y: auto; - display: flex; - flex-direction: column; -} - -.empty-tip { - text-align: center; +.project-inline-item .item-unit { color: #909399; - margin-top: 20px; - font-size: 13px; -} - -/* 全局样式:调整 popover 的 padding 和 z-index */ -.diagnosis-project-popover { - padding: 0 !important; - z-index: 2000 !important; + font-size: 12px; + flex-shrink: 0; } \ No newline at end of file