<template>
|
<basicContainer>
|
<el-row :gutter="20">
|
<el-col :span="4" :xs="24">
|
<div class="head-container">
|
<el-input v-model="deptName" placeholder="请输入部门名称" @input="deptNameInput" clearable
|
prefix-icon="el-icon-search"/>
|
</div>
|
<div class="head-container" style="padding-top: 20px;background: #fff">
|
<el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false"
|
:filter-node-method="filterNode" ref="deptTreeRef" default-expand-all @node-click="handleNodeClick"/>
|
</div>
|
</el-col>
|
<el-col :span="20" :xs="24">
|
<avue-crud
|
:option="option"
|
:table-loading="pageF.loading"
|
:data="tableData"
|
:page="page"
|
:permission="permissionList"
|
:before-open="beforeOpen"
|
v-model="form"
|
ref="crudRef"
|
@row-update="rowUpdate"
|
@row-save="rowSave"
|
@row-del="rowDel"
|
@refresh-change="refreshChange"
|
@search-change="searchChange"
|
@search-reset="searchReset"
|
@selection-change="selectionChange"
|
@current-change="currentChange"
|
@size-change="sizeChange"
|
@on-load="onLoad"
|
>
|
<template #menu-left>
|
<el-button
|
type="success"
|
icon="Edit"
|
:disabled="pageF.single"
|
v-hasPermi="['system:user:edit']"
|
@click="handleUpdate">修改
|
</el-button>
|
<el-button
|
type="danger"
|
icon="Delete"
|
:disabled="pageF.multiple"
|
@click="handleDelete"
|
v-hasPermi="['system:user:remove']"
|
>删除
|
</el-button>
|
<el-button
|
type="info"
|
icon="Upload"
|
@click="handleImport"
|
v-hasPermi="['system:user:import']">导入</el-button>
|
|
<el-button
|
type="warning"
|
plain
|
icon="Download"
|
@click="handleExport"
|
v-hasPermi="['system:user:export']"
|
>导出
|
</el-button>
|
</template>
|
<template #status="scope">
|
<el-switch
|
v-model="scope.row.status"
|
active-value="0"
|
inactive-value="1"
|
@change="handleStatusChange(scope.row)"
|
></el-switch>
|
</template>
|
<template #menu="scope">
|
<div v-hasPermi="['system:user:resetPwd','system:user:edit']" style="display: inline-block;line-height: 1;vertical-align: middle;">
|
<el-dropdown size="small" @command="(command:any) => handleCommand(command, scope.row)" >
|
<el-link size="small" type="primary" :underline="false" icon="el-icon-d-arrow-right">更多</el-link>
|
<template #dropdown>
|
<el-dropdown-menu slot="dropdown">
|
<div v-hasPermi="['system:user:resetPwd']">
|
<el-dropdown-item command="handleResetPwd" icon="Key"
|
>重置密码</el-dropdown-item>
|
</div>
|
<div v-hasPermi="['system:user:edit']">
|
<el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check"
|
>分配角色</el-dropdown-item>
|
</div>
|
</el-dropdown-menu>
|
</template>
|
</el-dropdown>
|
</div>
|
</template>
|
</avue-crud>
|
</el-col>
|
</el-row>
|
|
|
<!-- 用户导入对话框 -->
|
<el-dialog 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>仅允许导入xls、xlsx格式文件。</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>
|
|
</basicContainer>
|
</template>
|
|
<script setup name="user" lang="ts">
|
import {
|
UserI,
|
addUser,
|
delUser,
|
exportUser,
|
getUser,
|
listUser,
|
updateUser,
|
deptTreeSelect,
|
resetUserPwd, changeUserStatus, importTemplateApi
|
} from "@/api/system/user";
|
import useCurrentInstance from "@/utils/useCurrentInstance";
|
import {computed, reactive, ref, toRefs} from "vue";
|
import {PagesInterface, PageQueryInterface} from "@/utils/globalInterface";
|
import {usePagePlus} from "@/hooks/usePagePlus";
|
import {hasPermission} from "@/utils/permissionUtils";
|
import "splitpanes/dist/splitpanes.css";
|
import type {ElTree, ElUpload} from "element-plus";
|
import {encryptSm4} from "@/utils/Sm4Utils";
|
import {useRouter} from "vue-router";
|
import {getToken} from "@/utils/auth";
|
import {getConfigKey} from "@/api/system/config";
|
|
|
const {proxy} = useCurrentInstance();
|
const crudRef = ref();
|
const deptTreeRef = ref();
|
const router = useRouter();
|
|
interface deptTree {
|
id: number,
|
label: string,
|
children?: deptTree[]
|
}
|
|
const permissionList = computed(() => {
|
return {
|
addBtn: hasPermission(["system:user:add"]),
|
delBtn: hasPermission(["system:user:remove"]),
|
editBtn: hasPermission(["system:user:edit"]),
|
viewBtn: hasPermission(["system:user:query"]),
|
}
|
})
|
|
const data = reactive({
|
form: <UserI>{},
|
queryParams: <UserI & PageQueryInterface>{},
|
page: <PagesInterface>{
|
pageSize: 10,
|
total: 0,
|
currentPage: 1,
|
},
|
selectionList: [],
|
deptName: '',
|
deptOptions: [],
|
initPassword:''
|
})
|
const {queryParams, form, page, selectionList, deptName, deptOptions,initPassword} = toRefs(data);
|
const option = ref({
|
pageKey: 'user',
|
rowKey: 'userId',
|
viewBtn: false,
|
column: {
|
userId: {
|
label: '用户编号', addDisplay: false, editDisplay: false,
|
},
|
|
userName: {
|
label: '用户账号', search: true, maxlength: 30,
|
rules: [
|
{required: true, message: "用户账号不能为空", trigger: "blur"},
|
{min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur'}
|
|
],
|
},
|
|
deptId: {
|
label: '归属部门', hide: true,
|
type: 'tree',
|
dicUrl: '/system/user/deptTree',
|
props: {
|
label: 'label',
|
value: 'id'
|
},
|
|
},
|
nickName: {
|
label: '用户昵称',
|
rules: [
|
{required: true, message: "用户昵称不能为空", trigger: "blur"}
|
],
|
},
|
|
phonenumber: {
|
label: '手机号码', search: true,
|
rules: [
|
{
|
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
message: "请输入正确的手机号码",
|
trigger: "blur"
|
}
|
]
|
},
|
email: {
|
label: '用户邮箱', hide: true,
|
rules: [
|
{
|
type: "email",
|
message: "请输入正确的邮箱地址",
|
trigger: "blur"
|
}
|
]
|
},
|
sex: {
|
label: '用户性别', hide: true,
|
type: 'radio', dicUrl: '/system/dict/data/type/sys_user_sex',
|
},
|
password: {
|
label: '密码', hide: true, editDisplay: false, viewDisplay: false,
|
type: "password", maxlength: 20,
|
rules: [
|
{required: true, message: "用户密码不能为空", trigger: "blur"},
|
{min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur'},
|
{pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur"}
|
]
|
},
|
status: {
|
label: '帐号状态', search: true,value:'0',
|
type: 'radio', dicUrl: '/system/dict/data/type/sys_normal_disable',
|
rules:[
|
{required: true, message: "请选择帐号状态", trigger: "blur"},
|
]
|
},
|
postIds: {
|
label: '岗位', hide: true, dicData: [], type: 'select', multiple: true,
|
props: {
|
label: 'postName',
|
value: 'postId'
|
},
|
},
|
roleIds: {
|
label: '角色', hide: true, dicData: [], type: 'select', multiple: true,
|
props: {
|
label: 'roleName',
|
value: 'roleId'
|
},
|
},
|
loginIp: {
|
label: '最后登录IP', hide: true, addDisplay: false, editDisplay: false,
|
},
|
loginDate: {
|
label: '最后登录时间', hide: true, addDisplay: false, editDisplay: false,
|
},
|
createBy: {
|
label: '创建者', hide: true, addDisplay: false, editDisplay: false,
|
},
|
createTime: {
|
label: '创建时间', addDisplay: false, editDisplay: false,
|
},
|
updateBy: {
|
label: '更新者', hide: true, addDisplay: false, editDisplay: false,
|
},
|
|
updateTime: {
|
label: '更新时间', hide: true, addDisplay: false, editDisplay: false,
|
},
|
remark: {
|
label: '备注', hide: true,
|
type: 'textarea', minRows: 3, maxRows: 5, span: 24
|
},
|
}
|
})
|
|
const {
|
tableData, pageF, rowSave, rowUpdate, rowDel, beforeOpen, searchChange,
|
searchReset, selectionChange, onLoad, currentChange, sizeChange, handleDelete, handleExport, handleUpdate,refreshChange
|
} = usePagePlus({
|
form: form,
|
option:option,
|
queryParams: queryParams,
|
idKey: 'userId',
|
page: page.value,
|
getListApi: listUser,
|
getDetailApi: getUser,
|
exportApi: exportUser,
|
deleteApi: delUser,
|
addApi: addUser,
|
updateApi: updateUser,
|
handleUpdateFunc: () => {
|
crudRef.value.rowEdit(selectionList.value[0]);
|
},
|
handleSelectionChangeFunc: (selection: any) => {
|
selectionList.value = selection;
|
},
|
handleBeforeOpenFunc: (type: string) => {
|
if (type == 'add') {
|
getUser().then(response => {
|
option.value.column.postIds.dicData = response.posts || [];
|
option.value.column.roleIds.dicData = response.roles || [];
|
form.value.password = initPassword.value;
|
});
|
}
|
},
|
handleEndOpenFunc:(type:string,res:any)=>{
|
form.value.postIds = res.postIds;
|
form.value.roleIds = res.roleIds;
|
option.value.column.postIds.dicData = res.posts || [];
|
option.value.column.roleIds.dicData = res.roles || [];
|
}
|
})
|
|
|
/*** 用户导入参数 */
|
const upload = reactive({
|
// 是否显示弹出层(用户导入)
|
open: false,
|
// 是否禁用上传
|
isUploading: false,
|
// 是否更新已经存在的用户数据
|
updateSupport: 0,
|
// 设置上传的请求头部
|
headers: { Authorization: "Bearer " + getToken() },
|
// 上传的地址
|
url: import.meta.env.VITE_APP_BASE_API + "system/user/importData"
|
});
|
|
|
const filterNode = (value: string, data: deptTree) => {
|
if (!value) return true;
|
return data.label.indexOf(value) !== -1;
|
};
|
|
/** 节点单击事件 */
|
function handleNodeClick(data: deptTree) {
|
onLoad({pageSize: 10, currentPage: 1}, {deptId: data.id})
|
}
|
|
/** 查询部门下拉树结构 */
|
function getTreeSelect() {
|
deptTreeSelect().then(response => {
|
deptOptions.value = response.data;
|
});
|
}
|
|
getTreeSelect();
|
|
const deptNameInput = () => {
|
deptTreeRef.value!.filter(deptName.value);
|
}
|
const handleCommand = (command:string,row:UserI)=>{
|
switch (command) {
|
case "handleResetPwd":
|
handleResetPwd(row);
|
break;
|
case "handleAuthRole":
|
handleAuthRole(row);
|
break;
|
default:
|
break;
|
}
|
}
|
/** 重置密码按钮操作 */
|
function handleResetPwd(row:UserI) {
|
proxy.$prompt(`请输入"${row.userName}"的新密码`, "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
closeOnClickModal: false,
|
inputPattern: /^.{5,20}$/,
|
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
|
}).then((res:any) => {
|
resetUserPwd({userId:row.userId, password:encryptSm4(res.value) }).then(response => {
|
proxy.$modal.msgSuccess("修改成功,新密码是:" + res.value);
|
});
|
})
|
}
|
|
/** 跳转角色分配 */
|
function handleAuthRole(row:UserI) {
|
const userId = row.userId;
|
router.push("/system/user-auth/role/" + userId);
|
}
|
const uploadRef = ref();
|
|
/** 下载模板操作 */
|
function importTemplate() {
|
importTemplateApi();
|
|
}
|
/**文件上传中处理 */
|
const handleFileUploadProgress = (event:any, file:any) => {
|
upload.isUploading = true;
|
};
|
/** 文件上传成功处理 */
|
const handleFileSuccess = (response:any, file:any) => {
|
upload.open = false;
|
upload.isUploading = false;
|
uploadRef.value!.clearFiles();
|
proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
|
onLoad(page.value);
|
};
|
/** 提交上传文件 */
|
function submitFileForm() {
|
uploadRef.value!.submit();
|
}
|
|
/** 导入按钮操作 */
|
function handleImport() {
|
upload.open = true;
|
}/** 用户状态修改 */
|
function handleStatusChange(row:UserI) {
|
if (!row.userId){
|
return
|
}
|
let text = row.status === "0" ? "启用" : "停用";
|
proxy.$modal.confirm(`确认要"${text}""${row.userName}"用户吗?`).then(function () {
|
return changeUserStatus({userId:row.userId,status: row.status});
|
}).then(() => {
|
proxy.$modal.msgSuccess(text + "成功");
|
}).catch(function () {
|
row.status = row.status === "0" ? "1" : "0";
|
});
|
}
|
|
const getConfig = ()=>{
|
getConfigKey("sys.user.initPassword").then(res=>{
|
initPassword.value = res.msg;
|
})
|
}
|
getConfig();
|
|
</script>
|