Revert "Fix Bug #550: AI修复"

This reverts commit 16c42ca108.
This commit is contained in:
2026-05-27 08:59:07 +08:00
parent bd14563691
commit 9db5ced4e3
5432 changed files with 778638 additions and 171 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,52 @@
import request from '@/utils/request'
export function getList(queryParams) {
return request({
url: '/base-data-manage/body-structure/body',
method: 'get',
param: queryParams
})
}
export function addBodyStructure(data) {
return request({
url: '/base-data-manage/body-structure/body',
method: 'post',
data: data
})
}
export function updateBodyStructure(data) {
return request({
url: '/base-data-manage/body-structure/body',
method: 'post',
data: data
})
}
export function deleteBodyStructure(ids) {
return request({
url: '/base-data-manage/body-structure/body?ids=' + ids ,
method: 'delete',
})
}
export function getBodyStructureDetail(id) {
return request({
url: '/base-data-manage/body-structure/body?id=' + id,
method: 'get',
})
}
export function disableBodyStructure(id) {
return request({
url: '/base-data-manage/body-structure/body-inactive?id=' + id,
method: 'put',
})
}
export function enableBodyStructure(id) {
return request({
url: '/base-data-manage/body-structure/body-active?id=' + id,
method: 'put',
})
}

View File

@@ -0,0 +1,236 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
</el-col>
<el-col :span="1.5">
<el-tooltip
:content="selectRowIds.length == 0 ? '至少选择一条数据' : ''"
placement="top"
:disabled="selectRowIds.length != 0"
>
<el-button
type=""
plain
icon="Delete"
:disabled="selectRowIds.length == 0"
@click="handleDelete"
>删除</el-button
>
</el-tooltip>
</el-col>
<el-col :span="1.5">
<el-button type="" plain icon="Download" @click="handleExport"> 导出 </el-button>
</el-col>
<el-col :span="1.5">
<el-button type="" plain icon="Refresh" @click="getPageList">刷新</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
ref="bodyStructureTableRef"
v-loading="loading"
:data="bodyStructure"
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="部位名称" align="left" prop="name" />
<el-table-column label="拼音" align="left" prop="pyStr" />
<el-table-column label="五笔拼音" align="left" prop="wbStr" />
<el-table-column label="状态" align="center" prop="statusEnum_enumText" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button link type="primary" @click="handelEdit(scope.row)">编辑</el-button>
<el-button
link
type="primary"
@click="handleDisabled(scope.row.id)"
v-if="scope.row.statusEnum == '2'"
>停用</el-button
>
<el-button link type="primary" @click="handelEnable(scope.row.id)" v-else>启用</el-button>
<el-button link type="primary" @click="handleAddInferior(scope.row)">
添加下级
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="600px" @close="cancel" append-to-body>
<el-form ref="bodyStructureRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="id" prop="id" v-show="false">
<el-input v-model="form.id" placeholder="请输入部位编号" />
</el-form-item>
<el-form-item label="部位编号" prop="busNo" v-show="false">
<el-input v-model="form.busNo" placeholder="请输入部位编号" />
</el-form-item>
<el-form-item label="部位名称" prop="name">
<el-input v-model="form.name" placeholder="请输入部位名称" />
</el-form-item>
<el-col>
<el-form-item label="上级部位" prop="busNoParent">
<el-tree-select
clearable
style="width: 100%"
v-model="form.busNoParent"
:data="bodyStructure"
:props="{ value: 'busNo', label: 'name', children: 'children' }"
value-key="id"
check-strictly
/>
</el-form-item>
</el-col>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="BodyStructure">
import {
addBodyStructure,
deleteBodyStructure,
disableBodyStructure,
enableBodyStructure,
getList,
updateBodyStructure,
} from './components/api';
const { proxy } = getCurrentInstance();
const loading = ref(true);
const bodyStructure = ref([]);
const queryParams = ref({});
const open = ref(false);
const form = ref({
id: undefined,
busNo: undefined,
name: undefined,
statusEnum: undefined,
busNoParent: undefined,
});
const bodyStructureTableRef = ref();
const bodyStructureRef = ref();
const selectRowIds = ref([]);
const total = ref(0);
const title = ref('');
const rules = ref({
busNo: [{ required: false, message: '请输入部位编号', trigger: 'input' }],
name: [
{ required: true, message: '请输入部位名称', trigger: 'change' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'change' },
],
});
getPageList();
function reset() {
form.value.id = undefined;
bodyStructureRef.value.resetFields();
}
function getPageList() {
loading.value = false;
getList(queryParams.value).then((res) => {
bodyStructure.value = res.data.records;
total.value = res.data.total;
loading.value = false;
});
}
function handleAdd() {
title.value = '添加身体部位';
open.value = true;
reset();
console.log(form.value);
}
function handelEdit(row) {
console.log(row.busNo);
title.value = '编辑部位';
open.value = true;
setTimeout(() => {
form.value.id = row.id;
form.value.busNo = row.busNo;
form.value.name = row.name;
form.value.statusEnum = row.statusEnum;
form.value.busNoParent = row.busNo.split('.').length > 1 ? row.busNo.split('.')[0] : undefined;
}, 50);
}
function cancel() {
open.value = false;
reset();
console.log(form.value);
}
// 新增/编辑
function submitForm() {
proxy.$refs['bodyStructureRef'].validate((valid) => {
if (valid) {
if (form.value.id == undefined) {
if (form.value.busNoParent) {
form.value.busNo = form.value.busNoParent;
}
addBodyStructure(form.value).then((res) => {
proxy.$modal.msgSuccess('操作成功');
open.value = false;
getPageList();
});
} else {
updateBodyStructure(form.value).then((res) => {
proxy.$modal.msgSuccess('操作成功');
open.value = false;
getPageList();
});
}
}
});
}
// 添加下级
function handleAddInferior(row) {
title.value = '添加下级';
open.value = true;
form.value.busNoParent = row.busNo;
}
// 删除
function handleDelete() {
loading.value = true;
deleteBodyStructure(selectRowIds.value.join(',')).then((res) => {
proxy.$modal.msgSuccess('操作成功');
loading.value = false;
getPageList();
});
}
// 停用
function handleDisabled(id) {
disableBodyStructure(id).then((res) => {
proxy.$modal.msgSuccess('操作成功');
getPageList();
});
}
// 启用
function handelEnable(id) {
enableBodyStructure(id).then((res) => {
proxy.$modal.msgSuccess('操作成功');
getPageList();
});
}
function handleSelectionChange() {
selectRowIds.value = bodyStructureTableRef.value.getSelectionRows().map((item) => item.id);
}
</script>

View File

@@ -0,0 +1,59 @@
import request from '@/utils/request'
// 查询诊疗项目列表
export function getTreeList(queryParams) {
return request({
url: '/document/definition/treeList',
method: 'get',
params: queryParams
})
}
// 初始化
export function init() {
return request({
url: '/document/definition/init',
method: 'get'
})
}
// 新增
export function add(data) {
return request({
url: '/document/definition/add',
method: 'post',
data
})
}
// 修改
export function update(data) {
return request({
url: '/document/definition/update',
method: 'post',
data
})
}
// 查询就诊科室
export function getLocationTree(query) {
return request({
url: '/charge-manage/register/org-list',
method: 'get',
params: query
})
}
// 查询就诊科室
export function getDefinitionById(id) {
return request({
url: `/document/definition/${id}`,
method: 'get'
})
}
export function deleteDefinition(id) {
return request({
url: `/document/definition/delete/${id}`,
method: 'delete'
})
}

View File

@@ -0,0 +1,251 @@
<template>
<!-- 病历文件基本信息弹窗 -->
<el-dialog
:title="title"
v-model="dialogVisible"
width="900px"
destroy-on-close
@open="handleOpen"
>
<!-- 使用el-form包裹表单 -->
<el-form :model="formData" ref="formRef" :rules="rules" label-width="120px">
<el-form-item label="一级菜单" prop="primaryMenuEnum">
<el-select
v-model="formData.primaryMenuEnum"
placeholder="请选择一级菜单"
value-key="value"
>
<el-option
v-for="item in props.docTypes || []"
:key="item.value"
:label="item.info"
:value="Number(item.value)"
/>
</el-select>
</el-form-item>
<el-form-item label="二级菜单" prop="subMenu">
<el-input v-model="formData.subMenu" placeholder="二级菜单"></el-input>
</el-form-item>
<el-form-item label="版本" prop="version">
<el-input v-model="formData.version" placeholder="请输入版本"></el-input>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称"></el-input>
</el-form-item>
<el-form-item label="文件" prop="vueRouter">
<el-select v-model="formData.vueRouter" placeholder="请选择文件路径">
<el-option
v-for="item in components"
:key="item.name"
:label="item.name"
:value="item.name"
/>
</el-select>
<!-- <el-input v-model="formData.vueRouter" placeholder="请输入文件路径"></el-input>s -->
</el-form-item>
<el-form-item label="显示顺序" prop="displayOrder">
<el-input-number v-model="formData.displayOrder" :min="1" label="描述文字"></el-input-number>
</el-form-item>
<el-form-item label="使用范围" prop="useRangeEnum">
<div class="radio-group">
<el-radio
v-for="item in props.useRanges"
:key="item.value"
v-model="formData.useRangeEnum"
:label="item.value"
>{{item.info}}</el-radio>
{{ formData.organizationIds }}
</div>
</el-form-item>
<!-- 科室选择框仅当使用范围为科室使用时显示 -->
<el-form-item v-if="formData.useRangeEnum === 2" label="科室选择" prop="organizationIds">
<el-transfer
v-model="formData.organizationIds"
filterable
:titles="['未分配科室', '已分配科室']"
:button-texts="['移除', '添加']"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}',
}"
:props="{key: 'id', label: 'name',}"
:data="transferData"
>
</el-transfer>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer"></div>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
import {onMounted, ref} from 'vue'
import useUserStore from '@/store/modules/user';
import {add, getLocationTree, update} from '../api';
import {ElMessage} from 'element-plus';
import {components} from '@/template';
const emits = defineEmits(['submitOk'])
const props = defineProps({
title: {
type: String,
default: '编辑病历文件信息'
},
formData: {
type: Object,
default: () => ({})
},
currentNodeData: {
type: Object,
default: () => ({})
},
docTypes: {
type: Array,
default: () => []
},
useRanges: {
type: Array,
default: () => []
},
})
const userStore = useUserStore();
const formRef = ref(null)
console.log(components,'----components');
const dialogVisible= defineModel( 'dialogVisible', {
type: Boolean,
default: false
})
// 表单数据
const formData = ref({
primaryMenuEnum: undefined,
subMenu: '',
displayOrder: 1,
version: '',
name: '',
vueRouter: '',
useRangeEnum: 0, // 默认"暂不使用"0暂不使用1全院使用2科室使用
organizationIds: [], // 选中的科室ID列表整数类型
environment: '0'
});
// 表单验证规则(响应式,支持动态验证)
const rules = reactive({
primaryMenuEnum: [{ required: true, message: '请选择一级菜单', trigger: 'change' }],
// subMenu: [{ required: true, message: '请输入二级菜单', trigger: 'blur' }],
version: [{ required: true, message: '请输入版本', trigger: 'blur' }],
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
vueRouter: [{ required: true, message: '请输入文件路径', trigger: 'blur' }],
displayOrder: [{ required: true, message: '请设置显示顺序', trigger: 'change' }],
useRangeEnum: [{ required: true, message: '请选择使用范围', trigger: 'change' }],
organizationIds: [
{
required: () => formData.value.useRangeEnum === 2, // 仅当"科室使用"时必填
message: '请选择科室',
trigger: ['change', 'blur']
}
]
});
const transferData = ref([]);
/** 查询科室树数据TreeSelect和Transfer共用 */
const getLocationInfo = () => {
getLocationTree().then((response) => {
transferData.value = response?.data || [];
}).catch((error) => {
ElMessage.error('获取科室树失败');
});
}
/** 递归将所有ID转换为整数类型 */
const convertIdsToNumbers = (item) => {
return {
...item,
id: Number(item.id), // 确保ID为整数
...(item.children && item.children.length > 0
? { children: item.children.map((child ) => convertIdsToNumbers(child)) }
: {})
};
};
const handleOpen = () => {
if (props.formData) {
console.log('props.formData', props.formData);
formData.value= props.formData
} else {
resetForm();
formRef.value.resetFields()
}
}
// 提交表单
const submitForm = () => {
formRef.value.validate((valid) => {
if (valid) {
// 表单验证通过,执行保存操作
saveForm();
} else {
// 表单验证失败
ElMessage.error('请填写必填项');
return false;
}
});
};
// 保存表单
const saveForm =async () => {
const userStore = useUserStore()
console.log('提交表单数据:', formData.value);
let data = { ...formData.value, tenantId: userStore.tenantId }
if (formData.value.useRangeEnum !== 2) {
formData.value.organizationIds = [];
}
try {
// 如果有当前节点数据,表示是编辑操作
if (props.currentNodeData) {
data.id = props.currentNodeData.id; // 添加ID
data.busNo = props.currentNodeData.busNo;
data.hospitalId = props.currentNodeData.hospitalId;
console.log('data',data)
const res = await update(data);
if (res.code == 200) {
ElMessage.success('更新成功');
emits('submitOk')
}else {
ElMessage.error('保存失败');
}
} else {
// 新建操作
const res = await add(data);
if (res.code == 200) {
ElMessage.success('保存成功');
emits('submitOk')
}else {
ElMessage.error('保存失败',error);
}
}
}
catch (error) {
console.log(error)
// ElMessage.error('保存失败',error);
}
}
// 重置表单
const resetForm = () => {
formRef.value?.resetFields();
formData.value.useRangeEnum = 0;
formData.value.environment = '0';
formData.value.organizationIds = [];
};
onMounted(()=>{
getLocationInfo()
})
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,436 @@
<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 {nextTick, onMounted, reactive, ref} from 'vue';
import {ElLoading, ElMessage, ElMessageBox, ElTree} from 'element-plus';
import {deleteDefinition, getDefinitionById, getLocationTree, getTreeList, init} 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>

View File

@@ -0,0 +1,81 @@
import request from '@/utils/request'
const baseUrl='/document/statistics/definition'
// 初始化
export function init() {
return request({
url: `${baseUrl}/init`,
method: 'get'
})
}
// 新增
export function add(data) {
return request({
url: `${baseUrl}/add`,
method: 'post',
data
})
}
// 修改
export function update(data) {
return request({
url: `${baseUrl}/update`,
method: 'post',
data
})
}
// 删除
export function deleteStatistics(id) {
return request({
url: `${baseUrl}/deleteDocStatisticsDefinition`,
method: 'delete',
params: {
id
}
})
}
// 页查询列表-不包含options
export function getPageList(query) {
return request({
url: `${baseUrl}/getPageList`,
method: 'get',
params: query
})
}
// 根据id获取统计定义详情
export function getDocStatisticsDefinitionById(id) {
return request({
url: `${baseUrl}/getDocStatisticsDefinitionById`,
method: 'get',
params: {id}
})
}
// 根据code获取统计定义详情
export function getDocStatisticsDefinitionByCode(code) {
return request({
url: `${baseUrl}/getDocStatisticsDefinitionByCode`,
method: 'get',
params: {code}
})
}
// 获取文档统计定义选项列表并按指定格式返回
export function getOptionList() {
return request({
url: `${baseUrl}/getOptionList`,
method: 'get',
params: {}
})
}
// 获取文档统计定义列表(包含options即字典数据)
// isStatistics
// integer
// (query)
// 文档统计定义是否启用 0不统计 1统计 可不传
export function getListWithOptionList(isStatistics=undefined) {
return request({
url: `${baseUrl}/getListWithOptionList`,
method: 'get',
params: {isStatistics}
})
}

View File

@@ -0,0 +1,131 @@
<template>
<!-- 病历文件基本信息弹窗 -->
<el-dialog
:title="formData.id ? '编辑模板' : '新增模板'"
v-model="dialogVisible"
width="900px"
destroy-on-close
@open="handleOpen"
>
<!-- 使用el-form包裹表单 -->
<el-form :model="formData" ref="formRef" :rules="rules" label-width="120px">
<el-row :gutter="24" class="mb8">
<el-col :span="24">
<el-form-item label="模板名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入模板名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="使用范围" prop="useRange">
<el-radio-group v-model="formData.useRange">
<el-radio :value="0" size="large">全院</el-radio>
<el-radio :value="1" size="large">指定机构</el-radio>
<el-radio :value="2" size="large">指定用户</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入版本"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer"></div>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
import {onMounted, ref} from 'vue';
import {ElMessage} from 'element-plus';
const emits = defineEmits(['submitOk']);
const props = defineProps({
formData: {
type: Object,
default: () => ({}),
},
});
const formRef = ref(null);
const dialogVisible = defineModel('dialogVisible', {
type: Boolean,
default: false,
});
// 表单数据
const formData = ref({
id: 0,
name: '',
displayOrder: 0,
contextJson: '',
definitionId: 0,
useRange: 2,
organizationId: 0,
userId: 0,
remark: '',
});
// 表单验证规则(响应式,支持动态验证)
const rules = reactive({
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
});
const handleOpen = () => {
if (props.formData) {
formData.value = props.formData;
} else {
resetForm();
}
};
// 提交表单
const submitForm = () => {
formRef.value.validate((valid) => {
if (valid) {
// 表单验证通过,执行保存操作
saveForm();
} else {
// 表单验证失败
ElMessage.error('请填写必填项');
return false;
}
});
};
// 保存表单
const saveForm = async () => {
// try {
// // 如果有当前节点数据,表示是编辑操作
// if (formData.value.id && formData.value.id !== '') {
// const res = await updateTemplate(formData.value);
// if (res.code == 200) {
// ElMessage.success('更新成功');
// emits('submitOk');
// } else {
// ElMessage.error('更新失败', error);
// }
// } else {
// // 新建操作
// const res = await addTemplate(formData.value);
// if (res.code == 200) {
// ElMessage.success('保存成功');
// emits('submitOk');
// } else {
// ElMessage.error('保存失败', error);
// }
// }
// } catch (error) {
// console.log(error);
// // ElMessage.error('保存失败',error);
// }
};
// 重置表单
const resetForm = () => {
formRef.value?.resetFields();
};
onMounted(() => {});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,303 @@
<template>
<div class="app-container case-templates-statistics-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
<el-form-item label="名称" prop="searchKey">
<el-input
v-model="queryParams.searchKey"
placeholder="请输入名称搜索"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="statisticsList">
<el-table-column label="属性名称" align="center" prop="name" />
<el-table-column label="属性代码" align="center" prop="code" />
<el-table-column label="属性类型" align="center" prop="typeEnum">
<template #default="scope">
{{ typeEnums(scope.row.typeEnum) }}
</template>
</el-table-column>
<el-table-column label="统计值单位" align="center" prop="unit" />
<el-table-column label="是否必填" align="center" prop="required">
<template #default="scope">
{{ scope.row.required === 1 ? '必填' : '不必填' }}
</template>
</el-table-column>
<el-table-column label="是否统计" align="center" prop="isStatistics">
<template #default="scope">
{{ scope.row.isStatistics === 1 ? '统计' : '不统计' }}
</template>
</el-table-column>
<el-table-column label="字典名称" align="center" prop="dictName"> </el-table-column>
<el-table-column label="字典类型" align="center" prop="dictType"> </el-table-column>
<el-table-column label="备注" align="center" prop="remark"> </el-table-column>
<el-table-column
label="操作"
width="180"
align="center"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
>修改</el-button
>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改岗位对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="statisticsRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="属性名称" prop="name">
<el-input v-model="form.name" placeholder="请输入属性名称" />
</el-form-item>
<el-form-item label="属性代码" prop="code">
<el-input v-model="form.code" placeholder="请输入属性代码" />
</el-form-item>
<el-form-item label="体温单类型编码" prop="typeCode">
<el-input v-model="form.typeCode" placeholder="请输入体温单属性编码" />
</el-form-item>
<el-form-item label="岗位顺序" prop="displayOrder">
<el-input-number v-model="form.displayOrder" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="属性类型" prop="typeEnum">
<el-radio-group v-model="form.typeEnum">
<el-radio v-for="dict in typeEnumsOptions" :key="dict.value" :label="dict.value">{{
dict.info
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否必填" prop="required">
<el-switch
v-model="form.required"
:active-value="1"
:inactive-value="0"
active-text=""
inactive-text=""
inline-prompt
/>
</el-form-item>
<el-form-item label="是否统计" prop="isStatistics">
<el-switch
v-model="form.isStatistics"
:active-value="1"
:inactive-value="0"
active-text=""
inactive-text=""
inline-prompt
/>
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="请输入统计值单位" />
</el-form-item>
<el-form-item label="字典名称" prop="dictType">
<el-select v-model="form.dictType" style="width: 100%" filterable>
<el-option
v-for="item in typeOptions"
:key="item.dictId"
:label="item.dictName"
:value="item.dictType"
>
<span style="float: left">{{ item.dictName }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.dictType }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Post">
import {add, deleteStatistics, getPageList, init, update} from './api';
import {optionselect as getDictOptionselect} from '@/api/system/dict/type';
const { proxy } = getCurrentInstance();
const { sys_normal_disable } = proxy.useDict('sys_normal_disable');
const statisticsList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const title = ref('');
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 10,
searchKey: undefined,
},
rules: {
name: [{ required: true, message: '请输入属性名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入属性代码', trigger: 'blur' }],
},
});
const { queryParams, form, rules } = toRefs(data);
/** 查询岗位列表 */
function getList() {
loading.value = true;
getPageList(queryParams.value).then((response) => {
statisticsList.value = response.data.records;
total.value = response.data.total;
loading.value = false;
});
}
/** 取消按钮 */
function cancel() {
open.value = false;
reset();
}
/** 表单重置 */
function reset() {
form.value = {
id: undefined, // ID
name: '', // 名称
typeEnum: 1, // 类型枚举
code: '', // 编码
required: 0, // 是否必填(0-否 1-是)
remark: '', // 备注
unit: '', // 单位
isStatistics: 1, // 是否统计(0-否 1-是)
displayOrder: 0, // 显示顺序
dictName: '', //字典名称
dictType: '', //字典类型
typeCode:'',//体温单类型编码
};
proxy.resetForm('statisticsRef');
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNo = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm('queryRef');
handleQuery();
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = '添加统计';
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
form.value = row;
open.value = true;
title.value = '修改统计';
}
/** 提交按钮 */
function submitForm() {
proxy.$refs['statisticsRef'].validate((valid) => {
if (form.value.dictType ) {
form.value.dictName=typeOptions.value.find((item) => item.dictType == form.value.dictType)?.dictName || form.value.dictName;
}
if (valid) {
if (form.value.id != undefined) {
update(form.value).then((response) => {
proxy.$modal.msgSuccess('修改成功');
open.value = false;
getList();
});
} else {
add(form.value).then((response) => {
proxy.$modal.msgSuccess('新增成功');
open.value = false;
getList();
});
}
}
});
}
/** 删除按钮操作 */
function handleDelete(row) {
proxy.$modal
.confirm('是否确认删除编号为"' + row.id + '"的数据项?')
.then(function () {
return deleteStatistics(row.id);
})
.then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
})
.catch(() => {});
}
const typeEnumsOptions = ref([]);
const typeEnums = (value) => {
if (typeEnumsOptions.value.length) {
return typeEnumsOptions.value.find((item) => item.value == value)?.info || '未知';
}
};
const initPage = async () => {
try {
const response = await init();
typeEnumsOptions.value = response.data.doc_statistics_definition_type_enums || {};
} catch (error) {}
};
const typeOptions = ref([]);
/** 查询字典类型列表 */
function getTypeList() {
getDictOptionselect().then((response) => {
typeOptions.value = response.data;
});
}
initPage();
getTypeList();
getList();
</script>
<style scoped lang="scss">
.case-templates-statistics-container {
// height: calc(100vh - 84px);
.toolbar {
height: 48px;
display: flex;
padding: 0 8px;
align-items: center;
}
.statistics-data-container {
height: calc(100% - 48px);
}
}
</style>

View File

