vue之按钮权限及优雅请求API

系统开发中按钮级权限控制也是非常重要的功能之一,可以严格控制不同角色用户所拥有的功能权限。

自定义v-permission指令

首先可以通过vue的自定义指令来控制按钮(div,link也阔以)等的显示与否以及是否禁用状态。具体可查看官方文档vue自定义指令

/**
 * 定义vue permission指令
 *
 * el:指令所绑定的元素,可以用来直接操作 DOM 
 * binding:一个对象,包含以下属性:
 *     name:指令名,不包括 v- 前缀。
 *     value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
 *     oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子 
 *           中可用。无论值是否改变都可用。
 *    expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
 */
Vue.directive('permission', function (el, binding) {
  // 检查是否有该code代表的权限or该code是否为禁用状态,并将对应的dom进行相应的控制(没有则隐藏,禁用则置灰不可点击)
  // 前端可在登录后将拥有的菜单以及权限的树结构存于sessionStorage,也可每次进入页面后单独请求api判断是否存在等。
  Permission.checkCodeAndSetDom(binding.value, el);
})
  1. 在功能模块定义各自的permission.js文件用于设置需要控制的权限的code,uri,请求方法等。
const code = {
  // 菜单列表页面相关
  menu: {  
      // 保存资源权限
      save: Permission.code("resource:save").uri("/sys/resources").method(method.POST),
  }
}

export default code;
  1. 在vue页面中对需要控制的按钮等使用v-permission指令,并可对权限相关联的uri进行请求,并返回结果
// 对要限制的按钮进行控制
<el-button v-permission="permission.save.code" 
type="primary" icon="el-icon-plus"  @click="addResource(false)">添加</el-button>

// 以下是script区
// 导入permission.js文件
import permissions from '../permissions.js'

export default {
  data() {
    // 设置save权限
    savePermission: permissions.menu.save
  },
  methods: {
    // 在method中定义的按钮click事件方法
    async addResource() {
       // 该方法会用在permission.js文件中指定该权限的请求uri和请发方法发送请求,并获取响应结果
      let res = await this.savePermission.requset(this.form);
      ....
    }
  }
}
Permission.js文件之Permission类

以下为上述功能所需要依赖的Permission类

import request from './request'
import Config from './config'

/**
 * show: 菜单按钮是否显示
 * disabled: 菜单功能是否被禁用
 */
class PermissionInfo {
  constructor(show, disabled) {
    this.show = show;
    this.disabled = disabled;
  }
}

/**
 * 可用于各模块定义各自权限对象
 */
class Permission {
  constructor(code) {
    this.code = code;
  }

  /**
   * 权限对应的uri
   * @param {String} uri 请求uri
   */
  uri(uri) {
    this.uri = uri;
    return this;
  }

  /**
   * uri的请求方法(方法枚举)
   * @param {Number} method 请求方法
   */
  method(method) {
    this.method = method;
    return this;
  }
  
  /**
   * 获取权限的完整url
   */
  getUrl() {
    return request.getApiUrl(this.uri);
  }

  /**
   * 操作该权限,即请求对应的uri
   * @param {Object} param 请求该权限的参数
   */
  request(param) {
    return request.send(this, param);
  }


  /**    静态方法     **/

  /**
   * 静态工厂,设置code,并返回Permission对象
   * @param {String} code 权限code(权限标识符)Permission对象必有的属性
   */
  static code(code) {
    return new Permission(code);
  }

  /**
   * 登录成功保存对应的token以及菜单按钮列表
   */
  static savePermission(tokenMenuAndPermission) {
    //保存token
    Permission.saveToken(tokenMenuAndPermission.token);
    //保存menus
    sessionStorage.setItem(Config.name.resourcesKey, JSON.stringify(tokenMenuAndPermission.resources));
  }

  /**
   * 获取token
   */
  static getToken() {
    return sessionStorage.getItem(Config.name.tokenKey);
  }

  /**
   * 保存token
   * @param {Object} token token
   */
  static saveToken(token) {
    sessionStorage.setItem(Config.name.tokenKey, token);
  }

  /**
   * 从sessionStorage所有permissions获取指定permission对象的PermissionInfo
   */
  static getPermission(code) {
    // 超级管理员通行
    // if (JSON.parse(sessionStorage.getItem(Config.name.adminKey)).username == 'admin') {
    //   return new PermissionInfo(true, false);
    // }
    let menus = JSON.parse(sessionStorage.getItem(Config.name.resourcesKey));
    for (let menu of menus) {
      let leafs = Permission.getLeafs(menu);
      // 获取菜单的所有叶子节点
      for (let p of leafs) {
        // 如果是菜单类型,则跳过
        if (p.type === 1) {
          continue;
        }

        if (p.code === code) {
          // 如果是禁用状态,则禁止按钮点击
          if (p.status === 0) {
            return new PermissionInfo(true, true);
          }
          return new PermissionInfo(true, false);
        }
      }
    }

    return new PermissionInfo(false, true);
  }

  /**
   * 获取菜单的所有叶子节点
   * @param {Object} menu 根菜单
   */
  static getLeafs(menu) {
    let leafs = [];
    Permission.fillLeafs(menu, leafs);
    return leafs;
  }

  /**
   * 将所有叶子节点填充
   * @param {Object} meun  根菜单
   * @param {Object} leafs 需要填充的叶子节点
   */
  static fillLeafs(menu, leafs) {
    let children = menu.children;
    if (!children) {
      leafs.push(menu);
      return;
    }
    children.forEach(m => {
      Permission.fillLeafs(m, leafs);
    })
  }

  /**
   * 检查权限code并设定对应dom的属性
   * @param code 权限码
   * @param elDom  dom元素
   */
  static checkCodeAndSetDom(code, elDom) {
    // 根据权限code获取对应权限信息
    let permission = Permission.getPermission(code);
    // 如果没有显示权限,则隐藏该元素
    if (!permission.show) {
      elDom.style.display = 'none';
    }
    // 如果该权限被暂用,则禁止该btn
    if (permission.disabled) {
      // 将按钮置为禁用
      elDom.setAttribute('disabled', 'disabled');
      // element-ui需要添加该类样式
      elDom.className = elDom.className + ' ' + 'is-disabled';
    }
  }
}

export default Permission

到此为止前端的按钮,link, div等的权限控制就差不多了。再提一点的就是权限code就是与指定资源操作相关联的一个标识符。简单操作页面及详情页面如下:

权限code关联页面

当然前端权限是控制了,但是后端也需要对其进行相对应的控制才可以真正实现控制,具体的后端权限控制方案or完整的前端控制案例,可查看 https://gitee.com/objs/mayfly 项目中有完整的使用案例。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容