Files
his/openhis-ui-vue3/src/views/outpatient/ExaminationApplication.vue
2026-05-27 04:01:20 +08:00

231 lines
6.4 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="exam-application-container">
<el-row :gutter="16" class="layout-row">
<!-- 左侧分类 -->
<el-col :span="5">
<el-card shadow="never" class="panel-card">
<template #header>检查项目分类</template>
<el-tree
:data="categories"
node-key="id"
highlight-current
@node-click="handleCategoryClick"
data-cy="exam-category-tree"
/>
</el-card>
</el-col>
<!-- 中间项目列表 -->
<el-col :span="9">
<el-card shadow="never" class="panel-card">
<template #header>检查项目</template>
<div class="item-scroll-area">
<el-checkbox-group v-model="selectedItemIds" @change="handleItemChange">
<el-checkbox
v-for="item in currentItems"
:key="item.id"
:label="item.id"
:value="item.id"
class="item-checkbox"
:data-cy="`exam-item-${item.id}`"
>
{{ cleanName(item.name) }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-card>
</el-col>
<!-- 右侧已选择结构化展示 -->
<el-col :span="10">
<el-card shadow="never" class="panel-card">
<template #header>已选择</template>
<div class="selected-scroll-area">
<div
v-for="sel in selectedList"
:key="sel.id"
class="selected-card"
data-cy="selected-item-card"
>
<!-- 卡片头部名称 + 展开/收起 -->
<div class="card-header" @click="toggleExpand(sel.id)">
<el-tooltip
:content="cleanName(sel.name)"
placement="top"
:show-after="300"
:disabled="!isNameTruncated"
>
<span class="item-name" data-cy="item-name" ref="nameRefs">{{ cleanName(sel.name) }}</span>
</el-tooltip>
<el-icon class="expand-icon" :class="{ 'is-expanded': sel.expanded }" data-cy="expand-toggle">
<ArrowDown />
</el-icon>
</div>
<!-- 明细区域默认收起独立勾选检查方法 -->
<el-collapse-transition>
<div v-show="sel.expanded" class="method-detail" data-cy="method-detail">
<el-checkbox-group v-model="sel.selectedMethods" @change="handleMethodChange(sel)">
<el-checkbox
v-for="method in sel.methods"
:key="method.id"
:label="method.id"
:value="method.id"
>
{{ method.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-collapse-transition>
</div>
<el-empty v-if="selectedList.length === 0" description="暂无已选项目" :image-size="60" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, computed, nextTick } from 'vue'
import { ArrowDown } from '@element-plus/icons-vue'
// 模拟分类与项目数据结构(实际应从 API 获取)
const categories = ref([
{ id: 'ultrasound', label: '彩超', items: [
{ id: '128', name: '128线排彩超套餐', methods: [
{ id: 'm1', name: '常规腹部' },
{ id: 'm2', name: '心脏彩超' },
{ id: 'm3', name: '血管多普勒' }
]}
]}
])
const currentItems = ref([])
const selectedItemIds = ref([])
const selectedList = ref([])
const nameRefs = ref([])
// 清理冗余“套餐”字样
const cleanName = (name) => name.replace(/套餐/g, '')
// 判断名称是否被截断(用于控制 tooltip 显示)
const isNameTruncated = computed(() => {
return nameRefs.value.some(el => el && el.scrollWidth > el.clientWidth)
})
// 切换分类
const handleCategoryClick = (data) => {
currentItems.value = data.items || []
}
// 项目勾选变更(解耦:仅维护项目列表,不联动方法)
const handleItemChange = (ids) => {
// 移除未勾选的项目
selectedList.value = selectedList.value.filter(s => ids.includes(s.id))
// 新增勾选的项目
ids.forEach(id => {
if (!selectedList.value.find(s => s.id === id)) {
const item = currentItems.value.find(i => i.id === id)
if (item) {
selectedList.value.push({
id: item.id,
name: item.name,
methods: item.methods || [],
selectedMethods: [], // 独立维护已选方法
expanded: false // 默认收起
})
}
}
})
}
// 检查方法勾选变更(独立逻辑)
const handleMethodChange = (sel) => {
// 此处可触发后续业务逻辑(如价格计算、库存校验等)
console.log(`项目 ${sel.id} 方法变更:`, sel.selectedMethods)
}
// 展开/收起明细
const toggleExpand = (id) => {
const item = selectedList.value.find(s => s.id === id)
if (item) item.expanded = !item.expanded
}
</script>
<style scoped>
.exam-application-container {
padding: 16px;
background: #f5f7fa;
min-height: 100vh;
}
.layout-row {
align-items: stretch;
}
.panel-card {
height: 100%;
display: flex;
flex-direction: column;
}
.panel-card :deep(.el-card__body) {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.item-scroll-area, .selected-scroll-area {
flex: 1;
overflow-y: auto;
padding-right: 4px;
}
.item-checkbox {
display: block;
margin-bottom: 8px;
line-height: 1.5;
}
.selected-card {
border: 1px solid #e4e7ed;
border-radius: 6px;
margin-bottom: 10px;
background: #fff;
overflow: hidden;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
cursor: pointer;
background: #fafafa;
transition: background 0.2s;
}
.card-header:hover {
background: #f0f2f5;
}
.item-name {
flex: 1;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 8px;
}
.expand-icon {
transition: transform 0.3s ease;
color: #909399;
}
.is-expanded {
transform: rotate(180deg);
}
.method-detail {
padding: 8px 12px 12px;
border-top: 1px dashed #ebeef5;
background: #fff;
}
.method-detail :deep(.el-checkbox) {
margin-right: 16px;
margin-bottom: 6px;
}
</style>