Files
his/openhis-ui-vue3/src/views/system/tenant/index.vue
chenqi 0c35044231 feat(menu): 优化菜单路径唯一性校验并更新前端界面
- 在SysLoginController中添加optionMap数据返回
- 添加JSQLParser依赖支持MyBatis Plus功能
- 实现selectMenuByPathExcludeId方法用于排除当前菜单的路径唯一性校验
- 在SysMenuServiceImpl中添加日志记录并优化路径唯一性判断逻辑
- 在SysMenuMapper.xml中添加LIMIT 1限制并实现排除ID查询
- 在前端路由中注释患者管理相关路由配置
- 在用户store中添加optionMap配置项并优先从optionMap获取医院名称
- 重构检查项目设置页面的操作按钮样式为统一的圆形按钮设计
- 更新检查项目设置页面的导航栏样式和交互体验
- 优化门诊记录页面的搜索条件和表格展示功能
- 添加性别和状态筛选条件并改进数据加载逻辑
2026-01-03 23:47:09 +08:00

468 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true" label-width="68px">
<el-form-item label="租户ID" prop="tenantId">
<el-input v-model="queryParams.tenantId" placeholder="请输入租户ID" clearable style="width: 240px"
@keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="租户编码" prop="tenantCode">
<el-input v-model="queryParams.tenantCode" placeholder="请输入租户编码" clearable style="width: 240px"
@keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="租户名称" prop="tenantName">
<el-input v-model="queryParams.tenantName" placeholder="请输入租户名称" clearable style="width: 240px"
@keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="状态" clearable style="width: 240px">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</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-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="CircleCheck" :disabled="multiple" @click="handleEnable">启用</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="CircleClose" :disabled="multiple" @click="handleDisable">停用</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 表格数据 -->
<el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="租户ID" prop="id" width="200" />
<el-table-column label="租户编码" prop="tenantCode" :show-overflow-tooltip="true" width="250" />
<el-table-column label="租户名称" prop="tenantName" :show-overflow-tooltip="true" width="250" />
<el-table-column label="状态" align="center" width="100">
<template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
@change="handleStatusChange(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" :show-overflow-tooltip="true" width="200" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="所属用户" placement="top">
<el-button link type="primary" icon="User" @click="handleSetUser(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="合同管理" placement="top">
<el-button link type="primary" icon="Document" @click="handleSetContract(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="基本配置" placement="top">
<el-button link type="primary" icon="Setting" @click="handleSetOption(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改租户对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="tenantRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="租户编码" prop="tenantCode">
<el-input v-model="form.tenantCode" placeholder="请输入租户编码" />
</el-form-item>
<el-form-item label="租户名称" prop="tenantName">
<el-input v-model="form.tenantName" placeholder="请输入租户名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</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>
<!-- 修改租户配置项对话框 -->
<el-dialog :title="optionTitle" v-model="optionOpen" width="1600px" append-to-body class="tenant-option-dialog">
<el-form ref="optionRef" :model="optionForm" label-width="300px" class="three-column-form">
<div class="form-grid">
<el-form-item v-for="item in dynamicFormList" :key="item.code" :label="`${item.code}${item.name}`" :prop="item.code"
class="grid-item">
<el-input v-model="item.content" :placeholder="`请输入${item.name}`" clearable />
</el-form-item>
</div>
</el-form>
<template #footer>
<div class="dialog-footer" style="margin-top: 15px;">
<el-button type="primary" @click="submitOptionForm"> </el-button>
<el-button @click="cancelSubmitOption"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Tenant">
import {
addTenant,
delTenant,
disableTenant,
editTenant,
enableTenant,
getTenantDetail,
getTenantOptionDetailList,
getTenantOptionFormList,
getTenantPage,
saveTenantOptionDetailList
} from "@/api/system/tenant";
import useUserStore from '@/store/modules/user';
const router = useRouter();
const { proxy } = getCurrentInstance();
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
const userStore = useUserStore();
const dynamicFormList = ref([]);
// 当前租户信息
const currentTenantId = ref(null);
const currentTenantName = ref('');
// 配置项值存储对象
const optionContents = ref([]);
// 配置项表单数据
const optionForm = reactive({
tenantId: null,
optionList: []
});
const tenantList = ref([]);
const open = ref(false);
const optionOpen = 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 optionTitle = ref("");
const dateRange = ref([]);
const data = reactive({
form: {},
optionForm: {},
queryParams: {
pageNum: 1,
pageSize: 10,
tenantId: undefined,
tenantName: undefined,
status: undefined
},
rules: {
tenantCode: [
{ required: true, message: "租户编码不能为空", trigger: "blur" },
{ min: 1, max: 50, message: '长度需在1到50个字符之间', trigger: 'blur' }
],
tenantName: [
{ required: true, message: "租户名称不能为空", trigger: "blur" },
{ min: 1, max: 50, message: '长度需在1到50个字符之间', trigger: 'blur' }
],
remark: [
{ max: 300, message: '备注不能超过300个字符', trigger: 'blur' }
]
},
});
const { queryParams, form, rules } = toRefs(data);
/** 查询租户列表 */
function getList() {
loading.value = true;
getTenantPage(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
tenantList.value = response.data.records;
total.value = response.data.total;
loading.value = false;
});
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
handleQuery();
}
/** 删除按钮操作 */
function handleDelete(row) {
const tenantIds = row.id || ids.value;
proxy.$modal.confirm('是否确认删除租户ID为"' + tenantIds + '"的数据项?').then(function () {
return delTenant(tenantIds);
}).then((response) => {
getList();
if (response.code != 0) {
proxy.$modal.msgSuccess(response.msg);
} else {
proxy.$modal.msgSuccess("删除成功");
}
}).catch(() => { });
}
/** 启用按钮操作 */
function handleEnable() {
const tenantIds = ids.value;
proxy.$modal.confirm('是否确认启用租户ID为"' + tenantIds + '"的数据项?').then(function () {
return enableTenant(ids.value);
}).then(() => {
getList();
proxy.$modal.msgSuccess("启用成功");
}).catch(() => { });
}
/** 停用按钮操作 */
function handleDisable() {
const tenantIds = ids.value;
proxy.$modal.confirm('是否确认停用租户ID为"' + tenantIds + '"的数据项?').then(function () {
return disableTenant(tenantIds);
}).then(() => {
getList();
proxy.$modal.msgSuccess("停用成功");
}).catch(() => { });
}
/** 多选框选中数据 */
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 租户状态修改 */
function handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用";
proxy.$modal.confirm('确认要' + text + '"' + row.id + '"租户吗?').then(function () {
if (row.status === "0") {
return enableTenant(Array.of(row.id));
} else {
return disableTenant(Array.of(row.id));
}
}).then(() => {
proxy.$modal.msgSuccess(text + "成功");
}).catch(function () {
row.status = row.status === "0" ? "1" : "0";
});
}
/** 所属用户 */
function handleSetUser(row) {
router.push("/system/tenant-user/set/" + row.id);
}
/** 合同管理 */
function handleSetContract(row) {
router.push("/system/tenant-contract/set/" + row.id);
}
/** 重置新增的表单以及其他数据 */
function reset() {
form.value = {
id: undefined,
tenantName: undefined,
status: "0",
remark: undefined
};
proxy.resetForm("tenantRef");
}
/** 添加租户 */
function handleAdd() {
reset();
open.value = true;
title.value = "添加租户";
}
/** 修改租户 */
function handleUpdate(row) {
reset();
const tenantId = row.id || ids.value;
getTenantDetail(tenantId).then(response => {
form.value = response.data;
open.value = true;
title.value = "修改租户";
});
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["tenantRef"].validate(valid => {
if (valid) {
if (form.value.id != undefined) {
editTenant(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addTenant(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
}
/** 取消按钮 */
function cancel() {
open.value = false;
reset();
}
/** 打开配置项对话框 */
async function handleSetOption(row) {
try {
loading.value = true;
// 设置当前租户信息
currentTenantId.value = row.id;
currentTenantName.value = row.tenantName;
optionTitle.value = `基本配置`;
// 获取动态表单配置
const formListRes = await getTenantOptionFormList();
console.log('动态表单配置:', formListRes);
dynamicFormList.value = formListRes.data || [];
// 获取租户已有配置
const detailRes = await getTenantOptionDetailList(row.id);
console.log('租户已有配置:', detailRes);
console.log('租户已有配置数据:', detailRes.data);
// 填充已有配置值
if (detailRes.data && Array.isArray(detailRes.data)) {
console.log('开始填充配置值detailRes.data.length:', detailRes.data.length);
console.log('dynamicFormList.value.length:', dynamicFormList.value.length);
// 将已有配置数据转为 Map 方便查找
const configMap = {};
detailRes.data.forEach(config => {
configMap[config.code] = config.content || '';
});
dynamicFormList.value.forEach(item => {
const existingContent = configMap[item.code];
console.log(`配置项 ${item.code}, 找到配置值:`, existingContent);
item.content = existingContent || '';
});
console.log('填充后的dynamicFormList:', dynamicFormList.value);
} else {
console.log('detailRes.data 不是数组或为空,初始化空内容');
// 初始化空内容
dynamicFormList.value.forEach(item => {
item.content = '';
});
}
// 设置表单数据
optionForm.tenantId = row.id;
optionOpen.value = true;
} catch (error) {
console.error('获取配置项失败:', error);
proxy.$modal.msgError("获取配置项失败");
} finally {
loading.value = false;
}
}
/** Option提交按钮 */
async function submitOptionForm() {
try {
// 准备提交数据
const submitData = {
tenantId: optionForm.tenantId,
optionList: dynamicFormList.value
};
// 调用保存接口
loading.value = true;
const res = await saveTenantOptionDetailList(submitData);
if (res.code === 200) {
optionOpen.value = false;
// 如果修改的是当前登录用户的租户配置,需要刷新用户信息
if (userStore.tenantId === optionForm.tenantId) {
await userStore.getInfo();
proxy.$modal.msgSuccess("配置保存成功,用户信息已刷新");
} else {
proxy.$modal.msgSuccess("配置保存成功");
}
} else {
proxy.$modal.msgError(res.msg || "配置保存失败");
}
} catch (error) {
console.error('表单验证失败:', error);
} finally {
loading.value = false;
}
}
/** 重置配置项表单 */
function resetOption() {
if (proxy.$refs["optionRef"]) {
proxy.$refs["optionRef"].resetFields();
}
}
/** 取消配置项提交 */
function cancelSubmitOption() {
optionOpen.value = false;
resetOption();
}
getList();
</script>
<style>
.form-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px 15px;
}
.grid-item {
margin-bottom: 0;
background: white;
border-radius: 8px;
padding: 5px;
transition: all 0.3s ease;
}
.grid-item :deep(.el-input) {
width: 100%;
}
/* 描述文本样式 */
.item-description {
font-size: 12px;
color: #7f8c8d;
margin-top: 8px;
line-height: 1.4;
padding: 5px;
background-color: #f9f9f9;
border-radius: 4px;
}
</style>