refactor: 增加顶部菜单权限判断

This commit is contained in:
RubyLiu 2024-02-02 17:00:02 +08:00 committed by Craftsman
parent 2359bea50a
commit 29fc604d6b
13 changed files with 111 additions and 45 deletions

View File

@ -73,11 +73,6 @@
const checkIsLogin = async () => { const checkIsLogin = async () => {
const isLogin = await userStore.isLogin(); const isLogin = await userStore.isLogin();
const isLoginPage = route.name === 'login'; const isLoginPage = route.name === 'login';
if (isLoginPage && isLogin) {
//
const currentRouteName = getFirstRouteNameByPermission(router.getRoutes());
router.push({ name: currentRouteName });
}
if (isLogin && appStore.currentProjectId && appStore.currentProjectId !== 'no_such_project') { if (isLogin && appStore.currentProjectId && appStore.currentProjectId !== 'no_such_project') {
// //
try { try {
@ -94,6 +89,11 @@
console.log(err); console.log(err);
} }
} }
if (isLoginPage && isLogin) {
//
const currentRouteName = getFirstRouteNameByPermission(router.getRoutes());
router.push({ name: currentRouteName });
}
}; };
// //
const getPublicKey = async () => { const getPublicKey = async () => {

View File

@ -16,6 +16,7 @@
import { useAppStore, useUserStore } from '@/store'; import { useAppStore, useUserStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license'; import useLicenseStore from '@/store/modules/setting/license';
import { openWindow, regexUrl } from '@/utils'; import { openWindow, regexUrl } from '@/utils';
import { getFisrtRouterNameByCurrentRoute } from '@/utils/permission';
import { listenerRouteChange } from '@/utils/route-listener'; import { listenerRouteChange } from '@/utils/route-listener';
import useMenuTree from './use-menu-tree'; import useMenuTree from './use-menu-tree';
@ -58,9 +59,17 @@
selectedKey.value = [item.name as string]; selectedKey.value = [item.name as string];
return; return;
} }
router.push({ if (item.meta?.hideChildrenInMenu) {
name: item.name, //
}); const childName = getFisrtRouterNameByCurrentRoute(item.name as string);
router.push({
name: childName,
});
} else {
router.push({
name: item.name,
});
}
} else { } else {
router.push({ router.push({
name: 'notFound', name: 'notFound',
@ -365,7 +374,6 @@
} }
return nodes; return nodes;
} }
return travel(menuTree.value); return travel(menuTree.value);
}; };

View File

@ -82,17 +82,22 @@
); );
} }
const filterMenuTopRouter = currentParent?.children const filterMenuTopRouter =
?.filter((item: any) => permission.accessRouter(item) && item.meta?.isTopMenu) currentParent?.children
.filter((item: any) => { ?.filter((item: any) => permission.accessRouter(item) && item.meta?.isTopMenu)
if (item.name === RouteEnum.SETTING_SYSTEM_AUTHORIZED_MANAGEMENT) { .filter((item: any) => {
return appStore.packageType === 'enterprise'; if (item.name === RouteEnum.SETTING_SYSTEM_AUTHORIZED_MANAGEMENT) {
} return appStore.packageType === 'enterprise';
return true; }
}); return true;
}) || [];
appStore.setTopMenus(filterMenuTopRouter); appStore.setTopMenus(filterMenuTopRouter);
setCurrentTopMenu(name as string); if (!newRoute.meta.isTopMenu) {
setCurrentTopMenu(filterMenuTopRouter[0].name as string);
} else {
setCurrentTopMenu(name as string);
}
return; return;
} }
} }

View File

