refactor: ♻️ 用户管理代码重构,封装部门树组件
Former-commit-id: 6021909320b038a0f9d76dd711f9c551e0863ac8
This commit is contained in:
69
src/views/system/user/components/dept-tree.vue
Normal file
69
src/views/system/user/components/dept-tree.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<!-- 部门树 -->
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<el-input v-model="deptName" placeholder="部门名称" clearable>
|
||||
<template #prefix>
|
||||
<i-ep-search />
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
:data="deptList"
|
||||
:props="{ children: 'children', label: 'label', disabled: '' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="handleFilter"
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDeptOptions } from "@/api/dept";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Number],
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const deptList = ref<OptionType[]>(); // 部门列表
|
||||
const deptTreeRef = ref(ElTree); // 部门树
|
||||
const deptName = ref(); // 部门名称
|
||||
|
||||
const emits = defineEmits(["node-click"]);
|
||||
|
||||
const deptId = useVModel(props, "modelValue", emits);
|
||||
|
||||
watchEffect(
|
||||
() => {
|
||||
deptTreeRef.value.filter(deptName.value);
|
||||
},
|
||||
{
|
||||
flush: "post", // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行
|
||||
}
|
||||
);
|
||||
|
||||
/** 部门筛选 */
|
||||
function handleFilter(value: string, data: any) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
return data.label.indexOf(value) !== -1;
|
||||
}
|
||||
|
||||
/** 部门树节点 Click */
|
||||
function handleNodeClick(data: { [key: string]: any }) {
|
||||
deptId.value = data.value;
|
||||
emits("node-click");
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
getDeptOptions().then((response) => {
|
||||
deptList.value = response.data;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* @see {@link https://vuejs.org/api/sfc-script-setup.html#defineoptions}
|
||||
*/
|
||||
import { UploadFile } from "element-plus";
|
||||
defineOptions({
|
||||
name: "User",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import {
|
||||
getUserPage,
|
||||
getUserForm,
|
||||
@@ -15,26 +16,20 @@ import {
|
||||
exportUser,
|
||||
importUser,
|
||||
} from "@/api/user";
|
||||
import { listDeptOptions } from "@/api/dept";
|
||||
import { listRoleOptions } from "@/api/role";
|
||||
import { getDeptOptions } from "@/api/dept";
|
||||
import { getRoleOptions } from "@/api/role";
|
||||
|
||||
import { UserForm, UserQuery, UserPageVO } from "@/api/user/types";
|
||||
import type { UploadInstance } from "element-plus";
|
||||
import { genFileId } from "element-plus";
|
||||
|
||||
defineOptions({
|
||||
name: "User",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const deptTreeRef = ref(ElTree); // 部门树
|
||||
const queryFormRef = ref(ElForm); // 查询表单
|
||||
const userFormRef = ref(ElForm); // 用户表单
|
||||
const uploadRef = ref<UploadInstance>();
|
||||
|
||||
const loading = ref(false);
|
||||
const ids = ref([]);
|
||||
const total = ref(0);
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const queryParams = reactive<UserQuery>({
|
||||
pageNum: 1,
|
||||
@@ -46,6 +41,12 @@ const formData = reactive<UserForm>({
|
||||
status: 1,
|
||||
});
|
||||
|
||||
const importData = reactive({
|
||||
deptId: undefined,
|
||||
file: undefined,
|
||||
fileList: [],
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
|
||||
nickname: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
@@ -67,57 +68,30 @@ const rules = reactive({
|
||||
],
|
||||
});
|
||||
|
||||
const searchDeptName = ref();
|
||||
const deptList = ref<OptionType[]>();
|
||||
const roleList = ref<OptionType[]>();
|
||||
const importDialog = reactive<DialogOption>({
|
||||
title: "用户导入",
|
||||
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
type: "user-form",
|
||||
width: 1200,
|
||||
title: "",
|
||||
});
|
||||
|
||||
/**
|
||||
* 导入选择的部门ID
|
||||
*/
|
||||
const importDeptId = ref<number>();
|
||||
const excelFile = ref<File>();
|
||||
const excelFilelist = ref<File[]>([]);
|
||||
|
||||
watchEffect(
|
||||
() => {
|
||||
deptTreeRef.value.filter(searchDeptName.value);
|
||||
},
|
||||
{
|
||||
flush: "post", // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 部门筛选
|
||||
*/
|
||||
function handleDeptFilter(value: string, data: any) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
return data.label.indexOf(value) !== -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门树节点
|
||||
*/
|
||||
function handleDeptNodeClick(data: { [key: string]: any }) {
|
||||
queryParams.deptId = data.value;
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色下拉列表
|
||||
*/
|
||||
async function getRoleOptions() {
|
||||
listRoleOptions().then((response) => {
|
||||
/** 加载角色下拉数据源 */
|
||||
async function loadRoleOptions() {
|
||||
getRoleOptions().then((response) => {
|
||||
roleList.value = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
/** 加载部门下拉数据源 */
|
||||
async function loadDeptOptions() {
|
||||
getDeptOptions().then((response) => {
|
||||
deptList.value = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户状态
|
||||
*/
|
||||
@@ -139,9 +113,7 @@ function handleStatusChange(row: { [key: string]: any }) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
*/
|
||||
/** 查询 */
|
||||
function handleQuery() {
|
||||
loading.value = true;
|
||||
getUserPage(queryParams)
|
||||
@@ -189,61 +161,94 @@ function resetPassword(row: { [key: string]: any }) {
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
/** 打开表单弹窗 */
|
||||
async function openDialog(userId?: number) {
|
||||
await getDeptOptions();
|
||||
await getRoleOptions();
|
||||
/**
|
||||
* 打开弹窗
|
||||
*
|
||||
* @param type 弹窗类型 用户表单:user-form | 用户导入:user-import
|
||||
* @param id 用户ID
|
||||
*/
|
||||
async function openDialog(type: string, id?: number) {
|
||||
dialog.visible = true;
|
||||
if (userId) {
|
||||
dialog.title = "修改用户";
|
||||
getUserForm(userId).then(({ data }) => {
|
||||
Object.assign(formData, data);
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增用户";
|
||||
dialog.type = type;
|
||||
|
||||
if (dialog.type === "user-form") {
|
||||
// 用户表单弹窗
|
||||
await loadDeptOptions();
|
||||
await loadRoleOptions();
|
||||
if (id) {
|
||||
dialog.title = "修改用户";
|
||||
getUserForm(id).then(({ data }) => {
|
||||
Object.assign(formData, { ...data });
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增用户";
|
||||
}
|
||||
} else if (dialog.type === "user-import") {
|
||||
// 用户导入弹窗
|
||||
dialog.title = "导入用户";
|
||||
dialog.width = 600;
|
||||
loadDeptOptions();
|
||||
}
|
||||
}
|
||||
|
||||
/** 关闭表单弹窗 */
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*
|
||||
* @param type 弹窗类型 用户表单:user-form | 用户导入:user-import
|
||||
*/
|
||||
function closeDialog() {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
if (dialog.type === "user-form") {
|
||||
userFormRef.value.resetFields();
|
||||
userFormRef.value.clearValidate();
|
||||
|
||||
/** 重置表单 */
|
||||
function resetForm() {
|
||||
userFormRef.value.resetFields();
|
||||
userFormRef.value.clearValidate();
|
||||
|
||||
formData.id = undefined;
|
||||
formData.status = 1;
|
||||
formData.id = undefined;
|
||||
formData.status = 1;
|
||||
} else if (dialog.type === "user-import") {
|
||||
importData.file = undefined;
|
||||
importData.fileList = [];
|
||||
}
|
||||
resetQuery();
|
||||
}
|
||||
|
||||
/** 表单提交 */
|
||||
const handleSubmit = useThrottleFn(() => {
|
||||
userFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const userId = formData.id;
|
||||
loading.value = true;
|
||||
if (userId) {
|
||||
updateUser(userId, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改用户成功");
|
||||
closeDialog();
|
||||
resetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
addUser(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增用户成功");
|
||||
closeDialog();
|
||||
resetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
if (dialog.type === "user-form") {
|
||||
userFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const userId = formData.id;
|
||||
loading.value = true;
|
||||
if (userId) {
|
||||
updateUser(userId, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改用户成功");
|
||||
closeDialog();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
addUser(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增用户成功");
|
||||
closeDialog();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (dialog.type === "user-import") {
|
||||
if (!importData?.deptId) {
|
||||
ElMessage.warning("请选择部门");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (!importData?.file) {
|
||||
ElMessage.warning("上传Excel文件不能为空");
|
||||
return false;
|
||||
}
|
||||
importUser(importData?.deptId, importData?.file).then((response) => {
|
||||
ElMessage.success(response.data);
|
||||
closeDialog();
|
||||
});
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
/** 删除用户 */
|
||||
@@ -266,76 +271,44 @@ function handleDelete(id?: number) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门下拉项
|
||||
*/
|
||||
async function getDeptOptions() {
|
||||
listDeptOptions().then((response) => {
|
||||
deptList.value = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
/** 下载导入模板 */
|
||||
function downloadTemplate() {
|
||||
downloadTemplateApi().then((response: any) => {
|
||||
const blob = new Blob([response.data], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8",
|
||||
});
|
||||
const a = document.createElement("a");
|
||||
const href = window.URL.createObjectURL(blob); // 下载链接
|
||||
a.href = href;
|
||||
a.download = decodeURI(
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(
|
||||
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||
); // 获取后台设置的文件名称
|
||||
document.body.appendChild(a);
|
||||
a.click(); // 点击下载
|
||||
document.body.removeChild(a); // 下载完成移除元素
|
||||
window.URL.revokeObjectURL(href); // 释放掉blob对象
|
||||
);
|
||||
const fileType =
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Excel文件change事件
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
function handleExcelChange(file: UploadFile) {
|
||||
if (!/\.(xlsx|xls|XLSX|XLS)$/.test(file.name)) {
|
||||
ElMessage.warning("上传Excel只能为xlsx、xls格式");
|
||||
excelFile.value = undefined;
|
||||
excelFilelist.value = [];
|
||||
return false;
|
||||
}
|
||||
excelFile.value = file.raw;
|
||||
/** Excel文件Change事件 */
|
||||
function handleFileChange(file: any) {
|
||||
importData.file = file.raw;
|
||||
console.log(importData.file);
|
||||
}
|
||||
|
||||
/** 打开导入弹窗 */
|
||||
async function openImportDialog() {
|
||||
await getDeptOptions();
|
||||
importDeptId.value = undefined;
|
||||
importDialog.visible = true;
|
||||
}
|
||||
|
||||
/** 关闭导入弹窗 */
|
||||
function closeImportDialog() {
|
||||
importDialog.visible = false;
|
||||
excelFile.value = undefined;
|
||||
excelFilelist.value = [];
|
||||
}
|
||||
|
||||
/** 导入用户提交 */
|
||||
function handleImport() {
|
||||
if (importDeptId.value) {
|
||||
if (!excelFile.value) {
|
||||
ElMessage.warning("上传Excel文件不能为空");
|
||||
return false;
|
||||
}
|
||||
importUser(importDeptId.value, excelFile.value).then((response) => {
|
||||
ElMessage.success(response.data);
|
||||
closeImportDialog();
|
||||
resetQuery();
|
||||
});
|
||||
}
|
||||
/** Excel文件Exceed事件 */
|
||||
function handleFileExceed(files: any) {
|
||||
uploadRef.value!.clearFiles();
|
||||
const file = files[0];
|
||||
file.uid = genFileId();
|
||||
uploadRef.value!.handleStart(file);
|
||||
importData.file = file;
|
||||
}
|
||||
|
||||
/** 导出用户 */
|
||||
@@ -358,8 +331,7 @@ function handleExport() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDeptOptions(); // 初始化部门
|
||||
handleQuery(); // 初始化用户列表数据
|
||||
handleQuery();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -368,26 +340,10 @@ onMounted(() => {
|
||||
<el-row :gutter="20">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" class="mb-[12px]">
|
||||
<el-card shadow="never">
|
||||
<el-input v-model="searchDeptName" placeholder="部门名称" clearable>
|
||||
<template #prefix>
|
||||
<i-ep-search />
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
:data="deptList"
|
||||
:props="{ children: 'children', label: 'label', disabled: '' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="handleDeptFilter"
|
||||
default-expand-all
|
||||
@node-click="handleDeptNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
<dept-tree v-model="queryParams.deptId" @node-click="handleQuery" />
|
||||
</el-col>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<el-col :lg="20" :xs="24">
|
||||
<div class="search-container">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
@@ -432,7 +388,7 @@ onMounted(() => {
|
||||
<el-button
|
||||
v-hasPerm="['sys:user:add']"
|
||||
type="success"
|
||||
@click="openDialog()"
|
||||
@click="openDialog('user-form')"
|
||||
><i-ep-plus />新增</el-button
|
||||
>
|
||||
<el-button
|
||||
@@ -451,7 +407,7 @@ onMounted(() => {
|
||||
<el-dropdown-item @click="downloadTemplate">
|
||||
<i-ep-download />下载模板</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="openImportDialog">
|
||||
<el-dropdown-item @click="openDialog('user-import')">
|
||||
<i-ep-top />导入数据</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
@@ -541,7 +497,7 @@ onMounted(() => {
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click="openDialog(scope.row.id)"
|
||||
@click="openDialog('user-form', scope.row.id)"
|
||||
><i-ep-edit />编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
@@ -567,15 +523,16 @@ onMounted(() => {
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 表单弹窗 -->
|
||||
<!-- 弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="600px"
|
||||
:width="dialog.width"
|
||||
append-to-body
|
||||
@close="closeDialog"
|
||||
>
|
||||
<el-form
|
||||
v-if="dialog.type === 'user-form'"
|
||||
ref="userFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
@@ -642,26 +599,15 @@ onMounted(() => {
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入弹窗 -->
|
||||
<el-dialog
|
||||
v-model="importDialog.visible"
|
||||
:title="importDialog.title"
|
||||
width="600px"
|
||||
append-to-body
|
||||
@close="closeImportDialog"
|
||||
>
|
||||
<el-form label-width="80px">
|
||||
<el-form
|
||||
v-else-if="dialog.type === 'user-import'"
|
||||
:model="importData"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="部门">
|
||||
<el-tree-select
|
||||
v-model="importDeptId"
|
||||
v-model="importData.deptId"
|
||||
placeholder="请选择部门"
|
||||
:data="deptList"
|
||||
filterable
|
||||
@@ -669,16 +615,17 @@ onMounted(() => {
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Excel">
|
||||
<el-form-item label="Excel文件">
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
ref="uploadRef"
|
||||
action=""
|
||||
drag
|
||||
:auto-upload="false"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:file-list="excelFilelist"
|
||||
:on-change="handleExcelChange"
|
||||
:limit="1"
|
||||
:auto-upload="false"
|
||||
:file-list="importData.fileList"
|
||||
:on-change="handleFileChange"
|
||||
:on-exceed="handleFileExceed"
|
||||
>
|
||||
<el-icon class="el-icon--upload">
|
||||
<i-ep-upload-filled />
|
||||
@@ -688,15 +635,16 @@ onMounted(() => {
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">xls/xlsx files</div>
|
||||
<div>xls/xlsx files</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleImport">确 定</el-button>
|
||||
<el-button @click="closeImportDialog">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
Reference in New Issue
Block a user