Skip to content

Commit

Permalink
feat(projects): mock添加权限过滤
Browse files Browse the repository at this point in the history
  • Loading branch information
honghuangdc committed Apr 23, 2022
1 parent 807448a commit 7f4350a
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .env
Expand Up @@ -7,7 +7,7 @@ VITE_APP_TITLE=Soybean管理系统
VITE_APP_DESC=SoybeanAdmin是一个中后台管理系统模版

# 权限路由模式: static | dynamic
VITE_AUTH_ROUTE_MODE=static
VITE_AUTH_ROUTE_MODE=dynamic

VITE_VISUALIZER=false

Expand Down
15 changes: 15 additions & 0 deletions components.d.ts
Expand Up @@ -13,9 +13,14 @@ declare module 'vue' {
IconAntDesignCloseOutlined: typeof import('~icons/ant-design/close-outlined')['default']
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
IconCustomActivity: typeof import('~icons/custom/activity')['default']
IconCustomAvatar: typeof import('~icons/custom/avatar')['default']
IconCustomBanner: typeof import('~icons/custom/banner')['default']
IconCustomCast: typeof import('~icons/custom/cast')['default']
IconCustomEmptyData: typeof import('~icons/custom/empty-data')['default']
IconCustomLogo: typeof import('~icons/custom/logo')['default']
IconCustomLogoFill: typeof import('~icons/custom/logo-fill')['default']
IconCustomNetworkError: typeof import('~icons/custom/network-error')['default']
IconCustomNoPermission: typeof import('~icons/custom/no-permission')['default']
IconCustomNotFound: typeof import('~icons/custom/not-found')['default']
IconCustomServiceError: typeof import('~icons/custom/service-error')['default']
Expand Down Expand Up @@ -50,6 +55,8 @@ declare module 'vue' {
NColorPicker: typeof import('naive-ui')['NColorPicker']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDataTable: typeof import('naive-ui')['NDataTable']
NDescriptions: typeof import('naive-ui')['NDescriptions']
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDivider: typeof import('naive-ui')['NDivider']
NDrawer: typeof import('naive-ui')['NDrawer']
Expand All @@ -62,18 +69,26 @@ declare module 'vue' {
NGrid: typeof import('naive-ui')['NGrid']
NGridItem: typeof import('naive-ui')['NGridItem']
NInput: typeof import('naive-ui')['NInput']
NInputGroup: typeof import('naive-ui')['NInputGroup']
NInputNumber: typeof import('naive-ui')['NInputNumber']
NList: typeof import('naive-ui')['NList']
NListItem: typeof import('naive-ui')['NListItem']
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
NMenu: typeof import('naive-ui')['NMenu']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopover: typeof import('naive-ui')['NPopover']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace']
NSpin: typeof import('naive-ui')['NSpin']
NStatistic: typeof import('naive-ui')['NStatistic']
NSwitch: typeof import('naive-ui')['NSwitch']
NTabPane: typeof import('naive-ui')['NTabPane']
NTabs: typeof import('naive-ui')['NTabs']
NTag: typeof import('naive-ui')['NTag']
NThing: typeof import('naive-ui')['NThing']
NTimeline: typeof import('naive-ui')['NTimeline']
NTimelineItem: typeof import('naive-ui')['NTimelineItem']
NTooltip: typeof import('naive-ui')['NTooltip']
Expand Down
11 changes: 6 additions & 5 deletions mock/api/route.ts
@@ -1,4 +1,5 @@
import type { MockMethod } from 'vite-plugin-mock';
import { filterAuthRoutesByUserPermission } from '../utils';

const routes: AuthRoute.Route[] = [
{
Expand Down Expand Up @@ -241,7 +242,7 @@ const routes: AuthRoute.Route[] = [
path: '/auth-demo/permission',
component: 'self',
meta: {
title: '指令和权限切换',
title: '权限切换',
requiresAuth: true,
icon: 'ic:round-construction'
}
Expand Down Expand Up @@ -378,12 +379,12 @@ const routes: AuthRoute.Route[] = [
function dataMiddleware(data: AuthRoute.Route[]): ApiRoute.Route {
const routeHomeName: AuthRoute.RouteKey = 'dashboard_analysis';

function sortRoutes(sorts: AuthRoute.Route[]) {
return sorts.sort((next, pre) => Number(next.meta?.order) - Number(pre.meta?.order));
}
data.sort((next, pre) => Number(next.meta?.order) - Number(pre.meta?.order));

const filters = filterAuthRoutesByUserPermission(data, 'admin');

return {
routes: sortRoutes(data),
routes: filters,
home: routeHomeName
};
}
Expand Down
24 changes: 24 additions & 0 deletions mock/utils/index.ts
@@ -0,0 +1,24 @@
/**
* 根据用户权限过滤路由
* @param routes - 权限路由
* @param permission - 权限
*/
export function filterAuthRoutesByUserPermission(routes: AuthRoute.Route[], permission: Auth.RoleType) {
return routes.map(route => filterAuthRouteByUserPermission(route, permission)).flat(1);
}

/**
* 根据用户权限过滤单个路由
* @param route - 单个权限路由
* @param permission - 权限
*/
function filterAuthRouteByUserPermission(route: AuthRoute.Route, permission: Auth.RoleType): AuthRoute.Route[] {
const hasPermission =
!route.meta.permissions || permission === 'super' || route.meta.permissions.includes(permission);

if (route.children) {
const filterChildren = route.children.map(item => filterAuthRouteByUserPermission(item, permission)).flat(1);
Object.assign(route, { children: filterChildren });
}
return hasPermission ? [route] : [];
}
26 changes: 26 additions & 0 deletions src/composables/system.ts
@@ -1,4 +1,6 @@
import UAParser from 'ua-parser-js';
import { useAuthStore } from '@/store';
import { isArray, isString } from '@/utils';

interface AppInfo {
/** 项目名称 */
Expand Down Expand Up @@ -26,3 +28,27 @@ export function useDeviceInfo() {
const result = parser.getResult();
return result;
}

/** 权限判断 */
export function usePermission() {
const auth = useAuthStore();

function hasPermission(permission: Auth.RoleType | Auth.RoleType[]) {
const { userRole } = auth.userInfo;

let has = userRole === 'super';
if (!has) {
if (isArray(permission)) {
has = (permission as Auth.RoleType[]).includes(userRole);
}
if (isString(permission)) {
has = (permission as Auth.RoleType) === userRole;
}
}
return has;
}

return {
hasPermission
};
}
34 changes: 16 additions & 18 deletions src/directives/permission.ts
@@ -1,26 +1,24 @@
import type { App, Directive } from 'vue';
import { useAuthStore } from '@/store';
import { isArray, isString } from '@/utils';
import { usePermission } from '@/composables';

export default function setupPermissionDirective(app: App) {
const auth = useAuthStore();
const { hasPermission } = usePermission();

function updateElVisible(el: HTMLElement, permission: Auth.RoleType | Auth.RoleType[]) {
if (!permission) {
throw new Error(`need roles: like v-permission="'admin'", v-permission="['admin', 'test]"`);
}
if (!hasPermission(permission)) {
el.parentElement?.removeChild(el);
}
}

const permissionDirective: Directive<HTMLElement, Auth.RoleType | Auth.RoleType[]> = {
mounted(el: HTMLElement, binding) {
const { userRole } = auth.userInfo;
const elPermission = binding.value;
let hasPermission = userRole === 'super';
if (!hasPermission) {
if (isArray(elPermission)) {
hasPermission = (elPermission as Auth.RoleType[]).includes(userRole);
}
if (isString(elPermission)) {
hasPermission = (elPermission as Auth.RoleType) === userRole;
}
}
if (!hasPermission) {
el.remove();
}
mounted(el, binding) {
updateElVisible(el, binding.value);
},
beforeUpdate(el, binding) {
updateElVisible(el, binding.value);
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/router/modules/auth-demo.ts
Expand Up @@ -8,7 +8,7 @@ const authDemo: AuthRoute.Route = {
path: '/auth-demo/permission',
component: 'self',
meta: {
title: '指令和权限切换',
title: '权限切换',
requiresAuth: true,
icon: 'ic:round-construction'
}
Expand Down
2 changes: 1 addition & 1 deletion src/store/modules/tab/index.ts
Expand Up @@ -53,7 +53,7 @@ export const useTabStore = defineStore('tab-store', {
initHomeTab(routeHomeName: string, router: Router) {
const routes = router.getRoutes();
const findHome = routes.find(item => item.name === routeHomeName);
if (findHome && !findHome.children) {
if (findHome && !findHome.children.length) {
// 有子路由的不能作为Tab
this.homeTab = getTabRouteByVueRoute(findHome);
}
Expand Down
28 changes: 19 additions & 9 deletions src/views/auth-demo/permission/index.vue
@@ -1,34 +1,44 @@
<template>
<div class="h-full">
<n-card title="权限指令 v-permission" class="h-full shadow-sm rounded-16px">
<n-card title="权限切换" class="h-full shadow-sm rounded-16px">
<div class="pb-12px">
<n-gradient-text type="primary" :size="20">当前用户的权限:{{ auth.userInfo.userRole }}</n-gradient-text>
</div>
<n-space>
<n-button v-permission="`super`">super可见</n-button>
<n-button v-permission="`admin`">admin可见</n-button>
<n-button v-permission="['admin', 'test']">admin和test可见</n-button>
</n-space>
<div class="py-12px">
<n-gradient-text type="primary" :size="20">切换用户权限</n-gradient-text>
</div>
<n-select
:value="auth.userInfo.userRole"
class="w-120px"
size="small"
:options="roleList"
@update:value="auth.updateUserRole"
/>
<div class="py-12px">
<n-gradient-text type="primary" :size="20">权限指令 v-permission</n-gradient-text>
</div>
<div>
<n-button v-permission="'super'" class="mr-12px">super可见</n-button>
<n-button v-permission="'admin'" class="mr-12px">admin可见</n-button>
<n-button v-permission="['admin', 'test']">admin和test可见</n-button>
</div>
<div class="py-12px">
<n-gradient-text type="primary" :size="20">权限函数 hasPermission</n-gradient-text>
</div>
<n-space>
<n-button v-if="hasPermission('super')">super可见</n-button>
<n-button v-if="hasPermission('admin')">admin可见</n-button>
<n-button v-if="hasPermission(['admin', 'test'])">admin和test可见</n-button>
</n-space>
</n-card>
</div>
</template>

<script setup lang="ts">
import { watch } from 'vue';
import { useAppStore, useAuthStore } from '@/store';
import { usePermission } from '@/composables';
const app = useAppStore();
const auth = useAuthStore();
const { hasPermission } = usePermission();
const roleList = [
{ label: '超级管理员', value: 'super' },
Expand Down

0 comments on commit 7f4350a

Please sign in to comment.