Files
his/openhis-ui-vue3/src/views/outpatient/doctor/CheckRequest.vue
2026-05-27 02:12:06 +08:00

239 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="check-request-container">
<div class="layout-wrapper">
<!-- 左侧检查项目分类 -->
<div class="panel left-panel">
<h3 class="panel-title">检查项目分类</h3>
<el-tree
:data="categoryTree"
:props="{ label: 'name', children: 'children' }"
@node-click="handleCategoryClick"
highlight-current
default-expand-all
class="category-tree"
/>
</div>
<!-- 中间检查项目列表 -->
<div class="panel middle-panel">
<h3 class="panel-title">检查项目</h3>
<el-table
:data="currentItems"
@selection-change="handleItemSelection"
row-key="id"
border
style="width: 100%"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="项目名称" show-overflow-tooltip />
<el-table-column prop="spec" label="规格" width="120" />
</el-table>
</div>
<!-- 右侧已选择区域 & 检查方法 -->
<div class="panel right-panel">
<h3 class="panel-title">已选择项目</h3>
<div class="selected-list">
<div v-for="item in selectedItems" :key="item.id" class="selected-card">
<div class="card-header" @click="toggleExpand(item)">
<el-checkbox
v-model="item.checked"
@change="handleItemCheckChange(item)"
@click.stop
/>
<el-tooltip :content="cleanName(item.name)" placement="top" :show-after="300">
<span class="item-name">{{ cleanName(item.name) }}</span>
</el-tooltip>
<el-icon class="expand-icon">
<ArrowDown v-if="item.expanded" />
<ArrowRight v-else />
</el-icon>
</div>
<!-- 检查方法/明细区域 (默认收起) -->
<transition name="slide-fade">
<div v-show="item.expanded" class="method-list">
<div v-for="method in item.methods" :key="method.id" class="method-item">
<el-checkbox
v-model="method.checked"
@change="handleMethodCheckChange(item, method)"
@click.stop
/>
<span class="method-name" :title="method.name">{{ method.name }}</span>
</div>
</div>
</transition>
</div>
<el-empty v-if="selectedItems.length === 0" description="暂无已选项目" :image-size="60" />
</div>
<div class="panel-footer">
<el-button type="primary" @click="submitRequest" :disabled="selectedItems.length === 0">提交申请</el-button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
const categoryTree = ref([])
const currentItems = ref([])
const selectedItems = ref([])
const handleCategoryClick = (data) => {
currentItems.value = data.children || []
}
const handleItemSelection = (selection) => {
// 将选中的项目加入右侧列表,默认收起,独立勾选状态
selection.forEach(item => {
if (!selectedItems.value.find(s => s.id === item.id)) {
selectedItems.value.push({
...item,
checked: true,
expanded: false, // 默认收起
methods: item.methods || [] // 保持方法独立
})
}
})
// 处理取消选择
selectedItems.value = selectedItems.value.filter(s => selection.find(sel => sel.id === s.id))
}
const toggleExpand = (item) => {
item.expanded = !item.expanded
}
// 清理名称:去除“套餐”、“项目套餐明细”等冗余前缀/后缀
const cleanName = (name) => {
if (!name) return ''
return name.replace(/(项目套餐明细|套餐)/g, '').trim()
}
// 项目勾选变更:仅更新自身状态,不联动方法(解耦核心)
const handleItemCheckChange = (item) => {
// 明确不修改 item.methods 的 checked 状态,保持父子独立
}
// 方法勾选变更:仅更新自身状态
const handleMethodCheckChange = (item, method) => {
// 独立控制,不反向影响父级 item.checked
}
const submitRequest = async () => {
const payload = selectedItems.value
.filter(i => i.checked)
.map(i => ({
itemCode: i.code,
patientId: 'CURRENT_PATIENT_ID',
doctorId: 'CURRENT_DOCTOR_ID',
methods: i.methods.filter(m => m.checked).map(m => m.code)
}))
if (payload.length === 0) {
ElMessage.warning('请至少勾选一个项目或方法')
return
}
try {
// await submitCheckRequest(payload)
ElMessage.success('提交成功')
selectedItems.value = []
} catch (e) {
ElMessage.error(e.message || '提交失败')
}
}
</script>
<style scoped>
.check-request-container {
height: 100%;
padding: 16px;
box-sizing: border-box;
}
.layout-wrapper {
display: flex;
gap: 16px;
height: 100%;
}
.panel {
background: #fff;
border-radius: 8px;
padding: 16px;
display: flex;
flex-direction: column;
}
.left-panel { width: 20%; }
.middle-panel { width: 45%; }
.right-panel { width: 35%; }
.panel-title {
margin: 0 0 12px;
font-size: 16px;
font-weight: 600;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
.selected-list {
flex: 1;
overflow-y: auto;
padding-right: 4px;
}
.selected-card {
border: 1px solid #ebeef5;
border-radius: 6px;
margin-bottom: 10px;
background: #fafafa;
width: 100%; /* 宽度自适应容器 */
box-sizing: border-box;
}
.card-header {
display: flex;
align-items: center;
padding: 10px;
cursor: pointer;
gap: 8px;
}
.item-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 500;
}
.expand-icon {
font-size: 14px;
color: #909399;
}
.method-list {
padding: 0 10px 10px 30px;
border-top: 1px dashed #eee;
}
.method-item {
display: flex;
align-items: center;
padding: 6px 0;
gap: 8px;
}
.method-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
color: #606266;
}
.panel-footer {
margin-top: 12px;
text-align: right;
}
.slide-fade-enter-active, .slide-fade-leave-active {
transition: all 0.3s ease;
}
.slide-fade-enter-from, .slide-fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
</style>