1、前言

  之前《Vue前端访问控制方案 》一文中提出,使用class=“permissions”结合元素id来标识权限控制相关的dom元素,并通过公共方法checkRights来设置dom元素的可见属性,在实际使用中存在下列问题:

  • checkRights指定上级节点的domKey,结果document.getElementsByClassName获取了更上级的节点或其它子树的节点,没在指定上级节点下,结果节点没找到,导致错误禁用其它节点的权限。
  • style.display与v-if存在可见属性冲突。
  • 更为致命的是,document.getElementsByClassName找不到插槽的节点,如下列形式:
          <el-table-column label="操作">
<template slot-scope="scope">
<el-tooltip class="item,permissions" effect="dark" content="编辑" id="editUser" placement="left-start">
<el-button size="mini" type="primary" icon="el-icon-edit" circle @click="editUser(scope.row)"></el-button>
</el-tooltip>
<el-tooltip class="item,permissions" effect="dark" content="禁用" id="disableUser"
placement="left-start" v-if="!scope.row.deleteFlag">
<el-button size="mini" type="primary" icon="el-icon-lock" circle @click="disableUser(scope.row)"></el-button>
</el-tooltip>
<el-tooltip class="item,permissions" effect="dark" content="启用" id="enableUser"
placement="left-start" v-if="scope.row.deleteFlag">
<el-button size="mini" type="primary" icon="el-icon-unlock" circle @click="enableUser(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>

  此时,document.getElementsByClassName方法找不到class中有permissions的对象。Vue与JQuery的思想有很大不同。

  综上所述问题,因此需要对方案进行优化改进。

2、新的方案

  借鉴v-permission的Vue指令方法,并且不再使用可见属性,而是移除无权限节点的dom元素。具体方案如下:

2.1、定义v-permissions指令

  为区别"v-permission"及":v-permission",这里使用v-permissions。

  创建/src/common/permissions.js文件,代码如下:

import TreeNode from './treeNode.js'

/**
* 对使用v-permissions指令的dom元素,检查权限;如果无权限,则移除该元素
* 绑定参数形式:
* v-permissions 无参数值形式,表示不指定上级节点的domKey
* v-permissions="''",设置空串,注意里面需要包含单引号,也是无参数
* v-permissions="'someSuperDomkey'",设置上级节点的domKey,注意里面需要包含单引号
* @param {element对象} el
* @param {绑定参数} binding
*/
function checkRights(el,binding){
// 确保权限树已经加载
if (TreeNode.rightsTree == null){
let rights = localStorage.getItem('rights');
if (rights === null || rights === ''){
// 没有权限树,移除当前节点
if(el.parentNode){
el.parentNode.removeChild(el);
}
return;
}
// 加载权限树
TreeNode.rightsTree = TreeNode.loadData(rights);
} // 获取dom元素的id
var elementId = el.id;
if (elementId == undefined)
{
console.log("Format error! Without id property of the element with v-permissions:" + el);
return;
} // 获取上级节点的domkey
//console.log(binding);
var superDomkey = binding.value;
var superNode = null;
if(superDomkey != undefined && superDomkey != ""){
// 如果指定上级节点,先查找上级节点
superNode = TreeNode.lookupNodeByDomkey(TreeNode.rightsTree, superDomkey);
if (superNode == null){
// 上级key未找到,设置错误
console.log("Wrong superDomkey value for element:" + el);
// 忽略上级节点
}
} // 设置搜索的根节点
var rootNode = null;
if (superNode == null){
// 上级节点为空
rootNode = TreeNode.rightsTree;
} else{
rootNode = superNode;
} // 查找当前节点
var node = null;
node = TreeNode.lookupNodeByDomkey(rootNode, elementId);
if(node == null){
// 如果未在权限树中找到此节点,表示没有权限
// 移除此element对象
if(el.parentNode){
el.parentNode.removeChild(el);
}
}
} export default {
inserted(el,binding) {
checkRights(el,binding)
},
update(el,binding) {
checkRights(el,binding)
}
}

