Files
his/openhis-ui-vue3/src/views/basicmanage/caseTemplates/index.vue
2025-09-25 10:36:59 +08:00

437 lines
12 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="case-templates-container">
<!-- 顶部工具栏 -->
<div class="toolbar">
<el-button type="primary" @click="newTemplate">新建</el-button>
<el-button type="primary" @click="editTemplate">编辑</el-button>
<el-button @click="refresh">刷新</el-button>
<el-button type="danger" @click="deleteTemplate">删除</el-button>
<el-button @click="printTemplate">打印</el-button>
</div>
<div class="content-area">
<!-- 左侧病历类型树 -->
<div class="left-panel">
<div style="margin-bottom: 10px">
<el-tree-select
v-model="orgId"
:data="orgOptions"
:props="{
value: 'id',
label: 'name',
children: 'children',
}"
value-key="id"
placeholder="请选择就诊科室"
check-strictly
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="locationTreeRef"
node-key="id"
highlight-current
default-expand-all
@node-click="initTemplateTree"
@clear="handleOrgClear"
clearable
/>
</div>
<div class="search-box">
<el-input
placeholder="病历名称搜索..."
v-model="searchKeyword"
></el-input>
<el-button class="search-btn" @click="handleSearch">查询</el-button>
</div>
<el-tree
ref="templateTree"
:data="templateData"
:props="defaultProps"
node-key="id"
@node-click="handleNodeClick"
class="template-tree"
></el-tree>
<el-button @click="toggleExpand">{{ isExpanded ? '全部收起' : '全部展开' }}</el-button>
</div>
<div class="middle-panel">
<el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="打印预览" name="first">
<!-- {{components}} -->
<component :is="currentComponent" />
</el-tab-pane>
<!-- <el-tab-pane label="编辑内容" name="second">
<component :is="currentComponent" />
</el-tab-pane> -->
</el-tabs>
</div>
</div>
<EditTemplate
v-model:dialogVisible="dialogVisible"
:title="currentNodeData ? '编辑病历文件信息' : '病历文件基本信息'"
:formData="formData"
:currentNodeData="currentNodeData"
@submitOk="handleSubmitOk"
:docTypes="templateDataInit.docTypes"
:useRanges="templateDataInit.useRanges"
/>
</div>
</template>
<script setup>
// 从Vue导入所需的API
import { ref, reactive, onMounted, defineAsyncComponent, nextTick, watch } from 'vue';
import { ElMessageBox, ElMessage, ElLoading, ElTree } from 'element-plus';
import { getTreeList, init, getDefinitionById, deleteDefinition,getLocationTree } from './api';
import EditTemplate from './components/editTemplate.vue';
// 添加当前模板路径和组件的响应式变量
const currentComponent = ref('');
const currentNodeData = ref(null); // 存储当前选中的节点数据
const isExpanded = ref(true); // 控制树形结构的展开状态
// 弹窗可见性
const dialogVisible = ref(false);
const orgId = ref('');
const orgOptions = ref([]); // 科室选项整数ID
const locationTreeRef = ref(null); // TreeSelect引用
const templateTree = ref(null);
const searchKeyword = ref(''); // 搜索关键字
const activeName = ref('first');
// Transfer组件选项类型定义ID为整数类型
// 表单数据
const formData = reactive({
primaryMenuEnum: undefined,
subMenu: '',
displayOrder: 1,
version: '',
name: '',
vueRouter: '',
useRangeEnum: 0, // 默认"暂不使用"0暂不使用1全院使用2科室使用
organizationIds: [], // 选中的科室ID列表整数类型
environment: '0',
});
const initFormData = () => {
formData.primaryMenuEnum = undefined;
formData.subMenu = '';
formData.displayOrder = 1;
formData.version = '';
formData.name = '';
formData.vueRouter = '';
formData.useRangeEnum = 0;
formData.organizationIds = [];
formData.environment = '0';
};
const getLocationInfo = () => {
getLocationTree().then((response) => {
orgOptions.value = response?.data || [];
}).catch((error) => {
ElMessage.error('获取科室树失败');
});
}
const handleSubmitOk = () => {
dialogVisible.value = false;
initFormData();
refresh();
};
// 组件初始化时加载基础数据
onMounted(() => {
getInit(); // 加载模板初始化数据(一级菜单、使用范围等)
getLocationInfo(); // 加载科室树
handleOrgClear(); // 初始化模板树(加载所有科室模板)
});
/** 将树形结构转换为Transfer组件所需的格式不扁平化保留层级关系 */
const convertTreeToTransferFormat = (tree) => {
return tree.map((item) => {
const option = {
key: item.id, // 整数ID
label: item.name,
disabled: false,
};
// 如果有子节点递归处理Transfer组件会自动处理层级显示
if (item.children && item.children.length > 0) {
option.children = convertTreeToTransferFormat(item.children);
}
return option;
});
};
// 病历模板树数据
const templateData = ref([]);
const templateDataInit = ref({}); // 初始化数据(菜单、使用范围)
// 树配置(模板树)
const defaultProps = {
children: 'children',
label: 'name',
value: 'id',
};
/** 过滤节点科室TreeSelect搜索 */
const filterNode = (value, data) => {
if (!value) return true;
return data?.name.toLowerCase().includes(value.toLowerCase()); // 不区分大小写搜索
};
/** 统一的API错误处理函数 */
const handleApiError = (userMessage, logMessage) => {
return (error) => {
// 记录详细错误日志,包括错误对象和调用栈
console.error(`${logMessage}:`, error);
// 显示用户友好的错误提示
ElMessage.error(
`${userMessage}失败${error.message ? ': ' + error.message : ',请刷新页面重试'}`
);
};
};
/** 加载模板初始化数据(一级菜单、使用范围枚举) */
const getInit = async () => {
try {
const response = await init();
templateDataInit.value = response.data || {};
console.log('模板初始化数据:', templateDataInit.value);
} catch (error) {
handleApiError('初始化', '初始化接口异常')(error);
}
};
/** 清除科室选择时,加载所有科室模板 */
const handleOrgClear = () => {
orgId.value = '';
initTemplateTree({ id: '' });
};
/** 搜索模板(可扩展按科室+关键字筛选) */
const handleSearch = () => {
console.log('搜索模板,关键字:', searchKeyword.value);
initTemplateTree({ id: orgId.value });
};
/** 初始化病历模板树(按科室筛选) */
function initTemplateTree(data) {
const queryParams = {
organizationId: data.id || '', // 科室ID空表示所有科室
name: searchKeyword.value || '', // 模板名称(空表示不筛选)
};
getTreeList(queryParams)
.then((res) => {
templateData.value = res.data || [];
nextTick().then(() => {
expandTree(); // 展开树节点
})
// console.log('模板树数据(按科室筛选):', templateData.value);
})
.catch((error) => {
handleApiError('获取模板列表', '获取模板树失败')(error);
templateData.value = [];
});
}
/** 编辑模板(打开弹窗并回显数据) */
const editTemplate = async () => {
if (!currentNodeData.value) {
ElMessage.warning('请先选择一个模板节点');
return;
}
const loading = ElLoading.service({
lock: true,
text: '加载模板信息...',
background: 'rgba(0, 0, 0, 0.7)',
});
try {
const response = await getDefinitionById(currentNodeData.value.id);
if (response.data) {
openEditDialog(response.data);
}
} catch (error) {
handleApiError('加载模板信息', '加载模板信息失败')(error);
} finally {
loading.close();
}
};
/** 打开编辑弹窗,回显选中的模板数据并正确初始化科室选择 */
const openEditDialog = async (nodeData) => {
currentNodeData.value = nodeData;
console.log('回显模板数据:', nodeData);
// 回显表单数据(与接口返回字段匹配)
formData.primaryMenuEnum = nodeData.primaryMenuEnum;
formData.subMenu = nodeData.subMenu;
formData.version = nodeData.version;
formData.name = nodeData.name;
formData.vueRouter = nodeData.vueRouter;
formData.displayOrder = nodeData.displayOrder;
formData.useRangeEnum = nodeData.useRangeEnum;
formData.environment = nodeData.environment || '0';
formData.organizationIds = nodeData.organizationIds.map((id) => id.toString());
// 打开弹窗
dialogVisible.value = true;
};
// 处理节点点击,根据后台返回的路径加载组件
const handleNodeClick = async (data, node) => {
console.log('点击节点:', data, node);
// 检查是否为子节点没有children或children为空
// const isLeafNode = !data.children || data.children.length === 0;
if (node.isLeaf) {
// 存储当前节点数据
currentNodeData.value = data.document;
// 检查是否为子节点没有children或children为空
currentComponent.value = data.document.vueRouter || '';
} else {
currentNodeData.value = null;
currentComponent.value = null;
}
};
const toggleExpand = () => {
isExpanded.value = !isExpanded.value;
console.log('展开状态:', templateTree.value.store);
expandTree();
};
const expandTree = () => {
templateTree.value.store._getAllNodes().forEach((node) => {
node.expanded = isExpanded.value;
});
};
const refresh = () => {
initTemplateTree({ id: orgId.value });
};
// 新建模板
const newTemplate = () => {
dialogVisible.value = true;
};
// 删除模板
const deleteTemplate = async () => {
// 1. 检查是否选中节点
if (!currentNodeData.value) {
ElMessage.warning('请先选择一个模板节点');
return;
}
let loading = null;
const templateName = currentNodeData.value.name;
const templateId = currentNodeData.value.id;
try {
// 2. 显示确认对话框,增加操作描述
const confirmResult = await ElMessageBox.confirm(
`确定要删除模板「${templateName}」吗?<br>此操作不可撤销,删除后将无法恢复。`,
'删除确认',
{
confirmButtonText: '确认删除',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true,
center: true,
closeOnClickModal: false,
}
);
// 3. 用户确认删除后执行操作
if (confirmResult === 'confirm') {
// 显示加载状态
loading = ElLoading.service({
lock: true,
text: '正在删除模板...',
background: 'rgba(0, 0, 0, 0.7)',
});
// 调用删除API
await deleteDefinition(templateId);
// 删除成功处理
ElMessage.success(`模板「${templateName}」删除成功`);
// 清空当前选中状态
currentNodeData.value = null;
initFormData();
currentComponent.value = null;
// 刷新列表
refresh();
}
} catch (error) {
// 错误处理区分用户取消和API错误
if (error === 'cancel' || error === undefined) {
// 用户取消删除,不显示错误提示
console.log('用户取消删除操作');
} else {
// API错误或其他错误使用统一的错误处理函数
handleApiError('删除模板', '删除模板失败')(error);
}
} finally {
// 确保加载状态总是被关闭
if (loading) {
loading.close();
}
}
};
// 打印模板
const printTemplate = () => {
window.print();
};
// 标签页点击事件
const handleClick = (tab) => {
console.log('标签页点击:', tab);
};
</script>
<style scoped>
.case-templates-container {
display: flex;
flex-direction: column;
height: 91vh;
}
.toolbar {
display: flex;
padding: 10px;
background-color: #f5f5f5;
border-bottom: 1px solid #ddd;
gap: 10px;
}
.content-area {
display: flex;
flex: 1;
overflow: hidden;
}
.left-panel {
width: 280px;
border-right: 1px solid #ddd;
display: flex;
flex-direction: column;
padding: 10px;
}
.search-box {
display: flex;
margin-bottom: 10px;
}
.search-btn {
margin-left: 5px;
}
.template-tree {
flex: 1;
overflow-y: auto;
}
.middle-panel {
flex: 1;
overflow-y: auto;
padding: 10px;
}
</style>