refactor: ♻️ 用户管理代码重构,封装部门树组件

Former-commit-id: 6021909320b038a0f9d76dd711f9c551e0863ac8
This commit is contained in:
hxr
2023-10-06 00:01:16 +08:00
parent d7630c739b
commit 74bbfdcef7
2 changed files with 230 additions and 213 deletions

View 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>

View File

@@ -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>