2.2、注册该指令

  在main.js中,注册该指令。main.js代码如下:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import md5 from 'js-md5';
import axios from 'axios'
import VueAxios from 'vue-axios'
import TreeNode_ from './common/treeNode.js'
import CommonFuncs_ from './common/commonFuncs.js'
import instance_ from './api/index.js'
import global_ from './common/global.js'
import permissions from './common/permissions.js' Vue.use(VueAxios,axios)
Vue.prototype.$md5 = md5
Vue.prototype.TreeNode = TreeNode_
Vue.prototype.$baseUrl = process.env.API_ROOT
Vue.prototype.instance = instance_ //axios实例
Vue.prototype.global = global_
Vue.prototype.commonFuncs = CommonFuncs_ Vue.use(ElementUI)
Vue.config.productionTip = false // 注册一个全局自定义指令 v-permissions
Vue.directive('permissions', permissions) /* eslint-disable no-new */
var vue = new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>',
render:h=>h(App)
}) export default vue

2.3、删除checkRights方法

  在commonFuncs.js文件中,删除checkRights方法代码,因为不再调用此方法了。

  原来在App.vue和其它vue文件中调用checkRights方法的代码,也删除。

2.4、模板文件示例

  dom元素如果需要进行权限控制,则使用v-permissions指令,同时还要用id属性,匹配约定的domKey。这样就行了,无需编写其它javascript代码。

2.4.1、App.vue文件

  App.vue文件,代码如下:

<template>
<div id="app">
<!-- 其他页 -->
<el-container style="min-height: calc(100% - 50px);" v-if="$route.meta.keepAlive">
<!-- 无头部导航栏 -->
<el-container>
<el-aside :style="{width:collpaseWidth}">
<!-- 侧边栏 -->
<keep-alive>
<left></left>
</keep-alive>
</el-aside>
<el-main>
<!-- Body -->
<router-view></router-view>
</el-main>
</el-container>
<!-- 无足部 -->
</el-container> <!-- 登录页 -->
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</template> <script>
import left from './components/Left.vue' export default {
name: 'App',
components: {
left: left
},
data(){
return {
collpaseWidth:200
}
},
mounted:function(){ },
methods: { }
}
</script> <style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 20px;
} .el-main {
padding-top : 0px;
}
</style>

  现在不需要任何权限控制的代码了。

2.4.2、Left.vue文件

  侧边导航栏组件Left.vue文件,代码如下:

<template>
<div class="left-sidebar">
<el-menu :default-openeds="['1']" style="background:#F0F6F6;">
<el-submenu index="1">
<el-menu-item-group >
<el-menu-item index="1-1">
<router-link class="menu" tag="li" to="/home" exact-active-class="true"
id="homeMenu" active-class="_active">
<i class="el-icon-s-home"></i>首页
</router-link>
</el-menu-item>
<el-submenu index="1-2" v-permissions id="userManagementMain">
<template slot="title" ><i class="el-icon-user-solid"></i>用户管理</template>
<el-menu-item index="1-2-1" v-permissions id="userManagementSub">
<router-link class="menu" tag="li" to="/userManagement">
<i class="el-icon-user"></i>用户管理
</router-link>
</el-menu-item>
<el-menu-item index="1-2-2" v-permissions id="changePassword">
<router-link class="menu"tag="li" to="/changePassword">
<i class="el-icon-key"></i>修改密码
</router-link>
</el-menu-item>
</el-submenu>
<el-menu-item index="1-3" v-permissions id="questionnaireManagement">
<router-link class="menu" tag="li" to="/questionnaireManagement">
<i class="el-icon-document"></i>问卷内容管理
</router-link>
</el-menu-item>
<el-submenu index="1-4" v-permissions id="issueManagementMain">
<template slot="title"><i class="el-icon-message"></i>问卷发布管理</template>
<el-menu-item index="1-4-1" v-permissions id="issueManagementSub">
<router-link class="menu" tag="li" to="/issueManagement">
<i class="el-icon-phone"></i>发布问卷查询
</router-link>
</el-menu-item>
<el-menu-item index="1-4-2" v-permissions id="issueTaskQuery">
<router-link class="menu" tag="li" to="/issueTaskQuery">
<i class="el-icon-tickets"></i>发布任务查询
</router-link>
</el-menu-item>
</el-submenu>
<el-menu-item index="1-5" v-permissions id="answerSheetManagement">
<router-link class="menu" tag="li" to="/answerSheetManagement">
<i class="el-icon-receiving"></i>答卷管理
</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</div>
</template> <style>
/* 去掉右边框 */
.el-menu {
border-right: none;
} .el-submenu {
background-color: rgb(231, 235, 220) ;
}
</style>

  注意,需要权限控制的dom元素,都有v-permissions,并且有id的值。