@@ -0,0 +1,144 @@
<template>
<div class="case-templates-statistics-container">
<div class="toolbar">
<el-space>
<el-input v-model="queryParams.searchKey" placeholder="请输入名称搜索" clearable />
<el-button @click="refresh">查询</el-button>
<el-button type="primary" @click="newTemplate">新建</el-button>
</el-space>
</div>
<div class="statistics-data-container">
<el-table
:data="statisticsData"
node-key="id"
>
<el-table-column prop="name" label="名称" />
<el-table-column prop="count" label="数量" />
<el-table-column label="操作" >
<template #default="scope">
<el-button type="primary" @click="editStatistics(scope.row)">编辑</el-button>
<el-button type="danger" @click="deleteStatistics(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<EditStatistics
v-model:dialogVisible="dialogVisible"
:title="currentNodeData ? '编辑病历文件信息' : '病历文件基本信息'"
:formData="formData"
:currentNodeData="currentNodeData"
@submitOk="handleSubmitOk"
/>
</template>
<script setup>
// 从Vue导入所需的API
import {onMounted, reactive, ref} from 'vue';
import {init,} from './api';
import EditStatistics from './components/editStatistics.vue';
const queryParams = ref({
pageNo: 1,
pageSize: 10,
searchKey: ''
});
const currentNodeData = ref(null); // 存储当前选中的节点数据
// 弹窗可见性
const dialogVisible = ref(false);
// 表单数据
const formData = reactive({
id:''
});
const initFormData = () => {
formData.id = '';
};
const handleSubmitOk = () => {
dialogVisible.value = false;
refresh();
};
// 组件初始化时加载基础数据
onMounted(() => {
getInit(); // 加载模板初始化数据(一级菜单、使用范围等)
handleOrgClear(); // 初始化模板树(加载所有科室模板)
});
// 统计数据
const statisticsData = ref([]);
/** 过滤节点科室TreeSelect搜索 */
const filterNode = (value, data) => {
if (!value) return true;
return data?.name.toLowerCase().includes(value.toLowerCase()); // 不区分大小写搜索
};
/** 加载模板初始化数据(一级菜单、使用范围枚举) */
const getInit = async () => {
try {
const response = await init();
templateDataInit.value = response.data || {};
console.log('模板初始化数据:', templateDataInit.value);
} catch (error) {
handleApiError('初始化', '初始化接口异常')(error);
}
};
/** 编辑模板(打开弹窗并回显数据) */
const editStatistics = async () => {
};
/** 打开编辑弹窗,回显选中的模板数据并正确初始化科室选择 */
const openEditDialog = async (nodeData) => {
};
const getList = async () => {
try {
const response = await getTreeList();
statisticsData.value = response?.data || [];
} catch (error) {
}
};
const refresh = () => {
getList();
};
// 新建模版统计
const newTemplate = () => {
dialogVisible.value = true;
};
// 删除统计
const deleteStatistics = async () => {
};
</script>
<style scoped lang="scss">
.case-templates-statistics-container {
display: flex;
flex-direction: column;
height: 91vh;
.toolbar {
height: 48px;
display: flex;
padding: 0 8px;
align-items: center;
}
.statistics-data-container{
height: calc(100% - 48px);
}
}
</style>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,66 @@
<template>
<div>
<el-table ref="medicineRef" height="400" :data="activityList" @cell-click="clickRow" border>
<el-table-column label="项目名称" align="center" prop="name" width="300" />
<el-table-column label="类型" align="center" prop="typeEnum_enumText" />
<el-table-column label="价格" align="right" prop="retailPrice">
<template #default="scope">
{{ scope.row.retailPrice.toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="说明" align="center" prop="descriptionText" />
</el-table>
</div>
</template>
<script setup>
import {getActivityList} from './api';
import {watch} from 'vue';
import {throttle} from 'lodash-es';
const props = defineProps({
searchKey: {
type: String,
default: '',
},
});
const emit = defineEmits(['selectRow']);
const queryParams = ref({
pageNum: 1,
pageSize: 50,
itemType: props.itemType,
});
const activityList = ref([]);
// 节流函数
const throttledGetList = throttle(
() => {
getList();
},
300,
{ leading: true, trailing: true }
);
watch(
() => props,
(newValue) => {
queryParams.value.searchKey = newValue.searchKey;
throttledGetList();
},
{ immdiate: true, deep: true }
);
getList();
function getList() {
getActivityList(queryParams.value).then((res) => {
activityList.value = res.data.records;
});
}
function clickRow(row) {
emit('selectRow', row);
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,72 @@
import request from '@/utils/request'
// 查询诊疗项目列表
export function getActivityList(queryParams) {
return request({
url: '/personalization/activity-device/activity-page',
method: 'get',
params: queryParams
})
}
// 查询诊疗用法绑定耗材项目列表
export function getBindList(queryParams) {
return request({
url: '/personalization/activity-device/activity-device',
method: 'get',
params: queryParams
})
}
// 获取耗材列表受限制的API只返回单次消耗类耗材
export function getDeviceList(queryParams) {
return request({
url: '/personalization/activity-device/device-page',
method: 'get',
params: queryParams
})
}
// 获取完整的器材目录列表使用原始API不限制categoryCode
export function getFullDeviceList(queryParams) {
return request({
url: '/data-dictionary/device/information-page',
method: 'get',
params: queryParams
})
}
// 获取耗材列表
export function init() {
return request({
url: '/personalization/activity-device/init',
method: 'get',
})
}
// 绑定用法/诊疗
export function bind(data) {
return request({
url: '/personalization/activity-device/activity-device',
method: 'post',
data: data
})
}
// 删除绑定
export function deleteBind(bindId) {
return request({
url: '/personalization/activity-device/activity-device?bindId=' + bindId,
method: 'delete',
})
}
// 查询服务管理列表
export function getRegistrationfeeList(query) {
return request({
url: '/basic-service/healthcare/healthcare-service-page',
method: 'get',
params: query
})
}

View File

@@ -0,0 +1,349 @@
<template>
<div style="width: 100%">
<div class="mb8">
<el-button type="primary" plain @click="handleAddPrescription()" :disabled="buttonDisabled">
添加绑定项目
</el-button>
</div>
<el-form :model="form" :rules="rules" ref="formRef">
<el-table
ref="prescriptionRef"
:data="form.consumablesList"
row-key="patientId"
border
@selection-change="handleSelectionChange"
v-loading="props.loading"
>
<el-table-column label="项目类型" align="center" prop="type" width="150">
<template #default="scope">
<span v-if="!scope.row.isEdit">{{ scope.row.type_dictText }}</span>
<el-form-item v-else :prop="`consumablesList.${scope.$index}.type`" :rules="rules.type">
<el-select v-model="scope.row.type" placeholder="" @change="handleTypeChange">
<el-option v-if="props.tab == 1 || props.tab == 3" label="诊疗" value="1" />
<el-option v-if="props.tab != 3" label="耗材" value="2" />
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="项目名" align="center" prop="name">
<template #default="scope">
<span v-if="!scope.row.isEdit">{{ scope.row.name }}</span>
<el-form-item v-else :prop="`consumablesList.${scope.$index}.name`" :rules="rules.name">
<PopoverList @search="handleSearch" :width="800" :modelValue="scope.row.name">
<template #popover-content="{}">
<DeviceList
v-if="scope.row.type == '2' || props.tab == 2"
@selectRow="(row) => selectRow(row, scope.$index)"
:searchKey="searchKey"
/>
<ActivityList
v-else
@selectRow="(row) => selectRow(row, scope.$index)"
:searchKey="searchKey"
/>
</template>
</PopoverList>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="绑定数量" align="center" prop="quantity" width="250">
<template #default="scope">
<span v-if="!scope.row.isEdit">{{ scope.row.quantity }}</span>
<el-form-item
v-else
:prop="`consumablesList.${scope.$index}.quantity`"
:rules="rules.quantity"
>
<el-input v-model="scope.row.quantity" placeholder="" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="单位" align="center" prop="unitCode_dictText" width="100">
<template #default="scope">
<span v-if="!scope.row.isEdit">{{ scope.row.unitCode_dictText }}</span>
<el-form-item
v-else
:prop="`consumablesList.${scope.$index}.unitCode`"
:rules="rules.unitCode"
>
<el-select v-model="scope.row.unitCode" placeholder="">
<el-option
v-for="(item, index) in scope.row.unitCodeList"
@click="handleUnitCodeClick(item)"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<!-- <el-table-column label="使用范围" align="center" prop="rangeCode" width="150">
<template #default="scope">
<span v-if="!scope.row.isEdit">{{ scope.row.rangeCode_dictText }}</span>
<el-form-item
v-else
:prop="`consumablesList.${scope.$index}.rangeCode`"
:rules="rules.rangeCode"
>
<el-input v-model="scope.row.rangeCode" placeholder="" />
</el-form-item>
</template>
</el-table-column> -->
<!-- <el-table-column label="范围" align="center" prop="rangeCode" width="250">
<template #default="scope">
<span v-if="!scope.row.isEdit">{{ scope.row.rangeCode_dictText }}</span>
<el-form-item
v-else
:prop="`consumablesList.${scope.$index}.rangeCode`"
:rules="rules.statusEnum"
>
<el-select v-model="scope.row.rangeCode" placeholder="">
<el-option
v-for="dict in use_range"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column> -->
<el-table-column label="启用状态" align="center" prop="statusEnum" width="250">
<template #default="scope">
<span v-if="!scope.row.isEdit">{{ scope.row.statusEnum_dictText }}</span>
<el-form-item
v-else
:prop="`consumablesList.${scope.$index}.statusEnum`"
:rules="rules.statusEnum"
>
<el-select v-model="scope.row.statusEnum" placeholder="" @change="handleTypeChange">
<el-option
v-for="item in statusOptions"
:key="item.value"
:label="item.info"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="250">
<template #default="scope">
<el-button
v-if="!scope.row.isEdit"
link
type="primary"
@click="handleEdit(scope.$index)"
>
编辑
</el-button>
<el-button
v-if="scope.row.isEdit"
link
type="primary"
@click="handleSave(scope.row, scope.$index)"
>
保存
</el-button>
<el-button
v-if="scope.row.isEdit"
link
type="primary"
@click="
() => {
form.consumablesList[scope.$index].isEdit = false;
isAdd = true;
if (!scope.row.id) {
form.consumablesList.splice(scope.$index, 1);
}
}
"
>
取消
</el-button>
<el-button link type="primary" @click.stop="handleDelete(scope.row, scope.$index)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
</div>
</template>
<script setup>
import {reactive, watch} from 'vue';
import {bind, deleteBind, init} from './api';
import PopoverList from '@/components/OpenHis/popoverList/index.vue';
import DeviceList from './deviceList.vue';
import ActivityList from './activityList.vue';
const emit = defineEmits(['refresh']);
const total = ref(0);
const queryParams = ref({});
const consumablesList = ref([]);
const searchKey = ref('');
const rowIndex = ref(-1);
const isAdd = ref(true);
const rules = ref({
name: [{ required: true, message: '请选择耗材项', trigger: 'change' }],
quantity: [{ required: true, message: '请输入绑定数量', trigger: 'blur' }],
rangeCode: [{ required: true, message: '请选择使用范围', trigger: 'blur' }],
});
const form = reactive({
consumablesList: [],
});
const statusOptions = ref([]);
const props = defineProps({
bindList: {
type: Object,
required: false,
},
bindInfo: {
type: Object,
required: false,
},
tab: {
type: String,
required: false,
},
loading: {
type: Boolean,
required: false,
},
});
const buttonDisabled = computed(() => {
return !props.bindInfo.id;
});
watch(
() => props.bindList,
(newVal) => {
form.consumablesList = newVal.map((item) => {
return {
id: item.id,
name: item.deviceName ? item.deviceName : item.activityName,
devActId: item.devActId,
quantity: item.quantity,
rangeCode: item.rangeCode,
rangeCode_dictText: item.rangeCode_dictText,
unitCode: item.unitCode,
unitCode_dictText: item.unitCode_dictText,
statusEnum: item.statusEnum,
statusEnum_dictText: item.statusEnum_enumText,
typeCode: item.typeCode,
typeCode_dictText: item.typeCode_dictText,
type: item.activityName ? '1' : '2',
type_dictText: item.activityName ? '诊疗' : '耗材',
};
});
isAdd.value = true;
},
{ deep: true }
);
watch(
() => props.tab,
() => {
form.consumablesList = [];
isAdd.value = true;
},
{ deep: true }
);
const { proxy } = getCurrentInstance();
const { use_range } = proxy.useDict('use_range');
function handleAddPrescription() {
if (!isAdd.value) {
proxy.$modal.msgWarning('请先保存当前行');
return;
}
form.consumablesList.push({
isEdit: true,
// type: '2',
rangeCode: '3',
statusEnum: 2,
});
isAdd.value = false;
console.log(form.consumablesList, 234567890);
}
function handleEdit(index) {
form.consumablesList[index].isEdit = true;
}
function handleSave(row, index) {
proxy.$refs['formRef'].validate((valid) => {
if (valid) {
console.log(row, 'row');
bind({
itemNo: props.bindInfo.id,
devActId: row.devActId,
typeCode: props.bindInfo.typeCode,
rangeCode: '3',
quantity: row.quantity,
unitCode: row.unitCode,
statusEnum: row.statusEnum,
id: row.id ? row.id : undefined,
devActTable: row.type == '1' ? 'wor_activity_definition' : 'adm_device_definition',
}).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('操作成功');
emit('refresh');
isAdd.value = true;
}
});
form.consumablesList[index].isEdit = false;
}
});
}
function handleDelete(row, index) {
if (row.id) {
proxy.$modal.confirm('确认删除当前绑定项目吗').then(() => {
deleteBind(row.id).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('删除成功');
emit('refresh');
}
});
});
} else {
form.consumablesList.splice(index, 1);
isAdd.value = true;
}
}
function initOptions() {
init().then((res) => {
statusOptions.value = res.data.statusOptions;
});
}
function handleUnitCodeClick(row, item) {
row.unitCode_dictText = item.label;
}
function handleSearch(value) {
searchKey.value = value;
}
initOptions();
function selectRow(row, index) {
form.consumablesList[index].devActId = row.id;
form.consumablesList[index].name = row.name;
if (row.minUnitCode == row.unitCode) {
form.consumablesList[index].unitCodeList = [
{ label: row.unitCode_dictText, value: row.unitCode },
];
} else {
form.consumablesList[index].unitCodeList = [
{ label: row.unitCode_dictText, value: row.unitCode },
{ label: row.minUnitCode_dictText, value: row.minUnitCode },
];
}
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,74 @@
<template>
<div>
<el-table ref="medicineRef" height="400" :data="deviceList" @cell-click="clickRow" border>
<el-table-column label="项目名称" align="center" prop="name" width="200" />
<el-table-column label="分类" align="center" prop="categoryCode_dictText" width="150" />
<el-table-column label="种类" align="center" prop="typeCode_dictText" />
<el-table-column label="规格" align="center" prop="size" />
<el-table-column label="价格" align="right" prop="retailPrice">
<template #default="scope">
{{ scope.row.retailPrice.toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="生产厂家" align="center" prop="supplyId_dictText" />
</el-table>
</div>
</template>
<script setup>
import {getFullDeviceList} from './api';
import {watch} from 'vue';
import {throttle} from 'lodash-es';
const props = defineProps({
searchKey: {
type: String,
default: '',
},
});
const emit = defineEmits(['selectRow']);
// 修改查询参数添加searchKey字段以匹配搜索框输入
const queryParams = reactive({
searchKey: '',
pageNo: 1, // 新API使用pageNo而不是pageNum
pageSize: 50
// 移除categoryCode参数避免任何可能的限制
});
const deviceList = ref([]);
// 节流函数
const throttledGetList = throttle(
() => {
getList();
},
300,
{ leading: true, trailing: true }
);
// 初始化时设置搜索关键字
queryParams.searchKey = props.searchKey;
watch(
() => props.searchKey,
(newValue) => {
queryParams.searchKey = newValue;
throttledGetList();
},
{ immediate: true }
);
getList();
function getList() {
// 使用新的不受限制的API不设置任何会限制查询范围的参数
getFullDeviceList(queryParams).then((res) => {
deviceList.value = res.data.records;
});
}
function clickRow(row) {
emit('selectRow', row);
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,125 @@
<template>
<div class="app-container">
<div style="height: 780px; display: flex; justify-content: space-between">
<el-card style="height: 100%; width: 25%">
<el-tabs v-model="activeTab">
<el-tab-pane label="用法" :name="1">
<el-table
:data="method_code"
border
@cell-click="clickRow"
highlight-current-row
max-height="650"
>
<el-table-column label="项目名" align="center" prop="label" />
</el-table>
</el-tab-pane>
<el-tab-pane label="诊疗" :name="2">
<el-input
v-model="queryParams.searchKey"
placeholder="请输入项目名"
clearable
style="width: 100%; margin-bottom: 10px"
@keyup.enter="getList"
>
<template #append>
<el-button icon="Search" @click="getList" />
</template>
</el-input>
<el-table
:data="activityList"
border
@cell-click="clickRow"
highlight-current-row
max-height="650"
>
<el-table-column label="项目名" align="center" prop="name" />
</el-table>
</el-tab-pane>
<el-tab-pane label="号源" :name="3">
<el-input
v-model="queryParamsRegistration.searchKey"
placeholder="请输入项目名"
clearable
style="width: 100%; margin-bottom: 10px"
@keyup.enter="getRegistrationList"
>
<template #append>
<el-button icon="Search" @click="getRegistrationList" />
</template>
</el-input>
<el-table
:data="RegistrationfeeList"
border
@cell-click="clickRow"
highlight-current-row
max-height="650"
>
<el-table-column label="项目名" align="center" prop="name" />
</el-table>
</el-tab-pane>
</el-tabs>
</el-card>
<el-card style="height: 100%; width: 74%">
<ConsumablesList
:bindList="bindList"
:bindInfo="bindInfo"
:tab="activeTab"
:loading="loading"
@refresh="clickRow(currentRow)"
/>
</el-card>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue';
import {getActivityList, getBindList, getRegistrationfeeList} from './components/api.js';
import ConsumablesList from './components/consumablesList.vue';
const activityList = ref([]);
const queryParams = ref({
statusEnum: 2,
});
const queryParamsRegistration = ref({
activeFlag: 1,
});
const bindList = ref([]);
const bindInfo = ref({});
const activeTab = ref(1);
const currentRow = ref({});
const RegistrationfeeList = ref([]);
const loading = ref(false);
const { proxy } = getCurrentInstance();
const { method_code } = proxy.useDict('method_code');
getList();
getRegistrationList();
function getList() {
// queryParams.value.typeEnum = activeTab.value;
getActivityList(queryParams.value).then((res) => {
activityList.value = res.data.records;
});
}
function getRegistrationList() {
getRegistrationfeeList(queryParamsRegistration.value).then((res) => {
RegistrationfeeList.value = res.data.records;
});
}
// 点击诊疗列表 获取绑定的耗材
function clickRow(row) {
loading.value = true;
currentRow.value = row;
bindInfo.value.id = row.value ? row.value : row.id;
bindInfo.value.typeCode = activeTab.value;
getBindList({ itemNo: row.value ? row.value : row.id }).then((res) => {
bindList.value = res.data.records;
loading.value = false;
});
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,223 @@
<template>
<el-dialog
title="添加频次详情"
v-model="dialogVisible"
width="520px"
top="8vh"
:close-on-click-modal="false"
@close="resetForm"
>
<!-- 表单区域 -->
<el-form
:model="formData"
ref="form"
label-width="120px"
class="freq-form"
>
<!-- 字典代码 -->
<el-form-item label="字典代码">
<el-input
v-model="formData.rateCode"
readonly
class="input-readonly"
/>
</el-form-item>
<!-- 分割线 -->
<el-divider content-position="left">每日执行</el-divider>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="次数">
<el-input-number
v-model="formData.dayCount"
:min="1"
:max="99"
controls-position="right"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="间隔(天)">
<el-input-number
v-model="formData.dayInterval"
:min="0"
:max="99"
controls-position="right"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="时间点">
<el-select v-model="formData.dayTimes" multiple placeholder="选择时间点">
<el-option
v-for="time in timeOptions"
:key="time"
:value="time"
:label="time"
/>
</el-select>
</el-form-item>
<!-- &lt;!&ndash; 分割线 &ndash;&gt;-->
<!-- <el-divider content-position="left">每周执行</el-divider>-->
<!-- <el-row :gutter="16">-->
<!-- <el-col :span="12">-->
<!-- <el-form-item label="是否启用">-->
<!-- <el-switch-->
<!-- v-model="formData.weekCycleFlag"-->
<!-- :active-value="1"-->
<!-- :inactive-value="0"-->
<!-- active-text="是"-->
<!-- inactive-text="否"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <el-form-item label="间隔(周)">-->
<!-- <el-input-number-->
<!-- v-model="formData.weekInterval"-->
<!-- :min="1"-->
<!-- :max="52"-->
<!-- controls-position="right"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-form-item label="周次数">-->
<!-- <el-input-number-->
<!-- v-model="formData.weekTimes"-->
<!-- :min="1"-->
<!-- :max="7"-->
<!-- controls-position="right"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- 分割线 -->
<el-divider content-position="left">周期与总计</el-divider>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="总次数">
<el-input-number
v-model="formData.totalExecutionCount"
:min="1"
:max="999"
controls-position="right"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="周期长度">
<el-input-number
v-model="formData.executionPeriod"
:min="1"
:max="999"
controls-position="right"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="周期单位">
<el-select v-model="formData.executionPeriodUnit" style="width: 100%">
<el-option label="分钟" value="minute" />
<el-option label="天" value="day" />
<el-option label="周" value="week" />
<el-option label="月" value="month" />
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input
type="textarea"
v-model="formData.memo"
:rows="2"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<!-- 底部按钮 -->
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</template>
</el-dialog>
</template>
<style scoped>
.freq-form {
padding: 0 10px;
}
.input-readonly {
background-color: #f5f7fa;
color: #c0c4cc;
}
</style>
<script setup>
import {defineEmits, defineProps, ref, watch} from "vue";
const props = defineProps({
modelValue: { type: Boolean, default: false },
initialFormData: { type: Object, default: () => ({}) }
})
const emit = defineEmits(['update:modelValue', 'submit'])
const formData = ref({
rateCode: '',
name: '',
dayCount: 0,
dayInterval: 0,
dayTimes: [],
weekCycleFlag: 0,
weekInterval: 0,
weekTimes: 0,
continueFlag: 0,
totalExecutionCount: 0,
executionPeriod: 0,
executionPeriodUnit: 'day',
memo: ''
});
const generateTimeOptions = () => {
const options = [];
for (let hour = 0; hour < 24; hour++) {
for (let minute = 0; minute <= 59; minute += 30) {
const time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
options.push(time);
}
}
return options;
};
const timeOptions = ref(generateTimeOptions());
// 计算属性:让 el-dialog 直接绑定 v-model
const dialogVisible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
function resetForm() {
formData.value = { ...props.initialFormData }
}
function submitForm() {
emit('submit', formData.value)
dialogVisible.value = false // 关闭
}
watch(
() => props.initialFormData,
(newVal) => { formData.value = { ...newVal }; },
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,27 @@
import request from '@/utils/request'
// 初始化
export function getFrequencyDetailList(query) {
return request({
url: '/personalization/frequency/getPageList',
method: 'get',
params: query
})
}
// 新增
export function addFrequencyDetail(data) {
return request({
url: '/personalization/frequency/save',
method: 'post',
data: data
})
}
// 删除
export function deleteFrequencyDetail(data) {
return request({
url: '/personalization/frequency/' + data,
method: 'delete',
})
}

View File

@@ -0,0 +1,342 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!--诊疗目录-->
<el-col :span="4" :xs="24">
<div class="head-title">频次字典</div>
<div>
<el-table
ref="freTableRef"
v-loading="loading"
:data="frequency"
row-key="id"
@row-click="handleRowClick"
>
<el-table-column label="字典频次名称" align="left" prop="dictLabel" />
<el-table-column label="字典频次代码" align="left" prop="dictValue" />
</el-table>
<pagination
v-show="frequencyTotal > 0"
:total="frequencyTotal"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getFrequencyList"
/>
</div>
</el-col>
<el-col :span="20" :xs="24">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="openDialog"
v-hasPermi="['system:user:add']"
>添加频次详情</el-button
>
</el-col>
</el-row>
<el-table
v-loading="loading"
:data="frequencyDetail"
>
<!-- 定义表格列 -->
<el-table-column
prop="rateCode"
label="频次代码"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="名称"
width="200">
</el-table-column>
<el-table-column
prop="dayCount"
label="每天执行次数"
width="120">
</el-table-column>
<!-- <el-table-column-->
<!-- prop="dayInterval"-->
<!-- label="每次执行间隔(天数)"-->
<!-- width="180">-->
<!-- </el-table-column>-->
<el-table-column
prop="dayTimes"
label="执行时间点"
width="300">
</el-table-column>
<!-- <el-table-column-->
<!-- prop="weekCycleFlag"-->
<!-- label="是否周期性每周执行"-->
<!-- width="150">-->
<!-- <template #default="scope">-->
<!-- {{ scope.row.weekCycleFlag === 1 ? '是' : '否' }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- prop="weekInterval"-->
<!-- label="每周执行的间隔"-->
<!-- width="150">-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- prop="weekTimes"-->
<!-- label="每周执行的次数"-->
<!-- width="120">-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- prop="continueFlag"-->
<!-- label="是否为连续执行"-->
<!-- width="150">-->
<!-- <template #default="scope">-->
<!-- {{ scope.row.continueFlag === 1 ? '是' : '否' }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column
prop="totalExecutionCount"
label="执行总次数"
width="120">
</el-table-column>
<el-table-column
prop="executionPeriod"
label="执行周期长度"
width="150">
</el-table-column>
<el-table-column
prop="executionPeriodUnit"
label="执行周期单位"
width="150">
</el-table-column>
<!-- <el-table-column-->
<!-- prop="memo"-->
<!-- label="备注信息"-->
<!-- width="200">-->
<!-- </el-table-column>-->
<el-table-column
label="操作"
align="center"
width="150"
class-name="small-padding fixed-width"
fixed="right"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Edit"
@click="openSave(scope.row, scope.$index)"
v-hasPermi="['system:user:edit']"
>编辑</el-button
>
<el-button
type="danger"
link
icon="Delete"
:disabled="scope.row.id == ''"
@click="deleteSelectedRows(scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- <pagination-->
<!-- v-show="frequencyDetailTotal > 0"-->
<!-- :total="frequencyDetailTotal"-->
<!-- v-model:page="queryDetailParams.pageNum"-->
<!-- v-model:limit="queryDetailParams.pageSize"-->
<!-- @pagination="getDetailList"-->
<!-- />-->
</el-col>
</el-row>
<FreForm
v-model="dialogVisible"
:initialFormData="showFormData"
@submit="handleFormSubmit"
/>
</div>
</template>
<script setup name="frequency">
import {addFrequencyDetail, deleteFrequencyDetail, getFrequencyDetailList} from './components/frequency.js';
import {ElMessage} from "element-plus";
import {listData} from "@/api/system/dict/data.js";
import FreForm from "@/views/basicmanage/frequency/components/FreForm.vue";
const frequency = ref([]);
const frequencyDetail = ref([]);
const frequencyTotal = ref(0);
const frequencyDetailTotal = ref(0);
const currentLeftRow = ref(null);
let addFlag = ref(true);
const data = reactive({
form: {},
queryParams: {
dictType: 'rate_code',
status : 0,
pageNum: 1,
pageSize: 10,
},
queryDetailParams: {
code: '',
status : 0,
pageNum: 1,
pageSize: 10,
}
});
const dialogVisible = ref(false);
const showFormData = ref({}); // 真正给弹窗用的数据
// 初始表单数据
const { queryParams, form, tableRules,queryDetailParams } = toRefs(data);
/** 获取字典列表 */
function getFrequencyList() {
listData(queryParams.value).then((res) => {
if (res.code === 200) {
frequency.value =
res.rows.length > 0
? res.rows.map((res) => {
return {
...res,
isEditing: false, // 标记当前行是否正在编辑
error: false, // 新增 error 字段
};
})
: [];
}
frequencyTotal.value = res.total;
});
}
function getDetailList() {
getFrequencyDetailList(queryDetailParams.value).then((res) => {
if (res.code === 200) {
frequencyDetail.value =
res.data.records.length > 0
? res.data.records.map((record) => {
// 将 dayTimes 字符串转换为数组
const dayTimesArray = record.dayTimes.split(',');
const sortedDayTimesArray = dayTimesArray.sort((a, b) => {
const timeToMinutes = (time) => {
const [hours, minutes] = time.split(':').map(Number);
return hours * 60 + minutes;
};
return timeToMinutes(a) - timeToMinutes(b);
});
const sortedDayTimesString = sortedDayTimesArray.join(',');
return {
...record,
dayTimes: sortedDayTimesString, // 使用排序后的字符串
error: false, // 新增 error 字段
};
})
: [];
}
if( res.data.records.length > 0){
//有数据不允许添加
addFlag.value = false;
}else{
addFlag.value = true;
}
frequencyDetailTotal.value = res.data.total;
});
}
/** 节点单击事件 */
function handleRowClick(row) {
queryDetailParams.value.code = row.dictValue;
currentLeftRow.value = row; // 记录当前行
getDetailList();
}
function openDialog() {
if (!currentLeftRow?.value) {
ElMessage.warning('请先选择一行字典数据!')
return;
}
if (addFlag.value === false) {
ElMessage.warning('已有详情!请直接修改!')
addFlag.value = true;
return;
}
//默认显示数据
showFormData.value = {
rateCode: currentLeftRow.value.dictValue,
name : currentLeftRow.value.dictLabel,
dayCount: 0,
dayInterval: 0,
dayTimes: [],
weekCycleFlag: 0,
weekInterval: 0,
weekTimes: 0,
continueFlag: 0,
totalExecutionCount: 0,
executionPeriod: 0,
executionPeriodUnit: 'day',
memo: ''
};
dialogVisible.value = true;
}
// 处理表单提交
/* 保存/新增 */
function handleFormSubmit(initialFormData) {
addFrequencyDetail(initialFormData).then(() => {
getDetailList();
ElMessage.success('保存成功');
});
}
function openSave(row) {
//默认显示数据
showFormData.value = { ...row };
row.dayTimes = row.dayTimes.split(',');
row.dayTimes.sort((a, b) => {
// 将时间字符串转换为分钟数进行比较
const timeToMinutes = (time) => {
const [hours, minutes] = time.split(':').map(Number);
return hours * 60 + minutes;
};
return timeToMinutes(a) - timeToMinutes(b);
});
// 更新 showFormData 中的 dayTimes
showFormData.value.dayTimes = row.dayTimes;
dialogVisible.value = true;
}
/* 删除 */
function deleteSelectedRows(row) {
deleteFrequencyDetail(row.id).then(() => {
ElMessage.success('删除成功');
getDetailList();
});
}
getFrequencyList();
</script>
<style scoped>
.el-form--inline .el-form-item {
display: inline-flex;
vertical-align: middle;
margin-right: 10px !important;
}
.error-border {
border: 1px solid red;
}
</style>

View File

@@ -0,0 +1,192 @@
<template>
<div class="LaboratoryTests-container">
<el-dialog
title="批量导入项目"
:model-value="props.dialogVisible"
@update:modelValue="(val) => emit('update:dialogVisible', val)"
width="842"
append-to-body
destroy-on-close
:draggable="true"
>
<el-row :gutter="10">
<el-col :span="24">
<el-form ref="formEl" :rules="rules" :model="form" label-width="100px">
<el-form-item label="项目类型" prop="clinicalType">
<el-select
v-model="form.clinicalType"
placeholder="请选择"
@change="getList()"
filterable
clearable
style="width: 200px"
>
<el-option
v-for="item in categoryCodeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-row :gutter="30" style="margin-top: 30px">
<el-col :span="24">
<el-transfer
v-model="transferValue"
:data="applicationList"
filter-placeholder="项目代码/名称"
filterable
:titles="['未选择', '已选择']"
style="width: 100%"
/>
</el-col>
</el-row>
<template #footer>
<div class="dialog-footer">
<el-button @click="onCancel">取消</el-button>
<el-button type="primary" @click="onConfirm"> 确认 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="LaboratoryTests">
import {getCurrentInstance, onMounted, reactive, ref} from 'vue';
import {addImplementDepartment, editImplementDepartment, getApplicationList,} from './implementDepartment.js';
const { proxy } = getCurrentInstance();
// 属性
const props = defineProps({
// 弹框显示信息
dialogVisible: {
type: Boolean,
default: false,
},
// 选择科室
organizationId: {
type: String,
default: '',
},
});
const emit = defineEmits(['update:dialogVisible', 'confirm', 'cancel', 'submitOk']);
// 申请项目列表
const applicationListAll = ref();
// 获取所有申请项目
const applicationList = ref();
// 申请项目列表
const transferValue = ref([]);
// 项目类型
const formEl = ref();
const form = reactive({ clinicalType: '23' });
// 加载中
const loading = ref(false);
// 弹窗显隐由父组件控制,通过 v-model 事件同步
// 项目类型
const categoryCodeList = ref([
{ label: '治疗', value: '21' },
{ label: '检验', value: '22' },
{ label: '检查', value: '23' },
{ label: '手术', value: '24' },
]);
// 规则校验
const rules = ref({
clinicalType: [{ required: true, message: '请选择项目类型', trigger: 'change' }],
});
const getList = () => {
if (!form.clinicalType) {
return;
}
loading.value = true;
getApplicationList({
pageSize: 10000,
pageNum: 1,
// 项目类型 枚举类中
categoryCode: form.clinicalType,
adviceTypes: '3', //1 药品 2耗材 3诊疗
}).then((res) => {
if (res.code === 200) {
loading.value = false;
applicationListAll.value = res.data.records;
applicationList.value = res.data.records.map((item) => {
return {
label: item.adviceName + item.adviceDefinitionId,
key: item.adviceDefinitionId,
};
});
} else {
proxy.$message.error(res.message);
applicationList.value = [];
}
});
};
function onCancel() {
emit('cancel');
emit('update:dialogVisible', false);
}
// 批量添加
async function onConfirm() {
if (!formEl) return;
formEl.value.validate(async (valid) => {
if (!valid) return;
if (!transferValue.value || transferValue.value.length === 0) {
proxy.$message.error('请选择至少一个项目');
return;
}
const requests = (transferValue.value || []).map((defId) => {
const params = {
// 诊疗定义id
activityDefinitionId: defId,
// 科室ID
organizationId: props.organizationId,
// 诊疗类型
activityCategoryCode: form.clinicalType,
// 开始时间
startTime: '00:00:00',
// 结束时间
endTime: '23:59:59',
/*
方法类别DistributionCategoryCodeEnum 枚举
desription: 8代表诊疗
*/
distributionCategoryCode: '8',
};
return handleSave(params);
});
await Promise.all(requests);
emit('submitOk');
emit('update:dialogVisible', false);
transferValue.value = [];
});
}
// 保存
async function handleSave(params) {
if (params.id) {
return editImplementDepartment(params).then((res) => {
proxy.$modal.msgSuccess('保存成功!');
return res;
});
} else {
delete params.id;
return addImplementDepartment(params).then((res) => {
proxy.$modal.msgSuccess('保存成功!');
return res;
});
}
}
onMounted(() => {
getList();
});
</script>
<style lang="scss" scoped>
:deep(.el-transfer-panel) {
width: 300px;
}
</style>

View File

@@ -0,0 +1,83 @@
import request from '@/utils/request';
// 初始化
export function getImplementDepartmentList (query) {
return request ({
url: '/base-data-manage/organization/organization',
method: 'get',
params: query,
});
}
// 查询诊疗目录列表
export function getDiagnosisTreatmentList (query) {
return request ({
url: '/base-data-manage/org-loc/org-loc',
method: 'get',
params: query,
});
}
//查询诊疗目录详细
export function getImplementDepartmentOne (query) {
return request ({
url: '/data-dictionary/diagnosis-treatment/information-page',
method: 'get',
params: query, // 确保参数正确传递
});
}
// 新增
export function addImplementDepartment (data) {
return request ({
url: '/base-data-manage/org-loc/org-loc',
method: 'post',
data: data,
});
}
// 修改
export function editImplementDepartment (data) {
return request ({
url: '/base-data-manage/org-loc/org-loc',
method: 'post',
data: data,
});
}
// 删除
export function deleteImplementDepartment (data) {
return request ({
url: '/base-data-manage/org-loc/org-loc?orgLocId=' + data.orgLocId,
method: 'delete',
});
}
// 目录分类查询
export function getDiseaseTreatmentInit () {
return request ({
url: '/data-dictionary/diagnosis-treatment/init',
method: 'get',
});
}
// 目录分类子查询
export function getDiseaseTreatmentInitLoc (id) {
return request ({
url: '/data-dictionary/diagnosis-treatment/information-one?id=' + id,
method: 'get',
});
}
//医嘱大下拉
export function getApplicationList (queryParams) {
return request ({
url: '/doctor-station/advice/advice-base-info',
method: 'get',
params: queryParams,
});
}
// 获取所有诊疗项目
export function getAllTreatmentList () {
return request ({
url: '/app-common/activity-definition',
method: 'get',
});
}

View File

@@ -0,0 +1,541 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!--诊疗目录-->
<el-col :span="4" :xs="24">
<div class="head-title">执行科室配置</div>
<div class="head-container">
<el-tree
:data="organization"
:props="{ label: 'name', children: 'children' }"
:expand-on-click-node="true"
:filter-node-method="filterNode"
ref="treeRef"
node-key="id"
highlight-current
check-strictly
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-col>
<!--诊疗目录-->
<el-col :span="20" :xs="24">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
:disabled="isAddDisable"
@click="handleAddItem"
v-hasPermi="['system:user:add']"
>添加新项目</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Plus"
:disabled="isAddDisable"
@click="handleBacthAddItem"
v-hasPermi="['system:user:add']"
>批量新增项目配置</el-button
>
</el-col>
</el-row>
<el-table
v-loading="loading"
:data="catagoryList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="100" align="center" />
<el-table-column
label="诊疗目录"
width="150"
align="center"
:show-overflow-tooltip="true"
>
<template #default="scope">
<el-select
v-model="scope.row.activityCategoryCode"
placeholder="请选择"
ref="dropdown"
:class="{ 'error-border': scope.row.error }"
clearable
>
<el-option
v-for="dict in catagoryDicts"
:key="dict.value"
:label="dict.info"
:value="dict.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="项目名称" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<el-select
v-model="scope.row.activityDefinitionId"
filterable
remote
:remote-method="(query) => handleRemoteQuery(query, scope.row)"
:loading="scope.row.loading"
placeholder="请输入并搜索项目"
style="width: 400px; max-width: 500px"
:class="{ 'error-border': scope.row.error }"
clearable
>
<el-option
v-for="item in scope.row.filteredOptions || []"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column
label="开始时间"
align="center"
key="startTime"
prop="startTime"
:show-overflow-tooltip="true"
>
<template #default="scope">
<el-time-picker
v-model="scope.row.startTime"
placeholder="选择时间"
format="HH:mm:ss"
value-format="HH:mm:ss"
>
</el-time-picker>
</template>
</el-table-column>
<el-table-column
label="结束时间"
align="center"
key="endTime"
prop="endTime"
show-overflow-tooltip
>
<template #default="scope">
<el-time-picker
v-model="scope.row.endTime"
placeholder="选择时间"
format="HH:mm:ss"
value-format="HH:mm:ss"
>
</el-time-picker>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="150"
class-name="small-padding fixed-width"
fixed="right"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Edit"
@click="openSaveImplementDepartment(scope.row, scope.$index)"
v-hasPermi="['system:user:edit']"
>保存</el-button
>
<el-button
type="danger"
link
icon="Delete"
:disabled="scope.row.id == ''"
@click="deleteSelectedRows(scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
<BacthAddItemDialog
v-model:dialogVisible="bacthAddItemDialogVisible"
:organizationId="organizationId"
@submitOk="getList"
/>
</div>
</template>
<script setup name="implementDepartment">
import {ref} from 'vue';
import {
addImplementDepartment,
deleteImplementDepartment,
editImplementDepartment,
getAllTreatmentList,
getDiagnosisTreatmentList,
getDiseaseTreatmentInit,
getImplementDepartmentList,
} from './components/implementDepartment';
import BacthAddItemDialog from './components/batchAddDialog.vue';
const { proxy } = getCurrentInstance();
const organization = ref([]);
const loading = ref(true);
const ids = ref([]); // 存储选择的行数据
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const catagoryList = ref([]);
const catagoryDicts = ref([]);
const isAddDisable = ref(true);
const organizationId = ref('');
// 批量添加
const bacthAddItemDialogVisible = ref(false);
//默认传8(诊疗)
const distributionCategoryCode = ref('8');
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 10,
},
tableRules: {
activityCategoryCode: [{ required: true, message: '诊疗目录不能为空', trigger: 'blur' }],
activityDefinitionId: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
},
isAdding: false,
});
const { queryParams, tableRules } = toRefs(data);
/** 查询表格数据列表 */
function getList() {
queryParams.value.organizationId = organizationId.value;
queryParams.value.distributionCategoryCode = distributionCategoryCode.value;
loading.value = true;
getDiagnosisTreatmentList(queryParams.value).then((res) => {
loading.value = false;
catagoryList.value = res.data.records.map(record => {
const filteredOptions = allImplementDepartmentList.value.slice(0, 100);
// 确保后端返回的项目名称选项存在于 filteredOptions 中,避免 el-select 因找不到选项而回显为 ID
if (record.activityDefinitionId && !filteredOptions.some(o => o.value === record.activityDefinitionId)) {
filteredOptions.push({
value: record.activityDefinitionId,
label: record.activityDefinitionId_dictText || record.activityDefinitionId
});
}
return {
...record,
loading: false,
filteredOptions: filteredOptions
};
});
total.value = res.data.total;
});
}
/** 通过条件过滤节点 */
const filterNode = (value, data) => {
if (!value) return true;
return data.name.indexOf(value) !== -1;
};
// 所有诊疗项目列表
const allImplementDepartmentList = ref([]);
async function getAllImplementDepartment() {
loading.value = true;
try {
const res = await getAllTreatmentList();
allImplementDepartmentList.value = res.data.map((item) => ({
value: item.activityDefinitionId,
label: item.activityDefinitionName,
}));
// 为所有现有行初始化过滤选项(使用防抖处理,避免频繁更新)
if (catagoryList.value && catagoryList.value.length > 0) {
// 使用 setTimeout 将 DOM 更新推迟到下一个事件循环,避免阻塞
setTimeout(() => {
catagoryList.value.forEach(row => {
if (!row.hasOwnProperty('filteredOptions')) {
row.filteredOptions = allImplementDepartmentList.value.slice(0, 100); // 限制为前100个
row.loading = false;
}
});
}, 0);
}
loading.value = false;
} catch (error) {
console.error('获取诊疗项目列表失败:', error);
loading.value = false;
proxy.$message.error('获取诊疗项目列表失败');
}
}
/** 选择条数 */
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
// 远程搜索处理函数
function handleRemoteQuery(query, row) {
if (query !== '') {
// 设置加载状态
row.loading = true;
// 模拟异步延迟
setTimeout(() => {
// 确保数据已加载
if (!allImplementDepartmentList.value || allImplementDepartmentList.value.length === 0) {
row.filteredOptions = [];
row.loading = false;
return;
}
// 过滤选项,限制结果数量以提高性能
const filtered = allImplementDepartmentList.value.filter(item => {
return item.label.toLowerCase().includes(query.toLowerCase()) ||
item.value.toLowerCase().includes(query.toLowerCase());
});
// 限制返回结果数量,避免过多选项导致性能问题
row.filteredOptions = filtered.slice(0, 100); // 限制为前100个匹配项
row.loading = false;
}, 300); // 300ms 延迟,模拟网络请求
} else {
// 如果查询为空,显示所有选项(但限制数量以提高性能)
if (allImplementDepartmentList.value && allImplementDepartmentList.value.length > 0) {
row.filteredOptions = allImplementDepartmentList.value.slice(0, 100); // 限制为前100个
} else {
row.filteredOptions = [];
}
}
}
// 新增项目
function handleAddItem() {
if (data.isAdding) {
proxy.$message.warning('请先保存当前行后再新增!');
return;
}
// 确保 allImplementDepartmentList 已经初始化
if (!allImplementDepartmentList.value || allImplementDepartmentList.value.length === 0) {
proxy.$message.warning('正在加载数据,请稍后再试!');
// 如果数据还未加载完成,尝试重新加载
getAllImplementDepartment();
return;
}
const newRow = {
startTime: '00:00:00',
endTime: '23:59:59',
loading: false, // 添加加载状态
filteredOptions: allImplementDepartmentList.value.slice(0, 100), // 初始化过滤选项,限制数量
};
catagoryList.value.push(newRow);
total.value = catagoryList.value.length; // 修正:使用实际数据列表长度,而不是组织结构长度
data.isAdding = true; // 设置标志位为 true表示有未保存的
}
// 批量添加
function handleBacthAddItem() {
// 批量添加显示对话框
bacthAddItemDialogVisible.value = true;
}
// 检验 编辑或 保存数据
function handleBlur(row, index) {
let hasError = false;
const fields = ['activityCategoryCode', 'activityDefinitionId', 'startTime', 'endTime'];
fields.forEach((field) => {
if (!row[field]) {
hasError = true;
const message = tableRules.value[field]?.[0]?.message;
if (message) {
proxy.$message.error(message);
} else {
proxy.$message.error(`检验未通过`);
}
}
});
row.error = hasError;
}
// 编辑或 保存当前行
function openSaveImplementDepartment(row) {
const params = {
// 科室id
organizationId: organizationId.value,
// 类别
distributionCategoryCode: distributionCategoryCode.value,
...row,
};
let hasError = false;
handleBlur(row);
if (row.error) {
hasError = true;
return;
}
const startTime = params.startTime;
const endTime = params.endTime;
if (startTime > endTime) {
proxy.$message.error('开始时间不能大于结束时间');
return;
}
if (hasError) {
proxy.$message.error('请填写完整信息');
return;
}
if (row.id) {
editImplementDepartment(params).then((res) => {
data.isAdding = false; // 允许新增下一行
proxy.$modal.msgSuccess('保存成功!');
// 确保选中项在 filteredOptions 中,使 el-select 能正确显示名称
const savedItem = allImplementDepartmentList.value.find(i => i.value === row.activityDefinitionId);
if (savedItem && !row.filteredOptions.some(o => o.value === row.activityDefinitionId)) {
row.filteredOptions.push(savedItem);
}
getList();
});
} else {
delete params.id;
addImplementDepartment(params).then((res) => {
data.isAdding = false; // 允许新增下一行
proxy.$modal.msgSuccess('保存成功!');
// 确保选中项在 filteredOptions 中,使 el-select 能正确显示名称
const savedItem = allImplementDepartmentList.value.find(i => i.value === row.activityDefinitionId);
if (savedItem && !row.filteredOptions.some(o => o.value === row.activityDefinitionId)) {
row.filteredOptions.push(savedItem);
}
getList();
});
}
}
// 删除当前所选行
function deleteSelectedRows(row) {
proxy.$modal.confirm('是否删除选中数据').then(() => {
if (row.id) {
deleteImplementDepartment({ orgLocId: row.id }).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('删除成功');
}
}).catch(() => {
proxy.$modal.msgError('删除失败');
});
} else {
catagoryList.value.pop();
proxy.$modal.msgSuccess('删除成功');
}
data.isAdding = false;
getList();
});
}
/** 节点单击事件 */
function handleNodeClick(res, node) {
// 检查是否有未保存的数据
if (data.isAdding) {
proxy.$modal.confirm('当前有未保存的数据,切换节点将丢失未保存的数据,是否继续?').then(() => {
// 确认切换,重置状态
data.isAdding = false;
continueHandleNodeClick(node);
}).catch(() => {
// 取消切换,保持当前状态
return;
});
} else {
continueHandleNodeClick(node);
}
}
// 实际的节点点击处理逻辑
function continueHandleNodeClick(node) {
// 新增按钮是否 disable
isAddDisable.value = false;
// 检查节点是否有子节点
if (node.data.children && node.data.children.length > 0) {
// proxy.$message.warning("不能选择父节点");
return;
}
// 选中科室id
organizationId.value = node.data.id;
// 获取 右侧 table 信息
getList();
}
/** 目录分类查询 */
async function getDiseaseTreatmentList() {
loading.value = true;
try {
const { data } = await getDiseaseTreatmentInit();
// 分类目录初始化获取
catagoryDicts.value = data.diagnosisCategoryOptions.sort((a, b) => {
return parseInt(a.value) - parseInt(b.value);
});
} catch (error) {
console.error('获取疾病治疗初始化数据失败:', error);
proxy.$message.error('获取分类目录失败');
}
// 诊疗目录分类查询下拉树结构
await getImplDepartList();
loading.value = false;
}
// 诊疗目录分类查询下拉树结d构
async function getImplDepartList() {
try {
// 只查询科室类型typeEnum=2不包含专业等其他类型
const res = await getImplementDepartmentList({ typeEnum: 2 });
if (res.code === 200) {
if (res.data.records.length > 0) {
organization.value = res.data.records.map((res) => {
return {
...res,
isEditing: false, // 标记当前行是否正在编辑
error: false, // 新增 error 字段
};
});
} else {
organization.value = [];
}
} else {
proxy.$modal.msgError(res.code);
}
} catch (error) {
console.error('获取实施部门列表失败:', error);
proxy.$message.error('获取科室信息失败');
}
}
onMounted(async () => {
try {
// 并行加载数据,提高效率
await Promise.all([
getAllImplementDepartment(),
getDiseaseTreatmentList()
]);
} catch (error) {
console.error('初始化数据加载失败:', error);
proxy.$message.error('数据加载失败,请稍后重试');
}
});
</script>
<style scoped>
.el-form--inline .el-form-item {
display: inline-flex;
vertical-align: middle;
margin-right: 10px !important;
}
.error-border {
border: 1px solid red;
}
</style>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,928 @@
<template>
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="1300px" @close="handleDialogClose">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<div class="dialog-top-row">
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" style="width: 220px" />
</el-form-item>
<el-form-item label="使用范围" v-if="showRangeSelector && !isEdit">
<el-select
v-model="rangeSelectValue"
placeholder="个人/科室/全院"
style="width: 220px"
@change="handleRangeChange"
>
<el-option label="个人" value="personal" />
<el-option label="科室" value="department" />
<el-option label="全院" value="hospital" />
</el-select>
</el-form-item>
<el-form-item
label="参与者"
prop="practitionerId"
v-if="currentTab === 'personal' && !isDoctorStation && !showRangeSelector"
>
<el-select
v-model="formData.practitionerId"
placeholder="请选择参与者"
clearable
style="width: 220px"
>
<el-option
v-for="item in participantListOptions"
:key="item.practitionerId"
:label="item.practitionerName"
:value="item.practitionerId"
/>
</el-select>
</el-form-item>
<el-form-item label="科室" prop="organizationId" v-if="currentTab === 'department'">
<el-tree-select
clearable
v-model="formData.organizationId"
:data="organization"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
default-expand-all
placeholder="请选择科室"
style="width: 220px"
:render-after-expand="false"
@change="handleOrgChange"
/>
</el-form-item>
</div>
</el-form>
<div style="margin-bottom: 10px">
<el-button type="primary" @click="handleAddRow">新增</el-button>
<el-button @click="handleCombineGroup">组合</el-button>
<el-button @click="handleSplitGroup">拆组</el-button>
</div>
<el-table
max-height="650"
ref="prescriptionRef"
:data="prescriptionList"
row-key="uniqueKey"
border
@cell-click="clickRow"
@selection-change="handleSelectionChange"
:expand-row-keys="expandOrder"
>
<el-table-column type="selection" width="55" :selectable="isRowSelectable" />
<el-table-column label="组" align="center" width="60">
<template #default="scope">
<span v-if="scope.row.groupId">{{ getGroupIcon(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column label="类型" align="center" width="120">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<el-radio-group v-model="scope.row.therapyEnum" size="small">
<el-radio-button label="1">长期</el-radio-button>
<el-radio-button label="2">临时</el-radio-button>
</el-radio-group>
</template>
<span v-else>
{{
scope.row.therapyEnum == '1' ? '长期' : scope.row.therapyEnum == '2' ? '临时' : '-'
}}
</span>
</template>
</el-table-column>
<el-table-column label="医嘱" align="center" prop="productName" width="300">
<template #default="scope">
<template v-if="getRowDisabled(scope.row)">
<el-popover
:popper-style="{ padding: '0' }"
placement="bottom-start"
:visible="scope.row.showPopover"
:width="1200"
>
<adviceBaseList
ref="adviceTableRef"
:popoverVisible="scope.row.showPopover"
:adviceQueryParams="adviceQueryParams"
@selectAdviceBase="(row) => selectAdviceBase(scope.row.uniqueKey, row)"
/>
<template #reference>
<el-input
:ref="'adviceRef' + scope.$index"
style="width: 100%"
v-model="scope.row.adviceName"
placeholder="请选择项目"
@input="(value) => handleInput(value, scope.row, scope.$index)"
@click="handleFocus(scope.row, scope.$index)"
@keyup.enter.stop="handleFocus(scope.row, scope.$index)"
@keydown="
(e) => {
if (!scope.row.showPopover) return;
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
e.preventDefault();
adviceTableRef.handleKeyDown(e);
}
}
"
@blur="handleBlur(scope.row)"
/>
</template>
</el-popover>
</template>
<span v-else>{{ scope.row.adviceName }}</span>
</template>
</el-table-column>
<el-table-column label="单次剂量" align="center" width="250" prop="sortNumber">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<template v-if="scope.row.adviceType == 1 || !scope.row.adviceDefinitionId">
<!-- 新增/未保存行统一按"数量 + 单位 = 剂量 + 单位"展示 -->
<el-input
style="width: 70px; margin-right: 10px"
v-model="scope.row.doseQuantity"
@input="
(value) => {
scope.row.dose = value * scope.row.unitConversionRatio;
}
"
/>
<span>
{{
scope.row.minUnitCode_dictText ||
scope.row.unitCodeName ||
scope.row.unitCode_dictText ||
''
}}
</span>
<span>{{ ' = ' }}</span>
<el-input
style="width: 70px; margin-right: 10px"
v-model="scope.row.dose"
@input="
(value) => {
scope.row.doseQuantity = value / scope.row.unitConversionRatio;
}
"
/>
<span>
{{
scope.row.doseUnitCode_dictText ||
scope.row.unitCodeName ||
scope.row.unitCode_dictText ||
''
}}
</span>
</template>
<span v-else>-</span>
</template>
<template v-else>
<span v-if="scope.row.adviceType == 1">{{ scope.row.dose }}</span>
<span v-else>-</span>
</template>
</template>
</el-table-column>
<el-table-column label="给药途径" align="center" width="150" prop="sortNumber">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<template v-if="scope.row.adviceType == 1 || !scope.row.adviceDefinitionId">
<el-select v-model="scope.row.methodCode" placeholder="给药途径" clearable filterable>
<el-option
v-for="dict in method_code"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</template>
<span v-else>-</span>
</template>
<template v-else>
<span v-if="scope.row.adviceType == 1">{{ scope.row.methodCode_dictText || scope.row.methodCode }}</span>
<span v-else>-</span>
</template>
</template>
</el-table-column>
<el-table-column label="用药频次" align="center" width="150" prop="sortNumber">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<template v-if="scope.row.adviceType == 1 || !scope.row.adviceDefinitionId">
<el-select
v-model="scope.row.rateCode"
placeholder="频次"
style="width: 120px"
filterable
>
<el-option
v-for="dict in getRateOptions(scope.row)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</template>
<span v-else>-</span>
</template>
<template v-else>
<span v-if="scope.row.adviceType == 1">{{ scope.row.rateCode_dictText }}</span>
<span v-else>-</span>
</template>
</template>
</el-table-column>
<el-table-column label="用药天数" align="center" width="100" prop="sortNumber">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<template v-if="scope.row.adviceType == 1 || !scope.row.adviceDefinitionId">
<el-input
v-model="scope.row.dispensePerDuration"
@change="handleQuantityChange(scope.row)"
/>
</template>
<span v-else>-</span>
</template>
<template v-else>
<span v-if="scope.row.adviceType == 1">{{ scope.row.dispensePerDuration }}</span>
<span v-else>-</span>
</template>
</template>
</el-table-column>
<el-table-column label="总量/执行次数" align="center" width="150" prop="sortNumber">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<el-input
v-model="scope.row.sortNumber"
type="number"
min="1"
@change="handleQuantityChange(scope.row)"
/>
</template>
<span v-else>{{ scope.row.sortNumber }}</span>
</template>
</el-table-column>
<el-table-column label="单位" align="center" width="120" prop="unitCode">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<el-select
v-model="scope.row.selectUnitCode"
placeholder="请选择单位"
@change="handleUnitChange(scope.row)"
>
<el-option
v-if="scope.row.minUnitCode"
:key="scope.row.minUnitCode"
:label="scope.row.minUnitCode_dictText || scope.row.minUnitCode"
:value="scope.row.minUnitCode"
/>
<el-option
v-if="scope.row.unitCode"
:key="scope.row.unitCode"
:label="scope.row.unitCode_dictText || scope.row.unitCode"
:value="scope.row.unitCode"
/>
</el-select>
</template>
<span>{{ scope.row.unitCodeName }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="80">
<template #default="scope">
<el-button
type="danger"
icon="Delete"
circle
size="small"
@click="handleDeleteRow(scope.$index, scope.row)"
/>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="submitForm" :loading="submitLoading"> </el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue';
import adviceBaseList from './adviceBaseList';
import {
queryParticipantList,
queryGroupDetail,
getOrgTree,
} from './api.js';
import { saveOrderGroup } from '@/views/doctorstation/components/api.js';
import useUserStore from '@/store/modules/user';
import { ElMessage } from 'element-plus';
const props = defineProps({
// 是否在“医生站/住院医生站”场景复用个人不选人practitionerId 置空),科室可选科室
isDoctorStation: {
type: Boolean,
default: false,
},
method_code: {
type: Array,
default: () => [],
},
rate_code: {
type: Array,
default: () => [],
},
// 顶部是否显示“个人/科室/全院”切换(用于住院医生站顶部【组套】按钮场景)
showRangeSelector: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['saved']);
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const dialogVisible = ref(false);
const dialogTitle = ref('');
const currentTab = ref('personal'); // personal | department | hospital
const isEdit = ref(false);
const submitLoading = ref(false);
const rangeSelectValue = ref('personal');
const participantListOptions = ref([]);
const organization = ref([]);
const adviceQueryParams = reactive({});
const formData = reactive({
groupPackageId: undefined,
name: '',
practitionerId: undefined,
organizationId: undefined,
});
const prescriptionList = ref([]);
const expandOrder = ref([]);
const nextId = ref(1);
const rowIndex = ref(0);
const selectedRows = ref([]);
const groupIndex = ref(1);
const formRef = ref();
const prescriptionRef = ref();
const adviceTableRef = ref();
const formRules = computed(() => {
const rules = {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
};
// 独立页面:个人必须选参与者;科室必须选科室
if (!props.isDoctorStation && currentTab.value === 'personal') {
rules.practitionerId = [{ required: true, message: '请选择参与者', trigger: 'change' }];
}
if (currentTab.value === 'department') {
rules.organizationId = [{ required: true, message: '请选择科室', trigger: 'change' }];
}
return rules;
});
onMounted(() => {
queryParticipantList().then((res) => {
participantListOptions.value = res.data || [];
});
getOrgTree().then((res) => {
organization.value = res.data?.records || [];
});
});
function openAdd(tab) {
currentTab.value = tab;
isEdit.value = false;
dialogTitle.value = '新增医嘱';
dialogVisible.value = true;
if (props.showRangeSelector) rangeSelectValue.value = tab;
prescriptionList.value = [];
expandOrder.value = [];
nextId.value = 1;
rowIndex.value = 0;
selectedRows.value = [];
groupIndex.value = 1;
formData.groupPackageId = undefined;
formData.name = '';
formData.practitionerId = undefined;
formData.organizationId = undefined;
Object.keys(adviceQueryParams).forEach((key) => {
delete adviceQueryParams[key];
});
prescriptionRef.value?.clearSelection?.();
if (tab === 'personal') {
// 医生站场景:个人组套需要关联当前医生才能查询到
formData.practitionerId = userStore.practitionerId;
} else if (tab === 'department') {
formData.organizationId = userStore.orgId;
}
addEmptyRow();
}
function handleRangeChange(tab) {
openAdd(tab);
}
/**
* 从外部选中的医嘱直接生成组套明细
* @param tab 使用范围personal / department / hospital
* @param rows 选中的处方行列表
*/
function openFromSelection(tab, rows = []) {
currentTab.value = tab;
isEdit.value = false;
dialogTitle.value = '另存组套';
dialogVisible.value = true;
if (props.showRangeSelector) rangeSelectValue.value = tab;
// 重置表单
formData.groupPackageId = undefined;
formData.name = '';
formData.practitionerId = undefined;
formData.organizationId = undefined;
if (tab === 'personal') {
// 医生站场景:个人组套需要关联当前医生才能查询到
formData.practitionerId = userStore.practitionerId;
} else if (tab === 'department') {
formData.organizationId = userStore.orgId;
}
const validRows = (rows || []).filter((i) => i.adviceDefinitionId);
if (validRows.length === 0) {
proxy.$modal.msgWarning('所选医嘱中没有有效的医嘱项,请先选择医嘱后再另存组套');
dialogVisible.value = false;
return;
}
prescriptionList.value = validRows.map((row, index) => {
let therapyEnum = row.therapyEnum;
if (!therapyEnum && row.contentJson) {
try {
const content = JSON.parse(row.contentJson);
therapyEnum = content.therapyEnum;
} catch (e) {}
}
therapyEnum = therapyEnum != null ? String(therapyEnum) : '1';
return {
uniqueKey: index + 1,
showPopover: false,
check: false,
isEdit: false,
statusEnum: 1,
adviceDefinitionId: row.adviceDefinitionId,
adviceTableName: row.adviceTableName || 'advice_definition',
sortNumber: row.quantity ?? row.sortNumber ?? 1,
selectUnitCode: row.selectUnitCode || row.unitCode,
adviceName: row.adviceName || row.productName,
unitCodeName: row.unitCodeName || row.unitCode_dictText || '',
methodCode: row.methodCode,
rateCode: row.rateCode,
dispensePerDuration: row.dispensePerDuration,
dose: row.dose,
doseQuantity: row.doseQuantity,
minUnitCode: row.minUnitCode,
minUnitCode_dictText: row.minUnitCode_dictText,
unitCode: row.unitCode,
unitCode_dictText: row.unitCode_dictText,
groupId: row.groupId,
groupOrder: row.groupOrder,
therapyEnum: therapyEnum,
adviceType: row.adviceType,
};
});
expandOrder.value = [];
nextId.value = prescriptionList.value.length + 1;
}
function openEdit(tab, row) {
currentTab.value = tab;
isEdit.value = true;
dialogTitle.value = '编辑医嘱';
dialogVisible.value = true;
formData.groupPackageId = row.groupPackageId;
formData.name = row.name;
formData.practitionerId = row.practitionerId;
formData.organizationId = row.organizationId;
queryGroupDetail({ groupPackageId: row.groupPackageId }).then((res) => {
const detailList = res.data || [];
prescriptionList.value = detailList.map((item, index) => {
const therapyEnum = item.therapyEnum != null ? String(item.therapyEnum) : '1'; // 统一转成字符串
return {
uniqueKey: index + 1,
showPopover: false,
check: false,
isEdit: false,
statusEnum: 1,
groupPackageId: item.groupPackageId,
adviceDefinitionId: item.orderDefinitionId,
adviceTableName: item.orderDefinitionTable,
sortNumber: item.quantity,
selectUnitCode: item.unitCode,
adviceName: item.orderDefinitionName,
unitCodeName: item.unitCodeName,
groupId: item.groupId,
groupOrder: item.groupOrder,
therapyEnum, // 长期/临时类型1-长期2-临时
// 回显单次剂量/给药途径/用药频次/天数等字段
dispensePerDuration: item.dispensePerDuration,
dose: item.dose,
doseQuantity: item.doseQuantity,
methodCode: item.methodCode,
methodCode_dictText: item.methodCode_dictText,
rateCode: item.rateCode,
rateCode_dictText: item.rateCode_dictText,
// 医嘱类型(药品=1没有则按表名推断药品表 -> 药品)
adviceType:
item.adviceType !== undefined
? item.adviceType
: item.orderDefinitionTable === 'med_medication_definition'
? 1
: undefined,
};
});
nextId.value = prescriptionList.value.length + 1;
addEmptyRow();
});
}
function addEmptyRow() {
prescriptionList.value.unshift({
uniqueKey: nextId.value++,
showPopover: false,
check: false,
isEdit: true,
statusEnum: 1,
therapyEnum: '1', // 默认长期
});
}
function handleAddRow() {
addEmptyRow();
}
function handleSelectionChange(selection) {
selectedRows.value = selection || [];
}
function handleCombineGroup() {
const rows = selectedRows.value || [];
if (rows.length <= 1) {
proxy.$modal.msgWarning('至少选择两项');
return;
}
// 必须都已选择医嘱
if (rows.some((r) => !r.adviceDefinitionId)) {
proxy.$modal.msgWarning('请先完成医嘱选择');
return;
}
// 相同分组用法需要相同
const methodSet = new Set();
rows.forEach((r) => {
if (r.methodCode != null) methodSet.add(r.methodCode);
});
if (methodSet.size > 1) {
proxy.$modal.msgWarning('同一分组药品用法必须相同');
return;
}
// 相同分组频次需要相同
const rateSet = new Set();
rows.forEach((r) => {
if (r.rateCode != null) rateSet.add(r.rateCode);
});
if (rateSet.size > 1) {
proxy.$modal.msgWarning('同一分组药品频次必须相同');
return;
}
const timestamp = Date.now().toString();
const gid = timestamp + groupIndex.value;
const newList = [...prescriptionList.value];
rows.forEach((item, selectIndex) => {
const idx = newList.findIndex((r) => r.uniqueKey === item.uniqueKey);
if (idx !== -1) {
newList[idx].groupId = gid;
newList[idx].groupOrder = selectIndex + 1;
}
});
groupIndex.value += 1;
prescriptionList.value = newList;
prescriptionRef.value?.clearSelection?.();
proxy.$modal.msgSuccess('组套成功');
}
function isRowSelectable(row) {
return !!row.adviceDefinitionId;
}
function getGroupIcon(row) {
if (!row.groupId) return '';
const sameGroup = prescriptionList.value
.filter((item) => item.groupId === row.groupId)
.sort((a, b) => {
const aOrder = a.groupOrder ?? 0;
const bOrder = b.groupOrder ?? 0;
return aOrder - bOrder;
});
if (sameGroup.length === 0) return '';
const index = sameGroup.findIndex((item) => item.uniqueKey === row.uniqueKey);
if (index === 0) {
return '┏';
}
if (index === sameGroup.length - 1) {
return '┗';
}
return '┃';
}
function getRateOptions(row) {
const all = props.rate_code || [];
if (row?.therapyEnum == '2') {
return all.filter(
(dict) => dict.value === 'ST' || (dict.label && dict.label.indexOf('立即') !== -1)
);
}
return all;
}
function handleSplitGroup() {
const rows = selectedRows.value || [];
if (rows.length < 1) {
proxy.$modal.msgWarning('至少选择一项');
return;
}
if (rows.some((r) => !r.groupId)) {
proxy.$modal.msgWarning('包含非组合数据无法拆组');
return;
}
const newList = [...prescriptionList.value];
rows.forEach((item) => {
const idx = newList.findIndex((r) => r.uniqueKey === item.uniqueKey);
if (idx !== -1) {
newList[idx].groupId = undefined;
newList[idx].groupOrder = undefined;
}
});
prescriptionList.value = newList;
prescriptionRef.value?.clearSelection?.();
proxy.$modal.msgSuccess('拆组成功');
}
function submitForm() {
formRef.value.validate((valid) => {
if (!valid) return;
// 验证至少选择了一条医嘱
const detailList = prescriptionList.value.filter((item) => item.adviceDefinitionId);
if (detailList.length === 0) {
proxy?.$modal?.msgWarning?.('请至少选择一条医嘱');
return;
}
// 验证西药类医嘱的必填字段
const westernMedicineRows = detailList.filter((item) => item.adviceType == 1);
for (const row of westernMedicineRows) {
if (!row.dose && row.dose !== 0) {
proxy?.$modal?.msgWarning?.('单次剂量为必填项');
return;
}
if (!row.methodCode) {
proxy?.$modal?.msgWarning?.('给药途径为必填项');
return;
}
if (!row.rateCode) {
proxy?.$modal?.msgWarning?.('用药频次为必填项');
return;
}
if (!row.dispensePerDuration && row.dispensePerDuration !== 0) {
proxy?.$modal?.msgWarning?.('用药天数为必填项');
return;
}
if (!row.sortNumber && row.sortNumber !== 0) {
proxy?.$modal?.msgWarning?.('总量/执行次数为必填项');
return;
}
if (!row.selectUnitCode) {
proxy?.$modal?.msgWarning?.('单位为必填项');
return;
}
}
submitLoading.value = true;
setTimeout(() => {
console.log('[submitForm] 当前tab:', currentTab.value);
const params = { ...formData };
console.log('[submitForm] formData:', formData);
// 添加 rangeCode 字段,用于后端判断组套类型
if (currentTab.value === 'personal') {
params.rangeCode = 1;
} else if (currentTab.value === 'department') {
params.rangeCode = 2;
} else {
params.rangeCode = 3;
}
console.log('[submitForm] 设置后的rangeCode:', params.rangeCode);
// 医生站场景:个人组套需要设置 practitionerId 为当前医生,否则查询时查不到
console.log('[submitForm] isDoctorStation:', props.isDoctorStation, 'currentTab:', currentTab.value);
console.log('[submitForm] userStore.practitionerId:', userStore.practitionerId);
if (props.isDoctorStation && currentTab.value === 'personal') {
params.practitionerId = userStore.practitionerId;
console.log('[submitForm] 已设置 practitionerId:', params.practitionerId);
} else {
console.log('[submitForm] 未设置 practitionerId, 当前值:', params.practitionerId);
}
params.detailList = detailList.map((item) => ({
orderDefinitionId: item.adviceDefinitionId,
orderDefinitionTable: item.adviceTableName,
quantity: item.sortNumber,
unitCode: item.selectUnitCode,
methodCode: item.methodCode,
rateCode: item.rateCode,
dispensePerDuration: item.dispensePerDuration,
dose: item.dose,
doseQuantity: item.doseQuantity,
groupId: item.groupId,
groupOrder: item.groupOrder,
therapyEnum: item.therapyEnum || '1',
}));
console.log('[submitForm] 准备保存组套, params:', params);
// 使用 saveOrderGroup它会根据 rangeCode 自动选择正确的API
saveOrderGroup(params)
.then((res) => {
console.log('[submitForm] 保存API返回:', res);
if (res.code === 200) {
ElMessage.success(res.message || '保存成功');
console.log('[submitForm] 准备触发 saved 事件');
emit('saved');
console.log('[submitForm] saved 事件已触发');
dialogVisible.value = false;
prescriptionList.value = [];
} else {
proxy?.$modal?.msgError?.(res.message || '保存失败');
}
})
.catch((err) => {
console.error('保存失败:', err);
proxy?.$modal?.msgError?.(err?.message || '保存失败,请检查网络或联系管理员');
})
.finally(() => {
submitLoading.value = false;
});
}, 200);
});
}
function selectAdviceBase(key, row) {
const currentRow = prescriptionList.value[rowIndex.value];
const preservedTherapyEnum = currentRow?.therapyEnum || row.therapyEnum || '1';
prescriptionList.value[rowIndex.value] = {
...prescriptionList.value[rowIndex.value],
...JSON.parse(JSON.stringify(row)),
};
prescriptionList.value[rowIndex.value].dose = undefined;
prescriptionList.value[rowIndex.value].doseQuantity = undefined;
prescriptionList.value[rowIndex.value].doseUnitCode = row.doseUnitCode;
prescriptionList.value[rowIndex.value].minUnitCode = row.minUnitCode;
prescriptionList.value[rowIndex.value].unitCode =
row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode;
prescriptionList.value[rowIndex.value].categoryEnum = row.categoryCode;
prescriptionList.value[rowIndex.value].isEdit = false;
prescriptionList.value[rowIndex.value].definitionId = JSON.parse(
JSON.stringify(row)
).chargeItemDefinitionId;
prescriptionList.value[rowIndex.value].therapyEnum = preservedTherapyEnum;
addEmptyRow();
expandOrder.value = [key];
}
function handleFocus(row, index) {
rowIndex.value = index;
row.showPopover = true;
adviceQueryParams.searchKey = row.adviceName || '';
// 设置 organizationId否则 adviceBaseList 会跳过 API 调用
adviceQueryParams.organizationId = userStore.orgId;
}
function handleInput(value, row, index) {
adviceQueryParams.searchKey = value || '';
handleFocus(row, index);
}
function handleBlur(row) {
row.showPopover = false;
}
function clickRow() {}
function getRowDisabled(row) {
return row.isEdit;
}
function handleQuantityChange(row) {
if (row.sortNumber && row.sortNumber > 0) row.sortNumber = parseInt(row.sortNumber);
else row.sortNumber = 1;
prescriptionList.value = [...prescriptionList.value];
}
function handleUnitChange(row) {
prescriptionList.value = [...prescriptionList.value];
}
function handleDeleteRow(index) {
if (prescriptionList.value.length <= 1) {
proxy.$modal.msgWarning('至少保留一行');
return;
}
proxy.$modal
.confirm('确定要删除该行吗?')
.then(() => {
prescriptionList.value.splice(index, 1);
proxy.$modal.msgSuccess('删除成功');
})
.catch(() => {});
}
function isLeafNode(node) {
return !node.children || node.children.length === 0;
}
function handleDialogClose() {
formRef.value?.resetFields?.();
prescriptionList.value = [];
}
function handleOrgChange(value) {
if (!value) return;
const findNode = (nodes) => {
for (let node of nodes) {
if (node.id === value) return node;
if (node.children && node.children.length > 0) {
const found = findNode(node.children);
if (found) return found;
}
}
return null;
};
const selectedNode = findNode(organization.value);
if (selectedNode && !isLeafNode(selectedNode)) {
proxy.$modal.msgWarning('只能选择末级科室');
formData.organizationId = null;
}
}
defineExpose({
openAdd,
openEdit,
openFromSelection,
});
</script>
<style scoped>
.dialog-top-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 10px;
}
.dialog-top-row :deep(.el-form-item) {
margin-bottom: 0;
}
</style>

View File

@@ -0,0 +1,285 @@
<template>
<div @keyup="handleKeyDown" tabindex="0" ref="tableWrapper" class="table-container">
<el-table
ref="adviceBaseRef"
height="350"
:data="adviceBaseList"
highlight-current-row
@current-change="handleCurrentChange"
row-key="patientId"
v-loading="loading"
@cell-click="clickRow"
>
<el-table-column label="名称" align="center" prop="adviceName" />
<el-table-column label="类型" align="center" prop="activityType_enumText" />
<el-table-column label="包装单位" align="center" prop="unitCode_dictText" />
<el-table-column label="最小单位" align="center" prop="minUnitCode_dictText" />
<el-table-column label="规格" align="center" prop="volume" />
<el-table-column label="用法" align="center" prop="methodCode_dictText" />
<el-table-column label="频次" align="center" prop="rateCode_dictText" />
<el-table-column label="单次剂量" align="center" prop="dose" />
<el-table-column label="剂量单位" align="center" prop="doseUnitCode_dictText" />
<el-table-column label="注射药品" align="center" prop="injectFlag_enumText" />
<el-table-column label="皮试" align="center" prop="skinTestFlag_enumText" />
</el-table>
<!-- 分页组件 -->
<div class="pagination-wrapper">
<el-pagination
v-model:current-page="queryParams.pageNum"
:page-size="20"
:total="total"
layout="prev, pager, next"
@current-change="handlePageChange"
/>
</div>
</div>
</template>
<script setup>
import {nextTick} from 'vue';
import {getAdviceBaseInfo} from './api';
import {debounce} from 'lodash-es';
const props = defineProps({
adviceQueryParams: {
type: Object,
default: '',
},
patientInfo: {
type: Object,
required: true,
},
});
const emit = defineEmits(['selectAdviceBase']);
const total = ref(0);
const loading = ref(false);
const adviceBaseRef = ref();
const tableWrapper = ref();
const currentIndex = ref(0); // 当前选中行索引
const currentSelectRow = ref({});
const queryParams = ref({
pageSize: 20,
pageNum: 1,
adviceTypes: '1,2,3',
organizationId: null,
});
const adviceBaseList = ref([]);
// 前端缓存 - 使用 sessionStorage 持久化缓存
const CACHE_PREFIX = 'advice_cache_';
const CACHE_EXPIRE = 5 * 60 * 1000; // 缓存5分钟
function getCacheKey(orgId, pageNum) {
return CACHE_PREFIX + orgId + '_' + pageNum;
}
function getFromCache(orgId, pageNum) {
try {
const key = getCacheKey(orgId, pageNum);
const cachedData = sessionStorage.getItem(key);
if (!cachedData) return null;
const cacheData = JSON.parse(cachedData);
if (Date.now() - cacheData.timestamp > CACHE_EXPIRE) {
sessionStorage.removeItem(key);
return null;
}
console.log('从前端缓存获取:', key);
return cacheData;
} catch (e) {
console.error('读取缓存失败:', e);
return null;
}
}
function setToCache(orgId, pageNum, data, total) {
try {
const key = getCacheKey(orgId, pageNum);
const cacheData = {
data: data,
total: total,
timestamp: Date.now()
};
sessionStorage.setItem(key, JSON.stringify(cacheData));
console.log('写入前端缓存:', key);
} catch (e) {
console.error('写入缓存失败:', e);
}
}
// 防抖函数 - 避免重复请求
const debouncedGetList = debounce(
() => {
// 只有当 organizationId 有效时才请求
if (!queryParams.value.organizationId) {
console.log('organizationId 无效,跳过请求');
return;
}
getList();
},
300,
{ leading: false, trailing: true }
);
// 监听adviceQueryParams变化
watch(
() => props.adviceQueryParams,
(newValue) => {
console.log('adviceQueryParams 变化:', newValue);
queryParams.value.searchKey = newValue.searchKey;
queryParams.value.adviceType = newValue.adviceType;
queryParams.value.organizationId = newValue.organizationId;
console.log('更新后的queryParams:', queryParams.value);
debouncedGetList();
},
{ deep: true }
);
// 监听searchKey变化
watch(
() => props.adviceQueryParams?.searchKey,
(newVal) => {
queryParams.value.searchKey = newVal;
debouncedGetList();
}
);
// 监听organizationId变化
watch(
() => props.adviceQueryParams?.organizationId,
(newVal) => {
console.log('watch organizationId 变化:', newVal);
queryParams.value.organizationId = newVal;
// 当organizationId有效时重新获取数据
if (newVal) {
debouncedGetList();
}
}
);
// 不再页面加载时立即请求,等待用户点击时再请求
function getList() {
// 确保有 organizationId 才调用API
if (!queryParams.value.organizationId) {
console.log('organizationId 为空跳过API调用');
return;
}
const orgId = queryParams.value.organizationId;
const pageNum = queryParams.value.pageNum;
// 尝试从本地缓存获取(只有第一页且无搜索关键字时使用缓存)
if (pageNum === 1 && !queryParams.value.searchKey) {
const cached = getFromCache(orgId, pageNum);
if (cached) {
adviceBaseList.value = cached.data;
total.value = cached.total;
return;
}
}
// 显示 loading
loading.value = true;
// queryParams.value.organizationId = '1922545444781481985';
getAdviceBaseInfo(queryParams.value).then((res) => {
adviceBaseList.value = res.data.records || [];
console.log('获取到医嘱数据:', adviceBaseList.value)
total.value = res.data.total || 0;
// 缓存第一页数据
if (pageNum === 1 && !queryParams.value.searchKey) {
setToCache(orgId, pageNum, adviceBaseList.value, total.value);
}
nextTick(() => {
currentIndex.value = 0;
if (adviceBaseList.value.length > 0 && adviceBaseRef.value) {
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
}
});
})
.finally(() => {
loading.value = false;
});
}
// 当前页改变(分页)
function handlePageChange(page) {
queryParams.value.pageNum = page;
getList();
}
// 处理键盘事件
const handleKeyDown = (event) => {
const key = event.key;
const data = adviceBaseList.value;
switch (key) {
case 'ArrowUp': // 上箭头
event.preventDefault(); // 阻止默认滚动行为
if (currentIndex.value > 0) {
currentIndex.value--;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'ArrowDown': // 下箭头`
event.preventDefault();
if (currentIndex.value < data.length - 1) {
currentIndex.value++;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'Enter': // 回车键
// const currentRow = adviceBaseRef.value.getSelectionRows();
event.preventDefault();
if (currentSelectRow.value) {
// 这里可以触发自定义逻辑,如弹窗、跳转等
emit('selectAdviceBase', currentSelectRow.value);
}
break;
}
};
// 设置选中行(带滚动)
const setCurrentRow = (row) => {
adviceBaseRef.value.setCurrentRow(row);
// 滚动到选中行
const tableBody = adviceBaseRef.value.$el.querySelector('.el-table__body-wrapper');
const currentRowEl = adviceBaseRef.value.$el.querySelector('.current-row');
if (tableBody && currentRowEl) {
currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
};
// 当前行变化时更新索引
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
};
function clickRow(row) {
emit('selectAdviceBase', row);
}
defineExpose({
handleKeyDown,
});
</script>
<style scoped>
.table-container {
display: flex;
flex-direction: column;
}
.pagination-wrapper {
display: flex;
justify-content: center;
padding: 10px 0;
background: #fff;
}
.popover-table-wrapper:focus {
outline: 2px solid #409eff;
}
</style>

View File

@@ -0,0 +1,129 @@
import request from '@/utils/request'
/**
* 获取个人组套
* @param {*} queryParams
*/
export function getPersonalList(queryParams) {
return request({
url: '/personalization/orders-group-package/get-personal',
method: 'get',
params: queryParams
})
}
/**
* 获取科室组套
* @param {*} queryParams
*/
export function getDeptList(queryParams) {
return request({
url: '/personalization/orders-group-package/get-organization',
method: 'get',
params: queryParams
})
}
/**
* 获取全院组套
* @param {*} queryParams
*/
export function getAllList(queryParams) {
return request({
url: '/personalization/orders-group-package/get-hospital',
method: 'get',
params: queryParams
})
}
/**
* 保存个人组套
* @param {*} data
*/
export function savePersonal(data) {
return request({
url: '/personalization/orders-group-package/save-personal',
method: 'post',
data: data
})
}
/**
* 保存科室组套
* @param {*} data
*/
export function saveDepartment(data) {
return request({
url: '/personalization/orders-group-package/save-organization',
method: 'post',
data: data
})
}
/**
* 保存全院组套
* @param {*} data
*/
export function saveAll(data) {
return request({
url: '/personalization/orders-group-package/save-hospital',
method: 'post',
data: data
})
}
/**
* 查询组套明细
* @param {*} data
*/
export function queryGroupDetail(params) {
return request({
url: '/personalization/orders-group-package/get-group-package-detail',
method: 'get',
params: params
})
}
/**
* 删除组套
* @param {*} data
*/
export function deleteGroup(data) {
return request({
url: '/personalization/orders-group-package/group-package-detail?groupPackageId=' + data.groupPackageId,
method: 'delete'
})
}
/**
* 查询参与者下拉列表
* @param {*} data
*/
export function queryParticipantList(params) {
return request({
url: '/app-common/practitioner-list',
method: 'get',
params: params
})
}
/**
* 获取药品列表
*/
export function getAdviceBaseInfo(queryParams) {
return request({
url: '/doctor-station/advice/advice-base-info',
method: 'get',
params: queryParams
})
}
/**
* 获取科室列表
*/
export function getOrgTree() {
return request({
url: '/base-data-manage/organization/organization',
method: 'get',
})
}

View File

@@ -0,0 +1,875 @@
<template>
<div class="app-container">
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tab-pane label="个人" name="personal">
<div class="mb10">
<el-row :gutter="10" class="mb10">
<el-col :span="6">
<el-input
v-model="personalQuery.searchKey"
placeholder="请输入名称"
clearable
@keyup.enter="getPersonalListData"
/>
</el-col>
<el-col :span="1.5">
<el-button type="primary" icon="Search" @click="getPersonalListData">查询</el-button>
</el-col>
</el-row>
<el-button type="primary" plain icon="Plus" @click="handleAdd('personal')"
>新增</el-button
>
</div>
<el-table :data="personalList" border v-loading="loading.personal">
<el-table-column prop="name" label="名称" />
<el-table-column prop="practitionerName" label="参与者" />
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleEdit('personal', scope.row)"
>编辑</el-button
>
<el-button
link
type="danger"
icon="Delete"
@click="handleDelete('personal', scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="科室" name="department">
<div class="mb10">
<el-row :gutter="10" class="mb10">
<el-col :span="6">
<el-input
v-model="departmentQuery.searchKey"
placeholder="请输入名称"
clearable
@keyup.enter="getDepartmentListData"
/>
</el-col>
<el-col :span="1.5">
<el-button type="primary" icon="Search" @click="getDepartmentListData"
>查询</el-button
>
</el-col>
</el-row>
<el-button type="primary" plain icon="Plus" @click="handleAdd('department')"
>新增</el-button
>
</div>
<el-table :data="departmentList" border v-loading="loading.department">
<el-table-column prop="name" label="名称" />
<el-table-column prop="organizationName" label="科室" />
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button
link
type="primary"
icon="Edit"
@click="handleEdit('department', scope.row)"
>编辑</el-button
>
<el-button
link
type="danger"
icon="Delete"
@click="handleDelete('department', scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="全院" name="hospital">
<div class="mb10">
<el-row :gutter="10" class="mb10">
<el-col :span="6">
<el-input
v-model="hospitalQuery.searchKey"
placeholder="请输入名称"
clearable
@keyup.enter="getHospitalListData"
/>
</el-col>
<el-col :span="1.5">
<el-button type="primary" icon="Search" @click="getHospitalListData">查询</el-button>
</el-col>
</el-row>
<el-button type="primary" plain icon="Plus" @click="handleAdd('hospital')"
>新增</el-button
>
</div>
<el-table :data="hospitalList" border v-loading="loading.hospital">
<el-table-column prop="name" label="名称" />
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleEdit('hospital', scope.row)"
>编辑</el-button
>
<el-button
link
type="danger"
icon="Delete"
@click="handleDelete('hospital', scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 新增/编辑弹窗 -->
<el-dialog
:title="dialogTitle"
v-model="dialogVisible"
width="1300px"
@close="handleDialogClose"
>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="参与者" prop="practitionerId" v-if="currentTab == 'personal'">
<el-select v-model="formData.practitionerId" placeholder="请选择参与者" clearable>
<el-option
v-for="item in participantListOptions"
:key="item.practitionerId"
:label="item.practitionerName"
:value="item.practitionerId"
/>
</el-select>
</el-form-item>
<el-form-item label="科室" prop="organizationId" v-if="currentTab == 'department'">
<el-tree-select
clearable
v-model="formData.organizationId"
:data="organization"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
default-expand-all
placeholder="请选择科室"
:render-after-expand="false"
@change="handleOrgChange"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-table
max-height="650"
ref="prescriptionRef"
:data="prescriptionList"
row-key="uniqueKey"
border
@cell-click="clickRow"
:expand-row-keys="expandOrder"
>
<el-table-column label="医嘱" align="center" prop="productName">
<template #default="scope">
<template v-if="getRowDisabled(scope.row)">
<el-popover
:popper-style="{ padding: '0' }"
placement="bottom-start"
:visible="scope.row.showPopover"
:width="1200"
>
<adviceBaseList
ref="adviceTableRef"
:popoverVisible="scope.row.showPopover"
:adviceQueryParams="adviceQueryParams"
@selectAdviceBase="(row) => selectAdviceBase(scope.row.uniqueKey, row)"
/>
<template #reference>
<el-input
:ref="'adviceRef' + scope.$index"
style="width: 50%"
v-model="scope.row.adviceName"
placeholder="请选择项目"
@input="(value) => handleInput(value, scope.row, scope.$index)"
@click="handleFocus(scope.row, scope.$index)"
@keyup.enter.stop="handleFocus(scope.row, scope.$index)"
@keydown="
(e) => {
if (!scope.row.showPopover) return;
// 拦截上下键和回车事件
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
e.preventDefault();
// 传递事件到弹窗容器
adviceTableRef.handleKeyDown(e);
}
}
"
@blur="handleBlur(scope.row)"
/>
</template>
</el-popover>
</template>
<span v-else>{{ scope.row.adviceName }}</span>
</template>
</el-table-column>
<el-table-column label="单次剂量" align="center" width="250" prop="sortNumber">
<template #default="scope">
<template v-if="scope.row.adviceType == 1">
<template v-if="!scope.row.groupPackageId">
<el-input
style="width: 70px; margin-right: 10px"
v-model="scope.row.doseQuantity"
@input="
(value) => {
scope.row.dose = value * scope.row.unitConversionRatio;
}
"
/>
<span>{{ scope.row.minUnitCode_dictText }}</span>
<span>{{ ' = ' }}</span>
<el-input
style="width: 70px; margin-right: 10px"
v-model="scope.row.dose"
@input="
(value) => {
scope.row.doseQuantity = value / scope.row.unitConversionRatio;
}
"
/>
<span>{{ scope.row.doseUnitCode_dictText }}</span>
</template>
<span v-else>{{ scope.row.dose }}</span>
</template>
<span v-else>{{ '-' }}</span>
</template>
</el-table-column>
<el-table-column label="给药途径" align="center" width="150" prop="sortNumber">
<template #default="scope">
<template v-if="scope.row.adviceType == 1">
<template v-if="!scope.row.groupPackageId">
<el-select
v-model="scope.row.methodCode"
placeholder="给药途径"
clearable
filterable
>
<el-option
v-for="dict in method_code"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</template>
<span v-else>{{ scope.row.methodCode_dictText || scope.row.methodCode }}</span>
</template>
<span v-else>{{ '-' }}</span>
</template>
</el-table-column>
<el-table-column label="用药频次" align="center" width="150" prop="sortNumber">
<template #default="scope">
<template v-if="scope.row.adviceType == 1">
<template v-if="!scope.row.groupPackageId">
<el-select
v-model="scope.row.rateCode"
placeholder="频次"
style="width: 120px"
filterable
>
<el-option
v-for="dict in rate_code"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</template>
<span v-else>{{ scope.row.rateCode_dictText }}</span>
</template>
<span v-else>{{ '-' }}</span>
</template>
</el-table-column>
<el-table-column label="用药天数" align="center" width="100" prop="sortNumber">
<template #default="scope">
<template v-if="scope.row.adviceType == 1">
<template v-if="!scope.row.groupPackageId">
<el-input
v-model="scope.row.dispensePerDuration"
@change="handleQuantityChange(scope.row)"
/>
</template>
<span v-else>{{ scope.row.dispensePerDuration }}</span>
</template>
<span v-else>{{ '-' }}</span>
</template>
</el-table-column>
<el-table-column label="总量/执行次数" align="center" width="150" prop="sortNumber">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<el-input
v-model="scope.row.sortNumber"
type="number"
min="1"
@change="handleQuantityChange(scope.row)"
/>
</template>
<span v-else>{{ scope.row.sortNumber }}</span>
</template>
</el-table-column>
<el-table-column label="单位" align="center" width="120" prop="unitCode">
<template #default="scope">
<template v-if="!scope.row.groupPackageId">
<el-select
v-model="scope.row.selectUnitCode"
placeholder="请选择单位"
@change="handleUnitChange(scope.row)"
>
<el-option
v-if="scope.row.minUnitCode"
:key="scope.row.minUnitCode"
:label="scope.row.minUnitCode_dictText || scope.row.minUnitCode"
:value="scope.row.minUnitCode"
/>
<el-option
v-if="scope.row.unitCode"
:key="scope.row.unitCode"
:label="scope.row.unitCode_dictText || scope.row.unitCode"
:value="scope.row.unitCode"
/>
</el-select>
</template>
<span>{{ scope.row.unitCodeName }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="80">
<template #default="scope">
<el-button
type="danger"
icon="Delete"
circle
size="small"
@click="handleDeleteRow(scope.$index, scope.row)"
/>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="submitForm" :loading="submitLoading"> </el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {onMounted, reactive, ref} from 'vue';
import {
deleteGroup,
getAllList,
getDeptList,
getOrgTree,
getPersonalList,
queryGroupDetail,
queryParticipantList,
saveAll,
saveDepartment,
savePersonal,
} from './components/api.js';
import adviceBaseList from './components/adviceBaseList';
import {ElMessage} from 'element-plus';
import useUserStore from '@/store/modules/user';
// 获取当前实例用于使用proxy
const { proxy } = getCurrentInstance();
// 定义tab相关数据
const activeTab = ref('personal');
// 定义各tab列表数据
const personalList = ref([]);
const departmentList = ref([]);
const hospitalList = ref([]);
const prescriptionList = ref([]);
const expandOrder = ref([]); //目前的展开行
const { method_code, rate_code, distribution_category_code } = proxy.useDict(
'method_code',
'rate_code',
'distribution_category_code'
);
// 查询参数 - 西医组套 tcmFlag = 0
const personalQuery = reactive({ searchKey: '', tcmFlag: 0 });
const departmentQuery = reactive({ searchKey: '', tcmFlag: 0 });
const hospitalQuery = reactive({ searchKey: '', tcmFlag: 0 });
// 加载状态
const loading = reactive({
personal: false,
department: false,
hospital: false,
});
// 提交按钮加载状态
const submitLoading = ref(false);
// 弹窗相关数据
const dialogVisible = ref(false);
const dialogTitle = ref('');
const currentTab = ref(''); // 记录当前操作的tab
const isEdit = ref(false); // 是否为编辑模式
const currentRow = ref(null); // 当前编辑的行数据
const participantListOptions = ref([]);
const organization = ref([]);
const adviceQueryParams = reactive({});
// 表单数据
const formData = reactive({
id: undefined,
name: '',
});
const nextId = ref(1);
const rowIndex = ref(0);
// 表单验证规则
const formRules = {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
};
// 表单引用
const formRef = ref();
// 页面加载时获取所有列表数据
onMounted(() => {
// 可以添加初始查询参数
const initialParams = {
searchKey: '', // 初始查询关键字
};
fetchAllData(initialParams);
getInit();
});
function getInit() {
queryParticipantList().then((res) => {
participantListOptions.value = res.data;
});
getOrgTree().then((res) => {
organization.value = res.data.records;
});
}
// 获取所有数据(不传参,保留查询框已有的参数)
function fetchAllData() {
getPersonalListData();
getDepartmentListData();
getHospitalListData();
}
// 获取个人医嘱列表
function getPersonalListData() {
loading.personal = true;
getPersonalList(personalQuery)
.then((response) => {
personalList.value = response.data;
loading.personal = false;
})
.catch((error) => {
console.error('获取个人医嘱列表失败:', error);
loading.personal = false;
proxy.$modal.msgError('获取个人医嘱列表失败');
});
}
// 获取科室医嘱列表
function getDepartmentListData() {
loading.department = true;
getDeptList(departmentQuery)
.then((response) => {
departmentList.value = response.data;
loading.department = false;
})
.catch((error) => {
console.error('获取科室医嘱列表失败:', error);
loading.department = false;
proxy.$modal.msgError('获取科室医嘱列表失败');
});
}
// 获取全院医嘱列表
function getHospitalListData() {
loading.hospital = true;
getAllList(hospitalQuery)
.then((response) => {
hospitalList.value = response.data;
loading.hospital = false;
})
.catch((error) => {
console.error('获取全院医嘱列表失败:', error);
loading.hospital = false;
proxy.$modal.msgError('获取全院医嘱列表失败');
});
}
// tab切换处理
function handleTabChange(tab) {
// 根据切换到的tab调用相应的接口获取数据
switch (tab) {
case 'personal':
getPersonalListData();
break;
case 'department':
getDepartmentListData();
break;
case 'hospital':
getHospitalListData();
break;
}
}
// 新增按钮处理
function handleAdd(tab) {
currentTab.value = tab;
isEdit.value = false;
dialogTitle.value = '新增医嘱';
dialogVisible.value = true;
// 重置表单
prescriptionList.value.unshift({
uniqueKey: nextId.value++,
showPopover: false,
check: false,
isEdit: true,
statusEnum: 1,
});
formData.groupPackageId = undefined;
formData.name = '';
// 获取当前登录用户信息
const userStore = useUserStore();
const userId = userStore.id;
const orgId = userStore.orgId;
const practitionerId = userStore.practitionerId;
// 根据不同tab设置默认值
if (tab === 'personal') {
// 个人医嘱默认设置当前用户为参与者
formData.practitionerId = practitionerId;
} else if (tab === 'department') {
// 科室医嘱默认设置当前科室
formData.organizationId = orgId;
}
// 全院医嘱不需要设置默认值
}
// 编辑按钮处理
function handleEdit(tab, row) {
currentTab.value = tab;
isEdit.value = true;
currentRow.value = row;
dialogTitle.value = '编辑医嘱';
dialogVisible.value = true;
formData.groupPackageId = row.groupPackageId;
formData.name = row.name;
formData.practitionerId = row.practitionerId;
formData.organizationId = row.organizationId;
// 填充表单数据
queryGroupDetail({ groupPackageId: row.groupPackageId }).then((res) => {
prescriptionList.value = res.data.map((item) => {
return {
groupPackageId: item.groupPackageId,
adviceDefinitionId: item.orderDefinitionId,
adviceTableName: item.orderDefinitionTable,
sortNumber: item.quantity,
selectUnitCode: item.unitCode,
adviceName: item.orderDefinitionName,
unitCodeName: item.unitCodeName,
// 回显单次剂量/给药途径/用药频次/天数等字段
dispensePerDuration: item.dispensePerDuration,
dose: item.dose,
doseQuantity: item.doseQuantity,
methodCode: item.methodCode,
methodCode_dictText: item.methodCode_dictText,
rateCode: item.rateCode,
rateCode_dictText: item.rateCode_dictText,
groupId: item.groupId,
groupOrder: item.groupOrder,
therapyEnum: item.therapyEnum != null ? String(item.therapyEnum) : '1',
// 医嘱类型(药品=1没有则按表名推断
adviceType:
item.adviceType !== undefined
? item.adviceType
: item.orderDefinitionTable === 'med_medication_definition'
? 1
: undefined,
};
});
prescriptionList.value.unshift({
uniqueKey: nextId.value++,
showPopover: false,
check: false,
isEdit: true,
statusEnum: 1,
});
});
}
// 删除按钮处理
function handleDelete(tab, row) {
// 显示确认框
proxy.$modal
.confirm('确定要删除该医嘱吗?')
.then(() => {
deleteGroup({ groupPackageId: row.groupPackageId }).then((res) => {
proxy.$modal.msgSuccess(res.msg);
fetchAllData();
});
})
.catch(() => {});
}
// 提交表单
function submitForm() {
formRef.value.validate((valid) => {
if (valid) {
submitLoading.value = true;
// 提交数据
console.log('提交表单数据:', formData);
let params = { ...formData };
// 添加 tcmFlag西医组套为 0
params.tcmFlag = 0;
// 过滤掉空列表项没有adviceDefinitionId的项
params.detailList = prescriptionList.value
.filter((item) => item.adviceDefinitionId) // 过滤掉空列表项
.map((item) => {
return {
orderDefinitionId: item.adviceDefinitionId,
orderDefinitionTable: item.adviceTableName,
quantity: item.sortNumber,
unitCode: item.selectUnitCode,
methodCode: item.methodCode,
rateCode: item.rateCode,
dispensePerDuration: item.dispensePerDuration,
dose: item.dose,
doseQuantity: item.doseQuantity,
};
});
console.log('保存参数:', params);
// 编辑模式
switch (currentTab.value) {
case 'personal':
savePersonal(params).then((res) => {
console.log('保存个人组套返回:', res);
if (res.code === 200) {
ElMessage.success(res.msg || '保存成功');
// 重新获取数据以保持一致性
fetchAllData();
} else {
ElMessage.error(res.msg || '保存失败');
}
submitLoading.value = false;
dialogVisible.value = false;
// 清空处方列表
prescriptionList.value = [];
}).catch((err) => {
console.error('保存个人组套失败:', err);
ElMessage.error('保存失败: ' + (err.message || '未知错误'));
submitLoading.value = false;
});
break;
case 'department':
saveDepartment(params).then((res) => {
console.log('保存科室组套返回:', res);
if (res.code === 200) {
ElMessage.success(res.msg || '保存成功');
// 重新获取数据以保持一致性
fetchAllData();
} else {
ElMessage.error(res.msg || '保存失败');
}
submitLoading.value = false;
dialogVisible.value = false;
// 清空处方列表
prescriptionList.value = [];
}).catch((err) => {
console.error('保存科室组套失败:', err);
ElMessage.error('保存失败: ' + (err.message || '未知错误'));
submitLoading.value = false;
});
break;
case 'hospital':
saveAll(params).then((res) => {
console.log('保存全院组套返回:', res);
if (res.code === 200) {
ElMessage.success(res.msg || '保存成功');
// 重新获取数据以保持一致性
fetchAllData();
} else {
ElMessage.error(res.msg || '保存失败');
}
submitLoading.value = false;
dialogVisible.value = false;
// 清空处方列表
prescriptionList.value = [];
}).catch((err) => {
console.error('保存全院组套失败:', err);
ElMessage.error('保存失败: ' + (err.message || '未知错误'));
submitLoading.value = false;
});
break;
}
}
});
}
/**
* 选择药品回调
*/
function selectAdviceBase(key, row) {
prescriptionList.value[rowIndex.value] = {
...prescriptionList.value[rowIndex.value],
...JSON.parse(JSON.stringify(row)),
};
prescriptionList.value[rowIndex.value].dose = undefined;
prescriptionList.value[rowIndex.value].doseQuantity = undefined;
prescriptionList.value[rowIndex.value].doseUnitCode = row.doseUnitCode;
prescriptionList.value[rowIndex.value].minUnitCode = row.minUnitCode;
prescriptionList.value[rowIndex.value].unitCode =
row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode;
prescriptionList.value[rowIndex.value].categoryEnum = row.categoryCode;
prescriptionList.value[rowIndex.value].isEdit = false;
prescriptionList.value[rowIndex.value].definitionId = JSON.parse(
JSON.stringify(row)
).chargeItemDefinitionId;
prescriptionList.value.unshift({
uniqueKey: nextId.value++,
showPopover: false,
check: false,
isEdit: true,
statusEnum: 1,
});
expandOrder.value = [key];
}
function handleFocus(row, index) {
rowIndex.value = index;
row.showPopover = true;
// 将输入框的值传递给adviceBaseList组件作为查询条件
adviceQueryParams.searchKey = row.adviceName || '';
// 获取当前登录用户的科室ID
const userStore = useUserStore();
adviceQueryParams.organizationId = userStore.orgId;
console.log('handleFocus - organizationId:', userStore.orgId, 'userStore:', userStore);
}
// 处理输入事件
function handleInput(value, row, index) {
// 更新查询参数
adviceQueryParams.searchKey = value || '';
// 显示弹窗
handleFocus(row, index);
}
function handleBlur(row) {
row.showPopover = false;
}
/**
* 点击行赋值
*/
function clickRow(row) {}
function getRowDisabled(row) {
return row.isEdit;
}
// 处理数量变化
function handleQuantityChange(row) {
// 验证数量是否为正整数
if (row.sortNumber && row.sortNumber > 0) {
row.sortNumber = parseInt(row.sortNumber);
} else {
row.sortNumber = 1;
}
// 触发更新列表事件
prescriptionList.value = [...prescriptionList.value];
}
// 处理单位变化
function handleUnitChange(row) {
// 当单位改变时触发的逻辑可以在这里添加
console.log('单位已更改:', row.unitCode);
// 触发更新列表事件
prescriptionList.value = [...prescriptionList.value];
}
// 删除行
function handleDeleteRow(index, row) {
// 确保至少保留一行
if (prescriptionList.value.length <= 1) {
proxy.$modal.msgWarning('至少保留一行');
return;
}
// 确认删除
proxy.$modal
.confirm('确定要删除该行吗?')
.then(() => {
prescriptionList.value.splice(index, 1);
proxy.$modal.msgSuccess('删除成功');
})
.catch(() => {
// 取消删除
});
}
// 判断是否为叶子节点
function isLeafNode(node) {
return !node.children || node.children.length === 0;
}
// 弹窗关闭处理
function handleDialogClose() {
formRef.value.resetFields();
// 清空处方列表
prescriptionList.value = [];
}
// 处理科室选择变化
function handleOrgChange(value) {
if (!value) return;
// 查找选中的节点
const findNode = (nodes) => {
for (let node of nodes) {
if (node.id === value) {
return node;
}
if (node.children && node.children.length > 0) {
const found = findNode(node.children);
if (found) return found;
}
}
return null;
};
const selectedNode = findNode(organization.value);
// 如果选中的不是叶子节点,则清空选择
if (selectedNode && !isLeafNode(selectedNode)) {
proxy.$modal.msgWarning('只能选择末级科室');
formData.organizationId = null;
}
}
</script>
<style scoped>
.mb10 {
margin-bottom: 10px;
}
</style>

View File

@@ -0,0 +1,148 @@
<template>
<div @keyup="handleKeyDown" tabindex="0" ref="tableWrapper">
<el-table
ref="adviceBaseRef"
height="400"
:data="adviceBaseList"
highlight-current-row
@current-change="handleCurrentChange"
row-key="patientId"
@cell-click="clickRow"
>
<el-table-column label="名称" align="center" prop="adviceName" />
<el-table-column label="类型" align="center" prop="activityType_enumText" />
<el-table-column label="包装单位" align="center" prop="unitCode_dictText" />
<el-table-column label="最小单位" align="center" prop="minUnitCode_dictText" />
<el-table-column label="规格" align="center" prop="volume" />
<el-table-column label="用法" align="center" prop="methodCode_dictText" />
<el-table-column label="频次" align="center" prop="rateCode_dictText" />
<el-table-column label="单次剂量" align="center" prop="dose" />
<el-table-column label="剂量单位" align="center" prop="doseUnitCode_dictText" />
<el-table-column label="注射药品" align="center" prop="injectFlag_enumText" />
<el-table-column label="皮试" align="center" prop="skinTestFlag_enumText" />
</el-table>
</div>
</template>
<script setup>
import {nextTick} from 'vue';
import {getAdviceBaseInfo} from './api';
import {throttle} from 'lodash-es';
const props = defineProps({
adviceQueryParams: {
type: Object,
default: '',
},
patientInfo: {
type: Object,
required: true,
},
});
const emit = defineEmits(['selectAdviceBase']);
const total = ref(0);
const adviceBaseRef = ref();
const tableWrapper = ref();
const currentIndex = ref(0); // 当前选中行索引
const currentSelectRow = ref({});
const queryParams = ref({
pageSize: 100,
pageNum: 1,
});
const adviceBaseList = ref([]);
// 节流函数
const throttledGetList = throttle(
() => {
getList();
},
300,
{ leading: true, trailing: true }
);
watch(
() => props.adviceQueryParams,
(newValue) => {
queryParams.value.searchKey = newValue.searchKey;
queryParams.value.adviceType = newValue.adviceType;
console.log(queryParams.value);
throttledGetList();
},
{ deep: true }
);
getList();
function getList() {
queryParams.value.organizationId = '1922545444781481985';
getAdviceBaseInfo(queryParams.value).then((res) => {
adviceBaseList.value = res.data.records;
total.value = res.data.total;
nextTick(() => {
currentIndex.value = 0;
if (adviceBaseList.value.length > 0) {
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
}
});
});
}
// 处理键盘事件
const handleKeyDown = (event) => {
const key = event.key;
const data = adviceBaseList.value;
switch (key) {
case 'ArrowUp': // 上箭头
event.preventDefault(); // 阻止默认滚动行为
if (currentIndex.value > 0) {
currentIndex.value--;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'ArrowDown': // 下箭头`
event.preventDefault();
if (currentIndex.value < data.length - 1) {
currentIndex.value++;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'Enter': // 回车键
// const currentRow = adviceBaseRef.value.getSelectionRows();
event.preventDefault();
if (currentSelectRow.value) {
// 这里可以触发自定义逻辑,如弹窗、跳转等
emit('selectAdviceBase', currentSelectRow.value);
}
break;
}
};
// 设置选中行(带滚动)
const setCurrentRow = (row) => {
adviceBaseRef.value.setCurrentRow(row);
// 滚动到选中行
const tableBody = adviceBaseRef.value.$el.querySelector('.el-table__body-wrapper');
const currentRowEl = adviceBaseRef.value.$el.querySelector('.current-row');
if (tableBody && currentRowEl) {
currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
};
// 当前行变化时更新索引
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
};
function clickRow(row) {
emit('selectAdviceBase', row);
}
defineExpose({
handleKeyDown,
});
</script>
<style scoped>
.popover-table-wrapper:focus {
outline: 2px solid #409eff; /* 聚焦时的高亮效果 */
}
</style>

View File

@@ -0,0 +1,69 @@
import request from '@/utils/request'
export function getOrderGroup(queryParams) {
return request({
url: '/personalization/order-group/order-group',
method: 'get',
param: queryParams
})
}
/**
* 保存
*/
export function saveOrderGroup(data) {
return request({
url: '/personalization/order-group/order-group',
method: 'post',
data: data
})
}
export function getAdviceBaseInfo(queryParams) {
return request({
url: '/doctor-station/advice/advice-base-info',
method: 'get',
params: queryParams
})
}
/**
* 获取科室列表
*/
export function getOrgTree() {
return request({
url: '/base-data-manage/organization/organization',
method: 'get',
})
}
/**
* 添加组套获取科室列表
*/
export function getDepartmentList() {
return request({
url: '/app-common/department-list',
method: 'get',
})
}
/**
* 添加组套获取使用人列表
*/
export function getUserPractitionerPCage(params) {
return request({
url: '/base-data-manage/practitioner/user-practitioner-page',
method: 'get',
params:params
})
}
/**
* 删除组套
*/
export function deleteOrderGroup(params) {
return request({
url: '/personalization/order-group/order-group',
method: 'delete',
params:params
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,361 @@
<template>
<div class="main-content">
<!-- 中间组套列表 -->
<div class="section-card-left">
<div class="section-header">
<div class="actions">
<span class="descriptions-item-label">组套名称</span>
<el-input
v-model="queryParams.searchKey"
placeholder="请输入组套名称"
clearable
class="search-input"
@keydown.enter="getOrderGroupList"
>
<template #append>
<el-button icon="Search" @click="getOrderGroupList" />
</template>
</el-input>
<span class="descriptions-item-label">使用范围</span>
<el-select
v-model="queryParams.rangeCode"
placeholder="请选择使用范围"
prefix-icon="el-icon-search"
clearable
@change="getOrderGroupList"
class="search-input"
>
<el-option
v-for="item in use_range"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
<el-table
:data="orderGroupList"
highlight-current-row
@current-change="handleTemplateSelect"
style="width: 100%"
v-loading="templateLoading"
border
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="name" label="组套名称" min-width="180" header-align="center" />
<el-table-column prop="rangeCode_dictText" label="使用范围" width="100" align="center" />
<el-table-column prop="createdAt" label="创建时间" width="160" align="center">
<template #default="{ row }">
<!-- {{ formatDate(row.createdAt) }} -->
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right" align="center">
<template #default="{ row }">
<el-button link @click="editTemplate(row)">编辑</el-button>
<el-button link @click="deleteTemplate(row.id)" style="color: #f56c6c">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 右侧组套详情 -->
<div draggable="true" class="section-card-right">
<div class="template-detail">
<!-- 基本信息卡片 -->
<div class="info-card">
<div class="card-header">
<span>基本信息</span>
<el-button type="primary" @click="addTemplate">新建组套</el-button>
<el-button type="primary" @click="addTemplate">保存组套</el-button>
</div>
<el-form :model="currentTemplate" label-width="100px" class="info-form" :inline="true">
<el-form-item label="组套名称">
<el-input v-model="currentTemplate.name" ref="name" placeholder="请输入组套名称" />
</el-form-item>
<el-form-item label="组套类型">
<el-select
v-model="currentTemplate.typeEnum"
placeholder="请选择组套类型"
class="search-input"
>
<el-option
v-for="item in typeEnum"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="使用范围">
<el-select
v-model="currentTemplate.rangeCode"
placeholder="请选择使用范围"
class="search-input"
>
<el-option
v-for="item in use_range"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="info-card">
<div class="card-header">
<span>项目列表</span>
</div>
<div style="padding: 10px 10px">
<Prescriptionlist :comtination="comtination" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import Prescriptionlist from './components/prescriptionlist.vue';
import {deleteOrderGroup, getOrderGroup} from './components/api.js';
const { proxy } = getCurrentInstance();
const { use_range } = proxy.useDict('use_range');
const currentTemplate = ref({});
// 查询参数
const queryParams = ref({});
// 组套列表
const orderGroupList = ref([]);
const typeEnum = ref([
{
value: '1',
label: '医嘱组套',
},
{
value: '2',
label: '诊疗组套',
},
]);
const comtination = ref('');
const templateLoading = ref(false);
getOrderGroupList();
function getOrderGroupList() {
getOrderGroup(queryParams.value).then((res) => {
orderGroupList.value = res.data.records;
});
}
// 处理组套选择
const handleTemplateSelect = (row) => {
currentTemplate.value = row;
comtination.value = row;
};
// 添加组套
const addTemplate = () => {
currentTemplate.value = {};
proxy.$refs.name.focus();
};
// 编辑组套
const editTemplate = () => {};
// 删除组套
const deleteTemplate = (id) => {
deleteOrderGroup({ id: id }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('删除成功');
}
});
};
</script>
<style scoped>
.main-content {
flex: 1;
display: flex;
justify-content: space-between;
gap: 10px;
padding: 30px;
width: 100%;
height: calc(100% - 80px);
}
.section-card-left {
background: white;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.06);
width: 35%;
height: 100%;
overflow: hidden;
padding: 10px;
border: 1px solid #ebeef5;
}
.section-card-right {
background: white;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.06);
width: 64%;
height: 100%;
overflow: hidden;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 10px;
background: #f8fafc;
border-bottom: 1px solid #ebeef5;
}
.search-area {
padding: 10px 15px;
border-bottom: 1px solid #ebeef5;
background: #f8fafc;
}
.search-input {
width: 200px;
}
.template-detail {
display: flex;
flex-direction: column;
gap: 20px;
overflow-y: auto;
height: 100%;
}
.info-card {
background: #ffffff;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #ebeef5;
overflow: hidden;
}
.card-header {
display: flex;
align-items: center;
padding: 12px 15px;
background: #f8fafc;
border-bottom: 1px solid #ebeef5;
font-weight: 600;
color: #555;
}
.card-header i {
margin-right: 10px;
color: #409eff;
font-size: 18px;
}
.card-header span {
flex: 1;
}
.add-btn {
padding: 5px 10px;
}
.info-form {
padding: 20px 15px 10px;
}
.empty-state {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding: 5px 0;
}
.custom-tree-node i {
margin-right: 8px;
color: #909399;
}
.node-label {
flex: 1;
}
.count-badge {
background-color: #e6f7ff;
color: #1890ff;
border-radius: 10px;
padding: 2px 8px;
font-size: 14px;
margin-left: 10px;
}
.tree-actions {
visibility: hidden;
margin-left: 10px;
}
.el-tree-node__content:hover .tree-actions {
visibility: visible;
}
:deep(.el-tree-node.is-current > .el-tree-node__content) {
background-color: #ecf5ff;
font-weight: 500;
}
:deep(.el-tree-node__content) {
height: 40px;
}
:deep(.el-table) {
flex: 1;
}
:deep(.el-table__header) {
font-weight: 600;
}
:deep(.el-table__row) {
transition: background-color 0.3s;
}
:deep(.el-table__row:hover) {
background-color: #f5f7fa !important;
}
.el-button--primary {
background: linear-gradient(135deg, #409eff, #337ecc);
border: none;
}
.el-button--primary:hover {
opacity: 0.9;
}
.cost {
font-weight: 600;
color: #e53935;
}
:deep(.el-tag) {
border-radius: 12px;
padding: 0 10px;
}
:deep(.el-input__inner) {
border-radius: 8px;
}
</style>

View File

@@ -0,0 +1,61 @@
import request from '@/utils/request'
export function getList(queryParams) {
return request({
url: '/base-data-manage/organization/organization',
method: 'get',
params: queryParams
})
}
export function addOrganization(data) {
return request({
url: '/base-data-manage/organization/organization',
method: 'post',
data: data
})
}
export function updateOrganization(data) {
return request({
url: '/base-data-manage/organization/organization',
method: 'post',
data: data
})
}
export function deleteOrganization(orgIds) {
return request({
url: '/base-data-manage/organization/organization?orgIds=' + orgIds ,
method: 'delete',
})
}
export function getOrgDetail(id) {
return request({
url: '/base-data-manage/organization/organization-getById',
method: 'get',
params: { orgId: id }
})
}
export function initOrgTypeOption() {
return request({
url: '/base-data-manage/organization/init',
method: 'get'
})
}
export function disableOrg(id) {
return request({
url: '/base-data-manage/organization/organization-inactive?orgId=' + id,
method: 'put',
})
}
export function enableOrg(id) {
return request({
url: '/base-data-manage/organization/organization-active?orgId=' + id,
method: 'put',
})
}

View File

@@ -0,0 +1,598 @@
<template>
<div class="app-container">
<!-- 查询表单 -->
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" class="query-form">
<el-form-item label="科室名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入科室名称"
clearable
@keyup.enter="handleQuery"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="科室类型" prop="typeEnum">
<el-select v-model="queryParams.typeEnum" placeholder="请选择科室类型" clearable style="width: 200px">
<el-option
v-for="item in orgTypeOption"
:key="item.value"
:label="item.info"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="科室分类" prop="classEnum">
<el-select v-model="queryParams.classEnum" placeholder="请选择科室分类" clearable multiple style="width: 200px">
<el-option
v-for="item in classEnumOption"
:key="item.value"
:label="item.info"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item class="search-buttons">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
</el-col>
<el-col :span="1.5">
<el-tooltip
:content="selectRowIds.length == 0 ? '至少选择一条数据' : ''"
placement="top"
:disabled="selectRowIds.length != 0"
>
<el-button
type=""
plain
icon="Delete"
:disabled="selectRowIds.length == 0"
@click="handleDelete"
>删除</el-button
>
</el-tooltip>
</el-col>
<el-col :span="1.5">
<el-button type="" plain icon="Download" @click="handleExport"> 导出 </el-button>
</el-col>
<el-col :span="1.5">
<el-button type="" plain icon="Refresh" @click="getPageList">刷新</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
ref="orgTableRef"
v-loading="loading"
:data="organization"
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="科室名称" align="left" prop="name" />
<el-table-column label="科室类型" align="center" prop="typeEnum_dictText" />
<el-table-column label="科室分类" align="center">
<template #default="scope">
<span v-if="scope.row.classEnum_dictText">
<el-tag
v-for="item in scope.row.classEnum_dictText.split(',')"
:key="item"
size="small"
style="margin-right: 2px;"
>
{{ item }}
</el-tag>
</span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="医保码" align="center" prop="ybNo" />
<el-table-column label="医保名称" align="center" prop="ybName" />
<el-table-column label="挂号科室" align="center">
<template #default="scope">
{{ scope.row.registerFlag ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column label="科室位置" align="center" prop="location" show-overflow-tooltip />
<el-table-column label="科室简介" align="center" prop="intro" show-overflow-tooltip />
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
<el-table-column label="状态" align="center" prop="activeFlag_dictText" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button link type="primary" @click="handelEdit(scope.row)">编辑</el-button>
<el-button
link
type="primary"
@click="handleDisabled(scope.row.id)"
v-if="scope.row.activeFlag == '1'"
>停用</el-button
>
<el-button link type="primary" @click="handelEnable(scope.row.id)" v-else>启用</el-button>
<el-button link type="primary" @click="handleAddInferior(scope.row)">
添加下级
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getPageList"
/>
</div>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="600px" @close="cancel" append-to-body>
<el-form ref="orgRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="id" prop="id" v-show="false">
<el-input v-model="form.id" placeholder="请输入科室编号" />
</el-form-item>
<el-form-item label="科室编号" prop="busNo" v-show="false">
<el-input v-model="form.busNo" placeholder="请输入科室编号" />
</el-form-item>
<el-form-item label="科室名称" prop="name">
<el-input v-model="form.name" placeholder="请输入科室名称" />
</el-form-item>
<el-form-item label="医保码" prop="name">
<el-input v-model="form.ybNo" placeholder="请输入医保码" />
</el-form-item>
<el-form-item label="医保名称" prop="name">
<el-input v-model="form.ybName" placeholder="请输入医保名称" />
</el-form-item>
<el-form-item label="科室类型" prop="typeEnum">
<el-select
v-model="form.typeEnum"
placeholder="请选择科室类型"
clearable
style="width: 100%"
>
<el-option
v-for="item in orgTypeOption"
:key="item.value"
:label="item.info"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="科室分类" prop="classEnum">
<el-select
v-model="form.classEnum"
placeholder="请选择科室分类"
clearable
multiple
style="width: 100%"
:disabled="form.typeEnum != 2"
>
<el-option
v-for="item in classEnumOption"
:key="item.value"
:label="item.info"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-col>
<el-form-item label="上级科室" prop="busNoParent">
<el-tree-select
clearable
style="width: 100%"
v-model="form.busNoParent"
:data="organization"
:props="{ value: 'busNo', label: 'name', children: 'children' }"
value-key="id"
check-strictly
/>
</el-form-item>
</el-col>
<el-form-item label="挂号科室" prop="registerFlag">
<el-radio-group v-model="form.registerFlag" size="large">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="科室位置" prop="location">
<el-input v-model="form.location" placeholder="请输入科室位置" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item label="科室简介" prop="intro">
<el-input
v-model="form.intro"
type="textarea"
placeholder="请输入科室简介"
maxlength="500"
show-word-limit
:rows="4"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入备注信息"
maxlength="1000"
show-word-limit
:rows="3"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Organization">
import {
addOrganization,
deleteOrganization,
disableOrg,
enableOrg,
getList,
getOrgDetail,
initOrgTypeOption,
updateOrganization
} from './components/api';
const { proxy } = getCurrentInstance();
const loading = ref(true);
const showSearch = ref(true);
const organization = ref([]);
const queryParams = ref({
pageNo: 1,
pageSize: 10,
name: undefined,
typeEnum: undefined,
classEnum: undefined
});
const open = ref(false);
const form = ref({
id: undefined,
busNo: undefined,
name: undefined,
typeEnum: undefined,
busNoParent: undefined,
registerFlag: false, // 挂号科室标记
location: undefined, // 科室位置
intro: undefined, // 科室简介
remark: undefined, // 备注
});
const orgTableRef = ref();
const orgRef = ref();
const orgTypeOption = ref([]);
const classEnumOption = ref([]);
const selectRowIds = ref([]);
const total = ref(0);
const title = ref('');
const rules = ref({
busNo: [{ required: false, message: '请输入科室编号', trigger: 'input' }],
name: [
{ required: true, message: '请输入科室名称', trigger: 'change' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'change' },
],
typeEnum: [{ required: true, message: '请选择科室类型', trigger: 'change' }],
// 新增字段验证规则
location: [{ required: false, message: '请输入科室位置', trigger: 'blur' }],
intro: [{ required: false, message: '请输入科室简介', trigger: 'blur' }],
remark: [{ required: false, message: '请输入备注信息', trigger: 'blur' }],
});
getPageList();
initOption();
// 使用系统标准字典获取方法
const { organization_class } = proxy.useDict('organization_class');
// 统一的科室分类字典处理函数
function processOrganizationClassDict(dictData) {
return dictData.map(item => ({
value: String(item.value), // 将值转换为字符串类型,确保与表单值类型一致
info: item.label // 使用dict_label的值作为显示文本
}));
}
// 监听字典数据变化
watch(() => organization_class.value, (newVal) => {
if (newVal && newVal.length > 0) {
// 转换为组件需要的格式
classEnumOption.value = processOrganizationClassDict(newVal);
// 同步更新表格中显示的科室分类文本,确保主界面显示与字典一致
if (organization.value && organization.value.length > 0) {
organization.value = organization.value.map(item => {
// 保留原有显示文本作为基础
const originalText = item.classEnum_dictText || '';
// 获取字典中的对应文本
const dictLabel = getDictLabel(item.classEnum);
// 只有在字典中找到匹配值时才替换,否则保留原有文本
return {
...item,
classEnum_dictText: dictLabel || originalText
};
});
}
}
}, { immediate: true });
function initOption() {
if (orgTypeOption.value.length == 0) {
initOrgTypeOption().then((res) => {
orgTypeOption.value = res.data.organizationTypeOptions;
// 优先使用系统标准字典数据,确保编辑和新增科室使用相同的分类字典
if (organization_class.value && organization_class.value.length > 0) {
classEnumOption.value = processOrganizationClassDict(organization_class.value);
} else if (res.data.organizationClassOptions && res.data.organizationClassOptions.length > 0) {
// 只有在字典数据不存在时才使用接口返回的数据作为备选
// 将接口返回的科室分类选项值也转换为字符串类型,保持一致性
classEnumOption.value = res.data.organizationClassOptions.map(item => ({
...item,
value: String(item.value)
}));
}
});
}
}
function reset() {
form.value.id = undefined;
form.value.registerFlag = false;
form.value.location = undefined;
form.value.intro = undefined;
form.value.remark = undefined;
orgRef.value.resetFields();
}
// 从字典数据中查找对应的值,处理类型转换
function getDictLabel(value) {
if (!value || !organization_class.value || organization_class.value.length === 0) return '';
// 尝试进行类型转换比较,处理可能的字符串/数字不匹配问题
const stringValue = String(value);
const dict = organization_class.value.find(item => {
// 比较转换后的字符串值
return String(item.value) === stringValue;
});
return dict ? dict.label : '';
}
// 解析科室分类值,处理字符串或数组格式
function parseClassEnumValues(value) {
if (!value) return [];
if (Array.isArray(value)) {
return value.filter(item => item !== null && item !== undefined && item !== '');
} else if (typeof value === 'string') {
// 如果是逗号分隔的字符串,分割并过滤空值
return value.split(',').map(item => item.trim()).filter(item => item !== '');
} else {
// 如果是单个值,转换为字符串
return [String(value)].filter(item => item !== '');
}
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNo = 1;
getPageList();
}
/** 重置按钮操作 */
function resetQuery() {
queryParams.value.name = undefined;
queryParams.value.typeEnum = undefined;
queryParams.value.classEnum = undefined;
queryParams.value.pageNo = 1;
getPageList();
}
function getPageList(pagination) {
loading.value = true;
// 如果传入了分页参数更新queryParams
if (pagination) {
queryParams.value.pageNo = pagination.page;
queryParams.value.pageSize = pagination.limit;
}
// 创建API调用的查询参数副本处理classEnum的转换
const apiParams = { ...queryParams.value };
if (Array.isArray(apiParams.classEnum)) {
apiParams.classEnum =
apiParams.classEnum.length > 0
? apiParams.classEnum.join(',')
: undefined;
}
getList(apiParams).then((res) => {
// 处理返回的科室数据,确保科室分类显示与系统标准字典一致
const processedData = res.data.records.map(item => {
// 保留原有显示文本作为基础
const originalText = item.classEnum_dictText || '';
// 如果系统标准字典存在,尝试使用字典中的文本覆盖原有文本
if (organization_class.value && organization_class.value.length > 0) {
// 处理多选值的情况
let newText = '';
if (item.classEnum) {
// 如果classEnum是逗号分隔的字符串则处理每个值
const classEnumValues = parseClassEnumValues(item.classEnum);
const labels = classEnumValues.map(val => getDictLabel(val)).filter(label => label);
newText = labels.join(',');
}
// 只有在字典中找到匹配值时才替换,否则保留原有文本
return {
...item,
classEnum_dictText: newText || originalText
};
}
return item;
});
organization.value = processedData;
total.value = res.data.total;
}).catch(error => {
console.error('获取科室列表失败:', error);
proxy.$modal.msgError('获取科室列表失败,请稍后重试');
organization.value = [];
total.value = 0;
}).finally(() => {
loading.value = false;
});
}
function handleAdd() {
title.value = '添加科室';
open.value = true;
reset();
console.log(form.value);
}
function handelEdit(row) {
console.log(row.busNo);
title.value = '编辑科室';
open.value = true;
// 调用后端API获取完整的科室信息确保包含所有字段
getOrgDetail(row.id).then(res => {
if (res.code === 200) {
const orgInfo = res.data;
form.value.id = orgInfo.id;
form.value.busNo = orgInfo.busNo;
form.value.name = orgInfo.name;
form.value.ybNo = orgInfo.ybNo;
form.value.ybName = orgInfo.ybName;
form.value.typeEnum = orgInfo.typeEnum;
// 处理多选的科室分类,如果是逗号分隔的字符串则转换为数组
if (orgInfo.classEnum) {
if (typeof orgInfo.classEnum === 'string' && orgInfo.classEnum.includes(',')) {
form.value.classEnum = orgInfo.classEnum.split(',').map(item => item.trim());
} else {
form.value.classEnum = [String(orgInfo.classEnum)];
}
} else {
form.value.classEnum = [];
}
form.value.busNoParent = orgInfo.busNo.split('.').length > 1 ? orgInfo.busNo.split('.')[0] : undefined;
form.value.registerFlag = !!orgInfo.registerFlag;
form.value.location = orgInfo.location;
form.value.intro = orgInfo.intro;
form.value.remark = orgInfo.remark;
}
}).catch(error => {
console.error('获取科室信息失败:', error);
proxy.$modal.msgError('获取科室信息失败');
});
}
function cancel() {
open.value = false;
reset();
console.log(form.value);
}
// 新增/编辑
function submitForm() {
proxy.$refs['orgRef'].validate((valid) => {
if (valid) {
// 创建表单数据副本避免直接修改原始form对象
const formData = { ...form.value };
// 确保registerFlag从布尔值转换为整数true=1, false=0
formData.registerFlag = Number(formData.registerFlag ? 1 : 0);
// 处理多选的科室分类,如果是数组则转换为逗号分隔的字符串
if (Array.isArray(formData.classEnum)) {
formData.classEnum = formData.classEnum.length > 0 ? formData.classEnum.join(',') : null;
}
// 如果未定义设置默认值1
if (formData.classEnum === undefined || formData.classEnum === null || formData.classEnum === '') {
formData.classEnum = null;
}
// 验证提交数据
console.log('提交的数据:', formData);
if (form.value.id == undefined) {
if (form.value.busNoParent) {
formData.busNo = form.value.busNoParent;
}
addOrganization(formData).then((res) => {
proxy.$modal.msgSuccess('操作成功');
open.value = false;
getPageList();
}).catch(error => {
console.error('添加科室失败:', error);
proxy.$modal.msgError('添加科室失败,请稍后重试');
});
} else {
updateOrganization(formData).then((res) => {
proxy.$modal.msgSuccess('操作成功');
open.value = false;
getPageList();
}).catch(error => {
console.error('更新科室失败:', error);
proxy.$modal.msgError('更新科室失败,请稍后重试');
});
}
}
});
}
// 添加下级
function handleAddInferior(row) {
title.value = '添加下级';
open.value = true;
form.value.busNoParent = row.busNo;
}
// 删除
function handleDelete() {
loading.value = true;
deleteOrganization(selectRowIds.value.join(',')).then((res) => {
proxy.$modal.msgSuccess('操作成功');
getPageList();
}).catch(error => {
console.error('删除科室失败:', error);
proxy.$modal.msgError('删除科室失败,请稍后重试');
}).finally(() => {
loading.value = false;
});
}
// 停用
function handleDisabled(id) {
disableOrg(id).then((res) => {
proxy.$modal.msgSuccess('操作成功');
getPageList();
}).catch(error => {
console.error('停用科室失败:', error);
proxy.$modal.msgError('停用科室失败,请稍后重试');
});
}
// 启用
function handelEnable(id) {
enableOrg(id).then((res) => {
proxy.$modal.msgSuccess('操作成功');
getPageList();
}).catch(error => {
console.error('启用科室失败:', error);
proxy.$modal.msgError('启用科室失败,请稍后重试');
});
}
function handleSelectionChange() {
selectRowIds.value = orgTableRef.value.getSelectionRows().map((item) => item.id);
}
</script>

View File

@@ -0,0 +1,101 @@
/**
* 操作日志工具
* 所有操作必须有操作日志
*/
import {addOperationLog} from './outpatientNumber'
/**
* 记录操作日志
* @param {Object} params
* @param {string} params.operation - 操作类型(新增/修改/删除/查询)
* @param {string} params.details - 操作详情
* @param {boolean} params.success - 操作是否成功
* @param {string} params.errorMessage - 错误信息
* @param {Object} params.userInfo - 用户信息
*/
export async function logOperation({ operation, details, success, errorMessage, userInfo }) {
try {
const logData = {
operation,
details,
success,
errorMessage: errorMessage || null,
timestamp: new Date().toISOString(),
userId: userInfo?.id || null,
userName: userInfo?.name || null,
}
// 控制台输出(便于调试)
console.log('[门诊号码管理] 操作日志:', logData)
// 调用后端接口记录日志(如果接口不存在,静默失败,不会抛出异常)
await addOperationLog(logData)
} catch (error) {
console.error('[门诊号码管理] 记录日志失败:', error)
}
}
/**
* 记录查询操作
*/
export function logQuery(recordCount, userInfo) {
return logOperation({
operation: '查询',
details: `查询门诊号码段列表,共 ${recordCount} 条记录`,
success: true,
userInfo
})
}
/**
* 记录新增操作
*/
export function logCreate(record, success, errorMessage, userInfo) {
const details = success
? `新增门诊号码段:${record.startNo} - ${record.endNo}(操作员:${record.operatorName}`
: `尝试新增门诊号码段:${record.startNo} - ${record.endNo},失败原因:${errorMessage}`
return logOperation({
operation: '新增',
details,
success,
errorMessage,
userInfo
})
}
/**
* 记录修改操作
*/
export function logUpdate(record, success, errorMessage, userInfo) {
const details = success
? `修改门诊号码段:${record.startNo} - ${record.endNo}ID${record.id}`
: `尝试修改门诊号码段 ID${record.id},失败原因:${errorMessage}`
return logOperation({
operation: '修改',
details,
success,
errorMessage,
userInfo
})
}
/**
* 记录删除操作
*/
export function logDelete(records, success, errorMessage, userInfo) {
const recordsInfo = records.map(r => `${r.startNo}-${r.endNo}`).join('、')
const details = success
? `删除门诊号码段(共 ${records.length} 条):${recordsInfo}`
: `尝试删除门诊号码段(共 ${records.length} 条),失败原因:${errorMessage}`
return logOperation({
operation: '删除',
details,
success,
errorMessage,
userInfo
})
}

View File

@@ -0,0 +1,67 @@
/**
* 门诊号码管理 API 接口
* 严格按照要求实现
*/
import request from '@/utils/request'
/**
* 分页查询门诊号码段列表
* 要求:普通用户只能查看自己的,管理员可以查看所有
*/
export function listOutpatientNo(query) {
return request({
url: '/business-rule/outpatient-no/page',
method: 'get',
params: query,
})
}
/**
* 新增门诊号码段
* 要求:必须校验前缀一致性、长度一致性、重复检查
*/
export function addOutpatientNo(data) {
return request({
url: '/business-rule/outpatient-no',
method: 'post',
data,
})
}
/**
* 更新门诊号码段
*/
export function updateOutpatientNo(data) {
return request({
url: '/business-rule/outpatient-no',
method: 'put',
data,
})
}
/**
* 删除门诊号码段
*要求:双重校验(归属权+使用状态)
*/
export function deleteOutpatientNo(data) {
return request({
url: '/business-rule/outpatient-no',
method: 'delete',
data,
})
}
/**
* 记录操作日志
* 要求:所有操作必须有操作日志
* 注意由于后端接口不存在直接返回成功响应不发送实际请求避免404错误
*/
export function addOperationLog(data) {
// 直接返回成功响应不发送实际请求避免404错误显示在控制台
// 日志信息已经在控制台输出(在 operationLog.js 中),这里只需要确保不中断调用链
return Promise.resolve({
code: 200,
msg: '日志记录成功(接口不存在,已静默处理)',
data: null
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
import request from '@/utils/request'
// 初始化
export function getPharmacyDepartmentList(query) {
return request({
url: '/base-data-manage/org-loc/org-loc',
method: 'get',
params: query
})
}
// 查询诊疗目录详细
export function getPharmacyDepartmentOne(query) {
return request({
url: '/base-data-manage/org-loc/org-loc',
method: 'get',
params: query // 确保参数正确传递
})
}
// 新增
export function addPharmacyDepartment(data) {
return request({
url: '/base-data-manage/org-loc/org-loc',
method: 'post',
data: data
})
}
// 修改
export function editPharmacyDepartment(data) {
return request({
url: '/base-data-manage/org-loc/org-loc',
method: 'post',
data: data
})
}
// 删除
export function deletePharmacyDepartment(data) {
return request({
url: '/base-data-manage/org-loc/org-loc?orgLocId=' + data.orgLocId,
method: 'delete',
})
}
// 目录分类查询
export function getDiseaseTreatmentInit(id) {
return request({
url: '/base-data-manage/org-loc/init/',
method: 'get',
params: { id } // 确保参数正确传递
})
}
// 目录分类子查询
export function getDiseaseTreatmentInitLoc(id) {
return request({
url: '/base-data-manage/org-loc/loc-list?locationForm=' + id,
method: 'get'
})
}

View File

@@ -0,0 +1,422 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!--诊疗目录-->
<el-col :span="4" :xs="24">
<div class="head-title">取药科室</div>
<div>
<el-tree
:data="pharmacyDepartmentOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="treeRef"
node-key="id"
check-strictly
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-col>
<!--诊疗目录-->
<el-col :span="20" :xs="24">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="openAddPharmacyDepartment"
v-hasPermi="['system:user:add']"
>添加新项目</el-button
>
</el-col>
</el-row>
<el-table
v-loading="loading"
:data="diagnosisTreatmentList"
@selection-change="handleSelectionChange"
>
<!-- <el-table-column type="selection" width="50" align="center" /> -->
<el-table-column
label="开立科室"
align="center"
key="name"
prop="name"
width="300"
:show-overflow-tooltip="true"
>
<template #default="scope">
<div style="display: flex; align-items: center; justify-content: center">
<el-select
v-model="scope.row.organizationId"
placeholder="请选择"
clearable
style="width: 200px"
:class="{ 'error-border': scope.row.error }"
filterable
>
<el-option
v-for="item in departmentOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</template>
</el-table-column>
<el-table-column
label="项目类型"
align="center"
key="itemCode"
prop="itemCode"
:show-overflow-tooltip="true"
width="300"
>
<template #default="scope">
<div style="display: flex; align-items: center; justify-content: center">
<el-select
v-model="scope.row.itemCode"
placeholder="项目类型"
clearable
style="width: 100px"
:class="{ 'error-border': scope.row.error }"
>
<el-option label="药品" value="1" />
<el-option label="耗材" value="2" />
</el-select>
</div>
</template>
</el-table-column>
<el-table-column
label="药品类别"
align="center"
key="pyStr"
prop="pyStr"
:show-overflow-tooltip="true"
width="300"
>
<template #default="scope">
<div style="display: flex; align-items: center; justify-content: center">
<el-select
v-model="scope.row.distributionCategoryCode"
placeholder="药品类别"
clearable
style="width: 200px"
:class="{ 'error-border': scope.row.error }"
>
<template v-if="scope.row.itemCode == '1'">
<el-option
v-for="dict in med_category_code"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</template>
<template v-if="scope.row.itemCode == '2'">
<el-option
v-for="dict in device_category_code"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</template>
</el-select>
</div>
</template>
</el-table-column>
<el-table-column
label="开始时间"
align="center"
key="wbStr"
prop="wbStr"
:show-overflow-tooltip="true"
width="300"
>
<template #default="scope">
<el-time-picker
v-model="scope.row.startTime"
placeholder="选择时间"
format="HH:mm:ss"
value-format="HH:mm:ss"
>
</el-time-picker>
</template>
</el-table-column>
<el-table-column
label="结束时间"
align="center"
key="categoryCode_dictText"
prop="categoryCode_dictText"
:show-overflow-tooltip="true"
width="300"
>
<template #default="scope">
<el-time-picker
v-model="scope.row.endTime"
placeholder="选择时间"
format="HH:mm:ss"
value-format="HH:mm:ss"
>
</el-time-picker>
</template>
</el-table-column>
<!-- <el-table-column label="备注" align="center" key="typeEnum_enumText" prop="typeEnum_enumText"
:show-overflow-tooltip="true" width="300">
<template #default="scope">
<el-input v-model="scope.row.detailJson" placeholder="" />
</template>
</el-table-column> -->
<el-table-column
label="操作"
align="center"
width="150"
class-name="small-padding fixed-width"
fixed="right"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Edit"
@click="openSavePharmacyDepartment(scope.row, scope.$index)"
v-hasPermi="['system:user:edit']"
>保存</el-button
>
<el-button
type="danger"
link
icon="Delete"
:disabled="scope.row.id == ''"
@click="deleteSelectedRows(scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
</div>
</template>
<script setup name="pharmacyDepartment">
import {
addPharmacyDepartment,
deletePharmacyDepartment,
editPharmacyDepartment,
getDiseaseTreatmentInit,
getDiseaseTreatmentInitLoc,
getPharmacyDepartmentList,
} from './components/pharmacyDepartment';
const { proxy } = getCurrentInstance();
const { distribution_category_code, med_category_code, device_category_code } = proxy.useDict(
'distribution_category_code',
'med_category_code',
'device_category_code'
);
const diagnosisTreatmentList = ref([]);
const loading = ref(false);
const showSearch = ref(true);
const ids = ref([]); // 存储选择的行数据
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref('');
const pharmacyDepartmentOptions = ref(undefined);
const currentCategoryEnum = ref('');
const departmentOptions = ref([]);
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 10,
},
tableRules: {
organizationId: [{ required: true, message: '取药科室不能为空', trigger: 'blur' }],
distributionCategoryCode: [{ required: true, message: '药品类别不能为空', trigger: 'blur' }],
},
});
const { queryParams, form, tableRules } = toRefs(data);
/** 通过条件过滤节点 */
const filterNode = (value, data) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 目录分类查询 */
function getDiseaseTreatmentList() {
getDiseaseTreatmentInit().then((response) => {
pharmacyDepartmentOptions.value = response.data.locationFormOptions || [];
departmentOptions.value = response.data.departmentOptions || [];
// 动态加载所有 locationFormOptions 的子节点(包括药库、药房、耗材库)
if (pharmacyDepartmentOptions.value.length > 0) {
pharmacyDepartmentOptions.value.forEach((option, index) => {
if (!option?.value) return;
getDiseaseInitLoc(option.value).then((children) => {
if (pharmacyDepartmentOptions.value && pharmacyDepartmentOptions.value[index]) {
pharmacyDepartmentOptions.value[index].children = children || [];
}
});
});
}
});
}
function getDiseaseInitLoc(id) {
return getDiseaseTreatmentInitLoc(id).then((response) => {
return response.data.locationOptions;
});
}
/** 获取绑定科室列表 */
function getList() {
loading.value = true;
getPharmacyDepartmentList(queryParams.value).then((res) => {
loading.value = false;
if (res.code === 200) {
diagnosisTreatmentList.value =
res.data.records.length > 0
? res.data.records.map((res) => {
return {
...res,
isEditing: false, // 标记当前行是否正在编辑
error: false, // 新增 error 字段
};
})
: [];
}
console.log(diagnosisTreatmentList.value, 'res.data');
total.value = res.data.total;
});
}
/** 节点单击事件 */
function handleNodeClick(data, node) {
// 检查节点是否有子节点
if (node.data.children && node.data.children.length > 0) {
proxy.$message.warning('不能选择父节点');
return;
}
queryParams.value.defLocationId = data.value;
currentCategoryEnum.value = data.value;
handleQuery();
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNo = 1;
console.log(queryParams, 'queryParams搜索');
getList();
}
/** 选择条数 */
function handleSelectionChange(selection) {
console.log(selection, 'selection');
// selectedData.value = selection.map((item) => ({ ...item })); // 存储选择的行数据
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
function openAddPharmacyDepartment() {
const newRow = {
id: '',
organizationId: '',
defLocationId: '',
distributionCategoryCode: '',
startTime: '00:00:00',
endTime: '23:59:59',
isEditing: true, // 标记当前行是否正在编辑
error: false, // 新增 error 字段
};
diagnosisTreatmentList.value.push(newRow);
total.value = diagnosisTreatmentList.value.length;
data.isAdding = true; // 设置标志位为 true表示有未保存的
}
function handleBlur(row, index) {
console.log(row);
let hasError = false;
const fields = ['organizationId', 'distributionCategoryCode'];
fields.forEach((field) => {
if (!row[field]) {
hasError = true;
console.log(tableRules.value[field]);
proxy.$message.error(tableRules.value[field][0].message);
}
});
console.log(hasError);
row.error = hasError;
}
function openSavePharmacyDepartment(row) {
console.log(row, 'row');
let params = { ...row };
let hasError = false;
// this.purchaseinventoryList.forEach((row) => {
handleBlur(row);
if (row.error) {
hasError = true;
}
// });
if (hasError) {
proxy.$message.error('请填写完整信息');
return;
}
if (currentCategoryEnum.value) {
params.defLocationId = currentCategoryEnum.value;
}
if (row.id) {
editPharmacyDepartment(params).then((res) => {
data.isAdding = false; // 允许新增下一行
proxy.$message.success('保存成功!');
console.log(res, 'res');
});
} else {
delete params.id;
addPharmacyDepartment(params).then((res) => {
console.log(res, 'res');
proxy.$message.success('保存成功!');
});
}
}
function deleteSelectedRows(row) {
deletePharmacyDepartment({ orgLocId: row.id }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('删除成功');
getList();
}
});
}
getDiseaseTreatmentList();
</script>
<style scoped>
.el-form--inline .el-form-item {
display: inline-flex;
vertical-align: middle;
margin-right: 10px !important;
}
.error-border {
border: 1px solid red;
}
</style>

View File

@@ -0,0 +1,80 @@
import request from '@/utils/request'
// 查询厂商列表
export function getSupplierList(query) {
return request({
url: '/data-dictionary/supplier/get-supplier-list',
method: 'get',
params: query
})
}
// 查询厂商详细
export function getSupplierOne(id) {
return request({
url: '/data-dictionary/supplier/get-supplier-detail',
method: 'get',
params: {id}
})
}
// 新增厂商
export function addSupplier(data) {
return request({
url: '/data-dictionary/supplier/add-supplier',
method: 'post',
data: data
})
}
// 修改厂商
export function editSupplier(data) {
return request({
url: '/data-dictionary/supplier/edit-supplier',
method: 'put',
data: data
})
}
// // 删除厂商
// export function delUser(userId) {
// return request({
// url: '/system/user/' + userId,
// method: 'delete'
// })
// }
// 停用厂商
export function stopSupplier(ids) {
return request({
url: '/data-dictionary/supplier/information-stop',
method: 'put',
data: ids
})
}
// 启用厂商
export function startSupplier(ids) {
return request({
url: '/data-dictionary/supplier/information-start',
method: 'put',
data: ids
})
}
// 查询厂商类型
export function getSupplierInit() {
return request({
url: '/data-dictionary/supplier/information-init',
method: 'get'
})
}
// 查询部门树结构
export function deptTreeSelect(queryParams) {
return request({
url: '/base-data-manage/organization/organization',
method: 'get',
param: queryParams
})
}

View File

@@ -0,0 +1,454 @@
<template>
<div class="app-container">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="90px"
>
<el-form-item label="厂家名称:" prop="searchKey">
<el-input
v-model="queryParams.searchKey"
placeholder="品名/商品名/英文品名/编码/拼音"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="厂商种类:" prop="typeEnum">
<el-select
v-model="queryParams.typeEnum"
placeholder="生产商/供应商"
clearable
style="width: 240px"
>
<el-option
v-for="dict in supplierTypeOptions"
:key="dict.value"
:label="dict.info"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd">添加</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Remove" :disabled="multiple" @click="handleClose"
>停用</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="CirclePlus" :disabled="multiple" @click="handleStart"
>启用</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="Search" @click="getList">查询</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="CircleClose" @click="handleClear">清空条件</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="supplierList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="编号" align="center" key="busNo" prop="busNo" />
<el-table-column
label="名称"
align="center"
key="name"
prop="name"
:show-overflow-tooltip="true"
/>
<el-table-column
label="拼音助记码"
align="center"
key="pyStr"
prop="pyStr"
:show-overflow-tooltip="true"
/>
<el-table-column
label="类型 "
align="center"
key="typeEnum_enumText"
prop="typeEnum_enumText"
:show-overflow-tooltip="true"
/>
<el-table-column label="地址" align="center" key="address" prop="address" width="120" />
<el-table-column label="联系人电话" align="center" key="phone" prop="phone" />
<el-table-column label="联系人邮箱" align="center" key="email" prop="email" width="160" />
<el-table-column
label="活动标识"
align="center"
key="activeFlag_enumText"
prop="activeFlag_enumText"
width="160"
/>
<el-table-column
label="机构"
align="center"
key="orgId_dictText"
prop="orgId_dictText"
width="160"
/>
<el-table-column
label="操作"
align="center"
width="150"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
>编辑</el-button
>
<el-button link type="primary" icon="View" @click="handleView(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改用户配置对话框 -->
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
<el-form :model="form" :rules="rules" ref="supplierRef" label-width="110px">
<el-row>
<el-col :span="12">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.id != undefined">
<el-form-item label="编码" prop="busNo">
<el-input v-model="form.busNo" placeholder="请输入编码" disabled />
</el-form-item>
</el-col>
</el-row>
<!-- <el-row>
<el-col :span="12">
<el-form-item label="拼音" prop="pyStr">
<el-input v-model="form.pyStr" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="五笔码" prop="wbStr">
<el-input v-model="form.wbStr" maxlength="11" />
</el-form-item>
</el-col>
</el-row> -->
<el-row>
<el-col :span="12">
<el-form-item label="类型" prop="typeEnum">
<!-- <el-input v-model="form.typeEnum" maxlength="11" /> -->
<el-select
v-model="form.typeEnum"
placeholder="生产商/供应商"
clearable
style="width: 240px"
>
<el-option
v-for="dict in supplierTypeOptions"
:key="dict.value"
:label="dict.info"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" maxlength="255" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="联系人电话" prop="phone">
<el-input v-model="form.phone" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系人邮箱" prop="email">
<el-input v-model="form.email" maxlength="255" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="活动标识" prop="activeFlag">
<el-checkbox v-model="form.activeFlag"></el-checkbox>
</el-form-item>
</el-col>
<el-col :span="12">
<!-- <el-form-item label="机构编号" prop="orgId"> -->
<!-- <el-input v-model="form.orgId" maxlength="11" /> -->
<el-form-item label="提供部门" prop="orgId">
<el-tree-select
v-model="form.orgId"
:data="deptOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
placeholder="请选择提供部门"
check-strictly
clearable
/>
<!-- </el-form-item> -->
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer v-if="title != '查看'">
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Supplier">
import {
addSupplier,
deptTreeSelect,
editSupplier,
getSupplierInit,
getSupplierList,
getSupplierOne,
startSupplier,
stopSupplier,
} from './components/supplier';
const router = useRouter();
const { proxy } = getCurrentInstance();
const { sys_normal_disable, sys_user_sex } = proxy.useDict('sys_normal_disable', 'sys_user_sex');
const supplierList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref('');
const supplierTypeOptions = ref(undefined);
const deptOptions = ref(undefined); // 部门树选项
// 是否停用
const statusFlagOptions = ref(undefined);
// const initPassword = ref(undefined);
// const postOptions = ref([]);
// const roleOptions = ref([]);
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 10,
searchKey: undefined, // 供应商名称
// busNo: undefined, // 编码
typeEnum: undefined, // 状态(包括 1预置2启用3停用
// sourceEnum: undefined, // 来源(包括 1厂商/产地目录分类2自定义
},
rules: {
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
typeEnum: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
orgId: [{ required: true, message: '提供部门不能为空', trigger: 'blur' }],
// address: [{ required: true, message: "地址不能为空", trigger: "blur" }],
// phone: [{ required: true, message: "联系人电话不能为空", trigger: "blur" }],
// email: [{ required: true, message: "联系人邮箱不能为空", trigger: "blur" },
// { validator: validateEmail, trigger: 'blur' }
// ],
},
});
const { queryParams, form, rules } = toRefs(data);
/** 验证邮箱地址是否有效*/
function validateEmail(rule, value, callback) {
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(value)) {
callback(new Error('请输入有效的邮箱地址'));
} else {
callback();
}
}
/** 厂商种类查询下拉树结构 */
function getsupplierTypeList() {
getSupplierInit().then((response) => {
console.log(response, 'response');
supplierTypeOptions.value = response.data.supplierTypeOptions;
});
}
/** 查询部门下拉树结构 */
function getDeptTree() {
console.log('查询部门下拉树结构');
deptTreeSelect().then((response) => {
console.log(response, 'response查询部门下拉树结构');
deptOptions.value = response.data.records;
console.log(deptOptions.value, '部门下拉树结构');
});
}
/** 查询厂商/产地目录列表 */
function getList() {
loading.value = true;
// queryParams.value.statusEnum = +queryParams.value.statusEnum
console.log(queryParams.value, 'queryParams.value');
getSupplierList(queryParams.value).then((res) => {
loading.value = false;
console.log(res, 'res', res.data.records);
supplierList.value = res.data.records;
console.log(supplierList.value, 'supplierList.value');
total.value = res.data.total;
console.log(total.value, 'total.value');
});
}
/** 节点单击事件 */
function handleNodeClick(data) {
queryParams.value.sourceEnum = data.id;
handleQuery();
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNo = 1;
getList();
}
/** 启用按钮操作 */
function handleStart(row) {
const stardIds = row.id || ids.value;
proxy.$modal
.confirm('是否确定启用数据!')
.then(function () {
return startSupplier(stardIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess('启用成功');
})
.catch(() => {});
}
/** 停用按钮操作 */
function handleClose(row) {
const stopIds = row.id || ids.value;
proxy.$modal
.confirm('是否确认停用数据!')
.then(function () {
return stopSupplier(stopIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess('停用成功');
})
.catch(() => {});
}
/** 清空条件按钮操作 */
function handleClear() {
// queryParams.value.pageNo = 1;
// queryParams.value.searchKey = undefined;
// queryParams.value.statusEnum = undefined;
// queryParams.value.sourceEnum = undefined;
// queryParams.value.busNo = undefined;
// 清空查询条件
proxy.resetForm('queryRef');
getList();
}
/** 选择条数 */
function handleSelectionChange(selection) {
console.log(selection, 'selection');
// selectedData.value = selection.map((item) => ({ ...item })); // 存储选择的行数据
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 重置操作表单 */
function reset() {
form.value = {
id: undefined,
conditionCode: undefined,
pyStr: undefined,
status: undefined,
statusEnum: undefined,
};
proxy.resetForm('supplierRef');
}
/** 取消按钮 */
function cancel() {
open.value = false;
reset();
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = '新增';
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
console.log(row, 'row');
form.value = JSON.parse(JSON.stringify(row));
form.value.activeFlag == 1 ? (form.value.activeFlag = true) : (form.value.activeFlag = false); //是否为活性
// console.log(form.value.ty, "form.value");
open.value = true;
title.value = '厂商/产地编辑';
}
/** 提交按钮 */
function submitForm() {
proxy.$refs['supplierRef'].validate((valid) => {
if (valid) {
form.value.activeFlag == true ? (form.value.activeFlag = 1) : (form.value.activeFlag = 0); //是否为活性
console.log(form.value, '*****************');
if (form.value.id != undefined) {
console.log(form.value, 'editSupplier');
editSupplier(form.value).then((response) => {
proxy.$modal.msgSuccess('修改成功');
open.value = false;
getList();
});
} else {
addSupplier(form.value).then((response) => {
proxy.$modal.msgSuccess('新增成功');
open.value = false;
getList();
});
}
}
});
}
/** 详细按钮操作 */
function handleView(row) {
reset();
title.value = '查看';
open.value = true;
getSupplierOne(row.id).then((response) => {
console.log(response, 'responsebbbb', row.id);
form.value = response.data;
});
}
getsupplierTypeList();
getList();
getDeptTree();
</script>
<style scoped>
.custom-tree-node {
display: flex;
align-items: center;
}
</style>

View File

@@ -0,0 +1,129 @@
import request from '@/utils/request'
/**
* 获取个人组套
* @param {*} queryParams
*/
export function getPersonalList(queryParams) {
return request({
url: '/personalization/orders-group-package/get-personal',
method: 'get',
params: queryParams
})
}
/**
* 获取科室组套
* @param {*} queryParams
*/
export function getDeptList(queryParams) {
return request({
url: '/personalization/orders-group-package/get-organization',
method: 'get',
params: queryParams
})
}
/**
* 获取全院组套
* @param {*} queryParams
*/
export function getAllList(queryParams) {
return request({
url: '/personalization/orders-group-package/get-hospital',
method: 'get',
params: queryParams
})
}
/**
* 保存个人组套
* @param {*} data
*/
export function savePersonal(data) {
return request({
url: '/personalization/orders-group-package/save-personal',
method: 'post',
data: data
})
}
/**
* 保存科室组套
* @param {*} data
*/
export function saveDepartment(data) {
return request({
url: '/personalization/orders-group-package/save-organization',
method: 'post',
data: data
})
}
/**
* 保存全院组套
* @param {*} data
*/
export function saveAll(data) {
return request({
url: '/personalization/orders-group-package/save-hospital',
method: 'post',
data: data
})
}
/**
* 查询组套明细
* @param {*} data
*/
export function queryGroupDetail(params) {
return request({
url: '/personalization/orders-group-package/get-group-package-detail',
method: 'get',
params: params
})
}
/**
* 删除组套
* @param {*} data
*/
export function deleteGroup(data) {
return request({
url: '/personalization/orders-group-package/group-package-detail?groupPackageId=' + data.groupPackageId,
method: 'delete'
})
}
/**
* 查询参与者下拉列表
* @param {*} data
*/
export function queryParticipantList(params) {
return request({
url: '/app-common/practitioner-list',
method: 'get',
params: params
})
}
/**
* 获取科室列表
*/
export function getOrgTree() {
return request({
url: '/base-data-manage/organization/organization',
method: 'get',
})
}
/**
* 获取中药列表
*/
export function getTcmMedicine(params) {
return request({
url: '/doctor-station/chinese-medical/tcm-advice-base-info',
method: 'get',
params: params,
});
}

View File

@@ -0,0 +1,269 @@
<template>
<div @keyup="handleKeyDown" tabindex="0" ref="tableWrapper" class="table-container">
<el-table
ref="adviceBaseRef"
height="350"
:data="adviceBaseList"
highlight-current-row
@current-change="handleCurrentChange"
row-key="adviceCode"
v-loading="loading"
@cell-click="clickRow"
>
<el-table-column label="名称" align="center" prop="adviceName" />
<el-table-column label="类型" align="center" prop="categoryCodeText" />
<el-table-column label="医保等级" align="center" prop="chrgitmLv_dictText" />
<el-table-column label="包装单位" align="center" prop="unitCode_dictText" />
<el-table-column label="最小单位" align="center" prop="minUnitCode_dictText" />
<el-table-column label="库存数量" align="center">
<template #default="scope">{{ handleQuantity(scope.row) }}</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<div class="pagination-wrapper">
<el-pagination
v-model:current-page="queryParams.pageNum"
:page-size="20"
:total="total"
layout="prev, pager, next"
@current-change="handlePageChange"
/>
</div>
</div>
</template>
<script setup>
import { ref, watch, nextTick } from 'vue';
import { getTcmMedicine } from './api';
import { debounce } from 'lodash-es';
const props = defineProps({
searchKey: {
type: String,
default: '',
},
organizationId: {
type: String,
default: '',
},
});
const emit = defineEmits(['selectMedicine']);
const total = ref(0);
const loading = ref(false);
const adviceBaseRef = ref();
const tableWrapper = ref();
const currentIndex = ref(0);
const currentSelectRow = ref({});
const queryParams = ref({
pageSize: 20,
pageNum: 1,
searchKey: '',
organizationId: props.organizationId,
});
const adviceBaseList = ref([]);
// 前端缓存 - 使用 sessionStorage 持久化缓存
const TCM_CACHE_PREFIX = 'tcm_advice_cache_';
const TCM_CACHE_EXPIRE = 5 * 60 * 1000; // 缓存5分钟
function getTcmCacheKey(orgId, pageNum, searchKey) {
return TCM_CACHE_PREFIX + orgId + '_' + pageNum + '_' + (searchKey || 'none');
}
function getFromTcmCache(orgId, pageNum, searchKey) {
try {
const key = getTcmCacheKey(orgId, pageNum, searchKey);
const cachedData = sessionStorage.getItem(key);
if (!cachedData) return null;
const cacheData = JSON.parse(cachedData);
if (Date.now() - cacheData.timestamp > TCM_CACHE_EXPIRE) {
sessionStorage.removeItem(key);
return null;
}
console.log('从TCM前端缓存获取:', key);
return cacheData;
} catch (e) {
console.error('读取TCM缓存失败:', e);
return null;
}
}
function setToTcmCache(orgId, pageNum, searchKey, data, total) {
try {
const key = getTcmCacheKey(orgId, pageNum, searchKey);
const cacheData = {
data: data,
total: total,
timestamp: Date.now()
};
sessionStorage.setItem(key, JSON.stringify(cacheData));
console.log('写入TCM前端缓存:', key);
} catch (e) {
console.error('写入TCM缓存失败:', e);
}
}
// 防抖函数 - 避免重复请求
const debouncedGetList = debounce(
() => {
// 只有当 organizationId 有效时才请求
if (!queryParams.value.organizationId) {
console.log('organizationId 无效,跳过请求');
return;
}
getList();
},
300,
{ leading: false, trailing: true }
);
// 监听搜索关键字变化
watch(
() => props.searchKey,
(newValue) => {
queryParams.value.searchKey = newValue || '';
debouncedGetList();
},
{ deep: true }
);
// 监听 organizationId 变化
watch(
() => props.organizationId,
(newValue) => {
queryParams.value.organizationId = newValue;
// organizationId 变化时重新加载
if (newValue) {
debouncedGetList();
}
},
{ deep: true }
);
// 不再页面加载时立即请求,等待用户点击时再请求
function getList() {
const orgId = queryParams.value.organizationId;
const pageNum = queryParams.value.pageNum;
const searchKey = queryParams.value.searchKey;
// 尝试从本地缓存获取(只有第一页且无搜索关键字时使用缓存)
if (pageNum === 1 && !searchKey) {
const cached = getFromTcmCache(orgId, pageNum, searchKey);
if (cached) {
adviceBaseList.value = cached.data;
total.value = cached.total;
return;
}
}
// 显示 loading
loading.value = true;
getTcmMedicine(queryParams.value).then((res) => {
adviceBaseList.value = res.data.records || [];
total.value = res.data.total || 0;
// 缓存第一页数据
if (pageNum === 1 && !searchKey) {
setToTcmCache(orgId, pageNum, searchKey, adviceBaseList.value, total.value);
}
nextTick(() => {
currentIndex.value = 0;
if (adviceBaseList.value.length > 0 && adviceBaseRef.value) {
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
}
});
})
.finally(() => {
loading.value = false;
});
}
// 当前页改变
function handlePageChange(page) {
queryParams.value.pageNum = page;
getList();
}
// 处理键盘事件
const handleKeyDown = (event) => {
const key = event.key;
const data = adviceBaseList.value;
switch (key) {
case 'ArrowUp':
event.preventDefault();
if (currentIndex.value > 0) {
currentIndex.value--;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'ArrowDown':
event.preventDefault();
if (currentIndex.value < data.length - 1) {
currentIndex.value++;
setCurrentRow(data[currentIndex.value]);
}
break;
case 'Enter':
event.preventDefault();
if (currentSelectRow.value) {
emit('selectMedicine', currentSelectRow.value);
}
break;
}
};
function handleQuantity(row) {
if (row.inventoryList) {
const totalQuantity = row.inventoryList.reduce((sum, item) => sum + (item.quantity || 0), 0);
return totalQuantity.toString() + (row.minUnitCode_dictText || '');
}
return 0;
}
// 设置选中行(带滚动)
const setCurrentRow = (row) => {
adviceBaseRef.value.setCurrentRow(row);
const tableBody = adviceBaseRef.value.$el.querySelector('.el-table__body-wrapper');
const currentRowEl = adviceBaseRef.value.$el.querySelector('.current-row');
if (tableBody && currentRowEl) {
currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
};
// 当前行变化时更新索引
const handleCurrentChange = (currentRow) => {
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
currentSelectRow.value = currentRow;
};
function clickRow(row) {
emit('selectMedicine', row);
}
defineExpose({
handleKeyDown,
});
</script>
<style scoped>
.table-container {
display: flex;
flex-direction: column;
}
.pagination-wrapper {
display: flex;
justify-content: center;
padding: 10px 0;
background: #fff;
}
.popover-table-wrapper:focus {
outline: 2px solid #409eff;
}
</style>

View File

@@ -0,0 +1,820 @@
<template>
<div class="app-container tcm-medical-order-set">
<el-row :gutter="20" class="full-height">
<!-- 左侧树形结构 -->
<el-col :span="5" class="left-panel">
<el-card shadow="never" class="tree-card panel-shadow">
<template #header>
<span>中医组套</span>
</template>
<el-tree
:data="treeData"
node-key="id"
:props="treeProps"
default-expand-all
highlight-current
@node-click="(data, node) => handleTreeNodeClick(data, node)"
/>
</el-card>
</el-col>
<!-- 右侧操作区域 -->
<el-col :span="19" class="right-panel">
<!-- 上方表单:组套公共字段 -->
<el-card shadow="never" class="form-card panel-shadow">
<div style="display: flex; justify-content: flex-end; margin-bottom: 10px">
<el-button type="primary" plain @click="clearForm">清空 </el-button>
<el-button type="primary" @click="createNew">新建 </el-button>
<el-button type="primary" @click="saveCommonForm">保存 </el-button>
</div>
<div class="common-bar">
<el-form :model="commonForm" :inline="true" class="demo-form-inline">
<el-form-item label="组套名称">
<el-input v-model="commonForm.name" placeholder="组套名称" clearable />
</el-form-item>
<el-form-item label="使用范围">
<el-select
v-model="commonForm.useRange"
placeholder="个人/科室/全院"
clearable
@change="handleRangeChange"
>
<el-option label="个人" value="personal" />
<el-option label="科室" value="department" />
<el-option label="全院" value="hospital" />
</el-select>
</el-form-item>
<el-form-item label="使用人" v-if="commonForm.useRange === 'personal'">
<el-select
v-model="commonForm.practitionerId"
placeholder="请选择使用人"
clearable
style="width: 200px"
>
<el-option
v-for="item in participantListOptions"
:key="item.practitionerId"
:label="item.practitionerName"
:value="item.practitionerId"
/>
</el-select>
</el-form-item>
<el-form-item label="科室" v-if="commonForm.useRange === 'department'">
<el-tree-select
clearable
v-model="commonForm.organizationId"
:data="organization"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
default-expand-all
placeholder="请选择科室"
style="width: 200px"
:render-after-expand="false"
/>
</el-form-item>
<el-form-item label="用法">
<el-select v-model="commonForm.method" placeholder="用法" clearable>
<el-option
v-for="item in method_code"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="频次">
<el-select v-model="commonForm.frequency" placeholder="频次" clearable>
<el-option
v-for="item in rate_code"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 下方:中药明细卡片列表 -->
<el-card shadow="never" class="detail-card panel-shadow" style="height: 100%">
<div class="drug-card-list">
<!-- 已有药品卡片 -->
<el-card
v-for="(item, index) in drugList"
:key="item.id"
class="drug-card"
shadow="never"
>
<template v-if="!item.isEditing">
<div class="drug-card-body">
<div class="drug-top">
<div class="drug-view-name">{{ item.drugName || '-' }}</div>
</div>
<div class="drug-divider"></div>
<div class="drug-bottom" style="margin-top: 10px">
<div class="drug-bottom-left">
<span class="drug-view-qty">{{ item.quantity }} {{ item.unit }}</span>
</div>
<div class="drug-view-actions">
<el-button
type="primary"
text
:icon="Edit"
@click.stop="editDrug(item)"
title="编辑"
/>
<el-button
v-if="drugList.length > 1"
type="danger"
text
:icon="Delete"
@click.stop="removeDrug(index)"
title="删除"
/>
</div>
</div>
</div>
</template>
<template v-else>
<div class="drug-card-body">
<div class="drug-top">
<el-form :model="item" class="drug-card-form">
<el-form-item>
<el-popover
:popper-style="{ padding: '0' }"
placement="bottom-start"
:visible="item.showPopover"
:width="800"
>
<tcmMedicineList
:ref="(el) => setMedicineTableRef(el, item)"
:searchKey="item.searchKey"
:organizationId="commonForm.organizationId"
@selectMedicine="(row) => handleSelectMedicine(row, item)"
/>
<template #reference>
<el-input
v-model="item.drugName"
placeholder="请选择中药"
clearable
@input="(val) => handleSearchKeyChange(val, item)"
@click="() => handleClick(item)"
@keydown="(e) => handleKeyDown(e, item)"
@blur="() => handleBlur(item)"
/>
</template>
</el-popover>
</el-form-item>
</el-form>
</div>
<div class="drug-bottom">
<div class="drug-bottom-left">
<div class="quantity-wrap">
<el-input v-model="item.quantity" />
<el-input value="g" style="width: 80px" disabled />
</div>
</div>
</div>
<div class="drug-edit-actions">
<el-button type="primary" link @click.stop="saveDrug(item)"> 保存 </el-button>
<el-button type="danger" link @click.stop="removeDrug(index)"> 删除 </el-button>
</div>
</div>
</template>
</el-card>
<!-- 始终存在的新增卡片 -->
<el-card class="drug-card add-card" shadow="never" @click="addDrug">
<div class="add-card-content">
<el-icon class="add-icon">
<Plus />
</el-icon>
<span>新增</span>
</div>
</el-card>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup name="TcmMedicalOrderSet">
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
import { ElMessage } from 'element-plus';
import {
getPersonalList,
getDeptList,
getAllList,
queryGroupDetail,
queryParticipantList,
getOrgTree,
savePersonal,
saveDepartment,
saveAll,
} from './components/api';
import TcmMedicineList from './components/tcmMedicineList.vue';
import { Edit, Delete, Plus } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance();
// 字典数据
const { method_code, rate_code } = proxy.useDict('method_code', 'rate_code');
// 左侧树形结构数据
const treeData = ref([]);
const treeProps = {
children: 'children',
label: 'label',
value: 'value',
};
// 加载左侧树数据
function loadTreeData() {
Promise.all([
getPersonalList({ tcmFlag: 1 }),
getDeptList({ tcmFlag: 1 }),
getAllList({ tcmFlag: 1 }),
])
.then(([personalRes, deptRes, allRes]) => {
const personalList = personalRes?.data || [];
const deptList = deptRes?.data || [];
const allList = allRes?.data || [];
treeData.value = [
{
id: 'personal',
label: '个人',
children: personalList.map((item) => ({
id: item.groupPackageId,
value: item.groupPackageId,
label: item.name,
practitionerId: item.practitionerId || '',
organizationId: '',
})),
},
{
id: 'department',
label: '科室',
children: deptList.map((item) => ({
id: item.groupPackageId,
value: item.groupPackageId,
label: item.name,
practitionerId: '',
organizationId: item.organizationId || '',
})),
},
{
id: 'hospital',
label: '全院',
children: allList.map((item) => ({
id: item.groupPackageId,
value: item.groupPackageId,
label: item.name,
practitionerId: '',
organizationId: '',
})),
},
];
})
.catch(() => {
ElMessage.error('加载组套树失败');
});
}
onMounted(() => {
loadTreeData();
loadParticipantList();
loadOrganization();
});
// 使用人列表
const participantListOptions = ref([]);
// 科室树形数据
const organization = ref([]);
// 加载使用人列表
function loadParticipantList() {
queryParticipantList().then((res) => {
participantListOptions.value = res.data || [];
});
}
// 加载科室树
function loadOrganization() {
getOrgTree().then((res) => {
organization.value = res.data.records || [];
});
}
// 使用范围变更
function handleRangeChange() {
commonForm.practitionerId = '';
commonForm.organizationId = '';
}
// 上方公共字段表单
const commonForm = reactive({
name: '',
useRange: '',
practitionerId: '',
organizationId: '',
method: '',
frequency: '',
});
// 下方药品卡片列表
const drugList = ref([]);
const nextDrugId = ref(1);
const currentSetName = ref('');
// 动态设置表格组件引用
function setMedicineTableRef(el, item) {
if (el) {
item.tableRef = el;
}
}
function createEmptyDrug() {
return {
id: nextDrugId.value++,
drugCode: '',
drugName: '',
orderDefinitionId: '',
quantity: 1,
unitCode: '',
unit: 'g',
isEditing: true,
showPopover: false,
searchKey: '',
tableRef: null,
rateCode: '',
methodCode: '',
};
}
// 点击树节点
function handleTreeNodeClick(data, node) {
if (data.children != undefined) {
return;
}
// 从父节点获取使用范围personal/department/hospital
const useRange = node.parent?.data?.id || '';
// 组套名称从树节点获取
commonForm.name = data.label || '';
commonForm.useRange = useRange;
// 从树节点数据中获取使用人/科室
commonForm.practitionerId = data.practitionerId || '';
commonForm.organizationId = data.organizationId || '';
// 调用查询接口回显数据
queryGroupDetail({ groupPackageId: data.value })
.then((res) => {
const detailList = res.data || [];
if (detailList.length === 0) {
drugList.value = [createEmptyDrug()];
return;
}
// 取第一个药品的频次和用法赋值到表单
const firstDrug = detailList[0];
commonForm.method = firstDrug.methodCode || '';
commonForm.frequency = firstDrug.rateCode || '';
// 回显药品列表
drugList.value = detailList.map((detail, index) => ({
id: index,
drugCode: '',
drugName: detail.orderDefinitionName || '',
orderDefinitionId: detail.orderDefinitionId,
quantity: detail.quantity,
unitCode: detail.unitCode,
unit: detail.unitCode_dictText || 'g',
isEditing: false,
showPopover: false,
searchKey: '',
tableRef: null,
rateCode: detail.rateCode,
methodCode: detail.methodCode,
}));
currentSetName.value = data.label;
})
.catch(() => {
ElMessage.error('加载组套详情失败');
});
}
// 新增药品
function addDrug() {
// 如果有药品列表,检查上一个药品是否未保存
if (drugList.value.length > 0) {
const last = drugList.value[drugList.value.length - 1];
// 如果上一个药品未选择药品,不允许添加下一个
if (!last.orderDefinitionId) {
ElMessage.warning('请先选择上一个药品');
return;
}
// 如果上一个药品已有药品ID且处于编辑状态自动保存
if (last.isEditing) {
const msg = validateDrug(last);
if (!msg) {
last.isEditing = false;
}
}
}
drugList.value.push(createEmptyDrug());
}
// 删除药品
function removeDrug(index) {
if (drugList.value.length <= 1) {
ElMessage.warning('至少保留一条药品信息');
return;
}
drugList.value.splice(index, 1);
}
function validateDrug(item) {
if (!item.orderDefinitionId) return '请选择中药';
if (!item.quantity || Number(item.quantity) <= 0) return '请填写数量';
return '';
}
function saveDrug(item) {
const msg = validateDrug(item);
if (msg) {
ElMessage.warning(msg);
return false;
}
item.isEditing = false;
return true;
}
function editDrug(item) {
item.isEditing = true;
}
// 处理搜索关键字变化
function handleSearchKeyChange(value, item) {
item.searchKey = value;
}
// 处理点击输入框
function handleClick(item) {
item.showPopover = true;
}
// 处理失焦
function handleBlur(item) {
item.showPopover = false;
item.searchKey = '';
}
// 处理键盘事件(上下键和回车)
function handleKeyDown(event, item) {
if (!item.showPopover) return;
const key = event.key;
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(key)) {
event.preventDefault();
if (item.tableRef) {
item.tableRef.handleKeyDown(event);
}
}
}
// 选择中药时,回填信息
function handleSelectMedicine(row, item) {
item.drugCode = row.adviceCode;
item.drugName = row.adviceName;
item.orderDefinitionId = row.adviceDefinitionId;
item.unitCode = row.unitCode;
item.unit = row.unitCode_dictText || 'g';
item.showPopover = false;
item.searchKey = '';
}
// 清空表单
function clearForm() {
commonForm.name = '';
commonForm.useRange = '';
commonForm.practitionerId = '';
commonForm.organizationId = '';
commonForm.method = '';
commonForm.frequency = '';
drugList.value = [];
}
// 新建组套
function createNew() {
clearForm();
commonForm.useRange = 'personal';
drugList.value = [createEmptyDrug()];
}
// 保存组套
function saveCommonForm() {
// 校验组套名称
if (!commonForm.name) {
ElMessage.warning('请填写组套名称');
return;
}
// 校验使用范围
if (!commonForm.useRange) {
ElMessage.warning('请选择使用范围');
return;
}
// 校验个人时选择使用人
if (commonForm.useRange === 'personal' && !commonForm.practitionerId) {
ElMessage.warning('请选择使用人');
return;
}
// 校验科室时选择科室
if (commonForm.useRange === 'department' && !commonForm.organizationId) {
ElMessage.warning('请选择科室');
return;
}
// 校验药品列表
if (drugList.value.length === 0) {
ElMessage.warning('请添加药品');
return;
}
// 校验每个药品
for (let i = 0; i < drugList.value.length; i++) {
const item = drugList.value[i];
if (!item.orderDefinitionId) {
ElMessage.warning(`请选择第${i + 1}个药品`);
return;
}
if (!item.quantity || Number(item.quantity) <= 0) {
ElMessage.warning(`请填写第${i + 1}个药品的数量`);
return;
}
}
// 生成组套ID时间戳
const groupId = Date.now();
// 构建详情列表
const detailList = drugList.value.map((item) => ({
orderDefinitionId: item.orderDefinitionId,
orderDefinitionTable: 'order_definition',
quantity: item.quantity,
unitCode: item.unitCode,
dose: item.quantity,
rateCode: commonForm.frequency,
dispensePerDuration: 7,
methodCode: commonForm.method,
doseQuantity: item.quantity,
groupId: groupId,
}));
// 构建请求数据
const data = {
name: commonForm.name,
tcmFlag: 1,
detailList: detailList,
};
// 根据使用范围添加不同字段
if (commonForm.useRange === 'personal') {
data.practitionerId = commonForm.practitionerId;
} else if (commonForm.useRange === 'department') {
data.organizationId = commonForm.organizationId;
}
// 全院不传 practitionerId 和 organizationId
// 调用对应接口
let saveApi;
if (commonForm.useRange === 'personal') {
saveApi = savePersonal;
} else if (commonForm.useRange === 'department') {
saveApi = saveDepartment;
} else {
saveApi = saveAll;
}
saveApi(data)
.then(() => {
ElMessage.success('保存成功');
loadTreeData();
})
.catch((err) => {
ElMessage.error('保存失败:' + (err.message || '未知错误'));
});
}
</script>
<style scoped>
.tcm-medical-order-set {
height: 90vh;
}
.full-height {
height: 100%;
}
.left-panel,
.right-panel {
height: 100%;
}
.tree-card {
height: 100%;
}
.right-panel {
display: flex;
flex-direction: column;
gap: 10px;
}
.form-card,
.detail-card {
width: 100%;
}
.panel-shadow {
border: 1px solid var(--el-border-color);
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.1);
}
.common-form {
display: flex;
flex-wrap: wrap;
gap: 10px 14px;
}
.common-bar {
display: flex;
align-items: center;
justify-content: space-between;
}
.common-item {
width: 300px;
margin-bottom: 0;
}
.common-item :deep(.el-input__wrapper),
.common-item :deep(.el-select__wrapper) {
height: 40px;
}
.common-item :deep(.el-input__inner) {
height: 40px;
}
.drug-card-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.drug-card {
width: 220px;
border: 1px solid var(--el-border-color);
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.12);
height: 120px;
}
.drug-card :deep(.el-card__body) {
padding: 10px 10px 8px;
}
.drug-card-body {
display: flex;
flex-direction: column;
height: 120px;
}
.drug-top {
flex: 0 0 auto;
}
.drug-divider {
margin: 6px 0;
border-top: 1px dashed var(--el-border-color);
}
.drug-bottom {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.drug-bottom-left {
flex: 1 1 auto;
min-width: 0;
}
.drug-card-form {
margin-top: 0;
}
.compact-item {
margin-bottom: 8px;
}
.compact-item :deep(.el-form-item__label) {
padding: 0 0 4px;
line-height: 1.2;
}
.compact-item :deep(.el-form-item__content) {
line-height: 1.2;
}
.quantity-wrap {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: nowrap;
}
.unit-select {
width: 80px;
}
.drug-select {
flex: 1;
min-width: 0;
}
.drug-edit-actions {
display: flex;
justify-content: flex-end;
gap: 0px;
padding-top: 2px;
flex-shrink: 0;
}
.drug-view-name {
font-weight: 600;
color: var(--el-text-color-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.drug-view-qty {
font-size: 13px;
color: var(--el-text-color-regular);
flex: none;
}
.drug-view-actions {
display: flex;
justify-content: flex-end;
gap: 6px;
flex-shrink: 0;
}
.drug-view-actions :deep(.el-button),
.drug-edit-actions :deep(.el-button) {
padding: 4px;
min-width: 0;
}
.add-card {
border-style: dashed;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--el-color-primary);
box-shadow: 0 3px 14px rgba(64, 158, 255, 0.18);
}
.add-card-content {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.add-icon {
font-size: 18px;
}
.empty-tip {
margin-top: 80px;
}
.demo-form-inline .el-input {
--el-input-width: 200px;
}
.demo-form-inline .el-select {
--el-select-width: 200px;
}
.el-form-item--default {
margin-bottom: 10px;
}
</style>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,91 @@
import request from '@/utils/request'
/**
* 收费患者列表
*/
export function getList(queryParams) {
return request({
url: '/base-data-manage/location/location-page',
method: 'get',
params: queryParams
})
}
/**
* 获取科室下拉列表
*/
export function getOrgList(queryParams) {
return request({
url: '/base-data-manage/organization/organization',
method: 'get',
params: queryParams
})
}
/**
* 初始化
*/
export function init() {
return request({
url: '/base-data-manage/location/init',
method: 'get',
})
}
/**
* 新增病区/床位/病房
*/
export function addLocation(data) {
return request({
url: '/base-data-manage/location/location',
method: 'post',
data: data
})
}
/**
* 编辑病区/床位/病房
*/
export function editLocation(data) {
return request({
url: '/base-data-manage/location/location',
method: 'put',
data: data
})
}
/**
* 新增病区/床位/病房
*/
export function deleteLocation(busNo) {
return request({
url: '/base-data-manage/location/location?busNo=' + busNo,
method: 'delete',
})
}
/**
* 启用病区/床位/病房
*/
export function enableLocation(data) {
return request({
url: '/base-data-manage/location/enable',
method: 'put',
data: data
})
}
/**
* 停用病区/床位/病房
*/
export function unableLocation(data) {
return request({
url: '/base-data-manage/location/deactivate',
method: 'put',
data: data
})
}

View File

@@ -0,0 +1,758 @@
<template>
<div style="display: flex; justify-content: space-between; height: 90vh" class="app-container">
<el-card style="width: 30%">
<template #header>
<span style="vertical-align: middle">病区</span>
</template>
<div style="width: 100%">
<el-button type="primary" @click="onIncrease" class="mb8"> 新增 </el-button>
<el-button type="success" plain @click="handleEnableBatch('wardRef')" class="mb8">
批量启用
</el-button>
<el-button type="primary" plain @click="handleUnableBatch('wardRef')" class="mb8">
批量停用
</el-button>
<el-button style="float: right" @click="getWardList()" class="mb8" icon="refresh" />
<el-table
max-height="630"
:data="wardList"
@cell-click="(row) => clickRow(row, 10, 0)"
highlight-current-row
ref="wardRef"
>
<el-table-column type="selection" :selectable="checkSelectable" width="55" />
<el-table-column label="病区" align="center" prop="name" />
<el-table-column label="病区号" align="center" prop="startTime">
<template #default="scope">
{{ getLastPartOfString(scope.row.busNo) }}
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="statusEnum_enumText">
<template #default="scope">
<el-tag
:type="
scope.row.statusEnum === 1 || scope.row.statusEnum === 6
? 'success'
: scope.row.statusEnum === 2
? 'primary'
: 'warning'
"
>
{{ scope.row.statusEnum_enumText }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button type="primary" link @click.stop="handleEdit(scope.row)"> 编辑 </el-button>
<el-button
type="primary"
link
:disabled="scope.row.statusEnum === 5"
@click.stop="
() => {
handleUnable(scope.row, 10).then(() => {
getWardList();
});
}
"
v-if="
scope.row.statusEnum === 1 ||
scope.row.statusEnum === 6 ||
scope.row.statusEnum === 5
"
>
停用
</el-button>
<el-button
type="primary"
link
:disabled="scope.row.statusEnum === 5"
@click.stop="
() => {
handleEnable(scope.row).then(() => {
getWardList();
});
}
"
v-else-if="scope.row.statusEnum === 2"
>
启用
</el-button>
<el-button type="danger" link @click="handleDelete(scope.row)"> 删除 </el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<div style="width: 69%; height: 100%; padding-bottom: 20px">
<el-card style="margin-bottom: 20px">
<template #header>
<span style="vertical-align: middle">病房</span>
</template>
<el-button type="success" plain @click="handleEnableBatch('hourseRef')">批量启用</el-button>
<el-button type="primary" plain @click="handleUnableBatch('hourseRef')">批量停用</el-button>
<el-table
height="280"
:data="houseList"
@cell-click="(row) => clickRow(row, 8, 0, 1)"
highlight-current-row
v-loading="loading"
ref="hourseRef"
>
<el-table-column type="selection" :selectable="checkSelectable" width="55" />
<el-table-column label="病房" align="center" prop="name" />
<el-table-column label="病房号" align="center" prop="busNo">
<template #default="scope">
{{ getLastPartOfString(scope.row.busNo) }}
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="statusEnum_enumText">
<template #default="scope">
<el-tag
:type="
scope.row.statusEnum === 1 || scope.row.statusEnum === 6
? 'success'
: scope.row.statusEnum === 2
? 'primary'
: 'warning'
"
>
{{ scope.row.statusEnum_enumText }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="primary" link @click.stop="handleEdit(scope.row, 4)">
编辑
</el-button>
<el-button
type="primary"
link
:disabled="scope.row.statusEnum === 5"
@click.stop="
() => {
handleUnable(scope.row).then(() => {
getHouseList();
});
}
"
v-if="
scope.row.statusEnum === 1 ||
scope.row.statusEnum === 6 ||
scope.row.statusEnum === 5
"
>
停用
</el-button>
<el-button
type="primary"
link
:disabled="scope.row.statusEnum === 5"
@click.stop="
() => {
handleEnable(scope.row).then(() => {
getHouseList();
});
}
"
v-else-if="scope.row.statusEnum === 2"
>
启用
</el-button>
<el-button type="danger" link @click="handleDelete(scope.row)"> 删除 </el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-card>
<template #header>
<span style="vertical-align: middle">床位</span>
</template>
<el-button type="success" plain @click="handleEnableBatch('bedRef')">批量启用</el-button>
<el-button type="primary" plain @click="handleUnableBatch('bedRef')">批量停用</el-button>
<el-table ref="bedRef" height="270" :data="bedList" v-loading="loading" width="">
<el-table-column type="selection" :selectable="checkSelectable" width="55" />
<el-table-column label="病床" align="center" prop="name" />
<el-table-column label="病床号" align="center" prop="busNo">
<template #default="scope">
{{ getLastPartOfString(scope.row.busNo) }}
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="statusEnum_enumText">
<template #default="scope">
<el-tag
:type="
scope.row.statusEnum === 1 || scope.row.statusEnum === 6
? 'success'
: scope.row.statusEnum === 2
? 'primary'
: 'warning'
"
>{{ scope.row.statusEnum_enumText }}</el-tag
>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="primary" link @click.stop="handleEdit(scope.row, 10)">
编辑
</el-button>
<el-button
type="primary"
link
:disabled="scope.row.statusEnum === 5"
@click.stop="
() => {
handleUnable(scope.row, 10).then(() => {
getBedList();
});
}
"
v-if="
scope.row.statusEnum === 1 ||
scope.row.statusEnum === 6 ||
scope.row.statusEnum === 5
"
>
停用
</el-button>
<el-button
type="primary"
link
:disabled="scope.row.statusEnum === 5"
@click.stop="
() => {
handleEnable(scope.row, 10).then(() => {
getBedList();
});
}
"
v-else-if="scope.row.statusEnum === 2"
>
启用
</el-button>
<el-button type="danger" link @click="handleDelete(scope.row)"> 删除 </el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<el-dialog :title="title" v-model="open" width="600px" @close="cancel" append-to-body>
<el-form ref="orgRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="id" prop="id" v-show="false">
<el-input v-model="form.id" placeholder="请输入科室编号" />
</el-form-item>
<el-form-item label="科室编号" prop="busNo" v-show="false">
<el-input v-model="form.busNo" placeholder="请输入科室编号" />
</el-form-item>
<el-form-item :label="type + '分类'" prop="formEnum">
<el-radio-group v-model="form.formEnum" @change="handleRadioChange" :disabled="isEdit">
<el-radio :label="4">病区</el-radio>
<el-radio :label="10">病房</el-radio>
<el-radio :label="8">床位</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="type + '名称'" prop="name">
<el-input v-model="form.name" :placeholder="'请输入' + type + '名称'" />
</el-form-item>
<el-col>
<el-form-item :label="upLabel" prop="busNoParent">
<template v-if="form.formEnum == 4">
<el-tree-select
clearable
style="width: 100%"
v-model="form.busNoParent"
filterable
:data="organization"
:props="{
value: 'id',
label: 'name',
children: 'children',
}"
value-key="id"
check-strictly
placeholder="请选择上级科室/病区/床位"
/>
</template>
<template v-else>
<el-select
v-model="form.busNoParent"
placeholder="请选择上级科室/病区/床位"
clearable
filterable
style="width: 100%"
>
<el-option
v-for="item in wardListOption"
:key="item.busNo"
:label="item.name"
:value="item.busNo"
/>
</el-select>
</template>
</el-form-item>
</el-col>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Ward">
import {onMounted, ref} from 'vue';
import {
addLocation,
deleteLocation,
editLocation,
enableLocation,
getList,
getOrgList,
unableLocation,
} from './components/api';
import {ElMessage} from 'element-plus';
const { proxy } = getCurrentInstance();
// 病区参数
const queryParams = ref({
pageNum: 1,
pageSize: 50,
formEnum: 4, //4 病区 10 病房 8床位
// locationFormList: [4],
});
// 病房参数
const queryHouseParams = ref({
pageNum: 1,
pageSize: 50,
formEnum: 10, //4 病区 10 病房 8床位
// locationFormList: [4],
});
// 病床参数
const querybedParams = ref({
pageNum: 1,
pageSize: 50,
formEnum: 8, //4 病区 10 病房 8床位
// locationFormList: [4],
});
// 病区、病房、病床类型
const type = ref('病区');
// 病区列表
const wardList = ref([]);
// 床位列表
const bedList = ref([]);
// 病房列表
const houseList = ref([]);
const wardListOption = ref([]);
const organization = ref([]);
const loading = ref(false);
const isEdit = ref(false);
const open = ref(false);
// 病区row
const wardRow = ref({});
// 床位row
const bedRow = ref({});
// 病房row
const houseRow = ref({});
// 记录点击的是启用
const clickType = ref(0);
const orgRef = ref();
// 新增数据参数
const form = reactive({
formEnum: 4,
busNoParent: '',
organizationId: '',
name: '',
busNo: '',
});
const upLabel = ref('关联科室');
const title = ref('新增');
const rules = ref({
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ required: true, min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
],
busNoParent: [
{
required: true,
message: '请选择上级' + type.value,
trigger: 'blur',
},
],
});
// 递归过滤树形数据只保留classEnum包含住院(2)的科室
function filterOrgByClassEnum(orgList, targetClassEnum = '2') {
if (!orgList || !Array.isArray(orgList)) return [];
const result = [];
for (const item of orgList) {
// 深拷贝当前节点,避免修改原始数据
const newItem = { ...item };
// 检查当前节点的classEnum是否包含目标值
let isMatch = false;
if (newItem.classEnum !== null && newItem.classEnum !== undefined) {
const classEnumStr = String(newItem.classEnum);
const classEnumValues = classEnumStr.split(',').map(v => v.trim());
isMatch = classEnumValues.includes(targetClassEnum);
}
// 递归过滤子节点
if (newItem.children && newItem.children.length > 0) {
newItem.children = filterOrgByClassEnum(newItem.children, targetClassEnum);
}
// 如果当前节点匹配或者有匹配的子节点,则保留
if (isMatch || (newItem.children && newItem.children.length > 0)) {
result.push(newItem);
}
}
return result;
}
// 获取科室下啦树
function init() {
getOrgList().then((res) => {
const records = res.data.records || [];
// 过滤只保留住院科室(classEnum包含2)
organization.value = filterOrgByClassEnum(records, '2');
});
}
/**
* 病区列表
*/
function getWardList() {
houseList.value = [];
bedList.value = [];
getList(queryParams.value).then((res) => {
wardList.value = res.data.records;
});
}
// 获取病房列表
const getHouseList = () => {
// 4病区 10病房 8床位
loading.value = true;
// 病区号
queryHouseParams.value.busNo = wardRow.value.busNo;
bedList.value = [];
getList(queryHouseParams.value).then((res) => {
houseList.value = res.data.records;
loading.value = false;
});
};
// 获取床位列表
const getBedList = () => {
// 4病区 10病房 8床位
loading.value = true;
querybedParams.value.busNo = houseRow.value.busNo;
bedList.value = [];
getList(querybedParams.value).then((res) => {
bedList.value = res.data.records;
loading.value = false;
});
};
// 点击新增按钮
const onIncrease = () => {
open.value = true;
};
// 查询病房和病区的下拉选
const getHomeOrBed = (formEnum) => {
const params = {
formEnum,
pageNum: 1,
pageSize: 50,
};
getList(params).then((res) => {
if (formEnum == 10) {
const datas = res.data?.records || [];
const optionsList = datas.map((item) => {
let obj = {
...item,
};
obj.name = item.parentName + '-' + item.name;
return obj;
});
wardListOption.value = optionsList;
} else {
wardListOption.value = res.data?.records || [];
}
});
};
function handleRadioChange(val) {
let formEnum = 4;
if (val == 4) {
type.value = '病区';
upLabel.value = '关联科室';
return;
} else if (val == 10) {
type.value = '病房';
upLabel.value = '所属病区';
formEnum = 4;
// queryParams.value.formEnum = 4;
} else {
type.value = '床位';
upLabel.value = '所属病房';
formEnum = 10;
// queryParams.value.formEnum = 10;
}
form.organizationId = '';
form.busNo = '';
form.busNoParent = '';
form.name = '';
getHomeOrBed(formEnum);
}
/**
* 点击患者列表行 获取处方列表
*/
function clickRow(row, val, type) {
// 1点击了启用
clickType.value = type;
console.log('val=====>', JSON.stringify(row));
console.log('type=====>', JSON.stringify(type));
console.log('val=====>', JSON.stringify(val));
// if (type !== 1) {
// if (val == 10) {
// wardRow.value = row;
// getHouseList();
// } else if (val == 8) {
// houseRow.value = row;
// getBedList();
// }
// } else {
// if (val == 10) {
// houseRow.value = row;
// getHouseList();
// } else if (val == 8) {
// bedRow.value = row;
// getBedList();
// }
// }
if (val == 10) {
wardRow.value = row;
getHouseList();
} else if (val == 8) {
houseRow.value = row;
console.log('houseRow=====>', houseRow.value);
getBedList();
}
}
function checkSelectable(row, index) {
return row.statusEnum !== 5;
}
function handleEnable(row) {
return enableLocation([row.id]).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('启用成功');
}
});
}
function handleUnable(row) {
return unableLocation([row.id]).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('停用成功');
}
});
}
function handleEnableBatch(tableRef) {
let list = proxy.$refs[tableRef].getSelectionRows().map((item) => {
return item.id;
});
if (list.length == 0) {
proxy.$modal.msgError('请选择要启用的数据');
return;
}
enableLocation(list).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.message);
}
});
}
function handleUnableBatch(tableRef) {
let list = proxy.$refs[tableRef].getSelectionRows().map((item) => {
return item.id;
});
if (list.length == 0) {
proxy.$modal.msgError('请选择要停用的数据');
return;
}
unableLocation(list).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess(res.message);
}
});
}
// 新增病床拆分"busNo": "LOC055.LOC056",
const splitBusNo = (busNo) => {
const busNoArr = busNo.split('.') || [];
let busNoParent = '';
if (busNoArr.length > 1) {
busNoParent = busNoArr[0];
}
return busNoParent;
};
function submitForm() {
if (!orgRef) return;
const params = {
...form,
};
console.log('form========>', JSON.stringify(form));
console.log('params11========>', JSON.stringify(params));
orgRef.value.validate((valid) => {
if (valid) {
console.log('表单验证通过,准备提交数据');
if (form.busNoParent) {
if (form.formEnum == 4) {
params.organizationId = form.busNoParent;
} else {
if (!isEdit.value) {
params.busNo = form.busNoParent;
// params.busNoParent = splitBusNo(form.busNoParent);
}
}
} else {
if (form.formEnum == 4) {
if (!(params.organizationId && params.organizationId.length > 0)) {
ElMessage({
type: 'error',
message: '请选择关联科室!',
});
return;
}
} else if (form.formEnum == 10) {
if (!(params.busNo && params.busNo.length > 0)) {
ElMessage({
type: 'error',
message: '请选择所属病区!',
});
return;
}
} else {
if (!(params.busNo && params.busNo.length > 8)) {
ElMessage({
type: 'error',
message: '请选择所属病房!',
});
return;
}
}
}
// console.log('params========>', JSON.stringify(form));
console.log('params========>', JSON.stringify(params));
// console.log('params========>', isEdit.value);
if (!isEdit.value) {
addLocation(params).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
if (params.formEnum == 4) {
cancel();
getWardList();
} else if (params.formEnum == 10) {
getHouseList();
open.value = false;
} else if (params.formEnum == 8) {
getBedList();
open.value = false;
}
}
});
} else {
editLocation(params).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
if (params.formEnum == 4) {
cancel();
getWardList();
} else if (params.formEnum == 10) {
getHouseList();
open.value = false;
} else if (params.formEnum == 8) {
getBedList();
open.value = false;
}
}
});
}
}
});
}
function handleDelete(row) {
deleteLocation(row.busNo).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('删除成功');
if (row.formEnum == 4) {
getWardList();
} else if (row.formEnum == 10) {
getHouseList();
} else {
getBedList();
}
}
});
}
function getLastPartOfString(str) {
const parts = str.split('.');
return parts.pop();
}
function handleEdit(row, val) {
console.log('editRow=========>', JSON.stringify(row));
form.id = row.id;
form.name = row.name;
form.formEnum = row.formEnum;
form.busNo = row.busNo;
if (row.organizationId) {
form.busNoParent = row.organizationId;
form.organizationId = row.organizationId;
} else {
form.busNoParent = row.busNo.split('.').slice(0, -1).join('.');
}
isEdit.value = true;
title.value = '编辑';
open.value = true;
console.log('editRow1=========>', JSON.stringify(form));
if (val == 4) {
getHomeOrBed(4);
} else if (val == 10) {
getHomeOrBed(10);
}
}
function cancel() {
open.value = false;
form.id = undefined;
form.name = '';
form.formEnum = 4;
form.busNo = '';
form.busNoParent = '';
form.organizationId = '';
isEdit.value = false;
title.value = '新增';
}
// 页面挂在成功
onMounted(() => {
// 获取所有科室
init();
// 获取病区列表
getWardList();
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,71 @@
import request from '@/utils/request'
// 新增
export function addWarehouse(data) {
return request({
url: '/base-data-manage/location/location',
method: 'post',
data: data
})
}
// 查询菜单列表
export function getList(query) {
return request({
url: '/base-data-manage/location/location-page',
method: 'get',
params: query
})
}
export function init() {
return request({
url: '/base-data-manage/location/init',
method: 'get'
})
}
// 修改
export function updateWarehouse(data) {
return request({
url: '/base-data-manage/location/location',
method: 'put',
data: data
})
}
// 删除
export function deleteWarehouse(data) {
return request({
// url: '/base-data-manage/location/location?locationId=' + data.locationId,
url: '/base-data-manage/location/location',
method: 'delete',
params: {
locationId: data.locationId,
busNo: data.busNo
}
})
}
/**
* 启用病区/床位/病房
*/
export function enableLocation(data) {
return request({
url: '/base-data-manage/location/enable',
method: 'put',
data: data
})
}
/**
* 停用病区/床位/病房
*/
export function unableLocation(data) {
return request({
url: '/base-data-manage/location/deactivate',
method: 'put',
data: data
})
}

View File

@@ -0,0 +1,321 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="" prop="searchKey">
<el-input
v-model="queryParams.searchKey"
placeholder="仓库名称"
clearable
style="width: 150px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-tooltip
:content="selectRowIds.length == 0 ? '至少选择一条数据' : ''"
placement="top"
:disabled="selectRowIds.length != 0"
>
<el-button
type=""
plain
icon="Delete"
:disabled="selectRowIds.length == 0"
@click="handleDelete"
>删除</el-button
>
</el-tooltip>
</el-col> -->
<!-- <el-col :span="1.5">
<el-button type="" plain icon="Download" @click="handleExport">
导出
</el-button>
</el-col> -->
<el-col :span="1.5">
<el-button type="" plain icon="Refresh" @click="getPageList">刷新</el-button>
</el-col>
<!-- <right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar> -->
</el-row>
<el-table
ref="orgTableRef"
v-loading="loading"
:data="organization"
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="仓库名称" align="left" prop="name" />
<el-table-column label="仓库类型" align="center" prop="formEnum_enumText" />
<!-- <el-table-column
label="存放药品类型"
align="center"
prop="classEnum_dictText"
/> -->
<el-table-column label="是否使用" align="center" prop="operationalEnum_enumText" />
<el-table-column label="停用状态" align="center" prop="statusEnum_enumText" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button link type="primary" @click="handelEdit(scope.row)">编辑</el-button>
<!-- <el-button
type="primary"
link
@click.stop="handleUnable(scope.row, 10)"
v-if="scope.row.statusEnum === 1 || scope.row.statusEnum === 6"
>
停用
</el-button>
<el-button type="primary" link @click.stop="handleEnable(scope.row, 10)" v-else>
启用
</el-button> -->
<el-button
type="danger"
link
icon="Delete"
:disabled="scope.row.busNo == ''"
@click="handelDelete(scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getPageList"
/>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="600px" @close="cancel" append-to-body>
<el-form ref="orgRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="id" prop="busNo" v-show="false">
<el-input v-model="form.id" placeholder="请输入科室编号" />
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="类型" prop="formEnum">
<el-select
v-model="form.formEnum"
placeholder="请选择类型"
clearable
style="width: 100%"
:disabled="editShow"
>
<el-option
v-for="dict in warehous_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Organization">
import {
addWarehouse,
deleteWarehouse,
enableLocation,
getList,
init,
unableLocation,
updateWarehouse,
} from './components/api';
const { proxy } = getCurrentInstance();
const loading = ref(true);
const organization = ref([]);
const queryParams = ref({
locationFormList: [11, 16, 17],
pageNo: 1,
pageSize: 10,
});
const open = ref(false);
const form = ref({
id: undefined,
name: undefined,
formEnum: undefined,
});
const orgTableRef = ref();
const orgRef = ref();
const orgTypeOption = ref([]);
const classEnumOption = ref([]);
const selectRowIds = ref([]);
const total = ref(0);
const title = ref('');
const editShow = ref(false);
const { warehous_type } = proxy.useDict('warehous_type');
const rules = ref({
busNo: [{ required: false, message: '请输入科室编号', trigger: 'change' }],
name: [
{ required: true, message: '请输入', trigger: 'change' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'change' },
],
formEnum: [{ required: true, message: '请选择仓库类型', trigger: 'change' }],
});
getPageList();
initOption();
function initOption() {
if (orgTypeOption.value.length == 0) {
init().then((res) => {
orgTypeOption.value = res.data.organizationTypeOptions;
classEnumOption.value = res.data.organizationClassOptions;
});
}
}
function reset() {
form.value = {
id: undefined,
name: undefined,
formEnum: undefined,
};
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNo = 1;
getPageList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm('queryRef');
handleQuery();
}
function handleEnable(row) {
enableLocation({ locationId: row.id, busNo: row.busNo }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('启用成功');
handleQuery();
}
});
}
function handleUnable(row) {
unableLocation({ locationId: row.id, busNo: row.busNo }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('停用成功');
handleQuery();
}
});
}
function getPageList() {
loading.value = true;
getList(queryParams.value).then((res) => {
organization.value = res.data.records.map((record) => {
return {
...record,
formEnum: record.formEnum.toString(), // 确保 formEnum 是字符串
};
});
total.value = res.data.total;
loading.value = false;
});
}
function handleAdd() {
title.value = '添加库房/药房/耗材库';
open.value = true;
editShow.value = false;
reset();
}
function handelEdit(row) {
title.value = '编辑库房/药房/耗材库';
open.value = true;
editShow.value = true;
setTimeout(() => {
form.value.id = row.id;
form.value.name = row.name;
form.value.formEnum = row.formEnum.toString();
}, 10);
}
function cancel() {
open.value = false;
reset();
console.log(form.value);
}
// 新增/编辑
function submitForm() {
proxy.$refs['orgRef'].validate((valid) => {
if (valid) {
if (form.value.busNoParent) {
form.value.busNo = form.value.busNoParent;
}
if (form.value.id == undefined) {
addWarehouse(form.value).then((res) => {
proxy.$modal.msgSuccess('操作成功');
open.value = false;
getPageList();
});
} else {
updateWarehouse(form.value).then((res) => {
proxy.$modal.msgSuccess('操作成功');
open.value = false;
getPageList();
});
}
}
});
}
// 删除
function handelDelete(data) {
loading.value = true;
deleteWarehouse(data).then((res) => {
proxy.$modal.msgSuccess('操作成功');
loading.value = false;
getPageList();
});
}
// // 停用
// function handleDisabled(id) {
// disableOrg(id).then((res) => {
// proxy.$modal.msgSuccess("操作成功");
// getPageList();
// });
// }
// // 启用
// function handelEnable(id) {
// enableOrg(id).then((res) => {
// proxy.$modal.msgSuccess("操作成功");
// getPageList();
// });
// }
function handleSelectionChange() {
selectRowIds.value = orgTableRef.value.getSelectionRows().map((item) => item.id);
}
</script>