Для 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).
что еще
Если система относительно велика и включает много сложных разрешений, может быть не очень подходящим подходом вытягивать все разрешения пользователя локально в начале. В это время необходимо объединить актуальную системную архитектуру, а затем гибко обращаться с ней в соответствии с основными методами, представленными выше.