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"> <script setup lang="ts">
/** defineOptions({
* @see {@link https://vuejs.org/api/sfc-script-setup.html#defineoptions} name: "User",
*/ inheritAttrs: false,
import { UploadFile } from "element-plus"; });
import { import {
getUserPage, getUserPage,
getUserForm, getUserForm,
@@ -15,26 +16,20 @@ import {
exportUser, exportUser,
importUser, importUser,
} from "@/api/user"; } from "@/api/user";
import { listDeptOptions } from "@/api/dept"; import { getDeptOptions } from "@/api/dept";
import { listRoleOptions } from "@/api/role"; import { getRoleOptions } from "@/api/role";
import { UserForm, UserQuery, UserPageVO } from "@/api/user/types"; 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 queryFormRef = ref(ElForm); // 查询表单
const userFormRef = ref(ElForm); // 用户表单 const userFormRef = ref(ElForm); // 用户表单
const uploadRef = ref<UploadInstance>();
const loading = ref(false); const loading = ref(false);
const ids = ref([]); const ids = ref([]);
const total = ref(0); const total = ref(0);
const dialog = reactive<DialogOption>({
visible: false,
});
const queryParams = reactive<UserQuery>({ const queryParams = reactive<UserQuery>({
pageNum: 1, pageNum: 1,
@@ -46,6 +41,12 @@ const formData = reactive<UserForm>({
status: 1, status: 1,
}); });
const importData = reactive({
deptId: undefined,
file: undefined,
fileList: [],
});
const rules = reactive({ const rules = reactive({
username: [{ required: true, message: "用户名不能为空", trigger: "blur" }], username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
nickname: [{ 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 deptList = ref<OptionType[]>();
const roleList = ref<OptionType[]>(); const roleList = ref<OptionType[]>();
const importDialog = reactive<DialogOption>({
title: "用户导入", const dialog = reactive({
visible: false, visible: false,
type: "user-form",
width: 1200,
title: "",
}); });
/** /** 加载角色下拉数据源 */
* 导入选择的部门ID async function loadRoleOptions() {
*/ getRoleOptions().then((response) => {
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) => {
roleList.value = response.data; 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() { function handleQuery() {
loading.value = true; loading.value = true;
getUserPage(queryParams) getUserPage(queryParams)
@@ -189,61 +161,94 @@ function resetPassword(row: { [key: string]: any }) {
.catch(() => {}); .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; dialog.visible = true;
if (userId) { dialog.type = type;
dialog.title = "修改用户";
getUserForm(userId).then(({ data }) => { if (dialog.type === "user-form") {
Object.assign(formData, data); // 用户表单弹窗
}); await loadDeptOptions();
} else { await loadRoleOptions();
dialog.title = "新增用户"; 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() { function closeDialog() {
dialog.visible = false; dialog.visible = false;
resetForm(); if (dialog.type === "user-form") {
} userFormRef.value.resetFields();
userFormRef.value.clearValidate();
/** 重置表单 */ formData.id = undefined;
function resetForm() { formData.status = 1;
userFormRef.value.resetFields(); } else if (dialog.type === "user-import") {
userFormRef.value.clearValidate(); importData.file = undefined;
importData.fileList = [];
formData.id = undefined; }
formData.status = 1; resetQuery();
} }
/** 表单提交 */ /** 表单提交 */
const handleSubmit = useThrottleFn(() => { const handleSubmit = useThrottleFn(() => {
userFormRef.value.validate((valid: any) => { if (dialog.type === "user-form") {
if (valid) { userFormRef.value.validate((valid: any) => {
const userId = formData.id; if (valid) {
loading.value = true; const userId = formData.id;
if (userId) { loading.value = true;
updateUser(userId, formData) if (userId) {
.then(() => { updateUser(userId, formData)
ElMessage.success("修改用户成功"); .then(() => {
closeDialog(); ElMessage.success("修改用户成功");
resetQuery(); closeDialog();
}) })
.finally(() => (loading.value = false)); .finally(() => (loading.value = false));
} else { } else {
addUser(formData) addUser(formData)
.then(() => { .then(() => {
ElMessage.success("新增用户成功"); ElMessage.success("新增用户成功");
closeDialog(); closeDialog();
resetQuery(); })
}) .finally(() => (loading.value = false));
.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); }, 3000);
/** 删除用户 */ /** 删除用户 */
@@ -266,76 +271,44 @@ function handleDelete(id?: number) {
}); });
} }
/**
* 获取部门下拉项
*/
async function getDeptOptions() {
listDeptOptions().then((response) => {
deptList.value = response.data;
});
}
/** 下载导入模板 */ /** 下载导入模板 */
function downloadTemplate() { function downloadTemplate() {
downloadTemplateApi().then((response: any) => { downloadTemplateApi().then((response: any) => {
const blob = new Blob([response.data], { const fileData = response.data;
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8", const fileName = decodeURI(
});
const a = document.createElement("a");
const href = window.URL.createObjectURL(blob); // 下载链接
a.href = href;
a.download = decodeURI(
response.headers["content-disposition"].split(";")[1].split("=")[1] response.headers["content-disposition"].split(";")[1].split("=")[1]
); // 获取后台设置的文件名称 );
document.body.appendChild(a); const fileType =
a.click(); // 点击下载 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
document.body.removeChild(a); // 下载完成移除元素
window.URL.revokeObjectURL(href); // 释放掉blob对象 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事件 */
* Excel文件change事件 function handleFileChange(file: any) {
* importData.file = file.raw;
* @param file console.log(importData.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文件Exceed事件 */
async function openImportDialog() { function handleFileExceed(files: any) {
await getDeptOptions(); uploadRef.value!.clearFiles();
importDeptId.value = undefined; const file = files[0];
importDialog.visible = true; file.uid = genFileId();
} uploadRef.value!.handleStart(file);
importData.file = file;
/** 关闭导入弹窗 */
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();
});
}
} }
/** 导出用户 */ /** 导出用户 */
@@ -358,8 +331,7 @@ function handleExport() {
} }
onMounted(() => { onMounted(() => {
getDeptOptions(); // 初始化部门 handleQuery();
handleQuery(); // 初始化用户列表数据
}); });
</script> </script>
@@ -368,26 +340,10 @@ onMounted(() => {
<el-row :gutter="20"> <el-row :gutter="20">
<!-- 部门树 --> <!-- 部门树 -->
<el-col :lg="4" :xs="24" class="mb-[12px]"> <el-col :lg="4" :xs="24" class="mb-[12px]">
<el-card shadow="never"> <dept-tree v-model="queryParams.deptId" @node-click="handleQuery" />
<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>
</el-col> </el-col>
<!-- 用户列表 -->
<el-col :lg="20" :xs="24"> <el-col :lg="20" :xs="24">
<div class="search-container"> <div class="search-container">
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
@@ -432,7 +388,7 @@ onMounted(() => {
<el-button <el-button
v-hasPerm="['sys:user:add']" v-hasPerm="['sys:user:add']"
type="success" type="success"
@click="openDialog()" @click="openDialog('user-form')"
><i-ep-plus />新增</el-button ><i-ep-plus />新增</el-button
> >
<el-button <el-button
@@ -451,7 +407,7 @@ onMounted(() => {
<el-dropdown-item @click="downloadTemplate"> <el-dropdown-item @click="downloadTemplate">
<i-ep-download />下载模板</el-dropdown-item <i-ep-download />下载模板</el-dropdown-item
> >
<el-dropdown-item @click="openImportDialog"> <el-dropdown-item @click="openDialog('user-import')">
<i-ep-top />导入数据</el-dropdown-item <i-ep-top />导入数据</el-dropdown-item
> >
</el-dropdown-menu> </el-dropdown-menu>
@@ -541,7 +497,7 @@ onMounted(() => {
type="primary" type="primary"
link link
size="small" size="small"
@click="openDialog(scope.row.id)" @click="openDialog('user-form', scope.row.id)"
><i-ep-edit />编辑</el-button ><i-ep-edit />编辑</el-button
> >
<el-button <el-button
@@ -567,15 +523,16 @@ onMounted(() => {
</el-col> </el-col>
</el-row> </el-row>
<!-- 表单弹窗 --> <!-- 弹窗 -->
<el-dialog <el-dialog
v-model="dialog.visible" v-model="dialog.visible"
:title="dialog.title" :title="dialog.title"
width="600px" :width="dialog.width"
append-to-body append-to-body
@close="closeDialog" @close="closeDialog"
> >
<el-form <el-form
v-if="dialog.type === 'user-form'"
ref="userFormRef" ref="userFormRef"
:model="formData" :model="formData"
:rules="rules" :rules="rules"
@@ -642,26 +599,15 @@ onMounted(() => {
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </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-form
<el-dialog v-else-if="dialog.type === 'user-import'"
v-model="importDialog.visible" :model="importData"
:title="importDialog.title" label-width="100px"
width="600px" >
append-to-body
@close="closeImportDialog"
>
<el-form label-width="80px">
<el-form-item label="部门"> <el-form-item label="部门">
<el-tree-select <el-tree-select
v-model="importDeptId" v-model="importData.deptId"
placeholder="请选择部门" placeholder="请选择部门"
:data="deptList" :data="deptList"
filterable filterable
@@ -669,16 +615,17 @@ onMounted(() => {
/> />
</el-form-item> </el-form-item>
<el-form-item label="Excel"> <el-form-item label="Excel文件">
<el-upload <el-upload
class="upload-demo" ref="uploadRef"
action="" action=""
drag drag
:auto-upload="false"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
:file-list="excelFilelist"
:on-change="handleExcelChange"
:limit="1" :limit="1"
:auto-upload="false"
:file-list="importData.fileList"
:on-change="handleFileChange"
:on-exceed="handleFileExceed"
> >
<el-icon class="el-icon--upload"> <el-icon class="el-icon--upload">
<i-ep-upload-filled /> <i-ep-upload-filled />
@@ -688,15 +635,16 @@ onMounted(() => {
<em>点击上传</em> <em>点击上传</em>
</div> </div>
<template #tip> <template #tip>
<div class="el-upload__tip">xls/xlsx files</div> <div>xls/xlsx files</div>
</template> </template>
</el-upload> </el-upload>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="handleImport"> </el-button> <el-button type="primary" @click="handleSubmit"> </el-button>
<el-button @click="closeImportDialog"> </el-button> <el-button @click="closeDialog"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>