Commit dc42d434f0b2a3b269deb76d857b42340bd93e80
1 parent
90b3fab2
refactor: refactoring menu generation
Showing
11 changed files
with
104 additions
and
19 deletions
.env
@@ -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 '/@/utils/helper/routeHelper | @@ -19,7 +19,7 @@ import { genRouteModule, transformObjToRoute } from '/@/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 => import.meta.env.PROD; | @@ -46,3 +47,14 @@ export const isProdMode = (): boolean => 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
src/utils/helper/routeHelper.ts
@@ -6,6 +6,8 @@ import { tabStore } from '/@/store/modules/tab'; | @@ -6,6 +6,8 @@ import { tabStore } from '/@/store/modules/tab'; | ||
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 'path'; | @@ -5,6 +5,7 @@ import { resolve } from 'path'; | ||
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; |