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

1331 lines
40 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-row :gutter="20">
<!--部门数据-->
<el-col :span="4" :xs="24">
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
prefix-icon="Search"
style="margin-bottom: 20px"
/>
</div>
<div class="head-container">
<el-tree
:data="deptOptions"
:props="{ label: 'name', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="deptTreeRef"
node-key="id"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-col>
<!--用户数据-->
<el-col :span="20" :xs="24">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<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="phonenumber">
<el-input
v-model="queryParams.phonenumber"
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"
v-hasPermi="['system:user:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:user:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:user:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="Upload"
@click="handleImport"
v-hasPermi="['system:user:import']"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['system:user:export']"
>导出</el-button>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
:columns="columns"
></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column
label="用户编号"
align="center"
key="userId"
prop="userId"
v-if="columns[0].visible"
/>
<el-table-column
label="用户名称"
align="center"
key="userName"
prop="userName"
v-if="columns[1].visible"
:show-overflow-tooltip="true"
/>
<el-table-column
label="用户昵称"
align="center"
key="nickName"
prop="nickName"
v-if="columns[2].visible"
:show-overflow-tooltip="true"
/>
<el-table-column
label="部门"
align="center"
key="orgId_dictText"
prop="orgId_dictText"
v-if="columns[3].visible"
:show-overflow-tooltip="true"
/>
<el-table-column
label="手机号码"
align="center"
key="phonenumber"
prop="phonenumber"
v-if="columns[4].visible"
width="120"
/>
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
<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"
v-if="columns[6].visible"
width="160"
>
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="150"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-tooltip content="修改" placement="top" v-if="scope.row.userId !== 1">
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:user:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="scope.row.userId !== 1">
<el-button
link
type="primary"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:user:remove']"
></el-button>
</el-tooltip>
<el-tooltip content="重置密码" placement="top" v-if="scope.row.userId !== 1">
<el-button
link
type="primary"
icon="Key"
@click="handleResetPwd(scope.row)"
v-hasPermi="['system:user:resetPwd']"
></el-button>
</el-tooltip>
<el-tooltip content="分配角色" placement="top" v-if="scope.row.userId !== 1">
<el-button
link
type="primary"
icon="CircleCheck"
@click="handleAuthRole(scope.row)"
v-hasPermi="['system:user:edit']"
></el-button>
</el-tooltip>
</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>
<!-- 添加或修改用户配置对话框 -->
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<el-form :model="form" :rules="rules" ref="userRef" label-width="120px">
<div class="form-subtitle">用户信息</div>
<el-row>
<el-col :span="12">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="责任科室" prop="responsibilityOrgDtoList" style="width: 100%">
<el-tree-select
style="width: 100%"
multiple
v-model="form.responsibilityOrgDtoListIds"
@change="handleChange"
:data="deptOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
placeholder="请选择责任科室"
default-expand-all
check-strictly
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input
v-model="form.password"
placeholder="请输入用户密码"
type="password"
maxlength="20"
show-password
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option
v-for="dict in sys_user_sex"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<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-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="出生日期">
<el-date-picker
v-model="form.birthDate"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" placeholder="请输入地址" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="医保码" prop="ybNo">
<el-input v-model="form.ybNo" placeholder="请输入医保码" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="角色" prop="practitionerRolesDtoListIds" style="width: 100%">
<el-select
v-model="form.practitionerRolesDtoListIds"
multiple
placeholder="请选择"
@change="handleRoleChange"
style="width: 100%"
>
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:label="item.roleName"
:value="item.roleId"
:disabled="item.status == 1"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="职称编码" prop="drProfttlCode">
<el-select v-model="form.drProfttlCode" placeholder="请选择职称编码">
<el-option
v-for="dict in drord_dr_profttl"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="签名图片" prop="signature">
<!-- 上传区域 -->
<div v-if="!form.signature" class="upload-area">
<input
type="file"
accept="image/*"
@change="handleFileSelect"
ref="fileInput"
class="file-input"
/>
<div class="upload-placeholder">
<el-icon><Plus /></el-icon>
<p>点击上传图片</p>
</div>
</div>
<!-- 图片预览区域 -->
<div v-else class="preview-area">
<img :src="form.signature" class="preview-image" />
<el-button
type="danger"
icon="Delete"
circle
size="small"
@click="removeImage"
class="remove-button"
></el-button>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="职业资格证编号" prop="pharPracCertNo">
<el-input
v-model="form.pharPracCertNo"
placeholder="请输入职业资格证编号"
maxlength="200"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="出诊科室" style="width: 100%">
<el-tree-select
:disabled="doctorShow"
multiple
style="width: 100%"
v-model="form.doctorVisitOrgDtoListIds"
@change="handleLOrgDtoChange"
:data="deptOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
default-expand-all
placeholder="请选择出诊科室"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="管理药库" style="width: 100%">
<el-tree-select
:disabled="locationAdminShow"
multiple
style="width: 100%"
@change="handleLocationChange"
v-model="form.manageLocationDtoListIds"
:data="locationOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
placeholder="请选择管理药库"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="管理药房" style="width: 100%">
<el-tree-select
:disabled="pharmacyShow"
multiple
style="width: 100%"
@change="handlePharmacyChange"
v-model="form.manageMedicationLocationDtoListIds"
:data="pharmacyOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
placeholder="请选择管理药房"
check-strictly
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="管理科室" style="width: 100%">
<el-tree-select
:disabled="nurseShow"
multiple
style="width: 100%"
@change="handleOrgChange"
v-model="form.manageOrgDtoListIds"
:data="deptOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
placeholder="请选择管理科室"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="管理病区" style="width: 100%">
<el-select
:disabled="nurseShow"
v-model="form.manageWardLocationDtoListIds"
multiple
placeholder="请选择"
@change="handleWardChange"
style="width: 100%"
>
<el-option
v-for="item in wardListOptions"
:key="item.id"
:label="item.name"
:value="item.id"
:disabled="item.status == 1"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<div class="form-subtitle">备注</div>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</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="upload.title" v-model="upload.open" width="400px" append-to-body>
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link
type="primary"
:underline="false"
style="font-size: 12px; vertical-align: baseline"
@click="importTemplate"
>下载模板</el-link
>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="User">
import { getToken } from '@/utils/auth';
import {
changeUserStatus,
listUser,
resetUserPwd,
delUser,
getUser,
getRole,
updateUser,
addUser,
deptTreeSelect,
locationTreeSelect,
pharmacyTreeSelect,
wardList,
} from '@/api/system/user';
import { ref } from 'vue';
const router = useRouter();
const { proxy } = getCurrentInstance();
const { sys_normal_disable, sys_user_sex, drord_dr_profttl } = proxy.useDict('sys_normal_disable', 'sys_user_sex', 'drord_dr_profttl');
const fileList = ref([]);
const limit = ref(1); // 添加limit变量定义
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
const userList = 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 dateRange = ref([]);
const deptName = ref('');
const deptOptions = ref(undefined);
const wardListOptions = ref(undefined);
const locationOptions = ref([]);
const initPassword = ref(undefined);
const postOptions = ref([]);
const roleOptions = ref([]);
const doctorShow = ref(true);
const locationAdminShow = ref(true);
const pharmacyShow = ref(true);
const nurseShow = ref(true);
const pharmacyOptions = ref([]);
/*** 用户导入参数 */
const upload = reactive({
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: '',
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: 'Bearer ' + getToken() },
// 上传的地址
url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData',
});
// 列显隐信息
const columns = ref([
{ key: 0, label: `用户编号`, visible: true },
{ key: 1, label: `用户名称`, visible: true },
{ key: 2, label: `用户昵称`, visible: true },
{ key: 3, label: `部门`, visible: true },
{ key: 4, label: `手机号码`, visible: true },
{ key: 5, label: `状态`, visible: true },
{ key: 6, label: `创建时间`, visible: true },
]);
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 10,
userName: undefined,
phonenumber: undefined,
status: undefined,
orgId: undefined,
},
rules: {
userName: [
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' },
],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
responsibilityOrgDtoList: [{ required: true, message: '责任科室不能为空', trigger: 'blur' }],
practitionerRolesDtoListIds: [{ required: true, message: '角色不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' },
],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phonenumber: [
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' },
],
},
});
const { queryParams, form, rules } = toRefs(data);
/** 通过条件过滤节点 */
const filterNode = (value, data) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watch(deptName, (val) => {
proxy.$refs['deptTreeRef'].filter(val);
});
/** 查询部门下拉树结构 */
function getDeptTree() {
deptTreeSelect().then((response) => {
deptOptions.value = response.data.records;
console.log(deptOptions.value);
});
}
/** 获取病区下拉列表 */
function getWardList() {
wardList().then((response) => {
wardListOptions.value = response.data;
});
}
/** 查询位置下拉树结构 */
function getLocationTree() {
locationTreeSelect().then((response) => {
locationOptions.value = response.data;
});
pharmacyTreeSelect().then((res) => {
pharmacyOptions.value = res.data;
});
}
/** 查询用户列表 */
function getList() {
loading.value = true;
listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then((res) => {
loading.value = false;
userList.value = res.data.records;
total.value = res.data.total;
});
}
/** 节点单击事件 */
function handleNodeClick(data) {
queryParams.value.orgId = data.id;
handleQuery();
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNo = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = [];
proxy.resetForm('queryRef');
queryParams.value.orgId = undefined;
proxy.$refs.deptTreeRef.setCurrentKey(null);
handleQuery();
}
/** 删除按钮操作 */
function handleDelete(row) {
const userIds = row.userId || ids.value;
proxy.$modal
.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?')
.then(function () {
return delUser(userIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
})
.catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
'system/user/export',
{
...queryParams.value,
},
`user_${new Date().getTime()}.xlsx`
);
}
/** 用户状态修改 */
function handleStatusChange(row) {
let text = row.status === '0' ? '启用' : '停用';
proxy.$modal
.confirm('确认要"' + text + '""' + row.userName + '"用户吗?')
.then(function () {
return changeUserStatus(row.userId, row.status);
})
.then(() => {
proxy.$modal.msgSuccess(text + '成功');
})
.catch(function () {
row.status = row.status === '0' ? '1' : '0';
});
}
/** 更多操作 */
function handleCommand(command, row) {
switch (command) {
case 'handleResetPwd':
handleResetPwd(row);
break;
case 'handleAuthRole':
handleAuthRole(row);
break;
default:
break;
}
}
/** 跳转角色分配 */
function handleAuthRole(row) {
const userId = row.userId;
router.push('/system/user-auth/role/' + userId);
}
/** 重置密码按钮操作 */
function handleResetPwd(row) {
proxy
.$prompt('请输入"' + row.userName + '"的新密码', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return '不能包含非法字符:< > " \' \\ |';
}
},
})
.then(({ value }) => {
resetUserPwd(row.userId, value).then((response) => {
proxy.$modal.msgSuccess('修改成功,新密码是:' + value);
});
})
.catch(() => {});
}
/** 选择条数 */
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.userId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 导入按钮操作 */
function handleImport() {
upload.title = '用户导入';
upload.open = true;
}
/** 下载模板操作 */
function importTemplate() {
proxy.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`);
}
/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
upload.isUploading = true;
};
/** 文件上传成功处理 */
const handleFileSuccess = (response, file, fileList) => {
upload.open = false;
upload.isUploading = false;
proxy.$refs['uploadRef'].handleRemove(file);
proxy.$alert(
"<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
response.msg +
'</div>',
'导入结果',
{ dangerouslyUseHTMLString: true }
);
getList();
};
/** 提交上传文件 */
function submitFileForm() {
proxy.$refs['uploadRef'].submit();
}
/** 重置操作表单 */
function reset() {
form.value = {
userId: undefined,
orgId: undefined,
userName: undefined,
nickName: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: '0',
remark: undefined,
practitionerRolesDtoList: [],
responsibilityOrgDtoList: [],
manageLocationDtoList: [],
doctorVisitOrgDtoList: [],
manageOrgDtoList: [],
manageWardLocationDtoList: [],
};
proxy.resetForm('userRef');
// 清空图片相关数据
fileList.value = [];
form.value.signature = '';
if (proxy.$refs.uploadRef) {
proxy.$refs.uploadRef.clearFiles();
}
}
/** 取消按钮 */
function cancel() {
open.value = false;
reset();
// 清空图片相关数据
fileList.value = [];
if (proxy.$refs.uploadRef) {
proxy.$refs.uploadRef.clearFiles();
}
}
/** 新增按钮操作 */
function handleAdd() {
reset();
getRole().then((response) => {
postOptions.value = response.posts;
roleOptions.value = response.roles;
open.value = true;
// 默认设置为 true
doctorShow.value = true;
locationAdminShow.value = true;
pharmacyShow.value = true;
title.value = '添加用户';
form.value.password = initPassword.value;
});
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
const userId = row.userId || ids.value;
getUser(userId).then((response) => {
form.value = response.data;
form.value.responsibilityOrgDtoListIds = response.data.responsibilityOrgDtoList.map(
(res) => res.orgId
);
form.value.practitionerRolesDtoListIds = response.data.practitionerRolesDtoList.map((res) =>
Number(res.roleId)
);
form.value.manageLocationDtoListIds = response.data.manageLocationDtoList.map(
(res) => res.locationId
);
form.value.manageMedicationLocationDtoListIds =
response.data.manageMedicationLocationDtoList.map((res) => res.locationId);
form.value.doctorVisitOrgDtoListIds = response.data.doctorVisitOrgDtoList.map(
(res) => res.orgId
);
form.value.manageOrgDtoListIds = response.data.manageOrgDtoList.map((res) => res.orgId);
form.value.manageWardLocationDtoListIds = response.data.manageWardLocationDtoList.map(
(res) => res.locationId
);
// 检查 practitionerRolesDtoList 中是否有 roleCode 为 "doctor" 或 "locationAdmin"
const hasDoctorRole = response.data.practitionerRolesDtoList.some(
(role) => role.roleCode === 'doctor'
);
const hasLocationAdminRole = response.data.practitionerRolesDtoList.some(
(role) => role.roleCode === 'locationAdmin'
);
const hasPharmacyRole = response.data.practitionerRolesDtoList.some(
(role) => role.roleCode === 'pharmacist'
);
const hasNurseRole = response.data.practitionerRolesDtoList.some(
(role) => role.roleCode === 'nurse'
);
// 根据检查结果设置 doctorShow 和 locationAdminShow
doctorShow.value = !hasDoctorRole;
locationAdminShow.value = !hasLocationAdminRole;
pharmacyShow.value = !hasPharmacyRole;
nurseShow.value = !hasNurseRole;
open.value = true;
title.value = '修改用户';
form.password = '';
});
getRole().then((response) => {
postOptions.value = response.posts;
roleOptions.value = response.roles;
});
}
function handleRoleChange(selectedRoleIds) {
console.log(selectedRoleIds);
form.value.practitionerRolesDtoList = [];
// 默认设置为 true
doctorShow.value = true;
locationAdminShow.value = true;
pharmacyShow.value = true;
// 遍历选中的角色 ID 数组
selectedRoleIds.forEach((roleId) => {
// 从 roleOptions 中找到对应的角色对象
const role = roleOptions.value.find((role) => role.roleId === roleId);
form.value.practitionerRolesDtoList.push({ roleId: role.roleId });
if (role) {
if (role.roleKey.includes('doctor')) {
doctorShow.value = false;
}
if (role.roleKey.includes('locationAdmin')) {
locationAdminShow.value = false;
}
if (role.roleKey.includes('pharmacist')) {
pharmacyShow.value = false;
}
if (role.roleKey.includes('nurse')) {
nurseShow.value = false;
}
}
});
// 循环结束后检查
if (doctorShow.value) {
form.value.doctorVisitOrgDtoListIds = [];
form.value.doctorVisitOrgDtoList = [];
}
if (locationAdminShow.value) {
form.value.manageLocationDtoListIds = [];
form.value.manageLocationDtoList = [];
}
if (pharmacyShow.value) {
form.value.manageMedicationLocationDtoListIds = [];
form.value.manageMedicationLocationDtoList = [];
}
if (nurseShow.value) {
form.value.manageOrgDtoList = [];
form.value.manageWardLocationDtoList = [];
form.value.manageOrgDtoListIds = [];
form.value.manageWardLocationDtoListIds = [];
}
}
function handleLocationChange(e) {
form.value.manageLocationDtoList = [];
e.forEach((id) => {
// 使用递归函数查找节点
const location = findNodeById(locationOptions.value, id);
if (location) {
form.value.manageLocationDtoList.push({
orgId: location.organizationId,
locationId: location.id,
});
}
});
}
function handleOrgChange(e) {
form.value.manageOrgDtoList = [];
e.forEach((id) => {
// 使用递归函数查找节点
const org = findNodeById(deptOptions.value, id);
if (org) {
form.value.manageOrgDtoList.push({ orgId: org.id });
}
});
}
function handleWardChange(selectValue) {
form.value.manageWardLocationDtoList = [];
selectValue.forEach((id) => {
const ward = wardListOptions.value.find((ward) => ward.id === id);
form.value.manageWardLocationDtoList.push({ locationId: ward.id });
});
}
function handlePharmacyChange(e) {
form.value.manageMedicationLocationDtoList = [];
e.forEach((id) => {
// 使用递归函数查找节点
const location = findNodeById(pharmacyOptions.value, id);
if (location) {
form.value.manageMedicationLocationDtoList.push({
orgId: location.organizationId,
locationId: location.id,
});
}
});
}
function handleChange(e) {
form.value.responsibilityOrgDtoList = [];
e.forEach((id) => {
// 使用递归函数查找节点
const OrgDto = findNodeById(deptOptions.value, id);
if (OrgDto) {
form.value.responsibilityOrgDtoList.push({ orgId: OrgDto.id });
}
});
}
function handleLOrgDtoChange(e) {
form.value.doctorVisitOrgDtoList = [];
e.forEach((id) => {
// 使用递归函数查找节点
const OrgDto = findNodeById(deptOptions.value, id);
if (OrgDto) {
form.value.doctorVisitOrgDtoList.push({ orgId: OrgDto.id });
}
});
console.log(form.value.doctorVisitOrgDtoList);
}
function findNodeById(tree, id) {
for (const node of tree) {
if (node.id === id) {
return node;
}
if (node.children) {
const result = findNodeById(node.children, id);
if (result) {
return result;
}
}
}
return null;
}
// 自定义校验规则:确保 roleCode 不重复
function validateUniqueRole(rule, value, callback) {
const roleCodes = form.value.childList.map((child) => child.roleCode);
const duplicates = roleCodes.filter((item, index) => roleCodes.indexOf(item) !== index);
if (duplicates.length > 0) {
callback(new Error('角色不能重复'));
} else {
callback();
}
}
/** 覆盖默认上传行为 */
function requestUpload() {}
/** 上传预处理 */
function beforeUpload(file) {
if (file.type.indexOf("image/") == -1) {
proxy.$modal.msgError("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。");
} else {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
options.img = reader.result;
options.filename = file.name;
};
}
}
/** 提交按钮 */
function submitForm() {
console.log(form.value)
proxy.$refs['userRef'].validate((valid) => {
if (valid) {
if (form.value.userId != undefined) {
updateUser(form.value).then((response) => {
proxy.$modal.msgSuccess('修改成功');
open.value = false;
getList();
});
} else {
addUser(form.value).then((response) => {
proxy.$modal.msgSuccess('新增成功');
open.value = false;
getList();
});
}
}
});
}
/** 处理base64图片上传 */
function handleBase64Upload(option) {
// 由于图片已经在handleBeforeUpload中转为base64并存储在form中这里直接返回成功
option.onSuccess();
}
function handleUploadSuccess(res, file) {
console.log(res,file)
}
/** 上传预处理 */
function handleBeforeUpload(file) {
console.log(file)
// 检查是否已有一张图片,如果有则不允许继续上传
if (fileList.value.length >= limit.value) {
proxy.$modal.msgWarning("只能上传一张图片,请先删除已上传的图片!");
return false;
}
if (file.type.indexOf("image/") == -1) {
proxy.$modal.msgError("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。");
return false;
} else {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
// 将base64图片数据保存到表单中
form.value.signature = reader.result;
// 更新文件列表用于显示
fileList.value = [{ name: file.name, url: reader.result }];
};
return true;
}
}
/** 处理文件移除 */
function handleRemoveFile() {
fileList.value = [];
form.value.signature = '';
}
/** 移除图片 */
function removeImage() {
form.value.signature = '';
// 重置文件输入框
if (proxy.$refs.fileInput) {
proxy.$refs.fileInput.value = '';
}
}
/** 处理文件选择 */
function handleFileSelect(event) {
const file = event.target.files[0];
if (!file) return;
// 检查文件类型
if (!file.type.startsWith('image/')) {
proxy.$modal.msgError("请选择图片文件!");
return;
}
// 检查文件大小限制为2MB
if (file.size > 2 * 1024 * 1024) {
proxy.$modal.msgError("图片大小不能超过2MB");
return;
}
// 转换为base64
const reader = new FileReader();
reader.onload = (e) => {
form.value.signature = e.target.result;
};
reader.readAsDataURL(file);
}
/** 处理文件选择变化 */
function handleFileChange(file, fileList) {
console.log('File changed:', file, fileList);
// 如果已有一张图片,先清空之前的
if (fileList.value.length >= 1) {
fileList.value = [];
form.value.signature = '';
}
// 只处理第一个文件(最新选择的)
const currentFile = file.raw;
if (!currentFile) return;
// 检查文件类型
if (!currentFile.type.startsWith('image/')) {
proxy.$modal.msgError("请选择图片文件!");
proxy.$refs.uploadRef.clearFiles();
return;
}
// 检查文件大小限制为2MB
if (currentFile.size > 2 * 1024 * 1024) {
proxy.$modal.msgError("图片大小不能超过2MB");
proxy.$refs.uploadRef.clearFiles();
return;
}
// 转换为base64
const reader = new FileReader();
reader.onload = (e) => {
form.value.signature = e.target.result;
console.log('Image converted to base64');
// 更新文件列表用于显示
fileList.value = [{ name: currentFile.name, url: e.target.result }];
};
reader.readAsDataURL(currentFile);
}
getDeptTree();
getLocationTree();
getList();
getWardList();
</script>
<style lang="scss" scoped>
.form-subtitle {
margin-bottom: 12px;
border-left: 5px solid #2969ff;
padding-left: 12px;
height: 22px;
}
.upload-area {
position: relative;
display: inline-block;
}
.file-input {
position: absolute;
top: 0;
left: 0;
width: 148px;
height: 148px;
opacity: 0;
cursor: pointer;
}
.upload-placeholder {
width: 148px;
height: 148px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
transition: border-color 0.3s;
}
.upload-placeholder:hover {
border-color: #409eff;
}
.upload-placeholder .el-icon {
font-size: 28px;
color: #8c939d;
margin-bottom: 8px;
}
.preview-area {
position: relative;
display: inline-block;
}
.preview-image {
width: 148px;
height: 148px;
object-fit: cover;
border-radius: 6px;
}
.remove-button {
position: absolute;
top: -10px;
right: -10px;
}
</style>