【Dv3Admin】Vue3一键配置权限规则

在后台权限体系中,角色、菜单、接口按钮与数据范围往往被拆散在多个配置入口中维护,实际落地时极易出现按钮越权、数据范围错配、前端状态与后端存储不一致等问题。这类风险通常不源于单一逻辑错误,而是配置路径过长、默认策略缺失以及批量操作能力不足共同叠加后的结果。

本文围绕"角色权限配置"这一真实模块展开,聚焦权限配置抽屉 RoleDrawer 与接口权限面板 RoleMenuBtn 的协作方式,完整拆解从部门切换、菜单选择、按钮勾选到数据范围持久化的实现链路,重点关注参数传递、状态同步与接口封装方式。

文章目录

需求解析

该功能位于后台系统的角色管理模块中,运行在前端基于 Vue 与 Element Plus 的管理界面,后端通过统一的权限接口提供数据支撑,主要服务于角色创建或调整后的权限配置场景。使用过程中需要在一个统一界面内完成部门切换、菜单定位、接口按钮授权以及数据范围控制,且所有操作要求即时生效并持久化。

从操作链路上看,权限配置从角色列表页触发,打开权限配置抽屉后,通过部门筛选驱动菜单树刷新,菜单节点变化进一步触发接口权限面板的数据加载,按钮勾选与数据范围设置最终通过接口写入后端。
结果落库
接口

持久化
权限配置
选择

菜单
加载

接口按钮
勾选按钮

设置数据范围
上下文切换
切换

部门
加载

菜单树
入口触发
角色列表页
打开

RoleDrawer

功能实现

权限配置抽屉由 RoleDrawer.vue 承载,负责统一容纳部门切换、菜单树以及不同权限面板,通过抽屉形式避免页面跳转带来的上下文丢失。

vue 复制代码
<template>
	<el-drawer v-model="RoleDrawer.drawerVisible" title="权限配置" direction="rtl" size="80%" :close-on-click-modal="false"
		:before-close="RoleDrawer.handleDrawerClose" :destroy-on-close="true">
		<template #header>

			<div style="display: flex;align-items: center;">
				<el-form-item label="部门" prop="company" style="margin-top: 16px;margin-right: 12px;">
					<el-select v-model="RoleDrawer.company" placeholder="请选择部门" @change="handleDeptChange"
						style="width: 200px;">
						<el-option v-for="item in deptAllList" :key="item.key" :label="item.name" :value="item.name" />
					</el-select>
				</el-form-item>
				<div>当前授权角色:
					<el-tag style="margin-right: 20px">{{ RoleDrawer.roleName }}</el-tag>
					授权人员:
					<el-button size="small" :icon="UserFilled" @click="handleUsers">{{ RoleDrawer.users.length
					}}</el-button>
				</div>
			</div>
		</template>
		<splitpanes class="default-theme" style="height: 100%">
			<pane min-size="20" size="22">
				<div class="pane-box">
					<MenuTreeCom ref="refMenuTreeCom" />
				</div>
			</pane>
			<pane min-size="20">
				<div class="pane-box">
					<el-tabs v-model="activeName" class="demo-tabs">
						<el-tab-pane label="接口权限" name="first">
							<MenuBtnCom />
						</el-tab-pane>
						<el-tab-pane label="列字段权限" name="second">
							<MenuFieldCom />
						</el-tab-pane>
					</el-tabs>
				</div>
			</pane>
		</splitpanes>
	</el-drawer>

	<el-dialog v-model="dialogVisible" title="授权用户" width="700px" :close-on-click-modal="false">
		<RoleUsersCom />
	</el-dialog>
</template>

该组件的关键在于将部门、菜单与权限面板解耦,抽屉关闭时销毁实例,避免历史状态残留。

接口权限面板由 RoleMenuBtn.vue 实现,承担默认接口权限、按钮勾选与快捷配置能力。

vue 复制代码
<template>
	<div class="pccm-item" v-if="RoleMenuBtn.$state.length > 0">
		<div class="menu-form-alert">
			<div style="display: flex; align-items: center; white-space: nowrap; margin-bottom: 10px">
				<span>默认接口权限:</span>
				<el-select v-model="default_selectBtn.data_range" @change="defaulthandlePermissionRangeChange"
					placeholder="请选择" style="margin-left: 5px; width: 250px; min-width: 250px">
					<el-option v-for="item in dataPermissionRange" :key="item.value" :label="item.label"
						:value="item.value" />
				</el-select>
				<el-tree-select v-show="default_selectBtn.data_range === 4" node-key="id"
					v-model="default_selectBtn.dept" :props="defaultTreeProps" :data="deptData"
					@change="customhandlePermissionRangeChange(default_selectBtn.dept)" placeholder="请选择自定义部门" multiple
					check-strictly :render-after-expand="false" show-checkbox class="dialog-tree"
					style="margin-left: 15px; width: AUTO; min-width: 250px; margin-top: 0" />
			</div>
			<span>配置操作功能接口权限,配置数据权限点击小齿轮</span>
		</div>
		<div style="margin-bottom: 20px;">
			<el-button type="primary" @click="handleQuickQuery('快速查询')">快速查询</el-button>
			<el-button type="primary" @click="handleQuickQuery('标准业务')">标准业务</el-button>
		</div>
		<el-checkbox v-for="btn in RoleMenuBtn.$state" :key="btn.id" v-model="btn.isCheck"
			@change="handleCheckChange(btn)">
			<div class="btn-item">
				{{ btn.data_range !== null ? `${btn.name}(${formatDataRange(btn.data_range, btn.dept)})` : btn.name }}
				<span v-show="btn.isCheck" @click.stop.prevent="handleSettingClick(btn)">
					<el-icon>
						<Setting />
					</el-icon>
				</span>
				<span>【{{ btn.api }}】</span>
			</div>
		</el-checkbox>
	</div>

