feat: ✨ 角色管理
This commit is contained in:
76
src/api/system/menu.ts
Normal file
76
src/api/system/menu.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import request from "@/utils/request";
|
||||
// 菜单基础URL
|
||||
const MENU_BASE_URL = "/api/v1/menus";
|
||||
|
||||
const MenuAPI = {
|
||||
/**
|
||||
* 获取当前用户的路由列表
|
||||
* <p/>
|
||||
* 无需传入角色,后端解析token获取角色自行判断是否拥有路由的权限
|
||||
*
|
||||
* @returns 路由列表
|
||||
*/
|
||||
getRoutes() {
|
||||
return request<RouteVO[]>({
|
||||
url: `${MENU_BASE_URL}/routes`,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取菜单下拉数据源
|
||||
*
|
||||
* @returns 菜单下拉数据源
|
||||
*/
|
||||
getOptions(onlyParent?: boolean) {
|
||||
return request<OptionType[]>({
|
||||
url: `${MENU_BASE_URL}/options`,
|
||||
method: "GET",
|
||||
data: { onlyParent: onlyParent },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default MenuAPI;
|
||||
|
||||
/** RouteVO,路由对象 */
|
||||
export interface RouteVO {
|
||||
/** 子路由列表 */
|
||||
children: RouteVO[];
|
||||
/** 组件路径 */
|
||||
component?: string;
|
||||
/** 路由属性 */
|
||||
meta?: Meta;
|
||||
/** 路由名称 */
|
||||
name?: string;
|
||||
/** 路由路径 */
|
||||
path?: string;
|
||||
/** 跳转链接 */
|
||||
redirect?: string;
|
||||
}
|
||||
|
||||
/** Meta,路由属性 */
|
||||
export interface Meta {
|
||||
/** 【目录】只有一个子路由是否始终显示 */
|
||||
alwaysShow?: boolean;
|
||||
/** 是否隐藏(true-是 false-否) */
|
||||
hidden?: boolean;
|
||||
/** ICON */
|
||||
icon?: string;
|
||||
/** 【菜单】是否开启页面缓存 */
|
||||
keepAlive?: boolean;
|
||||
/** 路由title */
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件数据源
|
||||
*/
|
||||
interface OptionType {
|
||||
/** 值 */
|
||||
value: string | number;
|
||||
/** 文本 */
|
||||
label: string;
|
||||
/** 子列表 */
|
||||
children?: OptionType[];
|
||||
}
|
||||
@@ -67,6 +67,18 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "通知详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/role/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "角色管理"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/work/role/assign-perm",
|
||||
"style": {
|
||||
"navigationBarTitleText": "角色分配权限"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
@@ -36,6 +36,7 @@ const gridList = reactive([
|
||||
{
|
||||
icon: "/static/icons/role.png",
|
||||
title: "角色管理",
|
||||
url: "/pages/work/role/index",
|
||||
prem: "sys:role:query",
|
||||
},
|
||||
{
|
||||
|
||||
113
src/pages/work/role/assign-perm.vue
Normal file
113
src/pages/work/role/assign-perm.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="p-l-8 p-b-20">
|
||||
<DaTree
|
||||
ref="DaTreeRef"
|
||||
:data="menuPermOptions"
|
||||
:defaultExpandAll="true"
|
||||
:showCheckbox="true"
|
||||
/>
|
||||
</view>
|
||||
<wd-fab v-model:active="showFab" type="primary" position="right-top" direction="bottom">
|
||||
<wd-button custom-class="custom-button" type="primary" @click="doCheckedTree(rootKeys, true)">
|
||||
全 选
|
||||
</wd-button>
|
||||
<wd-button custom-class="custom-button" type="error" @click="doCheckedTree(rootKeys, false)">
|
||||
全不选
|
||||
</wd-button>
|
||||
<wd-button custom-class="custom-button" type="success" @click="doExpandTree('all', true)">
|
||||
全展开
|
||||
</wd-button>
|
||||
<wd-button custom-class="custom-button" type="warning" @click="doExpandTree('all', false)">
|
||||
全收起
|
||||
</wd-button>
|
||||
</wd-fab>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="footer-buttons" style="">
|
||||
<wd-button size="medium" type="primary" block @click="handleAssignPermSubmit">
|
||||
确 定
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import RoleAPI from "@/api/system/role";
|
||||
import MenuAPI from "@/api/system/menu";
|
||||
import DaTree from "@/components/da-tree/index.vue";
|
||||
import { useMessage } from "wot-design-uni";
|
||||
const message = useMessage();
|
||||
|
||||
let roleId = ref<number>(null); // 操作的角色ID
|
||||
const menuPermOptions = ref<OptionType[]>([]); // 所有菜单
|
||||
let rootKeys = ref<number[]>([]); // 根节点数组
|
||||
const DaTreeRef = ref(); // 树引用
|
||||
const showFab = ref(); // 悬浮按钮是否展开
|
||||
|
||||
/**
|
||||
* 分配菜单权限页面数据初始化
|
||||
*/
|
||||
async function initAssignPerm() {
|
||||
// 获取所有的菜单
|
||||
menuPermOptions.value = await MenuAPI.getOptions(false);
|
||||
console.log("menuPermOptions.value", menuPermOptions.value);
|
||||
rootKeys.value = menuPermOptions.value.map((item) => item.value);
|
||||
// 回显角色已拥有的菜单
|
||||
RoleAPI.getRoleMenuIds(roleId.value).then((data) => {
|
||||
const checkedMenuIds = data;
|
||||
console.log("checkedMenuIds=", checkedMenuIds);
|
||||
if (checkedMenuIds && checkedMenuIds.length > 0) {
|
||||
DaTreeRef.value?.setCheckedKeys(checkedMenuIds, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 展开/收起树节点
|
||||
function doExpandTree(keys, expand) {
|
||||
DaTreeRef.value?.setExpandedKeys(keys, expand);
|
||||
showFab.value = false;
|
||||
}
|
||||
// 选中/取消树节点
|
||||
function doCheckedTree(keys, checked) {
|
||||
DaTreeRef.value?.setCheckedKeys(keys, checked);
|
||||
showFab.value = false;
|
||||
}
|
||||
|
||||
// 分配菜单权限提交
|
||||
function handleAssignPermSubmit() {
|
||||
if (roleId.value) {
|
||||
const checkedMenuIds: number[] = DaTreeRef.value?.getCheckedKeys();
|
||||
RoleAPI.updateRoleMenus(roleId.value, JSON.stringify(checkedMenuIds)).then(() => {
|
||||
uni.showToast({ title: "分配权限成功", icon: "success" });
|
||||
uni.navigateBack({ delta: 1 });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
if (options && options.id) {
|
||||
roleId.value = options.id;
|
||||
}
|
||||
initAssignPerm();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer-buttons {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
:deep(.custom-button) {
|
||||
box-sizing: border-box;
|
||||
width: 64px !important;
|
||||
min-width: auto !important;
|
||||
height: 32px !important;
|
||||
margin: 8rpx;
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
</style>
|
||||
319
src/pages/work/role/index.vue
Normal file
319
src/pages/work/role/index.vue
Normal file
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 筛选 -->
|
||||
<wd-drop-menu>
|
||||
<wd-drop-menu-item ref="filterDropMenu" icon="filter" icon-size="18px" title="筛选">
|
||||
<view>
|
||||
<wd-input
|
||||
v-model="queryParams.keywords"
|
||||
label="关键字"
|
||||
type="text"
|
||||
placeholder="请输入关键字"
|
||||
/>
|
||||
<view class="flex-between py-2">
|
||||
<wd-button class="w-20%" type="info" @click="hendleResetQuery">重置</wd-button>
|
||||
<wd-button class="w-70%" @click="handleQuery">确定</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-drop-menu-item>
|
||||
</wd-drop-menu>
|
||||
<!-- 卡片列表 -->
|
||||
<view class="list-container">
|
||||
<wd-card v-for="item in dataList" class="role-card">
|
||||
<template #title>
|
||||
<view class="flex-between">
|
||||
<view class="flex-center">
|
||||
<view class="ml-2">
|
||||
<view class="font-bold">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<wd-tag v-if="item.status === 1" type="success" plain>正常</wd-tag>
|
||||
<wd-tag v-else-if="item.status === 0" plain>停用</wd-tag>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<wd-cell-group>
|
||||
<wd-cell title="编码" title-width="150rpx" :value="item.code" />
|
||||
<wd-cell title="排序号" title-width="150rpx" :value="item.sort" />
|
||||
</wd-cell-group>
|
||||
|
||||
<template #footer>
|
||||
<view class="flex-between">
|
||||
<view class="text-left">
|
||||
<wd-text text="创建时间:" size="small" class="font-bold" />
|
||||
<wd-text :text="item.createTime" size="small" />
|
||||
</view>
|
||||
<view class="flex-right">
|
||||
<wd-button type="icon" icon="more" @click="handleAction(item)" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-card>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<wd-loadmore :state="loadMoreState" @reload="queryPageData" />
|
||||
</view>
|
||||
<!-- 底部按钮 -->
|
||||
<wd-fab
|
||||
position="left-bottom"
|
||||
:expandable="false"
|
||||
customStyle="width: 1rem; height: 1rem; line-height: 1rem;z-index:9"
|
||||
@click="handleOpenDialog(null)"
|
||||
/>
|
||||
|
||||
<wd-popup v-model="dialog.visible" position="bottom" class="yl-popup">
|
||||
<wd-form ref="roleFormRef" :model="formData" :rules="rules">
|
||||
<wd-cell-group border>
|
||||
<wd-input v-model="formData.name" label="角色名称" prop="name" />
|
||||
<wd-input v-model="formData.code" label="角色编码" prop="code" />
|
||||
<wd-select-picker
|
||||
v-model="formData.dataScope"
|
||||
type="radio"
|
||||
label="数据权限"
|
||||
:columns="dataScopeOptions"
|
||||
:align-right="true"
|
||||
prop="dataScope"
|
||||
/>
|
||||
<wd-cell title="状态" prop="status">
|
||||
<wd-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
|
||||
</wd-cell>
|
||||
<wd-cell title="排序" prop="sort">
|
||||
<wd-input-number v-model="formData.sort" />
|
||||
</wd-cell>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
<view class="footer">
|
||||
<wd-button type="primary" block @click="handleSubmit">提交</wd-button>
|
||||
</view>
|
||||
</wd-popup>
|
||||
<wd-message-box />
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import RoleAPI, { RolePageVO, RolePageQuery, RoleForm } from "@/api/system/role";
|
||||
import { onLoad, onReachBottom } from "@dcloudio/uni-app";
|
||||
import { LoadMoreState } from "wot-design-uni/components/wd-loadmore/types";
|
||||
import { DropMenuItemExpose } from "wot-design-uni/components/wd-drop-menu-item/types";
|
||||
import { FormRules } from "wot-design-uni/components/wd-form/types";
|
||||
import { useMessage } from "wot-design-uni";
|
||||
const message = useMessage();
|
||||
|
||||
const loadMoreState = ref<LoadMoreState>("loading");
|
||||
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive<RolePageQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
keywords: "",
|
||||
});
|
||||
|
||||
/**
|
||||
* 搜索栏
|
||||
*/
|
||||
const filterDropMenu = ref<DropMenuItemExpose>();
|
||||
|
||||
function handleQuery() {
|
||||
filterDropMenu.value?.close();
|
||||
queryParams.pageNum = 1;
|
||||
dataList.value = [];
|
||||
queryPageData();
|
||||
}
|
||||
/**
|
||||
* 重置查询
|
||||
*/
|
||||
const hendleResetQuery = () => {
|
||||
filterDropMenu.value?.close();
|
||||
queryParams.keywords = "";
|
||||
queryParams.pageNum = 1;
|
||||
dataList.value = [];
|
||||
queryPageData();
|
||||
};
|
||||
|
||||
// 角色列表数据
|
||||
const dataList = ref<RolePageVO[]>([]);
|
||||
|
||||
/**
|
||||
* 查询分页数据
|
||||
*/
|
||||
function queryPageData() {
|
||||
loadMoreState.value = "loading";
|
||||
RoleAPI.getPage(queryParams)
|
||||
.then((data) => {
|
||||
dataList.value?.push(...data.list);
|
||||
total.value = data.total;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log("系统异常", e);
|
||||
})
|
||||
.finally(() => {
|
||||
loadMoreState.value = "finished";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 触底事件
|
||||
*/
|
||||
onReachBottom(() => {
|
||||
if (queryParams.pageNum * queryParams.pageSize < total.value) {
|
||||
queryParams.pageNum++;
|
||||
queryPageData();
|
||||
} else {
|
||||
loadMoreState.value = "finished";
|
||||
}
|
||||
});
|
||||
|
||||
// 操作按钮
|
||||
const handleAction = (item: RolePageVO) => {
|
||||
const actions = ["删除", "分配权限", "编辑"];
|
||||
uni.showActionSheet({
|
||||
itemList: actions,
|
||||
success: ({ tapIndex }) => {
|
||||
switch (actions[tapIndex]) {
|
||||
case "删除":
|
||||
handleDelete(item.id);
|
||||
break;
|
||||
case "分配权限":
|
||||
handleAssignPerm(item.id);
|
||||
break;
|
||||
case "编辑":
|
||||
handleOpenDialog(item.id);
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const dataScopeOptions = ref<Record<string, any>[]>([
|
||||
{ label: "全部数据", value: 0 },
|
||||
{ label: "部门及子部门数据", value: 1 },
|
||||
{ label: "本部门数据", value: 2 },
|
||||
{ label: "本人数据", value: 3 },
|
||||
]);
|
||||
|
||||
const formData = reactive<RoleForm>({});
|
||||
|
||||
const rules: FormRules = {
|
||||
name: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
|
||||
code: [{ required: true, message: "请输入角色编码", trigger: "blur" }],
|
||||
dataScope: [{ required: true, message: "请选择数据权限", trigger: "blur" }],
|
||||
status: [{ required: true, message: "请选择状态", trigger: "change" }],
|
||||
sort: [{ required: true, message: "请输入排序号", trigger: "change" }],
|
||||
};
|
||||
|
||||
const roleFormRef = ref();
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
*/
|
||||
async function handleOpenDialog(id?: number) {
|
||||
dialog.visible = true;
|
||||
formData.id = null;
|
||||
formData.name = "";
|
||||
formData.code = "";
|
||||
formData.dataScope = 0;
|
||||
formData.status = 1;
|
||||
formData.sort = 1;
|
||||
if (id) {
|
||||
RoleAPI.getFormData(id).then((data) => {
|
||||
Object.assign(formData, { ...data });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交保存
|
||||
*/
|
||||
function handleSubmit() {
|
||||
roleFormRef.value.validate().then(({ valid }: { valid: boolean }) => {
|
||||
if (valid) {
|
||||
const roleId = formData.id;
|
||||
if (roleId) {
|
||||
RoleAPI.update(roleId, formData).then(() => {
|
||||
uni.showToast({ title: "修改成功", icon: "success" });
|
||||
dialog.visible = false;
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
});
|
||||
} else {
|
||||
RoleAPI.add(formData).then(() => {
|
||||
uni.showToast({ title: "添加成功", icon: "success" });
|
||||
dialog.visible = false;
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param id 用户id
|
||||
*/
|
||||
function handleDelete(id: number) {
|
||||
message
|
||||
.confirm({
|
||||
msg: "确认删除角色吗?",
|
||||
title: "提示",
|
||||
})
|
||||
.then(() => {
|
||||
RoleAPI.deleteByIds(id + "").then(() => {
|
||||
uni.showToast({ title: "删除成功", icon: "success" });
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("点击了取消按钮");
|
||||
});
|
||||
}
|
||||
|
||||
function handleAssignPerm(id: number) {
|
||||
uni.navigateTo({
|
||||
url: "/pages/work/role/assign-perm?id=" + id,
|
||||
});
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
handleQuery();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-drop-menu .wd-drop-menu__item) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 50rpx;
|
||||
}
|
||||
.list-container {
|
||||
.role-card {
|
||||
margin-top: 20rpx;
|
||||
:deep(.wd-cell__wrapper) {
|
||||
padding: 4rpx 0;
|
||||
}
|
||||
:deep(.wd-cell) {
|
||||
padding-right: 10rpx;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.wd-fab__trigger) {
|
||||
width: 80rpx !important;
|
||||
height: 80rpx !important;
|
||||
}
|
||||
.yl-popup {
|
||||
.footer {
|
||||
margin: 30rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user