@ -458,10 +458,22 @@
popVisible.value[id] = visibleItem; popVisible.value[id] = visibleItem;
} }
if (item.eventTag === 'delete') { if (item.eventTag === 'delete') {
let content = '';
switch (authScope) {
case AuthScopeEnum.SYSTEM:
content = t('system.userGroup.beforeDeleteUserGroup');
break;
case AuthScopeEnum.ORGANIZATION:
content = t('org.userGroup.beforeDeleteUserGroup');
break;
default:
content = t('project.userGroup.beforeDeleteUserGroup');
break;
}
openModal({ openModal({
type: 'error', type: 'error',
title: t('system.userGroup.isDeleteUserGroup', { name: characterLimit(currentName.value) }), title: t('system.userGroup.isDeleteUserGroup', { name: characterLimit(currentName.value) }),
content: t('system.userGroup.beforeDeleteUserGroup'), content,
okText: t('system.userGroup.confirmDelete'), okText: t('system.userGroup.confirmDelete'),
cancelText: t('system.userGroup.cancel'), cancelText: t('system.userGroup.cancel'),
okButtonProps: { okButtonProps: {

View File

@ -267,6 +267,7 @@
BatchActionQueryParams, BatchActionQueryParams,
MsPaginationI, MsPaginationI,
MsTableColumn, MsTableColumn,
MsTableDataItem,
MsTableProps, MsTableProps,
} from './type'; } from './type';
import type { TableColumnData, TableData } from '@arco-design/web-vue'; import type { TableColumnData, TableData } from '@arco-design/web-vue';
@ -322,11 +323,11 @@
const selectTotal = computed(() => { const selectTotal = computed(() => {
const { selectorStatus } = props; const { selectorStatus } = props;
if (selectorStatus === SelectAllEnum.CURRENT) { if (selectorStatus === SelectAllEnum.CURRENT) {
const { pageSize, total } = attrs.msPagination as MsPaginationI;
if (!attrs.showPagination) { if (!attrs.showPagination) {
// total // total
return total; return (attrs.data as MsTableDataItem<TableData>[]).length;
} }
const { pageSize, total } = attrs.msPagination as MsPaginationI;
if (pageSize > total) { if (pageSize > total) {
return total; return total;
} }

View File

@ -0,0 +1,12 @@
// 这里控制项目模块选择,如果是顶层则验证 appStore 里的 currentMenuConfig
// 项目管理 和 系统设置 不受此控制
export const firstLevelMenu = [
'workstation',
'testPlan',
'bugManagement',
'caseManagement',
'apiTest',
'uiTest',
'loadTest',
];
export default {};

View File

@ -1,10 +1,9 @@
import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'; import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
import { includes } from 'lodash-es'; import { includes } from 'lodash-es';
import { firstLevelMenu } from '@/config/permission';
import { hasAnyPermission, topLevelMenuHasPermission } from '@/utils/permission'; import { hasAnyPermission, topLevelMenuHasPermission } from '@/utils/permission';
const firstLevelMenu = ['workstation', 'testPlan', 'bugManagement', 'caseManagement', 'apiTest', 'uiTest', 'loadTest'];
/** /**
* *
* @returns * @returns

View File

@ -10,7 +10,6 @@ export default function setupPermissionGuard(router: Router) {
const permissionsAllow = Permission.accessRouter(to); const permissionsAllow = Permission.accessRouter(to);
const exist = WHITE_LIST.find((el) => el.name === to.name); const exist = WHITE_LIST.find((el) => el.name === to.name);
if (exist || permissionsAllow) { if (exist || permissionsAllow) {
next(); next();
} else { } else {

View File

@ -44,7 +44,7 @@ const ProjectManagement: AppRouteRecordRaw = {
meta: { meta: {
locale: 'menu.projectManagement.projectPermission', locale: 'menu.projectManagement.projectPermission',
roles: [ roles: [
'SYSTEM_PARAMETER_SETTING_BASE:READ', 'PROJECT_BASE_INFO:READ',
// 菜单管理 // 菜单管理
'PROJECT_APPLICATION_WORKSTATION:READ', 'PROJECT_APPLICATION_WORKSTATION:READ',
'PROJECT_APPLICATION_TEST_PLAN:READ', 'PROJECT_APPLICATION_TEST_PLAN:READ',
@ -67,7 +67,7 @@ const ProjectManagement: AppRouteRecordRaw = {
component: () => import('@/views/project-management/projectAndPermission/basicInfos/index.vue'), component: () => import('@/views/project-management/projectAndPermission/basicInfos/index.vue'),
meta: { meta: {
locale: 'project.permission.basicInfo', locale: 'project.permission.basicInfo',
roles: ['SYSTEM_PARAMETER_SETTING_BASE:READ'], roles: ['PROJECT_BASE_INFO:READ'],
}, },
}, },
// 菜单管理 // 菜单管理

View File

@ -19,18 +19,19 @@ const Setting: AppRouteRecordRaw = {
'SYSTEM_TEST_RESOURCE_POOL:READ', 'SYSTEM_TEST_RESOURCE_POOL:READ',
'SYSTEM_AUTH:READ', 'SYSTEM_AUTH:READ',
'SYSTEM_PLUGIN:READ', 'SYSTEM_PLUGIN:READ',
'SYSTEM_LOG:READ',
'ORGANIZATION_MEMBER:READ', 'ORGANIZATION_MEMBER:READ',
'ORGANIZATION_USER_ROLE:READ', 'ORGANIZATION_USER_ROLE:READ',
'ORGANIZATION_PROJECT:READ', 'ORGANIZATION_PROJECT:READ',
'SYSTEM_SERVICE_INTEGRATION:READ', 'SYSTEM_SERVICE_INTEGRATION:READ',
'ORGANIZATION_TEMPLATE:READ', 'ORGANIZATION_TEMPLATE:READ',
'ORGANIZATION_LOG:READ',
], ],
}, },
children: [ children: [
{ {
path: 'system', path: 'system',
name: SettingRouteEnum.SETTING_SYSTEM, name: SettingRouteEnum.SETTING_SYSTEM,
redirect: '/setting/system/user',
component: null, component: null,
meta: { meta: {
locale: 'menu.settings.system', locale: 'menu.settings.system',
@ -42,6 +43,7 @@ const Setting: AppRouteRecordRaw = {
'SYSTEM_TEST_RESOURCE_POOL:READ', 'SYSTEM_TEST_RESOURCE_POOL:READ',
'SYSTEM_AUTH:READ', 'SYSTEM_AUTH:READ',
'SYSTEM_PLUGIN:READ', 'SYSTEM_PLUGIN:READ',
'SYSTEM_LOG:READ',
], ],
hideChildrenInMenu: true, hideChildrenInMenu: true,
}, },
@ -152,7 +154,7 @@ const Setting: AppRouteRecordRaw = {
{ {
path: 'organization', path: 'organization',
name: SettingRouteEnum.SETTING_ORGANIZATION, name: SettingRouteEnum.SETTING_ORGANIZATION,
redirect: '/setting/organization/member', redirect: '',
component: null, component: null,
meta: { meta: {
locale: 'menu.settings.organization', locale: 'menu.settings.organization',
@ -162,6 +164,7 @@ const Setting: AppRouteRecordRaw = {
'ORGANIZATION_PROJECT:READ', 'ORGANIZATION_PROJECT:READ',
'SYSTEM_SERVICE_INTEGRATION:READ', 'SYSTEM_SERVICE_INTEGRATION:READ',
'ORGANIZATION_TEMPLATE:READ', 'ORGANIZATION_TEMPLATE:READ',
'ORGANIZATION_LOG:READ',
], ],
hideChildrenInMenu: true, hideChildrenInMenu: true,
}, },
@ -267,7 +270,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/organization/template/components/templateDetail.vue'), component: () => import('@/views/setting/organization/template/components/templateDetail.vue'),
meta: { meta: {
locale: 'menu.settings.organization.templateManagementDetail', locale: 'menu.settings.organization.templateManagementDetail',
roles: ['*'], roles: ['ORGANIZATION_TEMPLATE:READ+UPDATE', 'ORGANIZATION_TEMPLATE:READ+ADD'],
breadcrumbs: [ breadcrumbs: [
{ {
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE, name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE,
@ -295,7 +298,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/organization/template/components/workFlowTableIndex.vue'), component: () => import('@/views/setting/organization/template/components/workFlowTableIndex.vue'),
meta: { meta: {
locale: 'menu.settings.organization.templateManagementWorkFlow', locale: 'menu.settings.organization.templateManagementWorkFlow',
roles: ['*'], roles: ['ORGANIZATION_TEMPLATE:READ+UPDATE'],
breadcrumbs: [ breadcrumbs: [
{ {
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE, name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE,

View File

@ -1,22 +1,12 @@
import { RouteLocationNormalized, RouteRecordNormalized, RouteRecordRaw } from 'vue-router'; import { RouteLocationNormalized, RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
import { includes } from 'lodash-es'; import { includes } from 'lodash-es';
import { firstLevelMenu } from '@/config/permission';
import { INDEX_ROUTE } from '@/router/routes/base'; import { INDEX_ROUTE } from '@/router/routes/base';
import appRoutes from '@/router/routes/index';
import { useAppStore, useUserStore } from '@/store'; import { useAppStore, useUserStore } from '@/store';
import { SystemScopeType, UserRole, UserRolePermissions } from '@/store/modules/user/types'; import { SystemScopeType, UserRole, UserRolePermissions } from '@/store/modules/user/types';
const firstLevelMenuNames = [
'workstation',
'testPlan',
'bugManagement',
'caseManagement',
'apiTest',
'uiTest',
'loadTest',
'setting',
'projectManagement',
];
export function hasPermission(permission: string, typeList: string[]) { export function hasPermission(permission: string, typeList: string[]) {
const userStore = useUserStore(); const userStore = useUserStore();
if (userStore.isAdmin) { if (userStore.isAdmin) {
@ -92,7 +82,7 @@ export function topLevelMenuHasPermission(route: RouteLocationNormalized | Route
// 有权限的第一个路由名如果没有找到则返回IndexRoute // 有权限的第一个路由名如果没有找到则返回IndexRoute
export function getFirstRouteNameByPermission(routerList: RouteRecordNormalized[]) { export function getFirstRouteNameByPermission(routerList: RouteRecordNormalized[]) {
const currentRoute = routerList const currentRoute = routerList
.filter((item) => includes(firstLevelMenuNames, item.name)) .filter((item) => includes(firstLevelMenu, item.name))
.sort((a, b) => { .sort((a, b) => {
return (a.meta.order || 0) - (b.meta.order || 0); return (a.meta.order || 0) - (b.meta.order || 0);
}) })
@ -105,3 +95,30 @@ export function routerNameHasPermission(routerName: string, routerList: RouteRec
const currentRoute = routerList.find((item) => item.name === routerName); const currentRoute = routerList.find((item) => item.name === routerName);
return currentRoute ? hasAnyPermission(currentRoute.meta?.roles || []) : false; return currentRoute ? hasAnyPermission(currentRoute.meta?.roles || []) : false;
} }
export function findRouteByName(name: string) {
const queue: RouteRecordNormalized[] = [...appRoutes];
while (queue.length > 0) {
const currentRoute = queue.shift();
if (!currentRoute) {
return;
}
if (currentRoute.name === name) {
return currentRoute;
}
if (currentRoute.children) {
queue.push(...(currentRoute.children as RouteRecordNormalized[]));
}
}
return null;
}
// 找到当前路由下 第一个由权限的子路由
export function getFisrtRouterNameByCurrentRoute(parentName: string) {
const currentRoute = findRouteByName(parentName);
if (currentRoute) {
const hasAuthChildrenRouter = currentRoute.children.find((item) => hasAnyPermission(item.meta?.roles || []));
return hasAuthChildrenRouter ? hasAuthChildrenRouter.name : parentName;
}
return parentName;
}

View File

@ -6,7 +6,7 @@
<span class="font-medium text-[var(--color-text-000)]">{{ t('project.basicInfo.basicInfo') }}</span> <span class="font-medium text-[var(--color-text-000)]">{{ t('project.basicInfo.basicInfo') }}</span>
<a-button <a-button
v-if="!projectDetail?.deleted" v-if="!projectDetail?.deleted"
v-permission="['SYSTEM_PARAMETER_SETTING_BASE:READ+UPDATE']" v-permission="['PROJECT_BASE_INFO:READ+UPDATE']"
type="outline" type="outline"
@click="editHandler" @click="editHandler"
>{{ t('project.basicInfo.edit') }}</a-button >{{ t('project.basicInfo.edit') }}</a-button

View File

@ -49,7 +49,7 @@ export default {
LOAD_TEST: '性能测试', LOAD_TEST: '性能测试',
PERSONAL: '我的设置', PERSONAL: '我的设置',
isDeleteUserGroup: '是否删除: {name}?', isDeleteUserGroup: '是否删除: {name}?',
beforeDeleteUserGroup: '删除后,该组织下的项目数据将一起删除,请谨慎操作!', beforeDeleteUserGroup: '删除后,该系统下的项目数据将一起删除,请谨慎操作!',
confirmDelete: '确认删除', confirmDelete: '确认删除',
function: '功能', function: '功能',
operationObject: '操作对象', operationObject: '操作对象',
@ -98,4 +98,14 @@ export default {
recover: '恢复', recover: '恢复',
}, },
}, },
org: {
userGroup: {
beforeDeleteUserGroup: '删除后,组织下用户组数据将一起删除,请谨慎操作!',
},
},
project: {
userGroup: {
beforeDeleteUserGroup: '删除后,项目下用户组数据将一起删除,请谨慎操作!',
},
},
}; };