2.4.3、业务模块vue模板示例

  业务模块vue模板示例,代码如下:

<template>
<div id="contentwrapper">
<el-form ref="form" :model="formData" label-width="80px">
<el-card>
<el-row>
<!--占整行-->
<el-col :span="24">
<h5 class="heading" align=left>用户管理 / 用户管理</h5>
<!-- 分隔线 -->
<el-divider></el-divider>
</el-col>
</el-row>
<el-row>
<el-col align="left" :span="6">
<el-button type="primary" v-permissions="'userManagementSub'" id="addUser" size="small" @click="addUser">
<i class="el-icon-circle-plus"></i>添加用户
</el-button>
</el-col> <!-- 查询条件 -->
<el-col align="left" :span="6">
<el-form-item label="用户类型:" label-width="100px">
<el-select v-model="formData.userTypeLabel" size="small" @change="selectUserType">
<el-option
v-for="(item,index) in userTypeList"
:key="index"
:label="item.itemValue"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="用户状态:" label-width="100px">
<el-select v-model="formData.userStatusLabel" size="small" @change="selectUserStatus">
<el-option
v-for="item in userStatusList"
:key="item.itemKey"
:label="item.itemValue"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
<el-col align="right" :span="6">
<el-button type="primary" v-permissions="'userManagementSub'" id="queryUser" size="small" @click="queryUsers">
<i class="el-icon-search"></i>查询
</el-button>
</el-col>
</el-row> <!-- 用户列表数据 -->
<el-table :data="userInfoList" border stripe :row-style="{height:'30px'}"
:cell-style="{padding:'0px','text-align':'center'}" style="font-size: 10px"
:header-cell-style="{'text-align':'center'}">
<el-table-column label="用户ID" width="60px" prop="userId"></el-table-column>
<el-table-column label="用户类型" width="100px" prop="userType">
<template slot-scope="scope">
<span v-if="userTypeMap.get(scope.row.userType) != null">
{{userTypeMap.get(scope.row.userType).itemValue}}
</span>
</template>
</el-table-column>
<el-table-column label="登录名" width="100px" prop="loginName"></el-table-column>
<el-table-column label="真实名称" width="80px" prop="userName"></el-table-column>
<el-table-column label="手机号码" width="100px" prop="phoneNumber"></el-table-column>
<el-table-column label="EMail" prop="email" width="160px"></el-table-column>
<el-table-column label="性别" width="60px" prop="gender">
<template slot-scope="scope">
<span v-if="genderMap.get(scope.row.gender) != null">
{{genderMap.get(scope.row.gender).itemValue}}
</span>
</template>
</el-table-column>
<el-table-column label="部门" width="100px" prop="deptId">
<template slot-scope="scope">
<span v-if="deptMap.get(scope.row.deptId) != null">
{{deptMap.get(scope.row.deptId).itemValue}}
</span>
</template>
</el-table-column>
<el-table-column label="状态" width="60px" prop="deleteFlag">
<template slot-scope="scope">
<span v-if="userStatusMap.get(scope.row.deleteFlag) != null">
{{userStatusMap.get(scope.row.deleteFlag).itemValue}}
</span>
</template>
</el-table-column>
<el-table-column label="角色" width="100px" prop="roles" :formatter="rolesFormatter"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="编辑" v-permissions="'userManagementSub'" id="editUser" placement="left-start">
<el-button size="mini" type="primary" icon="el-icon-edit" circle @click="editUser(scope.row)"></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="禁用" v-permissions="'userManagementSub'" id="disableUser"
placement="left-start" v-if="!scope.row.deleteFlag">
<el-button size="mini" type="primary" icon="el-icon-lock" circle @click="disableUser(scope.row)"></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="启用" v-permissions="'userManagementSub'" id="enableUser"
placement="left-start" v-if="scope.row.deleteFlag">
<el-button size="mini" type="primary" icon="el-icon-unlock" circle @click="enableUser(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table> <!-- 分页区域 -->
<el-pagination @size-change="handleSizeChange"
@current-change="handleCurrentChange" :current-page="formData.pageInfo.pagenum"
:page-sizes="[5, 10, 15, 20]" :page-size="formData.pageInfo.pagesize"
layout="total, sizes, prev, pager, next, jumper" :total="formData.pageInfo.total"
background>
</el-pagination>
</el-card>
</el-form> <!-- 新增、编辑 -->
<add-or-edit-user v-if="editVisible" ref="addOrEditUser"></add-or-edit-user> </div>
</template>

  权限控制项,都设置了:v-permissions="'userManagementSub'",指明了上级节点的domkey为userManagementSub。无需其它javascipt调用控制代码。

  还有,id="disableUser"和id="enableUser"的元素,都使用了v-if指令,现在也不会有可见属性的冲突。