默认接口权限在此作为批量勾选的基准,避免为每个按钮重复配置数据范围。

按钮勾选与快速配置通过统一接口完成写入,并同步更新本地状态。

ts 复制代码
const handleCheckChange = async (btn: RoleMenuBtnType) => {
	selectBtn.value = default_selectBtn.value;
	const put_data = {
		isCheck: btn.isCheck,
		roleId: RoleDrawer.roleId,
		menuId: RoleMenuTree.id,
		btnId: btn.id,
		data_range: default_selectBtn.value.data_range,
		dept: default_selectBtn.value.dept,
	};
	const { data, msg } = await setRoleMenuBtn(put_data);
	RoleMenuBtn.updateState(data);
	ElMessage({ message: msg, type: 'success' });
};

该逻辑保证勾选行为与后端状态保持同步,否则会导致前端显示与实际权限不一致。

快捷配置基于后端预设返回按钮集合,再批量写入权限。

ts 复制代码
const handleQuickQuery = async (type: string) => {
	const { data } = await getRoleMenuBtnField({
		roleId: RoleDrawer.roleId,
		menuId: RoleMenuTree.id,
		select: type,
	});
	const names = data.menu_btn.map((item: any) => item.name);
	const updatedButtons = RoleMenuBtn.$state.map((btn: RoleMenuBtnType) => ({
		...btn,
		isCheck: names.includes(btn.name)
	}));
	RoleMenuBtn.updateBatchState(updatedButtons);
	const promises = updatedButtons
		.filter(btn => names.includes(btn.name))
		.map(async (btn) => {
			const put_data = {
				isCheck: btn.isCheck,
				roleId: RoleDrawer.roleId,
				menuId: RoleMenuTree.id,
				btnId: btn.id,
				data_range: default_selectBtn.value.data_range,
				dept: default_selectBtn.value.dept,
			};
			return await setRoleMenuBtn(put_data);
		});
	try {
		await Promise.all(promises);
		ElMessage({ message: '快速配置成功', type: 'success' });
	} catch (error) {
		ElMessage({ message: '快速配置失败', type: 'error' });
	}
}

单按钮的数据范围通过弹窗单独配置,确认后立即持久化。

ts 复制代码
const handleDialogConfirm = async () => {
	const { data, msg } = await setRoleMenuBtnDataRange(selectBtn.value);
	selectBtn.value = data;
	dialogVisible.value = false;
	ElMessage({ message: msg, type: 'success' });
};

接口统一封装在组件内的 api.ts,确保权限相关读写路径集中维护。

ts 复制代码
export function getRoleMenu(query: object) {
	return request({
		url: '/api/system/role_menu_button_permission/get_role_menu/',
		method: 'get',
		params: query,
	}).then((res: any) => {
		return XEUtils.toArrayTree(res.data, { key: 'id', parentKey: 'parent', children: 'children', strict: false });
	});
}
/**
 * 设置 角色-菜单
 */
export function setRoleMenu(data: object) {
	return request({
		url: '/api/system/role_menu_button_permission/set_role_menu/',
		method: 'put',
		data,
	});
}

角色列表页通过权限控制暴露入口,避免无权限访问配置能力。

ts 复制代码
permission: {
	type: 'primary',
	text: '权限配置',
	show: auth('role:Permission'),
	click: (clickContext: any): void => {
		const { row } = clickContext;
		context.RoleDrawer.handleDrawerOpen(row);
		context.RoleMenuBtn.setState([]);
		context.RoleMenuField.setState([]);
	},
},

总结

该模块通过权限配置抽屉将部门、菜单与接口权限聚合在单一操作空间内,避免多页面切换造成的状态割裂,同时以默认接口权限作为批量授权的核心锚点。在实现层面,勾选即存与弹窗即存降低了配置遗漏风险,但接口调用频繁,对后端幂等性与性能提出更高要求,若重新设计可引入批量提交或事务化策略以减少请求数量。

整体链路在权限渲染、数据一致性与维护成本之间取得平衡,为复杂业务场景下的角色权限管理提供了一套可复用的实现范式。

相关推荐
Mr数据杨3 小时前
【Dv3Admin】Vue3通用自定义工作台卡片
django
云和数据.ChenGuang3 小时前
数据分析中的dataframe详解
python·数据挖掘·数据分析·django·pygame
Betelgeuse761 天前
DjangoBlog学习案例:掌握Django MVT架构与多应用协作实践
学习·架构·django
IT 行者1 天前
Claude Code Viewer: 打造 Web 端 Claude Code 会话管理利器
前端·人工智能·python·django
龙腾AI白云1 天前
数字孪生在航空领域的应用方法及案例
学习·django·virtualenv·pygame
q_35488851531 天前
计算机毕业设计源码:锦江酒店大数据分析与个性化推荐系统 Django框架 Vue 可视化 Hadoop 爬虫 协同过滤推荐算法 民宿 客栈(建议收藏)✅
python·机器学习·信息可视化·数据分析·django·课程设计·旅游
tryCbest1 天前
Django 基础入门教程(第四篇):Form组件、Auth认证、Cookie/Session与中间件
python·django
PD我是你的真爱粉1 天前
Django MVT vs FastAPI DDD架构
架构·django·fastapi
龙腾AI白云2 天前
数据可视化实战:用AI工具制作专业数据分析图表
深度学习·数据分析·django