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

273 lines
7.3 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="item.name" placement="top" :show-after="300">
<span class="item-name">{{ 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">{{ method.name }}</span>
</div>
</div>
</transition>
</div>
<div v-if="selectedItems.length === 0" class="empty-tip">暂无选择项目</div>
</div>
</div>
</div>
<div class="actions">
<el-button type="primary" @click="submitRequests" :loading="submitting">提交申请</el-button>
<el-button @click="clearAll">清空</el-button>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
import { fetchCheckRequests, submitCheckRequests } from '@/api/outpatient';
// 模拟分类树数据(实际应从接口获取)
const categoryTree = ref([
{ id: 'cat1', name: '彩超', children: [] },
{ id: 'cat2', name: 'CT', children: [] },
{ id: 'cat3', name: 'MRI', children: [] }
]);
const currentItems = ref([]);
const selectedItems = reactive([]);
const submitting = ref(false);
// 点击分类加载项目
const handleCategoryClick = async (data) => {
// 实际项目中替换为真实API调用
currentItems.value = [
{ id: `${data.id}_1`, name: '128线排彩超', spec: '常规', methods: [
{ id: 'm1', name: '腹部彩超', checked: false },
{ id: 'm2', name: '心脏彩超', checked: false }
]},
{ id: `${data.id}_2`, name: '高频浅表彩超', spec: '高频', methods: [
{ id: 'm3', name: '甲状腺彩超', checked: false }
]}
];
};
// 中间表格勾选联动(仅添加/移除卡片,不自动勾选方法)
const handleItemSelection = (selection) => {
const selectedIds = new Set(selection.map(i => i.id));
// 移除未勾选的
for (let i = selectedItems.length - 1; i >= 0; i--) {
if (!selectedIds.has(selectedItems[i].id)) {
selectedItems.splice(i, 1);
}
}
// 新增勾选的
selection.forEach(item => {
if (!selectedItems.find(s => s.id === item.id)) {
selectedItems.push({
...item,
checked: true,
expanded: false, // 默认收起
methods: item.methods.map(m => ({ ...m, checked: false })) // 方法独立状态
});
}
});
};
// 项目勾选状态变更
const handleItemCheckChange = (item) => {
// 仅控制项目本身状态,不联动方法
};
// 方法勾选状态变更(完全独立)
const handleMethodCheckChange = (item, method) => {
// 独立控制,无联动逻辑
};
// 展开/收起明细
const toggleExpand = (item) => {
item.expanded = !item.expanded;
};
// 提交申请
const submitRequests = async () => {
const payload = selectedItems
.filter(i => i.checked)
.map(i => ({
itemCode: i.id,
itemName: i.name,
spec: i.spec,
methods: i.methods.filter(m => m.checked).map(m => ({ methodCode: m.id, methodName: m.name }))
}));
if (payload.length === 0) {
ElMessage.warning('请至少选择一个检查项目');
return;
}
submitting.value = true;
try {
await submitCheckRequests(payload);
ElMessage.success('提交成功');
clearAll();
} catch (err) {
ElMessage.error(err.message || '提交失败');
} finally {
submitting.value = false;
}
};
// 清空
const clearAll = () => {
selectedItems.length = 0;
currentItems.value = [];
};
</script>
<style scoped>
.check-request-container {
padding: 20px;
background: #f5f7fa;
min-height: 100vh;
}
.layout-wrapper {
display: flex;
gap: 16px;
margin-bottom: 20px;
}
.panel {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.left-panel { flex: 1; min-width: 200px; }
.middle-panel { flex: 2; min-width: 300px; }
.right-panel { flex: 2; min-width: 300px; }
.panel-title {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 600;
color: #303133;
border-bottom: 1px solid #ebeef5;
padding-bottom: 8px;
}
.category-tree { max-height: 500px; overflow-y: auto; }
.selected-list {
max-height: 450px;
overflow-y: auto;
padding-right: 4px;
}
.selected-card {
border: 1px solid #ebeef5;
border-radius: 6px;
margin-bottom: 10px;
background: #fafafa;
overflow: hidden;
}
.card-header {
display: flex;
align-items: center;
padding: 10px 12px;
cursor: pointer;
background: #fff;
transition: background 0.2s;
}
.card-header:hover { background: #f0f2f5; }
.item-name {
flex: 1;
margin: 0 10px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.expand-icon { color: #909399; }
.method-list {
padding: 8px 12px 12px 32px;
background: #f9fafc;
border-top: 1px dashed #ebeef5;
}
.method-item {
display: flex;
align-items: center;
padding: 6px 0;
font-size: 14px;
color: #606266;
}
.method-name { margin-left: 8px; }
.empty-tip {
text-align: center;
color: #909399;
padding: 40px 0;
}
.actions {
display: flex;
justify-content: flex-end;
gap: 12px;
}
.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>