refactor: 增加顶部菜单权限判断
This commit is contained in:
parent
2359bea50a
commit
29fc604d6b
|
@ -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 () => {
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 这里控制项目模块选择,如果是顶层则验证 appStore 里的 currentMenuConfig
|
||||||
|
// 项目管理 和 系统设置 不受此控制
|
||||||
|
export const firstLevelMenu = [
|
||||||
|
'workstation',
|
||||||
|
'testPlan',
|
||||||
|
'bugManagement',
|
||||||
|
'caseManagement',
|
||||||
|
'apiTest',
|
||||||
|
'uiTest',
|
||||||
|
'loadTest',
|
||||||
|
];
|
||||||
|
export default {};
|
|
@ -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 调用方法
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 菜单管理
|
// 菜单管理
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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: '删除后,项目下用户组数据将一起删除,请谨慎操作!',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue