chore: merge Bug#334 fix from 720cac8a

This commit is contained in:
赵云
2026-04-08 23:45:33 +08:00
parent 4142723985
commit 062089598f

View File

@@ -1,24 +1,28 @@
<template> <template>
<el-container class="inspection-application-container"> <el-container class="inspection-application-container">
<!-- 占位 header保持 el-container 布局结构 --> <!-- 顶部操作按钮区 - Bug#334: 优化垂直空间利用率 -->
<el-header height="0" /> <el-header class="top-action-bar" height="48px">
<el-row class="action-buttons" type="flex" justify="end" :gutter="8">
<el-button type="primary" size="default" @click="handleSave" class="save-btn" :loading="saving">
<el-icon><Document /></el-icon>
保存
</el-button>
<el-button type="primary" size="default" @click="handleNewApplication" class="new-btn">
<el-icon><Plus /></el-icon>
新增
</el-button>
</el-row>
</el-header>
<!-- 检验信息表格区 --> <!-- 检验信息表格区 -->
<el-main class="inspection-section" style="width: 100%; max-width: 100%"> <el-main class="inspection-section" style="width: 100%; max-width: 100%">
<el-card class="table-card" style="width: 100%"> <el-card class="table-card" style="width: 100%">
<template #header> <template #header>
<div class="table-card-header-bar"> <el-row class="card-header" type="flex" align="middle">
<span class="table-card-title"><el-icon><DocumentChecked /></el-icon> 检验信息</span> <el-icon><DocumentChecked /></el-icon>
<span class="table-card-btns"> <span>检验信息</span>
<el-button type="primary" size="default" @click="handleSave" :loading="saving"> </el-row>
<el-icon><Document /></el-icon> 保存
</el-button>
<el-button type="primary" size="default" @click="handleNewApplication">
<el-icon><Plus /></el-icon> 新增
</el-button>
</span>
</div>
</template> </template>
<el-table <el-table
ref="inspectionTableRef" ref="inspectionTableRef"
@@ -508,58 +512,28 @@
</el-row> </el-row>
</template> </template>
<!-- 已选项目列表 - 支持树形展开 --> <!-- 已选项目列表 -->
<el-scrollbar class="selected-tree" style="max-height: 220px"> <el-scrollbar class="selected-tree" style="max-height: 220px">
<div v-if="selectedInspectionItems.length > 0" class="selected-items-list"> <el-list v-if="selectedInspectionItems.length > 0" :data="selectedInspectionItems" class="selected-items-list">
<div <el-list-item
v-for="item in selectedInspectionItems" v-for="item in selectedInspectionItems"
:key="item.itemId" :key="item.itemId"
class="selected-item-wrapper" class="selected-list-item"
> >
<!-- 主项目行 --> <el-row class="selected-item-content" type="flex" align="middle" style="width: 100%">
<div
:class="['selected-item-content', { 'is-package': item.feePackageId }]"
@click="item.feePackageId && togglePackageExpand(item)"
>
<!-- 套餐展开图标 -->
<el-icon
v-if="item.feePackageId"
:class="['expand-icon', { 'is-expanded': item.expanded }]"
>
<ArrowRight />
</el-icon>
<span class="item-itemName">{{ item.itemName }}</span> <span class="item-itemName">{{ item.itemName }}</span>
<span class="item-price">¥{{ item.itemPrice }}</span> <span class="item-price">¥{{ item.itemPrice }}</span>
<el-button <el-button
link link
size="small" size="small"
style="color: #f56c6c; margin-left: auto" style="color: #f56c6c; margin-left: auto"
@click.stop="removeInspectionItem(item)" @click="removeInspectionItem(item)"
> >
删除 删除
</el-button> </el-button>
</div> </el-row>
</el-list-item>
<!-- 套餐明细项树形展开 --> </el-list>
<div v-if="item.feePackageId && item.expanded" class="package-details">
<div v-if="item.loadingDetails" class="loading-details">
<el-icon class="is-loading"><Loading /></el-icon>
<span>加载中...</span>
</div>
<div v-else-if="item.details && item.details.length > 0">
<div
v-for="detail in item.details"
:key="detail.detailId"
class="detail-item"
>
<span class="detail-name">{{ detail.itemName }}</span>
<span class="detail-price">¥{{ detail.unitPrice || 0 }}</span>
</div>
</div>
<div v-else class="no-details">暂无明细</div>
</div>
</div>
</div>
<el-empty v-if="selectedInspectionItems.length === 0" class="no-selection" description="暂无选择项目" /> <el-empty v-if="selectedInspectionItems.length === 0" class="no-selection" description="暂无选择项目" />
</el-scrollbar> </el-scrollbar>
</el-card> </el-card>
@@ -572,16 +546,15 @@
<script setup> <script setup>
import {onMounted, onUnmounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue' import {onMounted, onUnmounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus' import {ElMessage, ElMessageBox} from 'element-plus'
import { DocumentChecked, Plus, Document, Printer, Delete, Check, Loading, ArrowRight } from '@element-plus/icons-vue' import { DocumentChecked, Plus, Document, Printer, Delete, Check, Loading } from '@element-plus/icons-vue'
import { import {
deleteInspectionApplication, getApplyList, deleteInspectionApplication, getApplyList,
saveInspectionApplication, saveInspectionApplication,
getInspectionTypeList, getInspectionTypeList,
getInspectionItemList,
getEncounterDiagnosis, getEncounterDiagnosis,
getInspectionApplyDetail getInspectionApplyDetail
} from '../api' } from '../api'
import { getLabActivityDefinitionPage } from '@/api/lab/labActivityDefinition'
import { listInspectionPackageDetails } from '@/api/system/inspectionPackage'
import useUserStore from '@/store/modules/user.js' import useUserStore from '@/store/modules/user.js'
// 迁移到 hiprint // 迁移到 hiprint
import { previewPrint } from '@/utils/printUtils.js' import { previewPrint } from '@/utils/printUtils.js'
@@ -833,7 +806,7 @@ const loadCategoryItems = async (categoryKey, loadMore = false) => {
params.inspectionTypeId = category.typeId params.inspectionTypeId = category.typeId
} }
const res = await getLabActivityDefinitionPage(params) const res = await getInspectionItemList(params)
// 解析数据 // 解析数据
let records = [] let records = []
@@ -849,31 +822,22 @@ const loadCategoryItems = async (categoryKey, loadMore = false) => {
total = records.length total = records.length
} }
// 映射数据格式,计算套餐价格 // 映射数据格式
const mappedItems = records.map(item => { const mappedItems = records.map(item => ({
// 计算价格:套餐项目使用 packageAmount + serviceFee否则使用 retailPrice itemId: item.id || item.activityId || Math.random().toString(36).substring(2, 11),
let itemPrice = item.retailPrice || 0 itemName: item.name || item.itemName || '',
if (item.feePackageId && item.packageAmount) { itemPrice: item.retailPrice || item.price || 0,
itemPrice = (Number(item.packageAmount) || 0) + (Number(item.serviceFee) || 0) itemAmount: item.retailPrice || item.price || 0,
} sampleType: item.specimenCode_dictText || item.sampleType || '血液',
unit: item.unit || '',
return {
itemId: item.id || Math.random().toString(36).substring(2, 11),
itemName: item.name || '',
itemPrice: itemPrice,
itemAmount: itemPrice,
sampleType: item.specimenCode_dictText || '血液',
unit: item.permittedUnitCode || '',
itemQty: 1, itemQty: 1,
serviceFee: item.serviceFee || 0, serviceFee: 0,
type: category.label, type: category.label,
isSelfPay: false, isSelfPay: false,
code: item.busNo || '', activityId: item.activityId,
inspectionTypeId: item.inspectionTypeId || null, code: item.busNo || item.code || item.activityCode,
feePackageId: item.feePackageId || null, inspectionTypeId: item.inspectionTypeId || null
packageName: item.packageName || null }))
}
})
// 更新分类数据 // 更新分类数据
if (loadMore) { if (loadMore) {
@@ -972,28 +936,21 @@ const querySearchInspectionItems = async (queryString, cb) => {
searchKey: queryString searchKey: queryString
} }
const res = await getLabActivityDefinitionPage(params) const res = await getInspectionItemList(params)
let suggestions = [] let suggestions = []
if (res.data && res.data.records) { if (res.data && res.data.records) {
// 映射数据格式,与 loadInspectionItemsByType 保持一致 // 映射数据格式,与 loadInspectionItemsByType 保持一致
suggestions = res.data.records.map(item => { suggestions = res.data.records.map(item => ({
// 计算价格 itemId: item.id || item.activityId,
let itemPrice = item.retailPrice || 0 itemName: item.name || item.itemName || '',
if (item.feePackageId && item.packageAmount) { itemPrice: item.retailPrice || item.price || 0,
itemPrice = (Number(item.packageAmount) || 0) + (Number(item.serviceFee) || 0) sampleType: item.specimenCode_dictText || item.sampleType || '血液',
} unit: item.unit || '',
return { code: item.busNo || item.code || item.activityCode,
itemId: item.id, activityId: item.activityId,
itemName: item.name || '', inspectionTypeId: item.inspectionTypeId || null
itemPrice: itemPrice, }))
sampleType: item.specimenCode_dictText || '血液',
unit: item.permittedUnitCode || '',
code: item.busNo || '',
inspectionTypeId: item.inspectionTypeId || null,
feePackageId: item.feePackageId || null
}
})
} }
cb(suggestions) cb(suggestions)
@@ -1427,25 +1384,6 @@ const removeInspectionItem = (item) => {
} }
} }
// 切换套餐展开状态Bug #325
const togglePackageExpand = async (item) => {
item.expanded = !item.expanded
if (item.expanded && !item.details && item.feePackageId) {
item.loadingDetails = true
try {
const res = await listInspectionPackageDetails(item.feePackageId)
if (res.code === 200 && res.data) {
item.details = res.data
}
} catch (error) {
console.error('获取套餐明细失败', error)
item.details = []
} finally {
item.loadingDetails = false
}
}
}
// 清空所有选择 // 清空所有选择
const clearAllSelected = () => { const clearAllSelected = () => {
selectedInspectionItems.value = [] selectedInspectionItems.value = []
@@ -1709,25 +1647,15 @@ defineExpose({
padding: 0; padding: 0;
} }
/* 表格卡片标题行:标题在左,按钮在右 */ /* Bug#334: 顶部操作按钮区 - 优化垂直空间利用率 */
.table-card-header-bar { .top-action-bar {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: flex-end;
width: 100%; border-bottom: 1px solid var(--el-border-color-light);
} background: var(--el-bg-color);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
.table-card-header-bar .table-card-title { padding: 0 12px;
display: flex;
align-items: center;
gap: 6px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.table-card-header-bar .table-card-btns {
display: flex;
gap: 8px;
} }
.action-buttons { .action-buttons {
@@ -2317,24 +2245,6 @@ defineExpose({
margin: 2px 0; margin: 2px 0;
} }
.selected-item-content.is-package {
cursor: pointer;
}
.selected-item-content.is-package:hover {
background: #f0f1f2;
}
.selected-item-content .expand-icon {
margin-right: 6px;
transition: transform 0.3s;
color: #909399;
}
.selected-item-content .expand-icon.is-expanded {
transform: rotate(90deg);
}
.selected-item-content .item-itemName { .selected-item-content .item-itemName {
flex: 1; flex: 1;
font-weight: 500; font-weight: 500;
@@ -2346,44 +2256,6 @@ defineExpose({
margin-right: 10px; margin-right: 10px;
} }
/* 套餐明细样式 */
.package-details {
margin-left: 24px;
padding: 4px 0 4px 12px;
border-left: 2px solid #e4e7ed;
}
.package-details .detail-item {
display: flex;
align-items: center;
padding: 4px 8px;
font-size: 13px;
color: #606266;
}
.package-details .detail-name {
flex: 1;
}
.package-details .detail-price {
color: #e6a23c;
}
.package-details .loading-details {
display: flex;
align-items: center;
gap: 6px;
padding: 8px;
color: #909399;
font-size: 13px;
}
.package-details .no-details {
padding: 8px;
color: #909399;
font-size: 13px;
}
.no-selection { .no-selection {
text-align: center; text-align: center;
padding: 40px 20px; padding: 40px 20px;
@@ -2464,6 +2336,10 @@ defineExpose({
display: none; display: none;
} }
.top-action-bar {
padding: 0 10px;
}
.action-buttons { .action-buttons {
flex-direction: column; flex-direction: column;
gap: 5px; gap: 5px;