2.5、方案总结

  利用Vue的指令,使得权限控制表述更为简洁,无需其它javascript脚本,且解决了class被屏蔽的问题。另外,使用移除元素的方法,避免了与v-if的可见属性的冲突。

  经测试,达到了权限控制的效果。

  如果权限动态发生改变,只需刷新页面,将重构页面,无需担心移除的节点彻底消失。

Vue 前端权限控制的优化改进版的更多相关文章

  1. vue项目权限控制

    Vue权限控制有各种方法,大概分为两个方向: 把当前角色对应的权限保存在浏览器本地(容易被恶意修改): 将操作权限保存在vuex中(推荐此种方式:页面一刷新就没了,可以再次向后端请求相关数据,始终保持 ...

  2. Vue页面权限控制和动态添加路由

    原文转自:点我 页面权限控制 页面权限控制是什么意思呢? 就是一个网站有不同的角色,比如管理员和普通用户,要求不同的角色能访问的页面是不一样的.如果一个页面,有角色越权访问,这时就得做出限制了. Vu ...

  3. abp angular 前端权限控制

    import { AppComponentBase } from '@shared/app-component-base'; this.permission.isGranted(menuItem.pe ...

  4. 循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

    在一个业务管理系统中,如果我们需要实现权限控制功能,我们需要定义好对应的权限功能点,然后在界面中对界面元素的功能点进行绑定,这样就可以在后台动态分配权限进行动态控制了,一般来说,权限功能点是针对角色进 ...

  5. 基于Vue实现后台系统权限控制

    原文地址:http://refined-x.com/2017/08/29/基于Vue实现后台系统权限控制/,转载请注明出处. 用Vue/React这类双向绑定框架做后台系统再适合不过,后台系统相比普通 ...

  6. VUE 后台管理系统权限控制

    谈一谈VUE 后台管理系统权限控制 前端权限从本质上来讲, 就是控制视图层的展示,比如说是某个页面或者某个按钮,后端权限可以控制某个用户是否能够查询数据, 是否能够修改数据等操作,后端权限大部分是基于 ...

  7. vue前端项目优化策略

    vue前端项目有什么优化策略? .生成打包报告.(可以发现一些问题,并进行解决)2.使用第三方库启用CDN加载3.使用Element-ui的话,按需加载组件4.使用路由懒加载 生成打包报告: .生成打 ...

  8. React+DvaJS 之 hook 路由权限控制

    博客 学院 下载 GitChat TinyMind 论坛 APP 问答 商城 VIP 活动 招聘 ITeye 写博客 发Chat 登录注册 原 React+DvaJS 之 hook 路由权限控制 20 ...

  9. Vue + Element UI 实现权限管理系统 前端篇(十三):页面权限控制

    权限控制方案 既然是后台权限管理系统,当然少不了权限控制啦,至于权限控制,前端方面当然就是对页面资源的访问和操作控制啦. 前端资源权限主要又分为两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作 ...

随机推荐

  1. jQuery清空元素和克隆元素

    1.清空 $(function () { $('#btn').click(function () { $('#ul1').html('') $('#ul1').empty() $('#ul1').re ...

  2. golang:三次握手四次挥手总结

    TCP的三次握手 所谓三次握手 Three-Way Handshake 是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立.好比两个人在打电话: 当连接被建立或被终止,交换的报 ...

  3. [刷题] 283 Move Zeros

    要求 将所有的0,移动到vector的后面比如; [1,3,0,12,5] -> [1,3,12,5,0] 实现 第一版程序,时间.空间复杂度都是O(n) 1 #include<iostr ...

  4. MegaRAID BIOS设置阵列

    MegaRAID BIOS设置阵列 1.在初始界面可以看到physical View的界面,或者在左侧的菜单栏中点击选中即可,可以看到物理磁盘. 2.点击上一步的配置向导可以进入到配置向导界面 3.选 ...

  5. rpm -ql BackupPC |grep etc

    # rpm -ql BackupPC |grep etc/etc/BackupPC/etc/BackupPC/config.pl/etc/BackupPC/hosts/etc/httpd/conf.d ...

  6. 最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示。 HardInfo 显示硬件具体信息,甚至包括一组八个的流行的性能基准程序,你可以用它们评估你的系统性能。 KInfoCenter 和 Lshw 也能够显示硬件的详细信息,并且可以从许多软件仓库中获取。

    最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示. HardInfo 显示硬件具体信息,甚至包括一组八个的流 ...

  7. 如何查看自己的电脑 CPU 是否支持硬件虚拟化

    引言 在你安装各种虚拟机之前,应该先测试一下自己的电脑 CPU 是否支持硬件虚拟化. 如果你的电脑比较老旧,可能不支持硬件虚拟化,那么将无法安装虚拟机软件. 如何查看自己 CPU 是否支持硬件虚拟化 ...

  8. kylin的rowkey优化之按维度分片

    我们知道,系统会对cuboid的数据进行分片处理. 但是默认的分片策略是随机的,如果group by a,b 的查询命中了某个cuboid,但是a=1 and b=1 的两条数据在不同的机器上存储, ...

  9. 基于Jira的运维发布平台的设计与实现

    作者:乔克 公众号:运维开发故事 上线发布是运维的日常工作,常见的发布方式有: 手动发布 Jenkins发布平台 Gitlab CI ...... 除此之外还有需要开源软件,他们都有非常不错的发布管理 ...

  10. 【进阶之路】多线程条件下分段处理List集合的几种方法

    这两个月来因为工作和家庭的事情,导致一直都很忙,没有多少时间去汲取养分,也就没有什么产出,最近稍微轻松了一点,后续的[进阶之路]会慢慢回到正轨. 开门见山的说,第一次接触到多线程处理同一个任务,是使用 ...