在权限管理系统中,角色管理是一个非常重要的功能,它可以让管理员为不同的用户分配不同的权限,从而使拥有这些角色的用户拥有这些权限。本篇文章将实现角色管理的功能,包括角色的创建、编辑、删除、查看等操作。
接口定义
首先我们在api/role/index.ts定义一下需要调用的接口。
import request from "@/utils/http/index";
import { QueryRoleParams, RoleForm } from "./types/role.dto";
//获取角色列表
export const getRoleList = (query: QueryRoleParams) => {
return request({
url: "/role/list",
method: "get",
params: query,
});
};
//新增
export const addRole = (data: RoleForm) => {
return request({
url: "/role/createRole",
data,
method: "post",
});
};
//新增
export const updateRole = (data: RoleForm) => {
return request({
url: "/role/updateRole",
data,
method: "put",
});
};
//删除角色
export const deleteRole = (menuId: number | number[]) => {
return request({
url: `/role/deleteRole/${menuId}`,
method: "delete",
});
};
其中传参类型定义在api/role/types/role.dto.ts中
export type RoleList = {
id: number,
role_name?: string,
description?: string,
order_num?: number,
create_time: Date,
update_time: Date,
status: number,
};
export type QueryRoleParams = {
role_name?: string,
status?: number | "",
end_time?: string,
begin_time?: string,
page_num?: number,
page_size?: number,
};
export type RoleForm = {
id?: number,
role_name: string,
remark?: string,
role_sort?: number,
order_num?: number,
menu_ids?: number[],
status: number,
};
然后我们在src/views/role/index.vue中实现一下页面。
角色查询
角色的查询我们需要传入角色名,状态,开始时间,结束时间,分页信息,我们将查询条件写在form中
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="角色名称">
<el-input
v-model="queryParams.role_name"
placeholder="角色名称"
class="w-[150px]"
clearable
/>
</el-form-item>
<el-form-item label="状态" prop="status" class="w-[150px]">
<el-select v-model="queryParams.status" placeholder="角色状态" clearable>
<el-option
v-for="dict in dickStatus"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button
type="primary"
v-hasPerm="['system:role:list']"
icon="Search"
@click="handleQuery"
>搜索</el-button
>
</el-form-item>
</el-form>
其中搜索按钮赋值了system:role:list权限,如果没有将不会展示。所以我们需要提前在菜单管理中新增一下这些权限。这里我已经提前加上了
然后我们定义一下传参以及查询方法
const queryParams =
reactive <
QueryRoleParams >
{
role_name: "",
status: "",
begin_time: "",
end_time: "",
page_num: 1,
page_size: 10,
};
const dateRange = ref < any > [];
const dickStatus = [
{
label: "启用",
value: 1,
},
{
label: "禁用",
value: 0,
},
];
因为我们的查询方法需要传入begin_time,end_time,所有查询的时候需要将dateRange的值赋值给begin_time,end_time。所有我们写一个公共的方法来处理它,因为后面还会多次用到
/**
* 处理时间范围查询
* @param dateRange 选择的时间范围
* @param params 查询参数
* @returns
*/
export function handleDateRangeChange(dateRange: any, params: any) {
if (typeof params !== "object" || params === null || Array.isArray(params)) {
params = {};
}
if (Array.isArray(dateRange) && dateRange.length === 2) {
params.begin_time = dateRange[0];
params.end_time = dateRange[1];
return;
}
params.begin_time = "";
params.end_time = "";
}
查询的时候处理一下时间即可
const getList = async () => {
handleDateRangeChange(dateRange.value, queryParams);
const { data } = await getRoleList(queryParams);
};
接下来我们需要将查询到的数据展示在表格中。我们将其定义为一个tableData,同时我们需要分页查询组件el-pagination
<el-table :data="tableData" class="w-full mt-2" row-key="id" border>
<el-table-column prop="role_name" label="角色名" />
<el-table-column prop="role_sort" label="显示顺序" width="100" />
<el-table-column prop="status" label="状态" width="80">
<template #default="scope">
<el-switch
@change="changeStatus(scope.row)"
:model-value="!!scope.row.status"
></el-switch>
</template>
</el-table-column>
<el-table-column prop="remark" label="描述" />
<el-table-column prop="create_time" label="创建时间" />
<el-table-column prop="update_time" label="更新时间" />
<el-table-column
label="操作"
align="center"
width="200"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-link
type="primary"
icon="Edit"
class="mr-1"
@click="handleUpdate(scope.row)"
v-hasPerm="['system:role:edit']"
>修改</el-link
>
<el-link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPerm="['system:role:delete']"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<el-pagination
v-show="total > 0"
background
class="p-5"
layout="total,sizes, prev, pager, next"
:total="total"
v-model:current-page="queryParams.page_num"
v-model:page-size="queryParams.page_size"
@change="getList"
/>
查询的时候将数据赋值给tableData,并将total赋值给el-pagination的total属性。
const getList = async () => {
handleDateRangeChange(dateRange.value, queryParams);
const { data } = await getRoleList(queryParams);
tableData.value = data.list;
total.value = data.total;
};
这样变完成了查询功能,并支持分页功能。
角色新增和修改
点击新增或修改按钮会弹出一个Dialog弹窗。我们根据参数isUpdate判断是新增还是修改。
<!-- 添加或修改角色配置对话框 -->
<el-dialog
:title="isUpdate ? '修改' : '新增'"
width="500px"
v-model="dialogVisible"
append-to-body
>
<el-form :model="form" ref="ruleFormRef" :rules="rules" label-width="100px">
<el-form-item label="角色名称" prop="role_name">
<el-input v-model="form.role_name" placeholder="请输入角色名称" />
</el-form-item>
<el-form-item label="角色顺序" prop="role_sort">
<el-input-number
v-model="form.role_sort"
controls-position="right"
:min="0"
/>
</el-form-item>
<el-form-item label="显示状态">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in dickStatus"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="菜单权限">
<el-tree
class="tree-border"
:data="menuOptions"
show-checkbox
node-key="id"
check-strictly
ref="menuRef"
@check="handleCheck"
empty-text="加载中,请稍候"
:props="{ label: 'title', children: 'children' }"
></el-tree>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入内容"
></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm(ruleFormRef)"
>确 定</el-button
>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
这里我们用到了el-tree来展示菜单权限,选择之后为角色分配权限。所以需要还需要查询菜单列表。当选择菜单的时候将其id赋值给form.menu_ids。同时通过useTemplateRef获取到树形结构dom,后续会调用setCheckedKeys设置选中的菜单。
const menuOptions = ref([]);
const getMenu = async () => {
const { data } = await getMenuList({});
menuOptions.value = data;
};
getMenu();
const menuRef = useTemplateRef < any > "menuRef";
// 树形控件菜单id集合
const handleCheck = (_: any, data: any) => {
form.value.menu_ids = data.checkedKeys;
};
其中rules是form表单校验规则。提交之前要校验一下是否符合规则,同时新增或修改之前都要调用一下resetForm方法重置一下条件
const rules = ref({
role_name: [{ required: true, message: "角色名称不能为空", trigger: "blur" }],
});
const resetForm = () => {
form.value = {
role_name: "",
role_sort: 0,
status: 1,
remark: "",
};
};
点击修改按钮的时候将传来的row赋值给form, 最后根据isUpdate判断是新增还是修改。注意修改的时候后台返回的 row 中是没有menu_ids的,取而代之的是menus。
所以我们需要将menus中的id赋值给menu_ids,同时使用setCheckedKeys设置选中的菜单。
//新增
const handleAdd = () => {
resetForm();
dialogVisible.value = true;
isUpdate.value = false;
};
//编辑
const handleUpdate = (row: RoleForm & { menus?: MenuList[] }) => {
resetForm();
isUpdate.value = true;
form.value = deepClone(row);
form.value.menu_ids = row.menus?.map((item) => item.id);
dialogVisible.value = true;
//等待树形组件渲染完毕后再设置选中的菜单
nextTick(() => {
if (menuRef.value) {
menuRef.value.setCheckedKeys(form.value.menu_ids);
}
});
};
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate(async (valid, fields) => {
if (valid) {
dialogVisible.value = false;
const action = isUpdate.value ? updateRole : addRole;
const successMessage = isUpdate.value ? "修改成功" : "添加成功";
await action(form.value);
ElMessage.success(successMessage);
getList();
} else {
console.log("error submit!", fields);
}
});
};
这时候我们就完成了角色的新增和修改功能。
角色删除
角色删除相对来说就很简单,调用删除接口传入 id 就行,注意这里删除是真的删除,慎用此功能,建议使用状态变更将其置为不可用
//删除
const handleDelete = async (row: RoleList) => {
await ElMessageBox.confirm("确认删除吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
});
await deleteRole(row.id);
ElMessage({
type: "success",
message: "删除成功",
});
getList();
};
完整代码地址各位彦祖们可以去下载源码,喜欢的话可以给个 star 哦!