Для front-end разработки существует большой бизнес-сценарий, который заключается в управлении back-end. В этих фоновых системах управления неизбежным и важным требованием является контроль разрешений.
возможные проблемы
Управление разрешениями можно условно разделить на页面级
а также页面元素级
. Изменить на мужские слова: Это меню можно только увидеть, на эту страницу нельзя зайти, у этой кнопки нет разрешений, эта область не позволяет видеть обычным пользователям! !
Далее мы преобразуем слова менеджера по продуктам в ерунду технического развития.
Предположим, у пользователя есть набор кодов разрешений (permissionCodes
):
- Если меню требует
code=100
можно только получить доступ, и текущий пользователь не владеет кодом, меню не будет отображаться - Если меню требует
code=100
можно получить только доступ, и текущий пользователь не владеет кодом, когда пользователь напрямую обращается к странице, соответствующей меню, он не будет запрашивать разрешение - Если нужна кнопка
code=200
можно получить только доступ, и текущий пользователь не владеет кодом, то кнопкаdisabled
свойство установлено наtrue
- Если область требует
code=200
доступ, а текущий пользователь не владеет кодом, тоdisplay
Установить какnone
Общий процесс выглядит следующим образом:
Реализация
Получить список разрешений пользователя
Перед рендерингом нам нужно получить список разрешений пользователя.Мы можем сначала запросить текущую информацию о пользователе с сервера, а затем перейти кVue
создать экземпляр.
$fetch.getPermissions(token)
.then(user => {
Vue.prototype.$user = user;
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
});
Предположим, нам нужно отобразить страницу входа? Нам просто нужно отделить службу входа в систему от основного приложения и добавить дополнительную запись, илиVue
Примеры могут решить эту проблему.
Рендеринг меню управления
Управлять отрисовкой меню легко, потому что меню обычно представляют собой настраиваемый массив, и вам нужно фильтровать только те меню, на которые у текущего пользователя нет разрешения.
// sidebar menus
const asideMenus = [
{
code: '100',
name: '首页',
path: '/home',
},
{
code: '500',
name: '系统设置',
path: '/manage',
children: [
{
code: '510',
name: '用户设置',
path: 'user',
},
{
code: '520',
name: '访问设置',
path: 'visit',
},
],
},
];
// Vue component options
{
computed: {
asideMenus() {
const { $user } = this;
return (function filter(arr) {
return arr.filter(menu => {
if (Array.isArray(menu.children)) {
menu.children = filter(menu.children);
}
if (menu.children && menu.children.length) {
return true
} else if (menu.code && $user && $user.codes) {
return ~$user.codes.indexOf(menu.code);
} else {
return true;
}
})
})(asideMenus);
},
},
}
Ограничить доступ к маршруту
Vue
Маршрутизация предоставляет общий хук перехвата маршрутизации, который нам удобен для контроля разрешений.
const noPermissionPage = '';
router.beforeEach((to, from, next) => {
const code = to.meta.code;
const user = router.app.$user;
if (code && user && user.codes && !~user.codes.indexOf(code)) {
next(noPermissionPage);
} else {
next();
}
});
Отключить действие кнопки/скрыть часть области
Далее мы подходим к основной части этой статьи: как отключить действия кнопок и скрыть некоторые области на основе разрешений?
существуетVue
тип проекта, элементы на странице почти всеVue component
, так что этот вопрос может быть эквивалентен: как присвоить компонентуdisabled
собственность илиvisible
свойство установлено наfalse
?
Сначала разберем проблему:
- Как определить, есть ли у пользователя права доступа к определенному компоненту?
- Как установить для отключенного свойства или видимого свойства компонента значение false?
Далее мы решим эти две задачи.
Мы можем легко придумать следующие идеи реализации:
// 代码片段
<el-button :disabled="$user && $user.codes && !~$user.codes.indexOf('200')"></el-button>
<div v-show="!$user || !$user.codes || ~$user.codes.indexOf('300')">some content</div>
Это решение может решить проблему, но его сложнее написать и сложнее поддерживать. Кроме того, если у пользователя есть разрешение,disabled
собственность илиv-show
На него также могут влиять другие условия, и выражения, написанные в это время, будут более сложными и сложными в обслуживании.
Вот еще один способ мышления:
import Vue from 'vue';
Vue.directive('p', {
bind: handler,
update: handler,
});
function handler(el, binding, vnode) {
const user = vnode.context.$user;
const code = binding.arg || binding.value;
const prop = binding.modifiers.visible ? 'visible': 'disabled';
const value = prop !== 'visible';
if (code && user && user.codes && !~user.codes.indexOf(code)) {
const vm = vnode.componentInstance;
if (vm && vm.hasOwnProperty(prop)) {
const silent = Vue.config.silent;
Vue.config.silent = true; // 强行忽略警告
vm[prop] = value;
Vue.config.silent = silent;
} else {
if (prop === 'visible') {
el.classList.add('display-none');
} else if (prop === 'disabled') {
el.setAttribute('disabled', true);
el.classList.add('is-disabled');
}
}
}
}
Как использовать:
<sa-button type="primary" v-p:200>操作</sa-button>
<p v-p:300.visible>操作提示</p>
Так что, не намного ли удобнее писать так?
Далее объясните, как мы можем напрямую изменить свойства компонента в директиве?
существуетVue
официальная документацияявно упоминается в модификации компонентаprop
является анти-шаблоном (не рекомендуется).
Итак, в режиме разработки, когда мы напрямую модифицируем компонентprop
, вы получите предупреждение.
К сожалению, большая часть того, что говорится в официальной документации, здесь не соответствует действительности, поэтому мы выбрали强行忽略警告
.
Проблема с этим подходом заключается в том, что еслиdisabled
Когда на атрибуты могут влиять и другие условия, ожидаемый эффект не будет достигнут. Например:
<!-- 权限的优先级大于submitting条件 -->
<sa-button type="primary" :disabled="submitting" v-p:200>操作</sa-button>
<p v-p:300.visible>操作提示</p>
Обычно разрешения, которые есть у пользователя, являются постоянными, поэтому мы можемbind
В функции ловушки значение атрибута напрямую закрепляется в соответствии с разрешением. Вот окончательная реализация:
import Vue from 'vue';
Vue.directive('p', {
bind: function (el, binding, vnode) {
const user = vnode.context.$user;
const code = binding.arg || binding.value;
const prop = binding.modifiers.visible ? 'visible': 'disabled';
const value = prop !== 'visible';
if (code && user && user.codes && !~user.codes.indexOf(code)) {
const vm = vnode.componentInstance;
if (vm && vm._props.hasOwnProperty(prop)) {
const silent = Vue.config.silent;
const property = Object.getOwnPropertyDescriptor(vm._props, prop);
Object.defineProperty(vm._props, prop, { ...property, get() { return value } });
Vue.config.silent = true; // 强行忽略警告
property.set(value); // 触发computed依赖更新
Vue.config.silent = silent;
} else {
if (prop === 'visible') {
el.classList.add('display-none');
} else if (prop === 'disabled') {
el.setAttribute('disabled', true);
el.classList.add('is-disabled');
}
}
}
}
});
Если вы не понимаете, почему это происходит, вы можете пойти и посмотретьVue
в исходном кодеinitProps
Методы (расположенныеsrc/core/instance/state.js#L64-L110
).
что еще
Если система относительно велика и включает много сложных разрешений, может быть не очень подходящим подходом вытягивать все разрешения пользователя локально в начале. В это время необходимо объединить актуальную системную архитектуру, а затем гибко обращаться с ней в соответствии с основными методами, представленными выше.