Commit dc42d434f0b2a3b269deb76d857b42340bd93e80

Authored by vben
1 parent 90b3fab2

refactor: refactoring menu generation

@@ -6,3 +6,7 @@ VITE_GLOB_APP_TITLE = Vben Admin @@ -6,3 +6,7 @@ VITE_GLOB_APP_TITLE = Vben Admin
6 6
7 # spa shortname 7 # spa shortname
8 VITE_GLOB_APP_SHORT_NAME = vue_vben_admin_2x 8 VITE_GLOB_APP_SHORT_NAME = vue_vben_admin_2x
  9 +
  10 +# Menu generation mode BACK|ROLE
  11 +# Need to delete LocalStorage after modification
  12 +VITE_GEN_MENU_MODE=ROLE
CHANGELOG.zh_CN.md
@@ -8,6 +8,10 @@ @@ -8,6 +8,10 @@
8 - openModal 和 openDrawer 第二个参数可以代替`transferModalData`传参到内部 8 - openModal 和 openDrawer 第二个参数可以代替`transferModalData`传参到内部
9 - 带参路由可以被缓存 9 - 带参路由可以被缓存
10 10
  11 +### ✨ Refactor
  12 +
  13 +- 重构由后台生成菜单的逻辑
  14 +
11 ### ⚡ Performance Improvements 15 ### ⚡ Performance Improvements
12 16
13 - 菜单性能继续优化,更流畅 17 - 菜单性能继续优化,更流畅
build/vite/plugin/dynamicImport/index.ts 0 → 100644
  1 +// Used to import all files under `src/views`
  2 +
  3 +// The built-in dynamic import of vite cannot meet the needs of importing all files under views
  4 +
  5 +import glob from 'glob';
  6 +import { Transform } from 'vite/dist/node/transform.js';
  7 +
  8 +function getPath(path: string) {
  9 + const lastIndex = path.lastIndexOf('.');
  10 + if (lastIndex !== -1) {
  11 + path = path.substring(0, lastIndex);
  12 + }
  13 + return path.replace('src/views', '');
  14 +}
  15 +
  16 +const dynamicImportTransform = function (env: any = {}): Transform {
  17 + return {
  18 + test({ path }) {
  19 + // Only convert the file
  20 + return path.includes('/src/utils/helper/dynamicImport.ts');
  21 + },
  22 + transform({ code }) {
  23 + const { VITE_GEN_MENU_MODE = '' } = env;
  24 + if (VITE_GEN_MENU_MODE !== 'BACK') {
  25 + return code;
  26 + }
  27 + // if (!isBuild) return code;
  28 + // Only convert the dir
  29 + try {
  30 + const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() });
  31 +
  32 + const _code = `
  33 + export default function (id) {
  34 + switch (id) {
  35 + ${files
  36 +
  37 + .map((p) =>
  38 + ` case '${getPath(p)}': return () => import('${p
  39 + .replace('src/views', '/@/views')
  40 + .replace(/\/\//g, '/')}');`.replace('.tsx', '')
  41 + )
  42 + .join('\n ')}
  43 + default: return Promise.reject(new Error("Unknown variable dynamic import: " + id));
  44 + }
  45 + }\n\n
  46 + `;
  47 + return _code;
  48 + } catch (error) {
  49 + console.error(error);
  50 + return code;
  51 + }
  52 + },
  53 + };
  54 +};
  55 +export default dynamicImportTransform;
mock/sys/menu.ts
@@ -16,7 +16,7 @@ const dashboardRoute = { @@ -16,7 +16,7 @@ const dashboardRoute = {
16 { 16 {
17 path: '/welcome', 17 path: '/welcome',
18 name: 'Welcome', 18 name: 'Welcome',
19 - component: '/dashboard/welcome/index.vue', 19 + component: '/dashboard/welcome/index',
20 meta: { 20 meta: {
21 title: '欢迎页', 21 title: '欢迎页',
22 affix: true, 22 affix: true,
@@ -34,28 +34,28 @@ const frontRoute = { @@ -34,28 +34,28 @@ const frontRoute = {
34 children: [ 34 children: [
35 { 35 {
36 path: 'page', 36 path: 'page',
37 - component: '/demo/permission/front/index.vue', 37 + component: '/demo/permission/front/index',
38 meta: { 38 meta: {
39 title: '页面权限', 39 title: '页面权限',
40 }, 40 },
41 }, 41 },
42 { 42 {
43 path: 'btn', 43 path: 'btn',
44 - component: '/demo/permission/front/Btn.vue', 44 + component: '/demo/permission/front/Btn',
45 meta: { 45 meta: {
46 title: '按钮权限', 46 title: '按钮权限',
47 }, 47 },
48 }, 48 },
49 { 49 {
50 path: 'auth-pageA', 50 path: 'auth-pageA',
51 - component: '/demo/permission/front/AuthPageA.vue', 51 + component: '/demo/permission/front/AuthPageA',
52 meta: { 52 meta: {
53 title: '权限测试页A', 53 title: '权限测试页A',
54 }, 54 },
55 }, 55 },
56 { 56 {
57 path: 'auth-pageB', 57 path: 'auth-pageB',
58 - component: '/demo/permission/front/AuthPageB.vue', 58 + component: '/demo/permission/front/AuthPageB',
59 meta: { 59 meta: {
60 title: '权限测试页B', 60 title: '权限测试页B',
61 }, 61 },
@@ -71,14 +71,14 @@ const backRoute = { @@ -71,14 +71,14 @@ const backRoute = {
71 children: [ 71 children: [
72 { 72 {
73 path: 'page', 73 path: 'page',
74 - component: 'demo/permission/back/index.vue', 74 + component: '/demo/permission/back/index',
75 meta: { 75 meta: {
76 title: '页面权限', 76 title: '页面权限',
77 }, 77 },
78 }, 78 },
79 { 79 {
80 path: 'btn', 80 path: 'btn',
81 - component: '/demo/permission/back/Btn.vue', 81 + component: '/demo/permission/back/Btn',
82 meta: { 82 meta: {
83 title: '按钮权限', 83 title: '按钮权限',
84 }, 84 },
src/layouts/default/index.tsx
@@ -107,7 +107,9 @@ export default defineComponent({ @@ -107,7 +107,9 @@ export default defineComponent({
107 unref(showHeaderRef) && <LayoutHeader />} 107 unref(showHeaderRef) && <LayoutHeader />}
108 108
109 {showTabs && !unref(getFullContent) && ( 109 {showTabs && !unref(getFullContent) && (
110 - <MultipleTabs class={`default-layout__tabs`} /> 110 + <Layout.Header class={`default-layout__tabs`}>
  111 + {() => <MultipleTabs />}
  112 + </Layout.Header>
111 )} 113 )}
112 114
113 {useOpenBackTop && <BackTop target={getTarget} />} 115 {useOpenBackTop && <BackTop target={getTarget} />}
src/settings/projectSetting.ts
1 import type { ProjectConfig } from '/@/types/config'; 1 import type { ProjectConfig } from '/@/types/config';
2 2
3 import { MenuTypeEnum, MenuThemeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum'; 3 import { MenuTypeEnum, MenuThemeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
4 -import { ContentEnum, PermissionModeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; 4 +import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
5 import { primaryColor } from '../../build/config/lessModifyVars'; 5 import { primaryColor } from '../../build/config/lessModifyVars';
6 -import { isProdMode } from '/@/utils/env'; 6 +import { isProdMode, getRoleMode } from '/@/utils/env';
  7 +
7 // ! You need to clear the browser cache after the change 8 // ! You need to clear the browser cache after the change
8 const setting: ProjectConfig = { 9 const setting: ProjectConfig = {
9 // Whether to show the configuration button 10 // Whether to show the configuration button
10 showSettingButton: true, 11 showSettingButton: true,
11 // 权限模式 12 // 权限模式
12 - permissionMode: PermissionModeEnum.ROLE, 13 + permissionMode: getRoleMode(),
13 // 网站灰色模式,用于可能悼念的日期开启 14 // 网站灰色模式,用于可能悼念的日期开启
14 grayMode: false, 15 grayMode: false,
15 // 色弱模式 16 // 色弱模式
src/store/modules/permission.ts
@@ -19,7 +19,7 @@ import { genRouteModule, transformObjToRoute } from &#39;/@/utils/helper/routeHelper @@ -19,7 +19,7 @@ import { genRouteModule, transformObjToRoute } from &#39;/@/utils/helper/routeHelper
19 import { transformRouteToMenu } from '/@/utils/helper/menuHelper'; 19 import { transformRouteToMenu } from '/@/utils/helper/menuHelper';
20 20
21 import { useMessage } from '/@/hooks/web/useMessage'; 21 import { useMessage } from '/@/hooks/web/useMessage';
22 -import { warn } from '/@/utils/log'; 22 +// import { warn } from '/@/utils/log';
23 23
24 const { createMessage } = useMessage(); 24 const { createMessage } = useMessage();
25 const NAME = 'permission'; 25 const NAME = 'permission';
@@ -99,9 +99,9 @@ class Permission extends VuexModule { @@ -99,9 +99,9 @@ class Permission extends VuexModule {
99 }); 99 });
100 // this.commitRoutesState(routes); 100 // this.commitRoutesState(routes);
101 // Background permissions 101 // Background permissions
102 - warn(  
103 - `当前权限模式为:${PermissionModeEnum.ROLE},请将src/store/modules/permission.ts内的后台菜单获取函数注释,如果已注释可以忽略此信息!`  
104 - ); 102 + // warn(
  103 + // `当前权限模式为:${PermissionModeEnum.ROLE},请将src/store/modules/permission.ts内的后台菜单获取函数注释,如果已注释可以忽略此信息!`
  104 + // );
105 // 如果确定不需要做后台动态权限,请将下面整个判断注释 105 // 如果确定不需要做后台动态权限,请将下面整个判断注释
106 } else if (permissionMode === PermissionModeEnum.BACK) { 106 } else if (permissionMode === PermissionModeEnum.BACK) {
107 const messageKey = 'loadMenu'; 107 const messageKey = 'loadMenu';
src/utils/env.ts
  1 +import { PermissionModeEnum } from '../enums/appEnum';
1 import type { GlobEnvConfig } from '/@/types/config'; 2 import type { GlobEnvConfig } from '/@/types/config';
2 3
3 export const getGlobEnvConfig = (): GlobEnvConfig => { 4 export const getGlobEnvConfig = (): GlobEnvConfig => {
@@ -46,3 +47,14 @@ export const isProdMode = (): boolean =&gt; import.meta.env.PROD; @@ -46,3 +47,14 @@ export const isProdMode = (): boolean =&gt; import.meta.env.PROD;
46 * @example: 47 * @example:
47 */ 48 */
48 export const isUseMock = (): boolean => import.meta.env.VITE_USE_MOCK === 'true'; 49 export const isUseMock = (): boolean => import.meta.env.VITE_USE_MOCK === 'true';
  50 +
  51 +/**
  52 + * @description: 获取菜单生成方式
  53 + * @param {type}
  54 + * @returns:
  55 + * @example:
  56 + */
  57 +export const getRoleMode = (): PermissionModeEnum =>
  58 + import.meta.env.VITE_GEN_MENU_MODE === PermissionModeEnum.ROLE
  59 + ? PermissionModeEnum.ROLE
  60 + : PermissionModeEnum.BACK;
src/utils/helper/dynamicImport.ts 0 → 100644
  1 +export default function (id: string) {
  2 + const dynamicImportModule: any = id;
  3 + return dynamicImportModule;
  4 +}
src/utils/helper/routeHelper.ts
@@ -6,6 +6,8 @@ import { tabStore } from &#39;/@/store/modules/tab&#39;; @@ -6,6 +6,8 @@ import { tabStore } from &#39;/@/store/modules/tab&#39;;
6 import { createRouter, createWebHashHistory } from 'vue-router'; 6 import { createRouter, createWebHashHistory } from 'vue-router';
7 import { toRaw } from 'vue'; 7 import { toRaw } from 'vue';
8 import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 8 import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant';
  9 +// import { isDevMode } from '/@/utils/env';
  10 +import dynamicImport from './dynamicImport';
9 11
10 let currentTo: RouteLocationNormalized | null = null; 12 let currentTo: RouteLocationNormalized | null = null;
11 13
@@ -45,12 +47,12 @@ export function genRouteModule(moduleList: AppRouteModule[]) { @@ -45,12 +47,12 @@ export function genRouteModule(moduleList: AppRouteModule[]) {
45 // TODO 错误写法 47 // TODO 错误写法
46 function asyncImportRoute(routes: AppRouteRecordRaw[]) { 48 function asyncImportRoute(routes: AppRouteRecordRaw[]) {
47 routes.forEach((item) => { 49 routes.forEach((item) => {
48 - let { component } = item; 50 + const { component } = item;
49 const { children } = item; 51 const { children } = item;
50 if (component) { 52 if (component) {
51 - component = component.replace(/^\//, '');  
52 - item.component = () => import(`/@/views/${component}`); 53 + item.component = dynamicImport(component);
53 } 54 }
  55 +
54 children && asyncImportRoute(children); 56 children && asyncImportRoute(children);
55 }); 57 });
56 } 58 }
vite.config.ts
@@ -5,6 +5,7 @@ import { resolve } from &#39;path&#39;; @@ -5,6 +5,7 @@ import { resolve } from &#39;path&#39;;
5 import { modifyVars } from './build/config/lessModifyVars'; 5 import { modifyVars } from './build/config/lessModifyVars';
6 import { createProxy } from './build/vite/proxy'; 6 import { createProxy } from './build/vite/proxy';
7 import globbyTransform from './build/vite/plugin/context/transform'; 7 import globbyTransform from './build/vite/plugin/context/transform';
  8 +import dynamicImportTransform from './build/vite/plugin/dynamicImport/index';
8 9
9 import { isDevFn, loadEnv } from './build/utils'; 10 import { isDevFn, loadEnv } from './build/utils';
10 11
@@ -134,5 +135,5 @@ const viteConfig: UserConfig = { @@ -134,5 +135,5 @@ const viteConfig: UserConfig = {
134 135
135 export default { 136 export default {
136 ...viteConfig, 137 ...viteConfig,
137 - transforms: [globbyTransform(viteConfig)], 138 + transforms: [globbyTransform(viteConfig), dynamicImportTransform(viteEnv)],
138 } as UserConfig; 139 } as UserConfig;