feat(menu): the route is automatically mapped to the menu

This commit is contained in:
vben 2021-06-27 23:58:14 +08:00
parent 327d71b8fb
commit 913c22c84f
40 changed files with 154 additions and 884 deletions

View File

@ -1,7 +1,15 @@
## Wip ## Wip
### ⚡ Performance Improvements
- **Icon** 移除 Icon 组件全局注册,防止特定情况下热更新问题 - **Icon** 移除 Icon 组件全局注册,防止特定情况下热更新问题
### ✨ Features
- **Menu** 新增 `permissionMode=PermissionModeEnum.ROUTE_MAPPING`模式
- 项目默认改为该模式,删除原有菜单文件
- 如果之前已经写好了菜单,可以更改为`PermissionModeEnum.ROLE`模式即可
## 2.5.1(2021-06-26) ## 2.5.1(2021-06-26)
### ⚡ Performance Improvements ### ⚡ Performance Improvements

View File

@ -33,6 +33,8 @@ export enum PermissionModeEnum {
ROLE = 'ROLE', ROLE = 'ROLE',
// black // black
BACK = 'BACK', BACK = 'BACK',
// route mapping
ROUTE_MAPPING = 'ROUTE_MAPPING',
} }
// Route switching animation // Route switching animation

View File

@ -31,7 +31,7 @@ export function usePermission() {
appStore.setProjectConfig({ appStore.setProjectConfig({
permissionMode: permissionMode:
projectSetting.permissionMode === PermissionModeEnum.BACK projectSetting.permissionMode === PermissionModeEnum.BACK
? PermissionModeEnum.ROLE ? PermissionModeEnum.ROUTE_MAPPING
: PermissionModeEnum.BACK, : PermissionModeEnum.BACK,
}); });
location.reload(); location.reload();
@ -59,7 +59,7 @@ export function usePermission() {
function hasPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean { function hasPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
const permMode = projectSetting.permissionMode; const permMode = projectSetting.permissionMode;
if (PermissionModeEnum.ROLE === permMode) { if (PermissionModeEnum.ROUTE_MAPPING === permMode) {
// Visible by default // Visible by default
if (!value) { if (!value) {
return def; return def;
@ -89,9 +89,9 @@ export function usePermission() {
* @param roles * @param roles
*/ */
async function changeRole(roles: RoleEnum | RoleEnum[]): Promise<void> { async function changeRole(roles: RoleEnum | RoleEnum[]): Promise<void> {
if (projectSetting.permissionMode !== PermissionModeEnum.ROLE) { if (projectSetting.permissionMode !== PermissionModeEnum.ROUTE_MAPPING) {
throw new Error( throw new Error(
'Please switch PermissionModeEnum to ROLE mode in the configuration to operate!' 'Please switch PermissionModeEnum to ROUTE_MAPPING mode in the configuration to operate!'
); );
} }

View File

@ -5,7 +5,7 @@
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useUserStoreWidthOut } from '/@/store/modules/user'; import { useUserStoreWithOut } from '/@/store/modules/user';
import { SettingButtonPositionEnum } from '/@/enums/appEnum'; import { SettingButtonPositionEnum } from '/@/enums/appEnum';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
@ -22,7 +22,7 @@
setup() { setup() {
const { getUseOpenBackTop, getShowSettingButton, getSettingButtonPosition, getFullContent } = const { getUseOpenBackTop, getShowSettingButton, getSettingButtonPosition, getFullContent } =
useRootSetting(); useRootSetting();
const userStore = useUserStoreWidthOut(); const userStore = useUserStoreWithOut();
const { prefixCls } = useDesign('setting-drawer-fearure'); const { prefixCls } = useDesign('setting-drawer-fearure');
const { getShowHeader } = useHeaderSetting(); const { getShowHeader } = useHeaderSetting();

View File

@ -1,6 +1,6 @@
import type { Router, RouteLocationNormalized } from 'vue-router'; import type { Router, RouteLocationNormalized } from 'vue-router';
import { useAppStoreWidthOut } from '/@/store/modules/app'; import { useAppStoreWithOut } from '/@/store/modules/app';
import { useUserStoreWidthOut } from '/@/store/modules/user'; import { useUserStoreWithOut } from '/@/store/modules/user';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
import { Modal, notification } from 'ant-design-vue'; import { Modal, notification } from 'ant-design-vue';
@ -46,8 +46,8 @@ function createPageGuard(router: Router) {
// Used to handle page loading status // Used to handle page loading status
function createPageLoadingGuard(router: Router) { function createPageLoadingGuard(router: Router) {
const userStore = useUserStoreWidthOut(); const userStore = useUserStoreWithOut();
const appStore = useAppStoreWidthOut(); const appStore = useAppStoreWithOut();
const { getOpenPageLoading } = useTransitionSetting(); const { getOpenPageLoading } = useTransitionSetting();
router.beforeEach(async (to) => { router.beforeEach(async (to) => {
if (!userStore.getToken) { if (!userStore.getToken) {

View File

@ -1,9 +1,9 @@
import type { Router, RouteRecordRaw } from 'vue-router'; import type { Router, RouteRecordRaw } from 'vue-router';
import { usePermissionStoreWidthOut } from '/@/store/modules/permission'; import { usePermissionStoreWithOut } from '/@/store/modules/permission';
import { PageEnum } from '/@/enums/pageEnum'; import { PageEnum } from '/@/enums/pageEnum';
import { useUserStoreWidthOut } from '/@/store/modules/user'; import { useUserStoreWithOut } from '/@/store/modules/user';
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'; import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
@ -12,8 +12,8 @@ const LOGIN_PATH = PageEnum.BASE_LOGIN;
const whitePathList: PageEnum[] = [LOGIN_PATH]; const whitePathList: PageEnum[] = [LOGIN_PATH];
export function createPermissionGuard(router: Router) { export function createPermissionGuard(router: Router) {
const userStore = useUserStoreWidthOut(); const userStore = useUserStoreWithOut();
const permissionStore = usePermissionStoreWidthOut(); const permissionStore = usePermissionStoreWithOut();
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
// Jump to the 404 page after processing the login // Jump to the 404 page after processing the login
if (from.path === LOGIN_PATH && to.name === PAGE_NOT_FOUND_ROUTE.name) { if (from.path === LOGIN_PATH && to.name === PAGE_NOT_FOUND_ROUTE.name) {

View File

@ -65,6 +65,8 @@ function dynamicImport(
// Turn background objects into routing objects // Turn background objects into routing objects
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] { export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
console.log(routeList);
routeList.forEach((route) => { routeList.forEach((route) => {
const component = route.component as string; const component = route.component as string;
if (component) { if (component) {

View File

@ -1,7 +1,7 @@
import type { Menu, MenuModule } from '/@/router/types'; import type { Menu, MenuModule } from '/@/router/types';
import type { RouteRecordNormalized } from 'vue-router'; import type { RouteRecordNormalized } from 'vue-router';
import { useAppStoreWidthOut } from '/@/store/modules/app'; import { useAppStoreWithOut } from '/@/store/modules/app';
import { usePermissionStore } from '/@/store/modules/permission'; import { usePermissionStore } from '/@/store/modules/permission';
import { transformMenuModule, getAllParentPath } from '/@/router/helper/menuHelper'; import { transformMenuModule, getAllParentPath } from '/@/router/helper/menuHelper';
import { filter } from '/@/utils/helper/treeHelper'; import { filter } from '/@/utils/helper/treeHelper';
@ -23,9 +23,21 @@ Object.keys(modules).forEach((key) => {
// =========================== // ===========================
// ==========Helper=========== // ==========Helper===========
// =========================== // ===========================
const getPermissionMode = () => {
const appStore = useAppStoreWithOut();
return appStore.getProjectConfig.permissionMode;
};
const isBackMode = () => { const isBackMode = () => {
const appStore = useAppStoreWidthOut(); return getPermissionMode() === PermissionModeEnum.BACK;
return appStore.getProjectConfig.permissionMode === PermissionModeEnum.BACK; };
const isRouteMappingMode = () => {
return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING;
};
const isRoleMode = () => {
return getPermissionMode() === PermissionModeEnum.ROLE;
}; };
const staticMenus: Menu[] = []; const staticMenus: Menu[] = [];
@ -41,40 +53,53 @@ const staticMenus: Menu[] = [];
async function getAsyncMenus() { async function getAsyncMenus() {
const permissionStore = usePermissionStore(); const permissionStore = usePermissionStore();
return !isBackMode() ? staticMenus : permissionStore.getBackMenuList; if (isBackMode()) {
return permissionStore.getBackMenuList;
}
if (isRouteMappingMode()) {
return permissionStore.getFrontMenuList.filter((item) => !item.hideMenu);
}
return staticMenus;
} }
export const getMenus = async (): Promise<Menu[]> => { export const getMenus = async (): Promise<Menu[]> => {
const menus = await getAsyncMenus(); const menus = await getAsyncMenus();
const routes = router.getRoutes(); if (isRoleMode()) {
const routes = router.getRoutes();
return !isBackMode() ? filter(menus, basicFilter(routes)) : menus; return filter(menus, basicFilter(routes));
}
return menus;
}; };
export async function getCurrentParentPath(currentPath: string) { export async function getCurrentParentPath(currentPath: string) {
const menus = await getAsyncMenus(); const menus = await getAsyncMenus();
const allParentPath = await getAllParentPath(menus, currentPath); const allParentPath = await getAllParentPath(menus, currentPath);
return allParentPath?.[0]; return allParentPath?.[0];
} }
// Get the level 1 menu, delete children // Get the level 1 menu, delete children
export async function getShallowMenus(): Promise<Menu[]> { export async function getShallowMenus(): Promise<Menu[]> {
const menus = await getAsyncMenus(); const menus = await getAsyncMenus();
const routes = router.getRoutes();
const shallowMenuList = menus.map((item) => ({ ...item, children: undefined })); const shallowMenuList = menus.map((item) => ({ ...item, children: undefined }));
return !isBackMode() ? shallowMenuList.filter(basicFilter(routes)) : shallowMenuList; if (isRoleMode()) {
const routes = router.getRoutes();
return shallowMenuList.filter(basicFilter(routes));
}
return shallowMenuList;
} }
// Get the children of the menu // Get the children of the menu
export async function getChildrenMenus(parentPath: string) { export async function getChildrenMenus(parentPath: string) {
const menus = await getMenus(); const menus = await getMenus();
const parent = menus.find((item) => item.path === parentPath); const parent = menus.find((item) => item.path === parentPath);
if (!parent || !parent.children || !!parent?.meta?.hideChildrenInMenu) return [] as Menu[]; if (!parent || !parent.children || !!parent?.meta?.hideChildrenInMenu) {
const routes = router.getRoutes(); return [] as Menu[];
}
return !isBackMode() ? filter(parent.children, basicFilter(routes)) : parent.children; if (isRoleMode()) {
const routes = router.getRoutes();
return filter(parent.children, basicFilter(routes));
}
return parent.children;
} }
function basicFilter(routes: RouteRecordNormalized[]) { function basicFilter(routes: RouteRecordNormalized[]) {

View File

@ -1,11 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const about: MenuModule = {
orderNo: 100000,
menu: {
path: '/about/index',
name: t('routes.dashboard.about'),
},
};
export default about;

View File

@ -1,22 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 10,
menu: {
name: t('routes.dashboard.dashboard'),
path: '/dashboard',
children: [
{
path: 'analysis',
name: t('routes.dashboard.analysis'),
},
{
path: 'workbench',
name: t('routes.dashboard.workbench'),
},
],
},
};
export default menu;

View File

@ -1,45 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 500,
menu: {
name: t('routes.demo.charts.charts'),
path: '/charts',
children: [
{
path: 'aMap',
name: t('routes.demo.charts.aMap'),
},
{
path: 'baiduMap',
name: t('routes.demo.charts.baiduMap'),
},
{
path: 'googleMap',
name: t('routes.demo.charts.googleMap'),
},
{
path: 'echarts',
name: 'Echarts',
children: [
{
path: 'map',
name: t('routes.demo.charts.map'),
},
{
path: 'line',
name: t('routes.demo.charts.line'),
},
{
path: 'pie',
name: t('routes.demo.charts.pie'),
},
],
},
],
},
};
export default menu;

View File

@ -1,279 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 30,
menu: {
name: t('routes.demo.comp.comp'),
path: '/comp',
tag: { dot: true },
children: [
{
path: 'basic',
name: t('routes.demo.comp.basic'),
},
{
path: 'form',
name: t('routes.demo.form.form'),
children: [
{
path: 'basic',
name: t('routes.demo.form.basic'),
},
{
path: 'useForm',
name: t('routes.demo.form.useForm'),
},
{
path: 'refForm',
name: t('routes.demo.form.refForm'),
},
{
path: 'advancedForm',
name: t('routes.demo.form.advancedForm'),
},
{
path: 'ruleForm',
name: t('routes.demo.form.ruleForm'),
},
{
path: 'dynamicForm',
name: t('routes.demo.form.dynamicForm'),
},
{
path: 'customerForm',
name: t('routes.demo.form.customerForm'),
},
{
path: 'appendForm',
name: t('routes.demo.form.appendForm'),
},
],
},
{
path: 'table',
name: t('routes.demo.table.table'),
children: [
{
path: 'basic',
name: t('routes.demo.table.basic'),
},
{
path: 'treeTable',
name: t('routes.demo.table.treeTable'),
},
{
path: 'fetchTable',
name: t('routes.demo.table.fetchTable'),
},
{
path: 'fixedColumn',
name: t('routes.demo.table.fixedColumn'),
},
{
path: 'customerCell',
name: t('routes.demo.table.customerCell'),
},
{
path: 'formTable',
name: t('routes.demo.table.formTable'),
},
{
path: 'useTable',
name: t('routes.demo.table.useTable'),
},
{
path: 'refTable',
name: t('routes.demo.table.refTable'),
},
{
path: 'multipleHeader',
name: t('routes.demo.table.multipleHeader'),
},
{
path: 'mergeHeader',
name: t('routes.demo.table.mergeHeader'),
},
{
path: 'expandTable',
name: t('routes.demo.table.expandTable'),
},
{
path: 'fixedHeight',
name: t('routes.demo.table.fixedHeight'),
},
{
path: 'footerTable',
name: t('routes.demo.table.footerTable'),
},
{
path: 'editCellTable',
name: t('routes.demo.table.editCellTable'),
},
{
path: 'editRowTable',
name: t('routes.demo.table.editRowTable'),
},
{
path: 'authColumn',
name: t('routes.demo.table.authColumn'),
},
],
},
{
path: 'cropper',
name: t('routes.demo.comp.cropperImage'),
tag: {
content: 'new',
},
},
{
path: 'countTo',
name: t('routes.demo.comp.countTo'),
},
{
path: 'timestamp',
name: t('routes.demo.comp.time'),
},
{
path: 'transition',
name: t('routes.demo.comp.transition'),
},
{
path: 'modal',
name: t('routes.demo.comp.modal'),
},
{
path: 'drawer',
name: t('routes.demo.comp.drawer'),
},
{
path: 'desc',
name: t('routes.demo.comp.desc'),
},
{
path: 'qrcode',
name: t('routes.demo.comp.qrcode'),
},
{
path: 'strength-meter',
name: t('routes.demo.comp.strength'),
},
{
path: 'upload',
name: t('routes.demo.comp.upload'),
},
{
path: 'loading',
name: t('routes.demo.comp.loading'),
},
{
path: 'tree',
name: t('routes.demo.comp.tree'),
children: [
{
path: 'basic',
name: t('routes.demo.comp.treeBasic'),
},
{
path: 'editTree',
name: t('routes.demo.comp.editTree'),
},
{
path: 'actionTree',
name: t('routes.demo.comp.actionTree'),
},
],
},
{
name: t('routes.demo.editor.editor'),
path: 'editor',
children: [
{
path: 'json',
name: t('routes.demo.editor.jsonEditor'),
},
{
path: 'markdown',
name: t('routes.demo.editor.markdown'),
children: [
{
path: 'index',
name: t('routes.demo.editor.tinymceBasic'),
},
{
path: 'editor',
name: t('routes.demo.editor.tinymceForm'),
},
],
},
{
path: 'tinymce',
name: t('routes.demo.editor.tinymce'),
children: [
{
path: 'index',
name: t('routes.demo.editor.tinymceBasic'),
},
{
path: 'editor',
name: t('routes.demo.editor.tinymceForm'),
},
],
},
],
},
{
path: 'scroll',
name: t('routes.demo.comp.scroll'),
children: [
{
path: 'basic',
name: t('routes.demo.comp.scrollBasic'),
},
{
path: 'action',
name: t('routes.demo.comp.scrollAction'),
},
{
path: 'virtualScroll',
name: t('routes.demo.comp.virtualScroll'),
},
],
},
{
path: 'lazy',
name: t('routes.demo.comp.lazy'),
children: [
{
path: 'basic',
name: t('routes.demo.comp.lazyBasic'),
},
{
path: 'transition',
name: t('routes.demo.comp.lazyTransition'),
},
],
},
{
path: 'verify',
name: t('routes.demo.comp.verify'),
children: [
{
path: 'drag',
name: t('routes.demo.comp.verifyDrag'),
},
{
path: 'rotate',
name: t('routes.demo.comp.verifyRotate'),
},
],
},
],
},
};
export default menu;

View File

@ -1,29 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 500,
menu: {
name: t('routes.demo.excel.excel'),
path: '/excel',
children: [
{
path: 'customExport',
name: t('routes.demo.excel.customExport'),
},
{
path: 'jsonExport',
name: t('routes.demo.excel.jsonExport'),
},
{
path: 'arrayExport',
name: t('routes.demo.excel.arrayExport'),
},
{
path: 'importExcel',
name: t('routes.demo.excel.importExcel'),
},
],
},
};
export default menu;

View File

@ -1,130 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 19,
menu: {
name: t('routes.demo.feat.feat'),
path: '/feat',
children: [
{
path: 'icon',
name: t('routes.demo.feat.icon'),
},
{
path: 'ws',
name: t('routes.demo.feat.ws'),
},
{
name: t('routes.demo.feat.sessionTimeout'),
path: 'session-timeout',
},
{
path: 'tabs',
name: t('routes.demo.feat.tabs'),
},
{
path: 'context-menu',
name: t('routes.demo.feat.contextMenu'),
},
{
path: 'download',
name: t('routes.demo.feat.download'),
},
{
path: 'print',
name: t('routes.demo.feat.print'),
},
{
path: 'click-out-side',
name: t('routes.demo.feat.clickOutSide'),
},
{
path: 'img-preview',
name: t('routes.demo.feat.imgPreview'),
},
{
path: 'copy',
name: t('routes.demo.feat.copy'),
},
{
path: 'msg',
name: t('routes.demo.feat.msg'),
},
{
path: 'watermark',
name: t('routes.demo.feat.watermark'),
},
{
path: 'ripple',
name: t('routes.demo.feat.ripple'),
},
{
path: 'full-screen',
name: t('routes.demo.feat.fullScreen'),
},
{
path: 'error-log',
name: t('routes.demo.feat.errorLog'),
},
{
name: t('routes.demo.excel.excel'),
path: 'excel',
children: [
{
path: 'customExport',
name: t('routes.demo.excel.customExport'),
},
{
path: 'jsonExport',
name: t('routes.demo.excel.jsonExport'),
},
{
path: 'arrayExport',
name: t('routes.demo.excel.arrayExport'),
},
{
path: 'importExcel',
name: t('routes.demo.excel.importExcel'),
},
],
},
{
name: t('routes.demo.feat.breadcrumb'),
path: 'breadcrumb',
children: [
// {
// path: 'flat',
// name: t('routes.demo.feat.breadcrumbFlat'),
// },
// {
// path: 'flatDetail',
// name: t('routes.demo.feat.breadcrumbFlatDetail'),
// },
{
path: 'children',
name: t('routes.demo.feat.breadcrumbChildren'),
},
],
},
{
path: 'testTab',
name: t('routes.demo.feat.tab'),
children: [
{
path: 'id1',
name: t('routes.demo.feat.tab1'),
},
{
path: 'id2',
name: t('routes.demo.feat.tab2'),
},
],
},
],
},
};
export default menu;

View File

@ -1,17 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 5000,
menu: {
name: t('routes.demo.flow.name'),
path: '/flow',
children: [
{
path: 'flowChart',
name: t('routes.demo.flow.flowChart'),
},
],
},
};
export default menu;

View File

@ -1,25 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 1000,
menu: {
name: t('routes.demo.iframe.frame'),
path: '/frame',
children: [
{
path: 'doc',
name: t('routes.demo.iframe.doc'),
},
{
path: 'antv',
name: t('routes.demo.iframe.antv'),
},
{
path: 'https://vvbin.cn/doc-next/',
name: t('routes.demo.iframe.docExternal'),
},
],
},
};
export default menu;

View File

@ -1,37 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 2000,
menu: {
name: t('routes.demo.level.level'),
path: '/level',
children: [
{
path: 'menu1',
name: 'Menu1',
children: [
{
path: 'menu1-1',
name: 'Menu1-1',
children: [
{
path: 'menu1-1-1',
name: 'Menu1-1-1',
},
],
},
{
path: 'menu1-2',
name: 'Menu1-2',
},
],
},
{
path: 'menu2',
name: 'Menu2',
},
],
},
};
export default menu;

View File

@ -1,121 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 20,
menu: {
name: t('routes.demo.page.page'),
path: '/page-demo',
children: [
{
path: 'form',
name: t('routes.demo.page.form'),
children: [
{
path: 'basic',
name: t('routes.demo.page.formBasic'),
},
{
path: 'step',
name: t('routes.demo.page.formStep'),
},
{
path: 'high',
name: t('routes.demo.page.formHigh'),
},
],
},
{
path: 'desc',
name: t('routes.demo.page.desc'),
children: [
{
path: 'basic',
name: t('routes.demo.page.descBasic'),
},
{
path: 'high',
name: t('routes.demo.page.descHigh'),
},
],
},
{
path: 'result',
name: t('routes.demo.page.result'),
children: [
{
path: 'success',
name: t('routes.demo.page.resultSuccess'),
},
{
path: 'fail',
name: t('routes.demo.page.resultFail'),
},
],
},
{
path: 'exception',
name: t('routes.demo.page.exception'),
children: [
{
path: '403',
name: t('403'),
},
{
path: '404',
name: t('404'),
},
{
path: '500',
name: t('500'),
},
{
path: 'net-work-error',
name: t('routes.demo.page.netWorkError'),
},
{
path: 'not-data',
name: t('routes.demo.page.notData'),
},
],
},
{
path: 'account',
name: t('routes.demo.page.account'),
children: [
{
path: 'center',
name: t('routes.demo.page.accountCenter'),
},
{
path: 'setting',
name: t('routes.demo.page.accountSetting'),
},
],
},
{
path: 'list',
name: t('routes.demo.page.list'),
children: [
{
path: 'basic',
name: t('routes.demo.page.listBasic'),
},
{
path: 'card',
name: t('routes.demo.page.listCard'),
},
{
path: 'search',
name: t('routes.demo.page.listSearch'),
},
],
},
],
},
};
export default menu;

View File

@ -1,49 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 15,
menu: {
name: t('routes.demo.permission.permission'),
path: '/permission',
children: [
{
path: 'front',
name: t('routes.demo.permission.front'),
children: [
{
path: 'page',
name: t('routes.demo.permission.frontPage'),
},
{
path: 'btn',
name: t('routes.demo.permission.frontBtn'),
},
{
path: 'auth-pageA',
name: t('routes.demo.permission.frontTestA'),
},
{
path: 'auth-pageB',
name: t('routes.demo.permission.frontTestB'),
},
],
},
{
path: 'back',
name: t('routes.demo.permission.back'),
children: [
{
path: 'page',
name: t('routes.demo.permission.backPage'),
},
{
path: 'btn',
name: t('routes.demo.permission.backBtn'),
},
],
},
],
},
};
export default menu;

View File

@ -1,14 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const setup: MenuModule = {
orderNo: 90000,
menu: {
path: '/setup/index',
name: t('routes.demo.setup.page'),
tag: {
content: 'new',
},
},
};
export default setup;

View File

@ -1,34 +0,0 @@
import type { MenuModule } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 2000,
menu: {
name: t('routes.demo.system.moduleName'),
path: '/system',
children: [
{
path: 'account',
name: t('routes.demo.system.account'),
},
{
path: 'role',
name: t('routes.demo.system.role'),
},
{
path: 'menu',
name: t('routes.demo.system.menu'),
},
{
path: 'dept',
name: t('routes.demo.system.dept'),
},
{
path: 'changePassword',
name: t('routes.demo.system.password'),
},
],
},
};
export default menu;

View File

@ -10,6 +10,7 @@ export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
meta: { meta: {
title: 'ErrorPage', title: 'ErrorPage',
hideBreadcrumb: true, hideBreadcrumb: true,
hideMenu: true,
}, },
children: [ children: [
{ {
@ -31,6 +32,7 @@ export const REDIRECT_ROUTE: AppRouteRecordRaw = {
meta: { meta: {
title: REDIRECT_NAME, title: REDIRECT_NAME,
hideBreadcrumb: true, hideBreadcrumb: true,
hideMenu: true,
}, },
children: [ children: [
{ {

View File

@ -9,8 +9,10 @@ const dashboard: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/about/index', redirect: '/about/index',
meta: { meta: {
hideChildrenInMenu: true,
icon: 'simple-icons:about-dot-me', icon: 'simple-icons:about-dot-me',
title: t('routes.dashboard.about'), title: t('routes.dashboard.about'),
orderNo: 100000,
}, },
children: [ children: [
{ {

View File

@ -9,6 +9,7 @@ const dashboard: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/dashboard/analysis', redirect: '/dashboard/analysis',
meta: { meta: {
orderNo: 10,
icon: 'ion:grid-outline', icon: 'ion:grid-outline',
title: t('routes.dashboard.dashboard'), title: t('routes.dashboard.dashboard'),
}, },

View File

@ -9,6 +9,7 @@ const charts: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/charts/echarts/map', redirect: '/charts/echarts/map',
meta: { meta: {
orderNo: 500,
icon: 'ion:bar-chart-outline', icon: 'ion:bar-chart-outline',
title: t('routes.demo.charts.charts'), title: t('routes.demo.charts.charts'),
}, },

View File

@ -9,6 +9,7 @@ const comp: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/comp/basic', redirect: '/comp/basic',
meta: { meta: {
orderNo: 30,
icon: 'ion:layers-outline', icon: 'ion:layers-outline',
title: t('routes.demo.comp.comp'), title: t('routes.demo.comp.comp'),
}, },

View File

@ -9,6 +9,7 @@ const feat: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/feat/icon', redirect: '/feat/icon',
meta: { meta: {
orderNo: 19,
icon: 'ion:git-compare-outline', icon: 'ion:git-compare-outline',
title: t('routes.demo.feat.feat'), title: t('routes.demo.feat.feat'),
}, },

View File

@ -9,6 +9,7 @@ const charts: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/flow/flowChart', redirect: '/flow/flowChart',
meta: { meta: {
orderNo: 5000,
icon: 'tabler:chart-dots', icon: 'tabler:chart-dots',
title: t('routes.demo.flow.name'), title: t('routes.demo.flow.name'),
}, },

View File

@ -10,6 +10,7 @@ const iframe: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/frame/doc', redirect: '/frame/doc',
meta: { meta: {
orderNo: 1000,
icon: 'ion:tv-outline', icon: 'ion:tv-outline',
title: t('routes.demo.iframe.frame'), title: t('routes.demo.iframe.frame'),
}, },

View File

@ -9,6 +9,7 @@ const permission: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/level/menu1/menu1-1/menu1-1-1', redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: { meta: {
orderNo: 2000,
icon: 'ion:menu-outline', icon: 'ion:menu-outline',
title: t('routes.demo.level.level'), title: t('routes.demo.level.level'),
}, },

View File

@ -12,6 +12,7 @@ const page: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/page-demo/form/basic', redirect: '/page-demo/form/basic',
meta: { meta: {
orderNo: 20,
icon: 'ion:aperture-outline', icon: 'ion:aperture-outline',
title: t('routes.demo.page.page'), title: t('routes.demo.page.page'),
}, },

View File

@ -10,6 +10,7 @@ const permission: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/permission/front/page', redirect: '/permission/front/page',
meta: { meta: {
orderNo: 15,
icon: 'ion:key-outline', icon: 'ion:key-outline',
title: t('routes.demo.permission.permission'), title: t('routes.demo.permission.permission'),
}, },

View File

@ -9,6 +9,8 @@ const setup: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/setup/index', redirect: '/setup/index',
meta: { meta: {
orderNo: 90000,
hideChildrenInMenu: true,
icon: 'simple-icons:about-dot-me', icon: 'simple-icons:about-dot-me',
title: t('routes.demo.setup.page'), title: t('routes.demo.setup.page'),
}, },

View File

@ -9,6 +9,7 @@ const system: AppRouteModule = {
component: LAYOUT, component: LAYOUT,
redirect: '/system/account', redirect: '/system/account',
meta: { meta: {
orderNo: 2000,
icon: 'ion:settings-outline', icon: 'ion:settings-outline',
title: t('routes.demo.system.moduleName'), title: t('routes.demo.system.moduleName'),
}, },
@ -26,6 +27,7 @@ const system: AppRouteModule = {
path: 'account_detail/:id', path: 'account_detail/:id',
name: 'AccountDetail', name: 'AccountDetail',
meta: { meta: {
hideMenu: true,
title: t('routes.demo.system.account_detail'), title: t('routes.demo.system.account_detail'),
ignoreKeepAlive: true, ignoreKeepAlive: true,
showMenu: false, showMenu: false,

View File

@ -24,7 +24,7 @@ const setting: ProjectConfig = {
settingButtonPosition: SettingButtonPositionEnum.AUTO, settingButtonPosition: SettingButtonPositionEnum.AUTO,
// Permission mode // Permission mode
permissionMode: PermissionModeEnum.ROLE, permissionMode: PermissionModeEnum.ROUTE_MAPPING,
// Permission-related cache is stored in sessionStorage or localStorage // Permission-related cache is stored in sessionStorage or localStorage
permissionCacheType: CacheTypeEnum.LOCAL, permissionCacheType: CacheTypeEnum.LOCAL,

View File

@ -103,6 +103,6 @@ export const useAppStore = defineStore({
}); });
// Need to be used outside the setup // Need to be used outside the setup
export function useAppStoreWidthOut() { export function useAppStoreWithOut() {
return useAppStore(store); return useAppStore(store);
} }

View File

@ -4,7 +4,7 @@ import { defineStore } from 'pinia';
import { store } from '/@/store'; import { store } from '/@/store';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useUserStore } from './user'; import { useUserStore } from './user';
import { useAppStoreWidthOut } from './app'; import { useAppStoreWithOut } from './app';
import { toRaw } from 'vue'; import { toRaw } from 'vue';
import { transformObjToRoute, flatMultiLevelRoutes } from '/@/router/helper/routeHelper'; import { transformObjToRoute, flatMultiLevelRoutes } from '/@/router/helper/routeHelper';
import { transformRouteToMenu } from '/@/router/helper/menuHelper'; import { transformRouteToMenu } from '/@/router/helper/menuHelper';
@ -32,6 +32,7 @@ interface PermissionState {
lastBuildMenuTime: number; lastBuildMenuTime: number;
// Backstage menu list // Backstage menu list
backMenuList: Menu[]; backMenuList: Menu[];
frontMenuList: Menu[];
} }
export const usePermissionStore = defineStore({ export const usePermissionStore = defineStore({
id: 'app-permission', id: 'app-permission',
@ -43,6 +44,8 @@ export const usePermissionStore = defineStore({
lastBuildMenuTime: 0, lastBuildMenuTime: 0,
// Backstage menu list // Backstage menu list
backMenuList: [], backMenuList: [],
// menu List
frontMenuList: [],
}), }),
getters: { getters: {
getPermCodeList(): string[] | number[] { getPermCodeList(): string[] | number[] {
@ -51,6 +54,9 @@ export const usePermissionStore = defineStore({
getBackMenuList(): Menu[] { getBackMenuList(): Menu[] {
return this.backMenuList; return this.backMenuList;
}, },
getFrontMenuList(): Menu[] {
return this.frontMenuList;
},
getLastBuildMenuTime(): number { getLastBuildMenuTime(): number {
return this.lastBuildMenuTime; return this.lastBuildMenuTime;
}, },
@ -68,6 +74,10 @@ export const usePermissionStore = defineStore({
list?.length > 0 && this.setLastBuildMenuTime(); list?.length > 0 && this.setLastBuildMenuTime();
}, },
setFrontMenuList(list: Menu[]) {
this.frontMenuList = list;
},
setLastBuildMenuTime() { setLastBuildMenuTime() {
this.lastBuildMenuTime = new Date().getTime(); this.lastBuildMenuTime = new Date().getTime();
}, },
@ -88,52 +98,70 @@ export const usePermissionStore = defineStore({
async buildRoutesAction(): Promise<AppRouteRecordRaw[]> { async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStore(); const userStore = useUserStore();
const appStore = useAppStoreWidthOut(); const appStore = useAppStoreWithOut();
let routes: AppRouteRecordRaw[] = []; let routes: AppRouteRecordRaw[] = [];
const roleList = toRaw(userStore.getRoleList) || []; const roleList = toRaw(userStore.getRoleList) || [];
const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig; const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig;
// role permissions
if (permissionMode === PermissionModeEnum.ROLE) { const routeFilter = (route: AppRouteRecordRaw) => {
const routeFilter = (route: AppRouteRecordRaw) => { const { meta } = route;
const { meta } = route; const { roles } = meta || {};
const { roles } = meta || {}; if (!roles) return true;
if (!roles) return true; return roleList.some((role) => roles.includes(role));
return roleList.some((role) => roles.includes(role)); };
};
routes = filter(asyncRoutes, routeFilter); switch (permissionMode) {
routes = routes.filter(routeFilter); case PermissionModeEnum.ROLE:
// Convert multi-level routing to level 2 routing routes = filter(asyncRoutes, routeFilter);
routes = flatMultiLevelRoutes(routes); routes = routes.filter(routeFilter);
// Convert multi-level routing to level 2 routing
routes = flatMultiLevelRoutes(routes);
break;
case PermissionModeEnum.ROUTE_MAPPING:
routes = filter(asyncRoutes, routeFilter);
routes = routes.filter(routeFilter);
const menuList = transformRouteToMenu(asyncRoutes);
menuList.sort((a, b) => {
return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0);
});
this.setFrontMenuList(menuList);
// Convert multi-level routing to level 2 routing
routes = flatMultiLevelRoutes(routes);
break;
// If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below // If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
} else if (permissionMode === PermissionModeEnum.BACK) { case PermissionModeEnum.BACK:
const { createMessage } = useMessage(); const { createMessage } = useMessage();
createMessage.loading({ createMessage.loading({
content: t('sys.app.menuLoading'), content: t('sys.app.menuLoading'),
duration: 1, duration: 1,
}); });
// !Simulate to obtain permission codes from the background, // !Simulate to obtain permission codes from the background,
// this function may only need to be executed once, and the actual project can be put at the right time by itself // this function may only need to be executed once, and the actual project can be put at the right time by itself
let routeList: AppRouteRecordRaw[] = []; let routeList: AppRouteRecordRaw[] = [];
try { try {
this.changePermissionCode(); this.changePermissionCode();
routeList = (await getMenuList()) as AppRouteRecordRaw[]; routeList = (await getMenuList()) as AppRouteRecordRaw[];
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
// Dynamically introduce components // Dynamically introduce components
routeList = transformObjToRoute(routeList); routeList = transformObjToRoute(routeList);
// Background routing to menu structure // Background routing to menu structure
const backMenuList = transformRouteToMenu(routeList); const backMenuList = transformRouteToMenu(routeList);
this.setBackMenuList(backMenuList); this.setBackMenuList(backMenuList);
routeList = flatMultiLevelRoutes(routeList); routeList = flatMultiLevelRoutes(routeList);
routes = [PAGE_NOT_FOUND_ROUTE, ...routeList]; routes = [PAGE_NOT_FOUND_ROUTE, ...routeList];
break;
} }
routes.push(ERROR_LOG_ROUTE); routes.push(ERROR_LOG_ROUTE);
return routes; return routes;
}, },
@ -141,6 +169,6 @@ export const usePermissionStore = defineStore({
}); });
// Need to be used outside the setup // Need to be used outside the setup
export function usePermissionStoreWidthOut() { export function usePermissionStoreWithOut() {
return usePermissionStore(store); return usePermissionStore(store);
} }

View File

@ -128,6 +128,6 @@ export const useUserStore = defineStore({
}); });
// Need to be used outside the setup // Need to be used outside the setup
export function useUserStoreWidthOut() { export function useUserStoreWithOut() {
return useUserStore(store); return useUserStore(store);
} }

View File

@ -3,7 +3,7 @@ import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
// import router from '/@/router'; // import router from '/@/router';
// import { PageEnum } from '/@/enums/pageEnum'; // import { PageEnum } from '/@/enums/pageEnum';
import { useUserStoreWidthOut } from '/@/store/modules/user'; import { useUserStoreWithOut } from '/@/store/modules/user';
import projectSetting from '/@/settings/projectSetting'; import projectSetting from '/@/settings/projectSetting';
import { SessionTimeoutProcessingEnum } from '/@/enums/appEnum'; import { SessionTimeoutProcessingEnum } from '/@/enums/appEnum';
@ -17,7 +17,7 @@ export function checkStatus(
errorMessageMode: ErrorMessageMode = 'message' errorMessageMode: ErrorMessageMode = 'message'
): void { ): void {
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStoreWidthOut(); const userStore = useUserStoreWithOut();
let errMessage = ''; let errMessage = '';
switch (status) { switch (status) {

View File

@ -2,6 +2,7 @@ export {};
declare module 'vue-router' { declare module 'vue-router' {
interface RouteMeta extends Record<string | number | symbol, unknown> { interface RouteMeta extends Record<string | number | symbol, unknown> {
orderNo?: number;
// title // title
title: string; title: string;
// Whether to ignore permissions // Whether to ignore permissions