医院管理页新增医院管理员列并支持任命医院管理员

组织架构树展示医院管理员信息
科室与小组弹窗支持设置主任/组长并限制候选角色
患者页优化归属医生选择与字段文案
统一“小组组长”角色文案
This commit is contained in:
EL 2026-03-18 17:07:37 +08:00
parent b527256874
commit 5fdf4c80e6
7 changed files with 691 additions and 275 deletions

View File

@ -4,15 +4,34 @@
<template #header>
<h2 class="login-title">调压通管理后台</h2>
</template>
<el-form :model="loginForm" :rules="rules" ref="loginFormRef" @keyup.enter="handleLogin">
<el-form
:model="loginForm"
:rules="rules"
ref="loginFormRef"
@keyup.enter="handleLogin"
>
<el-form-item prop="phone">
<el-input v-model="loginForm.phone" placeholder="请输入手机号" :prefix-icon="User" />
<el-input
v-model="loginForm.phone"
placeholder="请输入手机号"
:prefix-icon="User"
/>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码" show-password :prefix-icon="Lock" />
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
show-password
:prefix-icon="Lock"
/>
</el-form-item>
<el-form-item prop="role">
<el-select v-model="loginForm.role" placeholder="请选择登录角色" style="width: 100%;">
<el-select
v-model="loginForm.role"
placeholder="请选择登录角色"
style="width: 100%"
>
<el-option label="系统管理员" value="SYSTEM_ADMIN" />
<el-option label="医院管理员" value="HOSPITAL_ADMIN" />
<el-option label="科室主任" value="DIRECTOR" />
@ -27,17 +46,23 @@
:min="1"
:controls="false"
placeholder="医院 ID多账号场景建议填写"
style="width: 100%;"
style="width: 100%"
/>
</el-form-item>
<el-alert
type="info"
:closable="false"
title="若同一手机号在多个医院有同角色账号,请填写医院 ID。"
style="margin-bottom: 16px;"
style="margin-bottom: 16px"
/>
<el-form-item>
<el-button type="primary" class="login-btn" :loading="loading" @click="handleLogin">登录</el-button>
<el-button
type="primary"
class="login-btn"
:loading="loading"
@click="handleLogin"
>登录</el-button
>
</el-form-item>
</el-form>
</el-card>
@ -68,13 +93,13 @@ const loginForm = reactive({
const rules = {
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1\d{10}$/, message: '请输入正确的手机号', trigger: 'blur' }
{ pattern: /^1\d{10}$/, message: '请输入正确的手机号', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 8, message: '密码长度至少为 8 位', trigger: 'blur' }
{ min: 8, message: '密码长度至少为 8 位', trigger: 'blur' },
],
role: [{ required: true, message: '请选择角色', trigger: 'change' }]
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
};
const handleLogin = async () => {

View File

@ -163,7 +163,7 @@
placeholder="可选:选择后将任命为科室主任"
clearable
filterable
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="user in directorOptions"
@ -416,7 +416,9 @@ const handleSubmit = async () => {
let targetHospitalId = form.hospitalId;
if (isEdit.value) {
// Some backend update APIs don't allow changing hospitalId, but we'll send it if needed, or just name
const updated = await updateDepartment(currentId.value, { name: form.name });
const updated = await updateDepartment(currentId.value, {
name: form.name,
});
targetDepartmentId = updated?.id ?? currentId.value;
targetHospitalId = updated?.hospitalId ?? form.hospitalId;
ElMessage.success('更新成功');
@ -431,10 +433,10 @@ const handleSubmit = async () => {
}
if (
canAssignDirectorInDialog.value
&& form.directorUserId
&& targetDepartmentId
&& targetHospitalId
canAssignDirectorInDialog.value &&
form.directorUserId &&
targetDepartmentId &&
targetHospitalId
) {
await updateUser(form.directorUserId, {
role: 'DIRECTOR',

View File

@ -200,7 +200,7 @@
placeholder="可选:选择后将任命为小组组长"
clearable
filterable
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="user in leaderOptions"
@ -497,7 +497,9 @@ const handleSubmit = async () => {
let targetHospitalId = form.hospitalId;
if (isEdit.value) {
// Backend patch dto might just accept name. Sending departmentId may not be allowed or needed.
const updated = await updateGroup(currentId.value, { name: form.name });
const updated = await updateGroup(currentId.value, {
name: form.name,
});
targetGroupId = updated?.id ?? currentId.value;
targetDepartmentId = updated?.departmentId ?? form.departmentId;
targetHospitalId = updated?.department?.hospitalId ?? form.hospitalId;
@ -513,11 +515,11 @@ const handleSubmit = async () => {
}
if (
canAssignLeaderInDialog.value
&& form.leaderUserId
&& targetGroupId
&& targetDepartmentId
&& targetHospitalId
canAssignLeaderInDialog.value &&
form.leaderUserId &&
targetGroupId &&
targetDepartmentId &&
targetHospitalId
) {
await updateUser(form.leaderUserId, {
role: 'LEADER',
@ -586,7 +588,11 @@ watch(
if (!dialogVisible.value) {
return;
}
await loadLeaderOptions(hospitalId, departmentId, isEdit.value ? currentId.value : null);
await loadLeaderOptions(
hospitalId,
departmentId,
isEdit.value ? currentId.value : null,
);
},
);
</script>

View File

@ -5,18 +5,36 @@
<div class="header-actions">
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="医院名称">
<el-input v-model="searchForm.keyword" placeholder="请输入关键词" clearable />
<el-input
v-model="searchForm.keyword"
placeholder="请输入关键词"
clearable
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchData" icon="Search">查询</el-button>
<el-button type="primary" @click="fetchData" icon="Search"
>查询</el-button
>
<el-button @click="resetSearch" icon="Refresh">重置</el-button>
<el-button v-if="userStore.role === 'SYSTEM_ADMIN'" type="success" @click="openCreateDialog" icon="Plus">新增医院</el-button>
<el-button
v-if="userStore.role === 'SYSTEM_ADMIN'"
type="success"
@click="openCreateDialog"
icon="Plus"
>新增医院</el-button
>
</el-form-item>
</el-form>
</div>
<!-- Table -->
<el-table :data="tableData" v-loading="loading" border stripe style="width: 100%">
<el-table
:data="tableData"
v-loading="loading"
border
stripe
style="width: 100%"
>
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="医院名称" min-width="200" />
<el-table-column prop="adminDisplay" label="医院管理员" min-width="220">
@ -31,9 +49,19 @@
</el-table-column>
<el-table-column label="操作" width="250" fixed="right" align="center">
<template #default="{ row }">
<el-button size="small" @click="goToDepartments(row)">管理科室</el-button>
<el-button size="small" type="primary" @click="openEditDialog(row)">编辑</el-button>
<el-button v-if="userStore.role === 'SYSTEM_ADMIN'" size="small" type="danger" @click="handleDelete(row)">删除</el-button>
<el-button size="small" @click="goToDepartments(row)"
>管理科室</el-button
>
<el-button size="small" type="primary" @click="openEditDialog(row)"
>编辑</el-button
>
<el-button
v-if="userStore.role === 'SYSTEM_ADMIN'"
size="small"
type="danger"
@click="handleDelete(row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
@ -54,18 +82,26 @@
</el-card>
<!-- Dialog for Create / Edit -->
<el-dialog :title="isEdit ? '编辑医院' : '新增医院'" v-model="dialogVisible" width="500px" @close="resetForm">
<el-dialog
:title="isEdit ? '编辑医院' : '新增医院'"
v-model="dialogVisible"
width="500px"
@close="resetForm"
>
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
<el-form-item label="医院名称" prop="name">
<el-input v-model="form.name" placeholder="请输入医院名称" />
</el-form-item>
<el-form-item label="医院管理员" v-if="userStore.role === 'SYSTEM_ADMIN'">
<el-form-item
label="医院管理员"
v-if="userStore.role === 'SYSTEM_ADMIN'"
>
<el-select
v-model="form.adminUserId"
placeholder="可选:选择后将任命为医院管理员"
clearable
filterable
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="user in hospitalAdminOptions"
@ -79,7 +115,12 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
<el-button
type="primary"
@click="handleSubmit"
:loading="submitLoading"
>确定</el-button
>
</div>
</template>
</el-dialog>
@ -90,7 +131,12 @@
import { ref, reactive, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getHospitals, createHospital, updateHospital, deleteHospital } from '../../api/organization';
import {
getHospitals,
createHospital,
updateHospital,
deleteHospital,
} from '../../api/organization';
import { getUsers, updateUser } from '../../api/users';
import { useUserStore } from '../../store/user';
@ -117,7 +163,7 @@ const roleMap = {
const getRoleName = (role) => roleMap[role] || role;
const searchForm = reactive({
keyword: ''
keyword: '',
});
// Dialog State
@ -133,7 +179,7 @@ const form = reactive({
});
const rules = {
name: [{ required: true, message: '请输入医院名称', trigger: 'blur' }]
name: [{ required: true, message: '请输入医院名称', trigger: 'blur' }],
};
// --- Methods ---
@ -143,7 +189,7 @@ const fetchData = async () => {
const res = await getHospitals({
page: page.value,
pageSize: pageSize.value,
keyword: searchForm.keyword || undefined
keyword: searchForm.keyword || undefined,
});
let hospitalAdminNameMap = {};
@ -166,7 +212,8 @@ const fetchData = async () => {
tableData.value = (res.list || []).map((hospital) => ({
...hospital,
adminDisplay: (hospitalAdminNameMap[hospital.id] || []).join('、') || '未设置',
adminDisplay:
(hospitalAdminNameMap[hospital.id] || []).join('、') || '未设置',
}));
total.value = res.total || 0;
} catch (error) {
@ -233,7 +280,9 @@ const handleSubmit = async () => {
try {
let targetHospitalId = null;
if (isEdit.value) {
const updated = await updateHospital(currentId.value, { name: form.name });
const updated = await updateHospital(currentId.value, {
name: form.name,
});
targetHospitalId = updated?.id ?? currentId.value;
ElMessage.success('更新成功');
} else {
@ -272,29 +321,27 @@ const handleSubmit = async () => {
};
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除医院 "${row.name}" 吗?`,
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(async () => {
try {
await deleteHospital(row.id);
ElMessage.success('删除成功');
fetchData();
} catch (error) {
console.error('Delete failed', error);
}
}).catch(() => {});
ElMessageBox.confirm(`确定要删除医院 "${row.name}" 吗?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
try {
await deleteHospital(row.id);
ElMessage.success('删除成功');
fetchData();
} catch (error) {
console.error('Delete failed', error);
}
})
.catch(() => {});
};
const goToDepartments = (row) => {
router.push({
path: '/organization/departments',
query: { hospitalId: row.id, hospitalName: row.name }
query: { hospitalId: row.id, hospitalName: row.name },
});
};

View File

@ -6,8 +6,17 @@
<el-card shadow="never" class="tree-card">
<template #header>
<div class="card-header">
<span class="header-title"><el-icon><Connection /></el-icon> 组织架构全景图</span>
<el-button type="primary" @click="fetchTreeData" icon="Refresh" round size="small">刷新</el-button>
<span class="header-title"
><el-icon><Connection /></el-icon> 组织架构全景图</span
>
<el-button
type="primary"
@click="fetchTreeData"
icon="Refresh"
round
size="small"
>刷新</el-button
>
</div>
</template>
@ -26,38 +35,58 @@
<div class="custom-tree-node" :class="`node-${data.type}`">
<div class="node-main">
<div class="node-icon-wrapper">
<el-icon v-if="data.type === 'hospital'"><OfficeBuilding /></el-icon>
<el-icon v-else-if="data.type === 'department'"><Filter /></el-icon>
<el-icon v-else-if="data.type === 'group'"><Connection /></el-icon>
<el-icon v-else-if="data.type === 'user'"><UserFilled /></el-icon>
<el-icon v-if="data.type === 'hospital'"
><OfficeBuilding
/></el-icon>
<el-icon v-else-if="data.type === 'department'"
><Filter
/></el-icon>
<el-icon v-else-if="data.type === 'group'"
><Connection
/></el-icon>
<el-icon v-else-if="data.type === 'user'"
><UserFilled
/></el-icon>
</div>
<div class="node-text">
<span class="node-label">{{ node.label }}</span>
<span v-if="data.type === 'hospital'" class="node-sub-label">
<span
v-if="data.type === 'hospital'"
class="node-sub-label"
>
医院管理员{{ data.adminDisplay || '未设置' }}
</span>
<span v-else-if="data.type === 'department'" class="node-sub-label">
<span
v-else-if="data.type === 'department'"
class="node-sub-label"
>
主任{{ data.directorDisplay || '未设置' }}
</span>
<span v-else-if="data.type === 'group'" class="node-sub-label">
<span
v-else-if="data.type === 'group'"
class="node-sub-label"
>
组长{{ data.leaderDisplay || '未设置' }}
</span>
</div>
<el-tag
v-if="data.type === 'user'"
size="small"
:type="getRoleTagType(data.role)"
effect="light"
<el-tag
v-if="data.type === 'user'"
size="small"
:type="getRoleTagType(data.role)"
effect="light"
class="role-tag"
round
>
{{ getRoleName(data.role) }}
</el-tag>
</div>
<div class="node-actions" v-if="data.type !== 'user'">
<el-button
v-if="canAssignOwner && (data.type === 'department' || data.type === 'group')"
v-if="
canAssignOwner &&
(data.type === 'department' || data.type === 'group')
"
type="warning"
link
size="small"
@ -65,17 +94,34 @@
>
{{ data.type === 'department' ? '设主任' : '设组长' }}
</el-button>
<el-button v-if="canEditNode(data)" type="info" link size="small" @click.stop="openEditDialog(data)" icon="EditPen">
<el-button
v-if="canEditNode(data)"
type="info"
link
size="small"
@click.stop="openEditDialog(data)"
icon="EditPen"
>
编辑
</el-button>
<el-button v-if="canDeleteNode(data)" type="danger" link size="small" @click.stop="handleDelete(data)" icon="Delete">
<el-button
v-if="canDeleteNode(data)"
type="danger"
link
size="small"
@click.stop="handleDelete(data)"
icon="Delete"
>
删除
</el-button>
</div>
</div>
</template>
</el-tree>
<el-empty v-if="!loading && treeData.length === 0" description="暂无组织架构数据" />
<el-empty
v-if="!loading && treeData.length === 0"
description="暂无组织架构数据"
/>
</div>
</el-card>
</el-col>
@ -86,50 +132,62 @@
<template #header>
<div class="card-header">
<span class="header-title">
<el-icon><Menu /></el-icon>
<el-icon><Menu /></el-icon>
{{ activePanelTitle }}
</span>
<div v-if="activeNode && activeNode.type !== 'user'" class="header-actions">
<el-button
v-if="canCreateDepartment(activeNode)"
type="primary"
<div
v-if="activeNode && activeNode.type !== 'user'"
class="header-actions"
>
<el-button
v-if="canCreateDepartment(activeNode)"
type="primary"
size="small"
icon="Plus"
icon="Plus"
@click="openCreateDialog('department', activeNode.id)"
>
新增科室
</el-button>
<el-button
v-if="canCreateGroup(activeNode)"
type="success"
<el-button
v-if="canCreateGroup(activeNode)"
type="success"
size="small"
icon="Plus"
icon="Plus"
@click="openCreateDialog('group', activeNode.id)"
>
新增小组
</el-button>
<el-button
v-if="canAddUser(activeNode)"
type="warning"
<el-button
v-if="canAddUser(activeNode)"
type="warning"
size="small"
icon="User"
icon="User"
@click="goToAddUser(activeNode)"
>
新增人员
</el-button>
<el-button
v-if="canAssignOwner && (activeNode.type === 'department' || activeNode.type === 'group')"
v-if="
canAssignOwner &&
(activeNode.type === 'department' ||
activeNode.type === 'group')
"
type="primary"
size="small"
@click="openSetOwnerDialog(activeNode)"
>
{{ activeNode.type === 'department' ? '设置主任' : '设置组长' }}
{{
activeNode.type === 'department' ? '设置主任' : '设置组长'
}}
</el-button>
</div>
</div>
</template>
<div v-if="activeNode && activeNode.type !== 'user'" class="node-detail-panel">
<div
v-if="activeNode && activeNode.type !== 'user'"
class="node-detail-panel"
>
<el-alert
v-if="activeNodeMeta"
:title="activeNodeMeta"
@ -137,31 +195,55 @@
:closable="false"
class="mb-12"
/>
<el-table :data="activeNodeChildren" border stripe style="width: 100%" max-height="600">
<el-table
:data="activeNodeChildren"
border
stripe
style="width: 100%"
max-height="600"
>
<el-table-column prop="name" label="名称" />
<el-table-column label="类型" width="100" align="center">
<template #default="{ row }">
<el-tag size="small" :type="getNodeTypeTag(row.type)">{{ getTypeName(row.type) }}</el-tag>
<el-tag size="small" :type="getNodeTypeTag(row.type)">{{
getTypeName(row.type)
}}</el-tag>
</template>
</el-table-column>
<el-table-column label="负责人" width="180" align="center">
<template #default="{ row }">
<span v-if="row.type === 'hospital'">医院管理员{{ row.adminDisplay || '未设置' }}</span>
<span v-else-if="row.type === 'department'">主任{{ row.directorDisplay || '未设置' }}</span>
<span v-else-if="row.type === 'group'">组长{{ row.leaderDisplay || '未设置' }}</span>
<span v-if="row.type === 'hospital'"
>医院管理员{{ row.adminDisplay || '未设置' }}</span
>
<span v-else-if="row.type === 'department'"
>主任{{ row.directorDisplay || '未设置' }}</span
>
<span v-else-if="row.type === 'group'"
>组长{{ row.leaderDisplay || '未设置' }}</span
>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="角色" width="120" align="center">
<template #default="{ row }">
<span v-if="row.type === 'user'">{{ getRoleName(row.role) }}</span>
<span v-if="row.type === 'user'">{{
getRoleName(row.role)
}}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center" fixed="right">
<el-table-column
label="操作"
width="220"
align="center"
fixed="right"
>
<template #default="{ row }">
<el-button
v-if="canAssignOwner && (row.type === 'department' || row.type === 'group')"
v-if="
canAssignOwner &&
(row.type === 'department' || row.type === 'group')
"
type="warning"
link
size="small"
@ -169,21 +251,47 @@
>
{{ row.type === 'department' ? '设主任' : '设组长' }}
</el-button>
<el-button v-if="canEditNode(row)" type="primary" link size="small" @click="openEditDialog(row)">编辑</el-button>
<el-button v-if="canDeleteNode(row)" type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
<el-button
v-if="canEditNode(row)"
type="primary"
link
size="small"
@click="openEditDialog(row)"
>编辑</el-button
>
<el-button
v-if="canDeleteNode(row)"
type="danger"
link
size="small"
@click="handleDelete(row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<div v-else-if="selectedUserDetail" class="user-detail-panel">
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{ selectedUserDetail.name || '-' }}</el-descriptions-item>
<el-descriptions-item label="角色">{{ getRoleName(selectedUserDetail.role) }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ selectedUserDetail.phone || '-' }}</el-descriptions-item>
<el-descriptions-item label="医院">{{ selectedUserDetail.hospitalName || '-' }}</el-descriptions-item>
<el-descriptions-item label="科室">{{ selectedUserDetail.departmentName || '-' }}</el-descriptions-item>
<el-descriptions-item label="小组">{{ selectedUserDetail.groupName || '-' }}</el-descriptions-item>
<el-descriptions-item label="姓名">{{
selectedUserDetail.name || '-'
}}</el-descriptions-item>
<el-descriptions-item label="角色">{{
getRoleName(selectedUserDetail.role)
}}</el-descriptions-item>
<el-descriptions-item label="手机号">{{
selectedUserDetail.phone || '-'
}}</el-descriptions-item>
<el-descriptions-item label="医院">{{
selectedUserDetail.hospitalName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="科室">{{
selectedUserDetail.departmentName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="小组">{{
selectedUserDetail.groupName || '-'
}}</el-descriptions-item>
</el-descriptions>
<el-alert
title="如需修改人员角色或组织归属,请前往“用户管理”页面操作。"
@ -198,16 +306,37 @@
</el-row>
<!-- Dialog for Create / Edit -->
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="450px" @close="resetForm" destroy-on-close>
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px" @submit.prevent>
<el-dialog
:title="dialogTitle"
v-model="dialogVisible"
width="450px"
@close="resetForm"
destroy-on-close
>
<el-form
:model="form"
:rules="rules"
ref="formRef"
label-width="100px"
@submit.prevent
>
<el-form-item :label="formLabel" prop="name">
<el-input v-model="form.name" :placeholder="`请输入${formLabel}`" clearable />
<el-input
v-model="form.name"
:placeholder="`请输入${formLabel}`"
clearable
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
<el-button
type="primary"
@click="handleSubmit"
:loading="submitLoading"
>确定</el-button
>
</div>
</template>
</el-dialog>
@ -224,7 +353,7 @@
v-model="selectedOwnerUserId"
filterable
placeholder="请选择人员"
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="user in ownerCandidates"
@ -243,7 +372,11 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="ownerDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="ownerSubmitLoading" @click="handleSetOwner">
<el-button
type="primary"
:loading="ownerSubmitLoading"
@click="handleSetOwner"
>
确定
</el-button>
</div>
@ -256,10 +389,33 @@
import { ref, reactive, onMounted, computed } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getHospitals, getDepartments, getGroups, createDepartment, updateDepartment, deleteDepartment, createGroup, updateGroup, deleteGroup, updateHospital, deleteHospital } from '../../api/organization';
import {
getHospitals,
getDepartments,
getGroups,
createDepartment,
updateDepartment,
deleteDepartment,
createGroup,
updateGroup,
deleteGroup,
updateHospital,
deleteHospital,
} from '../../api/organization';
import { getUsers, updateUser } from '../../api/users';
import { useUserStore } from '../../store/user';
import { OfficeBuilding, Filter, Connection, UserFilled, Refresh, Plus, EditPen, Delete, Menu, User } from '@element-plus/icons-vue';
import {
OfficeBuilding,
Filter,
Connection,
UserFilled,
Refresh,
Plus,
EditPen,
Delete,
Menu,
User,
} from '@element-plus/icons-vue';
const router = useRouter();
const userStore = useUserStore();
@ -288,7 +444,7 @@ const roleMap = {
DIRECTOR: '科室主任',
LEADER: '小组组长',
DOCTOR: '医生',
ENGINEER: '工程师'
ENGINEER: '工程师',
};
const getRoleName = (role) => roleMap[role] || role;
@ -302,12 +458,22 @@ const getRoleTagType = (role) => {
};
const getTypeName = (type) => {
const map = { hospital: '医院', department: '科室', group: '小组', user: '人员' };
const map = {
hospital: '医院',
department: '科室',
group: '小组',
user: '人员',
};
return map[type] || type;
};
const getNodeTypeTag = (type) => {
const map = { hospital: 'primary', department: 'success', group: 'warning', user: 'info' };
const map = {
hospital: 'primary',
department: 'success',
group: 'warning',
user: 'info',
};
return map[type] || 'info';
};
@ -326,16 +492,16 @@ const canCreateDepartment = (node) =>
const canCreateGroup = (node) =>
Boolean(
node
&& node.type === 'department'
&& (isOrgAdmin.value || isDirector.value),
node &&
node.type === 'department' &&
(isOrgAdmin.value || isDirector.value),
);
const canAddUser = (node) =>
Boolean(
node
&& (node.type === 'department' || node.type === 'group')
&& isOrgAdmin.value,
node &&
(node.type === 'department' || node.type === 'group') &&
isOrgAdmin.value,
);
const canEditNode = (node) => {
@ -382,9 +548,9 @@ const activePanelTitle = computed(() => {
const activeNodeChildren = computed(() => {
if (
!activeNode.value
|| activeNode.value.type === 'user'
|| !Array.isArray(activeNode.value.children)
!activeNode.value ||
activeNode.value.type === 'user' ||
!Array.isArray(activeNode.value.children)
) {
return [];
}
@ -410,7 +576,9 @@ const selectedUserDetail = computed(() => {
return null;
}
const current = allUsers.value.find((user) => user.id === activeNode.value.id);
const current = allUsers.value.find(
(user) => user.id === activeNode.value.id,
);
const userData = current || activeNode.value;
const hospitalId = userData.hospitalId || null;
const departmentId = userData.departmentId || null;
@ -466,7 +634,7 @@ const fetchTreeData = async () => {
const [deptRes, groupRes, userRes] = await Promise.all([
getDepartments({ pageSize: 100 }),
getGroups({ pageSize: 100 }),
getUsers()
getUsers(),
]);
const departments = deptRes.list || [];
@ -507,51 +675,86 @@ const fetchTreeData = async () => {
}
});
const tree = hospitals.map(h => {
const hDepts = departments.filter(d => d.hospitalId === h.id);
const tree = hospitals.map((h) => {
const hDepts = departments.filter((d) => d.hospitalId === h.id);
const adminDisplay =
(hospitalAdminNameMap[h.id] || []).join('、') || '未设置';
const deptNodes = hDepts.map(d => {
const dGroups = groups.filter(g => g.departmentId === d.id);
const deptNodes = hDepts.map((d) => {
const dGroups = groups.filter((g) => g.departmentId === d.id);
const directorDisplay =
(directorNameMap[d.id] || []).join('、') || '未设置';
const groupNodes = dGroups.map(g => {
const gUsers = users.filter(u => u.groupId === g.id);
const groupNodes = dGroups.map((g) => {
const gUsers = users.filter((u) => u.groupId === g.id);
const leaderDisplay =
(leaderNameMap[g.id] || []).join('、') || '未设置';
const userNodes = gUsers.map(u => ({
key: `u_${u.id}`, id: u.id, name: u.name, type: 'user', role: u.role,
hospitalId: h.id, departmentId: d.id, groupId: g.id
const userNodes = gUsers.map((u) => ({
key: `u_${u.id}`,
id: u.id,
name: u.name,
type: 'user',
role: u.role,
hospitalId: h.id,
departmentId: d.id,
groupId: g.id,
}));
return {
key: `g_${g.id}`, id: g.id, name: g.name, type: 'group',
departmentId: d.id, hospitalId: h.id, leaderDisplay, children: userNodes
key: `g_${g.id}`,
id: g.id,
name: g.name,
type: 'group',
departmentId: d.id,
hospitalId: h.id,
leaderDisplay,
children: userNodes,
};
});
const dUsers = users.filter(u => u.departmentId === d.id && !u.groupId);
const dUserNodes = dUsers.map(u => ({
key: `u_${u.id}`, id: u.id, name: u.name, type: 'user', role: u.role,
hospitalId: h.id, departmentId: d.id
const dUsers = users.filter(
(u) => u.departmentId === d.id && !u.groupId,
);
const dUserNodes = dUsers.map((u) => ({
key: `u_${u.id}`,
id: u.id,
name: u.name,
type: 'user',
role: u.role,
hospitalId: h.id,
departmentId: d.id,
}));
return {
key: `d_${d.id}`, id: d.id, name: d.name, type: 'department',
hospitalId: h.id, directorDisplay, children: [...groupNodes, ...dUserNodes]
key: `d_${d.id}`,
id: d.id,
name: d.name,
type: 'department',
hospitalId: h.id,
directorDisplay,
children: [...groupNodes, ...dUserNodes],
};
});
const hUsers = users.filter(u => u.hospitalId === h.id && !u.departmentId);
const hUserNodes = hUsers.map(u => ({
key: `u_${u.id}`, id: u.id, name: u.name, type: 'user', role: u.role,
hospitalId: h.id
const hUsers = users.filter(
(u) => u.hospitalId === h.id && !u.departmentId,
);
const hUserNodes = hUsers.map((u) => ({
key: `u_${u.id}`,
id: u.id,
name: u.name,
type: 'user',
role: u.role,
hospitalId: h.id,
}));
return {
key: `h_${h.id}`, id: h.id, name: h.name, type: 'hospital', adminDisplay, children: [...deptNodes, ...hUserNodes]
key: `h_${h.id}`,
id: h.id,
name: h.name,
type: 'hospital',
adminDisplay,
children: [...deptNodes, ...hUserNodes],
};
});
@ -580,17 +783,19 @@ const openSetOwnerDialog = (node) => {
selectedOwnerUserId.value = null;
if (node.type === 'department') {
ownerCandidates.value = allUsers.value.filter((user) =>
user.hospitalId === node.hospitalId
&& user.departmentId === node.id
&& user.role === 'DIRECTOR',
ownerCandidates.value = allUsers.value.filter(
(user) =>
user.hospitalId === node.hospitalId &&
user.departmentId === node.id &&
user.role === 'DIRECTOR',
);
} else {
ownerCandidates.value = allUsers.value.filter((user) =>
user.hospitalId === node.hospitalId
&& user.departmentId === node.departmentId
&& user.groupId === node.id
&& user.role === 'LEADER',
ownerCandidates.value = allUsers.value.filter(
(user) =>
user.hospitalId === node.hospitalId &&
user.departmentId === node.departmentId &&
user.groupId === node.id &&
user.role === 'LEADER',
);
}
@ -600,7 +805,9 @@ const openSetOwnerDialog = (node) => {
}
const currentOwner = ownerCandidates.value.find((user) =>
node.type === 'department' ? user.role === 'DIRECTOR' : user.role === 'LEADER',
node.type === 'department'
? user.role === 'DIRECTOR'
: user.role === 'LEADER',
);
selectedOwnerUserId.value = currentOwner?.id ?? ownerCandidates.value[0].id;
ownerDialogVisible.value = true;
@ -623,14 +830,14 @@ const handleSetOwner = async () => {
const isDepartment = ownerTargetNode.value.type === 'department';
const payload = isDepartment
? {
role: 'DIRECTOR',
// DOCTOR /
//
groupId: null,
}
role: 'DIRECTOR',
// DOCTOR /
//
groupId: null,
}
: {
role: 'LEADER',
};
role: 'LEADER',
};
ownerSubmitLoading.value = true;
try {
@ -644,15 +851,16 @@ const handleSetOwner = async () => {
};
const goToAddUser = (nodeData) => {
// We navigate to the Users list page. In a full implementation, you could
// We navigate to the Users list page. In a full implementation, you could
// pass query params to pre-fill a creation form or open a dialog directly.
router.push({
path: '/users',
query: {
action: 'create',
hospitalId: nodeData.hospitalId,
departmentId: nodeData.type === 'department' ? nodeData.id : nodeData.departmentId,
}
departmentId:
nodeData.type === 'department' ? nodeData.id : nodeData.departmentId,
},
});
};
@ -660,23 +868,51 @@ const goToAddUser = (nodeData) => {
const dialogVisible = ref(false);
const submitLoading = ref(false);
const formRef = ref(null);
const dialogType = ref('');
const dialogMode = ref('');
const dialogType = ref('');
const dialogMode = ref('');
const parentId = ref(null);
const currentId = ref(null);
const form = reactive({ name: '' });
const rules = { name: [{ required: true, message: '请输入名称', trigger: 'blur' }] };
const rules = {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
};
const dialogTitle = computed(() => {
const typeName = dialogType.value === 'hospital' ? '医院' : (dialogType.value === 'department' ? '科室' : '小组');
const typeName =
dialogType.value === 'hospital'
? '医院'
: dialogType.value === 'department'
? '科室'
: '小组';
return dialogMode.value === 'create' ? `新增${typeName}` : `编辑${typeName}`;
});
const formLabel = computed(() => dialogType.value === 'hospital' ? '医院名称' : (dialogType.value === 'department' ? '科室名称' : '小组名称'));
const formLabel = computed(() =>
dialogType.value === 'hospital'
? '医院名称'
: dialogType.value === 'department'
? '科室名称'
: '小组名称',
);
const openCreateDialog = (type, pId) => { dialogType.value = type; dialogMode.value = 'create'; parentId.value = pId; currentId.value = null; dialogVisible.value = true; };
const openEditDialog = (data) => { dialogType.value = data.type; dialogMode.value = 'edit'; currentId.value = data.id; form.name = data.name; dialogVisible.value = true; };
const resetForm = () => { if (formRef.value) formRef.value.resetFields(); form.name = ''; };
const openCreateDialog = (type, pId) => {
dialogType.value = type;
dialogMode.value = 'create';
parentId.value = pId;
currentId.value = null;
dialogVisible.value = true;
};
const openEditDialog = (data) => {
dialogType.value = data.type;
dialogMode.value = 'edit';
currentId.value = data.id;
form.name = data.name;
dialogVisible.value = true;
};
const resetForm = () => {
if (formRef.value) formRef.value.resetFields();
form.name = '';
};
const handleSubmit = async () => {
if (!formRef.value) return;
@ -685,45 +921,78 @@ const handleSubmit = async () => {
submitLoading.value = true;
try {
if (dialogMode.value === 'create') {
if (dialogType.value === 'department') await createDepartment({ name: form.name, hospitalId: parentId.value });
else if (dialogType.value === 'group') await createGroup({ name: form.name, departmentId: parentId.value });
if (dialogType.value === 'department')
await createDepartment({
name: form.name,
hospitalId: parentId.value,
});
else if (dialogType.value === 'group')
await createGroup({
name: form.name,
departmentId: parentId.value,
});
ElMessage.success('创建成功');
} else {
if (dialogType.value === 'hospital') await updateHospital(currentId.value, { name: form.name });
else if (dialogType.value === 'department') await updateDepartment(currentId.value, { name: form.name });
else if (dialogType.value === 'group') await updateGroup(currentId.value, { name: form.name });
if (dialogType.value === 'hospital')
await updateHospital(currentId.value, { name: form.name });
else if (dialogType.value === 'department')
await updateDepartment(currentId.value, { name: form.name });
else if (dialogType.value === 'group')
await updateGroup(currentId.value, { name: form.name });
ElMessage.success('更新成功');
// Update activeNode locally if it's the one edited
if (activeNode.value && activeNode.value.id === currentId.value && activeNode.value.type === dialogType.value) {
if (
activeNode.value &&
activeNode.value.id === currentId.value &&
activeNode.value.type === dialogType.value
) {
activeNode.value.name = form.name;
}
}
dialogVisible.value = false;
fetchTreeData();
} catch (error) { console.error(error); } finally { submitLoading.value = false; }
} catch (error) {
console.error(error);
} finally {
submitLoading.value = false;
}
}
});
};
const handleDelete = (data) => {
const typeName = data.type === 'hospital' ? '医院' : (data.type === 'department' ? '科室' : '小组');
ElMessageBox.confirm(`确定要删除${typeName} "${data.name}" 吗?`, '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(async () => {
try {
if (data.type === 'hospital') await deleteHospital(data.id);
else if (data.type === 'department') await deleteDepartment(data.id);
else if (data.type === 'group') await deleteGroup(data.id);
ElMessage.success('删除成功');
if (activeNode.value && activeNode.value.key === data.key) {
activeNode.value = null; // Clear active node if deleted
const typeName =
data.type === 'hospital'
? '医院'
: data.type === 'department'
? '科室'
: '小组';
ElMessageBox.confirm(`确定要删除${typeName} "${data.name}" 吗?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
try {
if (data.type === 'hospital') await deleteHospital(data.id);
else if (data.type === 'department') await deleteDepartment(data.id);
else if (data.type === 'group') await deleteGroup(data.id);
ElMessage.success('删除成功');
if (activeNode.value && activeNode.value.key === data.key) {
activeNode.value = null; // Clear active node if deleted
}
fetchTreeData();
} catch (error) {
console.error(error);
}
fetchTreeData();
} catch (error) { console.error(error); }
}).catch(() => {});
})
.catch(() => {});
};
onMounted(() => { fetchTreeData(); });
onMounted(() => {
fetchTreeData();
});
</script>
<style scoped>
@ -731,7 +1000,8 @@ onMounted(() => { fetchTreeData(); });
padding: 20px;
}
.tree-card, .detail-card {
.tree-card,
.detail-card {
border-radius: 8px;
height: calc(100vh - 120px);
display: flex;
@ -846,10 +1116,19 @@ onMounted(() => { fetchTreeData(); });
font-size: 16px;
}
.node-hospital .node-icon-wrapper { color: #409EFF; }
.node-department .node-icon-wrapper { color: #67C23A; }
.node-group .node-icon-wrapper { color: #E6A23C; }
.node-user .node-icon-wrapper { color: #909399; font-size: 14px; }
.node-hospital .node-icon-wrapper {
color: #409eff;
}
.node-department .node-icon-wrapper {
color: #67c23a;
}
.node-group .node-icon-wrapper {
color: #e6a23c;
}
.node-user .node-icon-wrapper {
color: #909399;
font-size: 14px;
}
.node-label {
font-size: 14px;

View File

@ -11,7 +11,7 @@
v-model="searchForm.hospitalId"
placeholder="系统管理员必须选择医院"
clearable
style="width: 240px;"
style="width: 240px"
@change="handleSearchHospitalChange"
>
<el-option
@ -30,9 +30,13 @@
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch" icon="Search">查询</el-button>
<el-button type="primary" @click="handleSearch" icon="Search"
>查询</el-button
>
<el-button @click="resetSearch" icon="Refresh">重置</el-button>
<el-button type="success" @click="openCreateDialog" icon="Plus">新增患者</el-button>
<el-button type="success" @click="openCreateDialog" icon="Plus"
>新增患者</el-button
>
</el-form-item>
</el-form>
</div>
@ -42,10 +46,16 @@
type="warning"
:closable="false"
title="系统管理员查询患者时必须先选择医院。"
style="margin-bottom: 16px;"
style="margin-bottom: 16px"
/>
<el-table :data="tableData" v-loading="loading" border stripe style="width: 100%">
<el-table
:data="tableData"
v-loading="loading"
border
stripe
style="width: 100%"
>
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="姓名" min-width="120" />
<el-table-column prop="phone" label="手机号" min-width="140" />
@ -62,7 +72,11 @@
</el-table-column>
<el-table-column label="操作" width="260" fixed="right" align="center">
<template #default="{ row }">
<el-button size="small" type="primary" @click="openRecordDialog(row)">
<el-button
size="small"
type="primary"
@click="openRecordDialog(row)"
>
详情
</el-button>
<el-button size="small" @click="openEditDialog(row)">
@ -114,7 +128,7 @@
filterable
clearable
placeholder="请选择归属医生(按科室/小组)"
style="width: 100%;"
style="width: 100%"
:disabled="userStore.role === 'DOCTOR'"
/>
</el-form-item>
@ -122,7 +136,11 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
<el-button
type="primary"
:loading="submitLoading"
@click="handleSubmit"
>
确定
</el-button>
</div>
@ -131,13 +149,27 @@
<el-dialog title="调压记录详情" v-model="recordDialogVisible" width="860px">
<el-descriptions :column="4" border class="mb-16">
<el-descriptions-item label="患者">{{ currentPatientName || '-' }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ recordSummary.phone || '-' }}</el-descriptions-item>
<el-descriptions-item label="身份证">{{ recordSummary.idCardHash || '-' }}</el-descriptions-item>
<el-descriptions-item label="记录数">{{ recordList.length }}</el-descriptions-item>
<el-descriptions-item label="患者">{{
currentPatientName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="手机号">{{
recordSummary.phone || '-'
}}</el-descriptions-item>
<el-descriptions-item label="身份证">{{
recordSummary.idCardHash || '-'
}}</el-descriptions-item>
<el-descriptions-item label="记录数">{{
recordList.length
}}</el-descriptions-item>
</el-descriptions>
<el-table :data="recordList" v-loading="recordLoading" border stripe max-height="520">
<el-table
:data="recordList"
v-loading="recordLoading"
border
stripe
max-height="520"
>
<el-table-column label="时间" width="180">
<template #default="{ row }">
{{ new Date(row.occurredAt).toLocaleString() }}
@ -155,7 +187,8 @@
</el-table-column>
<el-table-column label="压力变更" min-width="140">
<template #default="{ row }">
{{ row.taskItem?.oldPressure ?? '-' }} -> {{ row.taskItem?.targetPressure ?? '-' }}
{{ row.taskItem?.oldPressure ?? '-' }} ->
{{ row.taskItem?.targetPressure ?? '-' }}
</template>
</el-table-column>
<el-table-column label="医院" min-width="140">
@ -260,11 +293,15 @@ const doctorTreeProps = {
};
const departmentNameMap = computed(() => {
return Object.fromEntries((departments.value || []).map((item) => [item.id, item.name]));
return Object.fromEntries(
(departments.value || []).map((item) => [item.id, item.name]),
);
});
const groupNameMap = computed(() => {
return Object.fromEntries((groups.value || []).map((item) => [item.id, item.name]));
return Object.fromEntries(
(groups.value || []).map((item) => [item.id, item.name]),
);
});
const doctorTreeOptions = computed(() => {
@ -316,16 +353,19 @@ const doctorTreeOptions = computed(() => {
});
});
return Array.from(deptMap.values()).sort((a, b) => String(a.label).localeCompare(String(b.label), 'zh-Hans-CN'));
return Array.from(deptMap.values()).sort((a, b) =>
String(a.label).localeCompare(String(b.label), 'zh-Hans-CN'),
);
});
const applyFiltersAndPagination = () => {
const keyword = searchForm.keyword.trim();
const filtered = allPatients.value.filter((patient) => {
const hitKeyword = !keyword
|| patient.name?.includes(keyword)
|| patient.phone?.includes(keyword);
const hitKeyword =
!keyword ||
patient.name?.includes(keyword) ||
patient.phone?.includes(keyword);
return hitKeyword;
});
@ -481,15 +521,11 @@ const handleSubmit = async () => {
};
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除患者 "${row.name}" 吗?`,
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
},
)
ElMessageBox.confirm(`确定要删除患者 "${row.name}" 吗?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await deletePatient(row.id);
ElMessage.success('删除成功');

View File

@ -36,7 +36,13 @@
</el-form>
</div>
<el-table :data="tableData" v-loading="loading" border stripe style="width: 100%">
<el-table
:data="tableData"
v-loading="loading"
border
stripe
style="width: 100%"
>
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="姓名" min-width="120" />
<el-table-column prop="phone" label="手机号" min-width="150" />
@ -65,7 +71,9 @@
<el-table-column label="操作" width="260" fixed="right" align="center">
<template #default="{ row }">
<el-button
v-if="row.role === 'ENGINEER' && userStore.role === 'SYSTEM_ADMIN'"
v-if="
row.role === 'ENGINEER' && userStore.role === 'SYSTEM_ADMIN'
"
size="small"
type="warning"
@click="openAssignDialog(row)"
@ -123,7 +131,11 @@
/>
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" placeholder="请选择角色" style="width: 100%;">
<el-select
v-model="form.role"
placeholder="请选择角色"
style="width: 100%"
>
<el-option label="系统管理员" value="SYSTEM_ADMIN" />
<el-option label="医院管理员" value="HOSPITAL_ADMIN" />
<el-option label="科室主任" value="DIRECTOR" />
@ -137,7 +149,7 @@
<el-select
v-model="form.hospitalId"
placeholder="请选择医院"
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="hospital in hospitals"
@ -148,11 +160,15 @@
</el-select>
</el-form-item>
<el-form-item label="所属科室" prop="departmentId" v-if="needDepartment">
<el-form-item
label="所属科室"
prop="departmentId"
v-if="needDepartment"
>
<el-select
v-model="form.departmentId"
placeholder="请选择科室"
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="department in formDepartments"
@ -167,7 +183,7 @@
<el-select
v-model="form.groupId"
placeholder="请选择小组"
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="group in formGroups"
@ -181,7 +197,11 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">
<el-button
type="primary"
@click="handleSubmit"
:loading="submitLoading"
>
确定
</el-button>
</div>
@ -201,7 +221,7 @@
<el-select
v-model="assignHospitalId"
placeholder="请选择医院"
style="width: 100%;"
style="width: 100%"
>
<el-option
v-for="hospital in hospitals"
@ -215,7 +235,11 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="assignDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleAssignSubmit" :loading="submitLoading">
<el-button
type="primary"
@click="handleAssignSubmit"
:loading="submitLoading"
>
确定
</el-button>
</div>
@ -286,9 +310,7 @@ const needHospital = computed(() => form.role && form.role !== 'SYSTEM_ADMIN');
const needDepartment = computed(() =>
['DIRECTOR', 'LEADER', 'DOCTOR'].includes(form.role),
);
const needGroup = computed(() =>
['LEADER', 'DOCTOR'].includes(form.role),
);
const needGroup = computed(() => ['LEADER', 'DOCTOR'].includes(form.role));
const rules = computed(() => ({
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
@ -299,9 +321,9 @@ const rules = computed(() => ({
password: isEdit.value
? []
: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 8, message: '密码长度至少为 8 位', trigger: 'blur' },
],
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 8, message: '密码长度至少为 8 位', trigger: 'blur' },
],
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
hospitalId: needHospital.value
? [{ required: true, message: '请选择所属医院', trigger: 'change' }]
@ -332,14 +354,14 @@ const getRoleTagType = (role) => {
return 'info';
};
const hospitalMap = computed(() =>
new Map(hospitals.value.map((item) => [item.id, item.name])),
const hospitalMap = computed(
() => new Map(hospitals.value.map((item) => [item.id, item.name])),
);
const departmentMap = computed(() =>
new Map(departments.value.map((item) => [item.id, item.name])),
const departmentMap = computed(
() => new Map(departments.value.map((item) => [item.id, item.name])),
);
const groupMap = computed(() =>
new Map(groups.value.map((item) => [item.id, item.name])),
const groupMap = computed(
() => new Map(groups.value.map((item) => [item.id, item.name])),
);
const resolveHospitalName = (id) => {
@ -570,15 +592,11 @@ const handleSubmit = async () => {
};
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除用户 "${row.name}" 吗?`,
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
},
)
ElMessageBox.confirm(`确定要删除用户 "${row.name}" 吗?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
await deleteUser(row.id);
ElMessage.success('删除成功');
@ -601,7 +619,10 @@ const handleAssignSubmit = async () => {
submitLoading.value = true;
try {
await assignEngineerHospital(currentAssignUser.value.id, assignHospitalId.value);
await assignEngineerHospital(
currentAssignUser.value.id,
assignHospitalId.value,
);
ElMessage.success('分配成功');
assignDialogVisible.value = false;
await fetchCommonData();