for (var i = 0; i < rightmainData.tableData.length; i++) { if (rightmainData.tableData[i].handleButton.length != 0) { for (var j = 0; j < rightmainData.tableData[i].handleButton.length; j++) { var permission rightmainData.tableData[i].handleButton[j].permiss; if (permission && !app.$store.state.user.authority.hasOwnProperty(permission)) { rightmainData.tableData[i].handleButton[j].disabled = true; rightmainData.tableData[i].handleButton[j].havePermission = false } else { rightmainData.tableData[i].handleButton[j].havePermission = true } } } }
不仅如此,还有一个更大的问题是,如果我们的权限校验是依赖其他数据,每次数据改变的时候,我们都需要写新的方法来重新计算权限。既然这样,那我们为什么不在需要进行鉴权的地方再进行权限校验,并且每次数据变更后再重新计算呢?在Vue中最适合干这个事情的非自定义指令莫属,自定义指令也至少有两个优点,可以将它绑定到任何需要添加额外功能的DOM上,并且自定义指令拥有 update、install等事件钩子,可以在虚拟DOM更新的时候触发更新,这和权限校验需要动态更新的需求完美契合。
下面我要做的就是填前人留下的坑,将上面ugly的代码用v-auth来进行彻底重构,只需要在需要鉴权的元素上,添加一个v-auth指令即可,并且当数据改变时,还能重新进行权限校验。
import Vue from 'vue'; import store from '@/store' // 堆代码 duidaima.com const VAuth = { install(Vue) { Vue.directive('auth', { inserted: (el, bind) => { this.validate(el, bind); }, update: (el, bind) => { this.validate(el, bind); }, }); }, validate(el, bind) { const { disabled, forbidden, havePermission } = this.calcPermission(bind); const isHidden = this.hiddenEL(el, bind, forbidden) this.setDisabled(el, disabled); this.setDataset(el, { forbidden, havePermission, disabled, isHidden, }) }, }; Vue.use(VAuth);
上面的 validate 方法一共做了四件事情,首先, calcPermission 计算了用户权限,根据得到的权限,hiddenEL 用于隐藏元素,setDisabled 用于禁用按钮,最后再使用 setDataset 将权限信息绑定到 dataset 上。
calcPermission(bind) { const { value: options = {} } = bind; const permissionColumn = 'permiss'; const permission = options[permissionColumn]; // 权限为空或者操作权限包含在用户权限中 const havePermission = !permission || store.state.user.authority.hasOwnProperty(permission); const forbiddenFuncName = { from: 'dialogFormBtnDisabled', table: 'btnDisabled', }[options.type] || 'btnDisabled'; const forbidden = options.forbidden && options.data && Vue.prototype.$utiloptions.forbidden, options.data, options.tableType || false; const disabled = !havePermission || options.disabled || forbidden; return { disabled, forbidden, havePermission, } },隐藏元素
当用户没有权限的时候,通常有两种做法,一种是将元素直接隐藏掉,另一种则只是禁用掉元素。首先讲讲隐藏元素,因为我们默认是对元素进行禁用,需要通过修饰符中的 hidden 值或者 arg 来判断是不是需要开启对元素的隐藏。紧接着,就可以使用上一步计算得到的 forbidden 和 display:none 来隐藏元素。
// 据修根据参数或者饰符hidden控制显示隐藏 hiddenEL(el, bind, forbidden) { const { modifiers = {}, arg } = bind; const hidden = arg === true || modifiers.hidden; const isHidden = hidden && forbidden; const isInline = el.classList.contains('inline'); const display = isInline ? 'inline-block' : ''; el.style.display = isHidden ? 'none' : display; return isHidden; },禁用元素
setDisabled(el, disabled) { if(disabled) { el.setAttribute('disabled', disabled); el.classList.add("is-disabled"); } else { el.removeAttribute('disabled'); el.classList.remove("is-disabled"); } },共享数据
setDataset(el, datasetMap) { Object.keys(datasetMap).forEach(key => { el.dataset[key] = !!datasetMap[key]; }) }然后,我们就可以在组件中通过ref获取到元素上的 dataset 属性。这里需要注意的是,我们获取到的true和false的字符串的,需要转换成布尔值,这里可以 使用JSON.parse 实现。
mounted() { const disabled = this.$refs.formItemRef.dataset.disabled; this.disabled = JSON.parse(disabled); console.log(this.disabled) // true }校验节流
import _ from 'lodash-es'; update: (el, bind) => { if(this.isEqual(bind)) return; this.validate(el, bind); }, isEqual(bind) { const { value, oldValue } = bind; return _.isEqual(value, oldValue); },使用指令
<el-button v-auth="AuthOptionsSchema" >{{ item.labelText }}</el-button>第二种用法可以为指令添加一个 hidden 修饰符,当用户没有权限时,元素会被隐藏掉。
<div :class="['kf-form-item', inline ? 'inline' : '']" ref="formItemRef" v-auth.hidden="AuthOptionsSchema"> </div>第三种和第二种类似,只是这里的 hidden 是动态传递的,而不是写死的。
<el-button v-auth:[item.hidden]="AuthOptionsSchema" >{{ item.labelText }}</el-button>需要特别注意的一点是,我们给指令绑定的是一个对象,对象的地址是不变的,当改变对象内的值并不会触发 update 方法。我们可以通过 JSON.parse(JSON.stringify(data)) 对绑定的对象进行深拷贝,来触发更新。
computed: { AuthOptionsSchema() { const data = { ...this.formItem, type: 'from', data: this.formList } return JSON.parse(JSON.stringify(data)) } },