EL 5fdf4c80e6 医院管理页新增医院管理员列并支持任命医院管理员
组织架构树展示医院管理员信息
科室与小组弹窗支持设置主任/组长并限制候选角色
患者页优化归属医生选择与字段文案
统一“小组组长”角色文案
2026-03-18 17:07:37 +08:00

367 lines
9.9 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="hospitals-container">
<el-card>
<!-- Header / Actions -->
<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-form-item>
<el-form-item>
<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-form-item>
</el-form>
</div>
<!-- Table -->
<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">
<template #default="{ row }">
{{ row.adminDisplay || '未设置' }}
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" width="180">
<template #default="{ row }">
{{ new Date(row.createdAt).toLocaleString() }}
</template>
</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
>
</template>
</el-table-column>
</el-table>
<!-- Pagination -->
<div class="pagination-container">
<el-pagination
v-model:current-page="page"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
background
layout="total, sizes, prev, pager, next, jumper"
@size-change="fetchData"
@current-change="fetchData"
/>
</div>
</el-card>
<!-- Dialog for Create / Edit -->
<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-select
v-model="form.adminUserId"
placeholder="可选:选择后将任命为医院管理员"
clearable
filterable
style="width: 100%"
>
<el-option
v-for="user in hospitalAdminOptions"
:key="user.id"
:label="`${user.name}${user.phone} / ${getRoleName(user.role)}`"
:value="user.id"
/>
</el-select>
</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
>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
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 { getUsers, updateUser } from '../../api/users';
import { useUserStore } from '../../store/user';
const router = useRouter();
const userStore = useUserStore();
// --- State ---
const loading = ref(false);
const tableData = ref([]);
const total = ref(0);
const page = ref(1);
const pageSize = ref(10);
const hospitalAdminOptions = ref([]);
const roleMap = {
SYSTEM_ADMIN: '系统管理员',
HOSPITAL_ADMIN: '医院管理员',
DIRECTOR: '科室主任',
LEADER: '小组组长',
DOCTOR: '医生',
ENGINEER: '工程师',
};
const getRoleName = (role) => roleMap[role] || role;
const searchForm = reactive({
keyword: '',
});
// Dialog State
const dialogVisible = ref(false);
const isEdit = ref(false);
const submitLoading = ref(false);
const formRef = ref(null);
const currentId = ref(null);
const form = reactive({
name: '',
adminUserId: null,
});
const rules = {
name: [{ required: true, message: '请输入医院名称', trigger: 'blur' }],
};
// --- Methods ---
const fetchData = async () => {
loading.value = true;
try {
const res = await getHospitals({
page: page.value,
pageSize: pageSize.value,
keyword: searchForm.keyword || undefined,
});
let hospitalAdminNameMap = {};
try {
const userRes = await getUsers();
const users = Array.isArray(userRes?.list) ? userRes.list : [];
hospitalAdminNameMap = users.reduce((acc, user) => {
if (user.role !== 'HOSPITAL_ADMIN' || !user.hospitalId) {
return acc;
}
if (!acc[user.hospitalId]) {
acc[user.hospitalId] = [];
}
acc[user.hospitalId].push(user.name || '-');
return acc;
}, {});
} catch (error) {
console.error('Failed to fetch hospital admins', error);
}
tableData.value = (res.list || []).map((hospital) => ({
...hospital,
adminDisplay:
(hospitalAdminNameMap[hospital.id] || []).join('、') || '未设置',
}));
total.value = res.total || 0;
} catch (error) {
console.error('Failed to fetch hospitals', error);
} finally {
loading.value = false;
}
};
const resetSearch = () => {
searchForm.keyword = '';
page.value = 1;
fetchData();
};
const openCreateDialog = () => {
isEdit.value = false;
currentId.value = null;
loadHospitalAdminOptions();
dialogVisible.value = true;
};
const openEditDialog = (row) => {
isEdit.value = true;
currentId.value = row.id;
form.name = row.name;
loadHospitalAdminOptions(row.id);
dialogVisible.value = true;
};
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
form.name = '';
form.adminUserId = null;
};
const loadHospitalAdminOptions = async (hospitalId) => {
if (userStore.role !== 'SYSTEM_ADMIN') {
hospitalAdminOptions.value = [];
return;
}
const userRes = await getUsers();
const users = Array.isArray(userRes?.list) ? userRes.list : [];
hospitalAdminOptions.value = users.filter((user) => {
// 仅允许选择“医院管理员”角色,避免误选普通人员。
if (user.role !== 'HOSPITAL_ADMIN') {
return false;
}
if (!hospitalId) {
return true;
}
return user.hospitalId == null || user.hospitalId === hospitalId;
});
};
const handleSubmit = async () => {
if (!formRef.value) return;
await formRef.value.validate(async (valid) => {
if (valid) {
submitLoading.value = true;
try {
let targetHospitalId = null;
if (isEdit.value) {
const updated = await updateHospital(currentId.value, {
name: form.name,
});
targetHospitalId = updated?.id ?? currentId.value;
ElMessage.success('更新成功');
} else {
const created = await createHospital({ name: form.name });
targetHospitalId = created?.id;
ElMessage.success('创建成功');
}
if (form.adminUserId && targetHospitalId) {
const selectedAdmin = hospitalAdminOptions.value.find(
(user) => user.id === form.adminUserId,
);
if (!selectedAdmin || selectedAdmin.role !== 'HOSPITAL_ADMIN') {
ElMessage.error('仅可选择医院管理员角色人员');
return;
}
await updateUser(form.adminUserId, {
role: 'HOSPITAL_ADMIN',
hospitalId: targetHospitalId,
departmentId: null,
groupId: null,
});
ElMessage.success('医院管理员已设置');
}
dialogVisible.value = false;
fetchData();
} catch (error) {
console.error('Submit failed', error);
} finally {
submitLoading.value = false;
}
}
});
};
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(() => {});
};
const goToDepartments = (row) => {
router.push({
path: '/organization/departments',
query: { hospitalId: row.id, hospitalName: row.name },
});
};
// --- Lifecycle ---
onMounted(() => {
fetchData();
});
</script>
<style scoped>
.hospitals-container {
padding: 0;
}
.header-actions {
margin-bottom: 20px;
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>