Files
his/openhis-ui-vue3/src/views/outpatient/ExaminationApply.vue
2026-05-27 03:52:50 +08:00

170 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="examination-apply-container">
<!-- 左侧检查项目分类 -->
<div class="panel category-panel" data-cy="category-tree">
<el-tree
:data="categories"
:props="{ label: 'name', children: 'children' }"
highlight-current
@node-click="handleCategoryClick"
/>
</div>
<!-- 中间检查项目列表 -->
<div class="panel item-panel" data-cy="item-list">
<el-checkbox-group v-model="selectedItemIds" @change="handleItemSelect">
<div v-for="item in currentItems" :key="item.id" class="item-row">
<el-checkbox :label="item.id" :data-cy="`item-checkbox-${item.id}`">
{{ cleanName(item.name) }}
</el-checkbox>
</div>
</el-checkbox-group>
</div>
<!-- 右侧已选择区域结构化展示项目 > 检查方法 -->
<div class="panel selected-panel" data-cy="selected-panel">
<h3 class="panel-title">已选择</h3>
<div v-if="selectedList.length === 0" class="empty-tip">暂无选择项目</div>
<div v-for="selected in selectedList" :key="selected.id" class="selected-card">
<div class="card-header" @click="toggleDetail(selected)">
<el-checkbox
:model-value="selected.checked"
@change="val => handleCardCheck(selected, val)"
@click.stop
/>
<el-tooltip :content="selected.displayName" placement="top" :show-after="300">
<span class="card-name" data-cy="selected-card-name">{{ selected.displayName }}</span>
</el-tooltip>
<el-icon class="toggle-icon" data-cy="selected-card-toggle">
<ArrowDown v-if="selected.expanded" />
<ArrowRight v-else />
</el-icon>
</div>
<!-- 默认收起点击展开显示检查方法 -->
<div v-show="selected.expanded" class="card-detail" data-cy="selected-card-detail">
<div class="detail-title">检查方法</div>
<el-checkbox-group v-model="selected.selectedMethods" @change="handleMethodSelect(selected)">
<div v-for="method in selected.methods" :key="method.id" class="method-row">
<el-checkbox :label="method.id" :data-cy="`method-checkbox-${selected.id}-${method.id}`">
{{ method.name }}
</el-checkbox>
</div>
</el-checkbox-group>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue'
// 状态定义
const categories = ref([])
const currentItems = ref([])
const selectedItemIds = ref([])
const selectedList = ref([])
// 清理名称:去除“套餐”等冗余字样,避免显示混乱
const cleanName = (name) => {
if (!name) return ''
return name.replace(/套餐/g, '').trim()
}
const handleCategoryClick = (node) => {
// 实际业务中根据分类ID请求接口加载项目列表
// 此处保留钩子,具体数据加载逻辑由业务层注入
console.log('切换分类:', node.name)
}
// 项目勾选:严格解耦,仅维护项目卡片状态,不联动检查方法
const handleItemSelect = (ids) => {
const addedIds = ids.filter(id => !selectedItemIds.value.includes(id))
const removedIds = selectedItemIds.value.filter(id => !ids.includes(id))
// 新增选中项
addedIds.forEach(id => {
const item = currentItems.value.find(i => i.id === id)
if (item && !selectedList.value.find(s => s.id === id)) {
selectedList.value.push({
id: item.id,
displayName: cleanName(item.name),
checked: true,
expanded: false, // 默认收起明细
methods: item.methods || [],
selectedMethods: [] // 方法独立初始化,不自动勾选
})
}
})
// 移除取消项
removedIds.forEach(id => {
const idx = selectedList.value.findIndex(s => s.id === id)
if (idx !== -1) selectedList.value.splice(idx, 1)
})
selectedItemIds.value = ids
}
// 卡片主勾选:仅控制项目本身状态,不影响已选方法
const handleCardCheck = (selected, val) => {
selected.checked = val
}
// 方法勾选:仅控制方法状态,不反向影响项目勾选状态
const handleMethodSelect = (selected) => {
// v-model 已自动同步 selected.selectedMethods此处保持解耦不添加联动逻辑
}
// 展开/收起明细面板
const toggleDetail = (selected) => {
selected.expanded = !selected.expanded
}
</script>
<style scoped>
.examination-apply-container {
display: flex;
gap: 16px;
padding: 16px;
height: 100%;
background: #f5f7fa;
}
.panel {
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 12px;
background: #fff;
overflow: hidden;
}
.category-panel { width: 22%; }
.item-panel { width: 38%; overflow-y: auto; }
.selected-panel { width: 40%; overflow-y: auto; }
.panel-title { margin: 0 0 12px; font-size: 15px; font-weight: 600; color: #303133; }
.empty-tip { color: #909399; text-align: center; padding: 30px 0; font-size: 13px; }
.item-row, .method-row { padding: 8px 0; border-bottom: 1px dashed #ebeef5; }
.selected-card { margin-bottom: 10px; border: 1px solid #dcdfe6; border-radius: 4px; overflow: hidden; }
.card-header {
display: flex;
align-items: center;
padding: 10px 12px;
cursor: pointer;
background: #fafafa;
transition: background 0.2s;
}
.card-header:hover { background: #f0f2f5; }
.card-name {
flex: 1;
margin: 0 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
color: #303133;
}
.toggle-icon { margin-left: auto; color: #909399; }
.card-detail { padding: 10px 12px; border-top: 1px solid #ebeef5; background: #fff; }
.detail-title { font-size: 12px; color: #606266; margin-bottom: 8px; font-weight: 500; }
</style>