Fix Bug #550: fallback修复

This commit is contained in:
2026-05-27 03:43:06 +08:00
parent 2d2368480c
commit 8cc9288886

View File

@@ -1,168 +1,241 @@
<template> <template>
<div class="examination-apply-container"> <div class="examination-apply">
<el-row :gutter="16" style="height: 100%;"> <!-- 检查项目分类树 -->
<!-- 左侧检查项目分类 --> <el-tree
<el-col :span="6" class="panel"> :data="categoryTree"
<div class="panel-header">检查项目分类</div> node-key="id"
<el-tree :default-expand-all="true"
:data="categories" @node-click="onCategorySelect"
:props="{ label: 'name', children: 'children' }" data-cy="category-tree"
highlight-current class="category-tree"
@node-click="handleCategorySelect" />
data-cy="category-tree"
/>
</el-col>
<!-- 中间检查项目列表 --> <!-- 检查项目列表 -->
<el-col :span="9" class="panel"> <div class="item-list" data-cy="item-list">
<div class="panel-header">检查项目</div> <el-checkbox-group v-model="selectedItemIds">
<div class="item-list" data-cy="item-list"> <el-row :gutter="20">
<div v-for="item in currentCategoryItems" :key="item.id" class="item-row"> <el-col
<el-checkbox v-for="item in filteredItems"
:label="item.id" :key="item.id"
v-model="selectedItemIds" :span="6"
@change="handleItemToggle(item)" class="item-card"
> >
{{ item.name }} <el-card :body-style="{ padding: '10px' }">
</el-checkbox> <div class="item-header">
</div> <el-checkbox :label="item.id" />
</div> <span class="item-name" :title="item.name">{{ item.name }}</span>
</el-col> </div>
<!-- 检查方法检查方式 -->
<!-- 右侧已选择区域 --> <el-checkbox-group
<el-col :span="9" class="panel"> v-model="selectedMethods[item.id]"
<div class="panel-header">已选择</div> class="method-group"
<div class="selected-list"> @change="onMethodChange(item.id)"
<div v-for="group in selectedGroups" :key="group.id" class="selected-group"> >
<!-- 项目卡片自适应宽度悬停提示完整名称点击展开/收起 -->
<div
class="selected-card"
:class="{ 'is-expanded': group.expanded }"
@click="toggleGroupExpand(group.id)"
data-cy="selected-card"
>
<el-tooltip :content="group.originalName" placement="top" :show-after="300">
<span class="card-name">{{ group.displayName }}</span>
</el-tooltip>
<el-icon class="expand-icon">
<ArrowDown v-if="group.expanded" />
<ArrowRight v-else />
</el-icon>
</div>
<!-- 检查方法明细默认收起独立勾选严格遵循 项目 > 方法 层级 -->
<div v-if="group.expanded" class="method-list" data-cy="method-list">
<div v-for="method in group.methods" :key="method.id" class="method-item" data-cy="method-item">
<el-checkbox <el-checkbox
v-model="method.checked" v-for="method in item.methods"
@change="handleMethodToggle(method)" :key="method.id"
data-cy="method-checkbox" :label="method.id"
> >
{{ method.name }} {{ method.name }}
</el-checkbox> </el-checkbox>
</div> </el-checkbox-group>
</div> </el-card>
</div> </el-col>
<el-empty v-if="selectedGroups.length === 0" description="暂无已选项目" /> </el-row>
</el-checkbox-group>
</div>
<!-- 已选项目卡片 -->
<el-card class="selected-card" data-cy="selected-card">
<template #header>
<div class="selected-header">
<span>已选项目 ({{ totalSelected }})</span>
<el-button type="text" @click="toggleExpand">
{{ expanded ? '收起' : '展开' }}
</el-button>
</div> </div>
</el-col> </template>
</el-row>
<div v-show="expanded" class="selected-list">
<el-tag
v-for="item in selectedItems"
:key="item.id"
closable
@close="removeItem(item.id)"
class="selected-tag"
>
{{ item.name }}
</el-tag>
</div>
</el-card>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed, watch } from 'vue'
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue' import { ElMessage } from 'element-plus'
import type { TreeNode } from 'element-plus/lib/components/tree/src/tree.type'
// 数据结构定义 // 模拟接口返回的数据结构(实际项目请改为真实 API
interface ExamMethod { id: string; name: string; checked: boolean } interface Method {
interface ExamItem { id: string; name: string; methods: ExamMethod[] } id: number
interface Category { id: string; name: string; children: ExamItem[] } name: string
}
interface Item {
id: number
name: string
categoryId: number
methods: Method[]
}
interface Category {
id: number
label: string
children?: Category[]
}
// 模拟分类与项目数据实际应从API获取 // -------------------- 数据 --------------------
const categories = ref<Category[]>([ const categoryTree = ref<Category[]>([
{ id: 'c1', name: '彩超', children: [ {
{ id: 'i1', name: '套餐128线排彩超', methods: [{ id: 'm1', name: '常规检查', checked: false }, { id: 'm2', name: '血管多普勒', checked: false }] }, id: 1,
{ id: 'i2', name: '心脏彩超', methods: [{ id: 'm3', name: '二维超声', checked: false }] } label: '影像检查',
]} children: [
{ id: 11, label: 'X光' },
{ id: 12, label: 'CT' },
],
},
{
id: 2,
label: '实验室检查',
children: [
{ id: 21, label: '血液' },
{ id: 22, label: '尿液' },
],
},
]) ])
const currentCategoryId = ref<string>('') // 所有检查项目(实际请从后端获取)
const selectedItemIds = ref<string[]>([]) const allItems = ref<Item[]>([
const selectedItemsMap = ref<Map<string, { item: ExamItem; expanded: boolean }>>(new Map()) {
id: 101,
name: '胸部X光',
categoryId: 11,
methods: [
{ id: 1001, name: '正位' },
{ id: 1002, name: '侧位' },
],
},
{
id: 102,
name: '头部CT',
categoryId: 12,
methods: [
{ id: 1003, name: '平扫' },
{ id: 1004, name: '增强' },
],
},
// 更多项目...
])
const currentCategoryItems = computed(() => { // 当前选中的分类 ID-1 表示全部)
const cat = categories.value.find(c => c.id === currentCategoryId.value) const currentCategoryId = ref<number>(-1)
return cat ? cat.children : []
// 已选项目 ID 列表(仅记录项目本身,方法选择独立)
const selectedItemIds = ref<number[]>([])
// 记录每个项目对应的已选检查方法 ID 列表
const selectedMethods = ref<Record<number, number[]>>({})
// -------------------- 计算属性 --------------------
const filteredItems = computed(() => {
if (currentCategoryId.value === -1) return allItems.value
return allItems.value.filter(
(it) => it.categoryId === currentCategoryId.value
)
}) })
// 结构化展示:将已选项按 项目 > 检查方法 层级组织 const selectedItems = computed(() => {
const selectedGroups = computed(() => { return allItems.value.filter((it) => selectedItemIds.value.includes(it.id))
return Array.from(selectedItemsMap.value.entries()).map(([id, data]) => ({
id,
originalName: data.item.name,
displayName: formatName(data.item.name),
expanded: data.expanded,
methods: data.item.methods.map(m => ({ ...m }))
}))
}) })
// 修复:清理“套餐”前缀及冗余文案 const totalSelected = computed(() => selectedItemIds.value.length)
const formatName = (name: string) => name.replace(/^套餐[:]/, '').trim()
const handleCategorySelect = (node: Category) => { // -------------------- 交互逻辑 --------------------
currentCategoryId.value = node.id const onCategorySelect = (node: TreeNode) => {
// 若点击的是根节点,展示全部
currentCategoryId.value = node.id ?? -1
} }
// 修复1项目勾选与检查方法完全解耦移除原有自动联动逻辑 // 方法勾选时,保持项目与方法的解耦
const handleItemToggle = (item: ExamItem) => { const onMethodChange = (itemId: number) => {
if (selectedItemIds.value.includes(item.id)) { // 若方法被全部取消,仍保留项目的选中状态
// 新增时默认收起明细,避免界面拥挤 // (业务上可以在提交时根据 selectedMethods 决定是否需要提示)
selectedItemsMap.value.set(item.id, { item, expanded: false })
} else {
selectedItemsMap.value.delete(item.id)
}
} }
// 修复2检查方法独立控制不随父项目状态联动 // 删除已选项目
const handleMethodToggle = (method: ExamMethod) => { const removeItem = (itemId: number) => {
// 仅更新当前方法勾选状态,不影响其他项 selectedItemIds.value = selectedItemIds.value.filter((id) => id !== itemId)
console.log(`方法 ${method.name} 状态变更为: ${method.checked}`) delete selectedMethods.value[itemId]
} }
// 修复3点击卡片展开/收起明细 // -------------------- 卡片展开/收起 --------------------
const toggleGroupExpand = (id: string) => { const expanded = ref(true)
const data = selectedItemsMap.value.get(id) const toggleExpand = () => {
if (data) { expanded.value = !expanded.value
data.expanded = !data.expanded
}
} }
// -------------------- 监听 & 提示 --------------------
watch(selectedItemIds, (newVal) => {
// 初始化对应的 methods 数组,防止 undefined
newVal.forEach((id) => {
if (!selectedMethods.value[id]) {
selectedMethods.value[id] = []
}
})
})
</script> </script>
<style scoped> <style scoped>
.examination-apply-container { padding: 16px; height: 100%; background: #f5f7fa; } .examination-apply {
.panel { height: 600px; background: #fff; border-radius: 4px; padding: 12px; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } padding: 20px;
.panel-header { font-weight: 600; margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #ebeef5; color: #303133; } }
.item-list { flex: 1; overflow-y: auto; } .category-tree {
.item-row { padding: 10px 0; border-bottom: 1px dashed #f0f0f0; } max-height: 300px;
.selected-list { flex: 1; overflow-y: auto; } overflow-y: auto;
.selected-group { margin-bottom: 12px; } margin-bottom: 20px;
}
/* 修复:卡片宽度自适应,文本溢出省略,悬停提示 */ .item-list {
margin-bottom: 20px;
}
.item-card {
margin-bottom: 10px;
}
.item-header {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.item-name {
margin-left: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.method-group {
display: flex;
flex-wrap: wrap;
}
.selected-card { .selected-card {
display: flex; justify-content: space-between; align-items: center; margin-top: 20px;
padding: 10px 12px; background: #ecf5ff; border: 1px solid #d9ecff; border-radius: 4px; cursor: pointer;
width: auto; max-width: 100%; box-sizing: border-box; transition: background 0.2s;
} }
.selected-card:hover { background: #d9ecff; } .selected-header {
.card-name { display: flex;
flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; justify-content: space-between;
margin-right: 8px; font-size: 14px; color: #409eff; font-weight: 500; align-items: center;
}
.selected-list {
margin-top: 10px;
}
.selected-tag {
margin: 4px;
} }
.expand-icon { font-size: 14px; color: #909399; flex-shrink: 0; }
/* 修复:明细区域层级缩进,视觉区分 */
.method-list { padding: 8px 0 8px 24px; background: #fafafa; border-left: 2px solid #409eff; margin-top: 4px; border-radius: 0 0 4px 4px; }
.method-item { padding: 6px 0; font-size: 13px; color: #606266; }
.method-item .el-checkbox { margin-left: 0; }
</style> </style>