Commit c303ec1a23c4b1fbad4fbda9007af2147dc327e2

Authored by vben
1 parent 7bfe5f75

refactor: refactor route

Showing 84 changed files with 1567 additions and 1524 deletions
.github/workflows/deploy.yml 0 → 100644
  1 +name: deploy
  2 +
  3 +on:
  4 + push:
  5 + branches:
  6 + - main
  7 +
  8 +jobs:
  9 + build-deploy:
  10 + runs-on: ubuntu-latest
  11 +
  12 + steps:
  13 + - uses: actions/checkout@v1
  14 + - run: npm install
  15 + - run: npm run build
  16 +
  17 + - name: Deploy
  18 + uses: peaceiris/actions-gh-pages@v2.5.0
  19 + env:
  20 + ACTIONS_DEPLOY_KEY: ${{secrets.ACTIONS_DEPLOY_KEY}}
  21 + PUBLISH_BRANCH: gh-pages
  22 + PUBLISH_DIR: dist
.vscode/launch.json
1 { 1 {
2 "version": "0.2.0", 2 "version": "0.2.0",
3 "configurations": [ 3 "configurations": [
4 - // node环境调试当前激活编辑器ts/js代码  
5 { 4 {
6 - "type": "node", 5 + "type": "chrome",
7 "request": "launch", 6 "request": "launch",
8 - "name": "file",  
9 - "cwd": "${workspaceFolder}",  
10 - "program": "${file}",  
11 - // .vscode 目录又不认识了???  
12 - "preLaunchTask": "tsc: 监视 - build/tsconfig.json", // cn  
13 - // "preLaunchTask": "tsc: watch - build/tsconfig.json", // en  
14 - "outFiles": ["${workspaceFolder}/compile/**/*.js"]  
15 - // "args": ["--experimental-modules", "--loader", "./loader.mjs"] 7 + "name": "Launch Chrome",
  8 + "url": "http://localhost:3100",
  9 + "webRoot": "${workspaceFolder}/src",
  10 + "sourceMaps": true
16 }, 11 },
17 - // 调试开发环境脚本  
18 - {  
19 - "type": "node",  
20 - "request": "launch",  
21 - "name": "dev",  
22 - // "stopOnEntry": true,  
23 - "cwd": "${workspaceFolder}",  
24 - "program": "${workspaceFolder}/node_modules/@vue/cli-service/bin/vue-cli-service.js",  
25 - "args": ["serve", "--open"]  
26 - },  
27 - // 调试生产环境脚本  
28 - {  
29 - "type": "node",  
30 - "request": "launch",  
31 - "name": "build",  
32 - // "stopOnEntry": true,  
33 - "cwd": "${workspaceFolder}",  
34 - "program": "${workspaceFolder}/node_modules/@vue/cli-service/bin/vue-cli-service.js",  
35 - "args": ["build"]  
36 - },  
37 - // 调试单元测试脚本  
38 - {  
39 - "type": "node",  
40 - "request": "launch",  
41 - "name": "test:unit",  
42 - // "stopOnEntry": true,  
43 - "cwd": "${workspaceFolder}",  
44 - "program": "${workspaceFolder}/node_modules/@vue/cli-service/bin/vue-cli-service.js",  
45 - "args": ["test:unit", "--detectOpenHandles"]  
46 - }  
47 ] 12 ]
48 } 13 }
.vscode/settings.json
@@ -163,12 +163,6 @@ @@ -163,12 +163,6 @@
163 "[typescriptreact]": { 163 "[typescriptreact]": {
164 "editor.defaultFormatter": "esbenp.prettier-vscode" 164 "editor.defaultFormatter": "esbenp.prettier-vscode"
165 }, 165 },
166 - "[json]": {  
167 - "editor.defaultFormatter": "vscode.json-language-features"  
168 - },  
169 - "[jsonc]": {  
170 - "editor.defaultFormatter": "vscode.json-language-features"  
171 - },  
172 "[html]": { 166 "[html]": {
173 "editor.defaultFormatter": "esbenp.prettier-vscode" 167 "editor.defaultFormatter": "esbenp.prettier-vscode"
174 }, 168 },
@@ -198,5 +192,8 @@ @@ -198,5 +192,8 @@
198 "ts" 192 "ts"
199 ], 193 ],
200 "i18n-ally.sourceLanguage": "zh", 194 "i18n-ally.sourceLanguage": "zh",
201 - "i18n-ally.enabledFrameworks":["vue","react"] 195 + "i18n-ally.enabledFrameworks": [
  196 + "vue",
  197 + "react"
  198 + ]
202 } 199 }
203 \ No newline at end of file 200 \ No newline at end of file
CHANGELOG.zh_CN.md
1 ## Wip 1 ## Wip
2 2
  3 +## (破坏性更新) Breaking changes
  4 +
  5 +- 路由重构, 不再支持以前的格式。改为支持 vue-router 最初的默认结构,具体格式可以参考示例更改。实现多级路由缓存,不再将路由转化为 2 级。
  6 +- 重构面包屑,使用 antd 的面包屑组件。之前的组件已删除
  7 +
3 ### ✨ Features 8 ### ✨ Features
4 9
5 - 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading`和`v-loading`指令。并增加示例 10 - 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading`和`v-loading`指令。并增加示例
6 - i18n 支持 vscode `i18n-ally`插件 11 - i18n 支持 vscode `i18n-ally`插件
  12 +- 新增多级路由缓存示例
7 13
8 ### 🎫 Chores 14 ### 🎫 Chores
9 15
10 - 首屏 loading 修改 16 - 首屏 loading 修改
11 17
  18 +### 🐛 Bug Fixes
  19 +
  20 +-修复表格 i18n 错误
  21 +
12 ## 2.0.0-rc.12 (2020-11-30) 22 ## 2.0.0-rc.12 (2020-11-30)
13 23
14 ## (破坏性更新) Breaking changes 24 ## (破坏性更新) Breaking changes
build/vite/plugin/transform/dynamic-import/index.ts
@@ -17,8 +17,8 @@ const dynamicImportTransform = function (enableDynamicImport: boolean): Transfor @@ -17,8 +17,8 @@ const dynamicImportTransform = function (enableDynamicImport: boolean): Transfor
17 test({ path }) { 17 test({ path }) {
18 // Only convert the file 18 // Only convert the file
19 return ( 19 return (
20 - path.includes('/src/utils/helper/dynamicImport.ts') ||  
21 - path.includes(`\\src\\utils\\helper\\dynamicImport.ts`) 20 + path.includes('/src/router/helper/dynamicImport.ts') ||
  21 + path.includes(`\\src\\router\\helper\\dynamicImport.ts`)
22 ); 22 );
23 }, 23 },
24 transform({ code }) { 24 transform({ code }) {
mock/sys/menu.ts
1 import { resultSuccess } from '../_util'; 1 import { resultSuccess } from '../_util';
2 import { MockMethod } from 'vite-plugin-mock'; 2 import { MockMethod } from 'vite-plugin-mock';
3 3
  4 +// single
4 const dashboardRoute = { 5 const dashboardRoute = {
5 - path: '/dashboard',  
6 - name: 'Dashboard',  
7 - component: 'PAGE_LAYOUT',  
8 - redirect: '/dashboard/welcome', 6 + path: '/home',
  7 + name: 'Home',
  8 + component: '/dashboard/welcome/index',
9 meta: { 9 meta: {
  10 + title: 'routes.dashboard.welcome',
  11 + affix: true,
10 icon: 'ant-design:home-outlined', 12 icon: 'ant-design:home-outlined',
11 - title: 'Dashboard',  
12 }, 13 },
13 - children: [  
14 - {  
15 - path: '/welcome',  
16 - name: 'Welcome',  
17 - component: '/dashboard/welcome/index',  
18 - meta: {  
19 - title: '欢迎页',  
20 - affix: true,  
21 - },  
22 - },  
23 - ],  
24 }; 14 };
25 15
26 const frontRoute = { 16 const frontRoute = {
27 - path: '/front', 17 + path: 'front',
28 name: 'PermissionFrontDemo', 18 name: 'PermissionFrontDemo',
29 meta: { 19 meta: {
30 - title: '基于前端权限', 20 + title: 'routes.demo.permission.front',
31 }, 21 },
32 children: [ 22 children: [
33 { 23 {
@@ -35,7 +25,7 @@ const frontRoute = { @@ -35,7 +25,7 @@ const frontRoute = {
35 name: 'FrontPageAuth', 25 name: 'FrontPageAuth',
36 component: '/demo/permission/front/index', 26 component: '/demo/permission/front/index',
37 meta: { 27 meta: {
38 - title: '页面权限', 28 + title: 'routes.demo.permission.frontPage',
39 }, 29 },
40 }, 30 },
41 { 31 {
@@ -43,7 +33,7 @@ const frontRoute = { @@ -43,7 +33,7 @@ const frontRoute = {
43 name: 'FrontBtnAuth', 33 name: 'FrontBtnAuth',
44 component: '/demo/permission/front/Btn', 34 component: '/demo/permission/front/Btn',
45 meta: { 35 meta: {
46 - title: '按钮权限', 36 + title: 'routes.demo.permission.frontBtn',
47 }, 37 },
48 }, 38 },
49 { 39 {
@@ -51,7 +41,7 @@ const frontRoute = { @@ -51,7 +41,7 @@ const frontRoute = {
51 name: 'FrontAuthPageA', 41 name: 'FrontAuthPageA',
52 component: '/demo/permission/front/AuthPageA', 42 component: '/demo/permission/front/AuthPageA',
53 meta: { 43 meta: {
54 - title: '权限测试页A', 44 + title: 'routes.demo.permission.frontTestA',
55 }, 45 },
56 }, 46 },
57 { 47 {
@@ -59,24 +49,25 @@ const frontRoute = { @@ -59,24 +49,25 @@ const frontRoute = {
59 name: 'FrontAuthPageB', 49 name: 'FrontAuthPageB',
60 component: '/demo/permission/front/AuthPageB', 50 component: '/demo/permission/front/AuthPageB',
61 meta: { 51 meta: {
62 - title: '权限测试页B', 52 + title: 'routes.demo.permission.frontTestB',
63 }, 53 },
64 }, 54 },
65 ], 55 ],
66 }; 56 };
67 const backRoute = { 57 const backRoute = {
68 - path: '/back', 58 + path: 'back',
69 name: 'PermissionBackDemo', 59 name: 'PermissionBackDemo',
70 meta: { 60 meta: {
71 - title: '基于后台权限', 61 + title: 'routes.demo.permission.back',
72 }, 62 },
  63 +
73 children: [ 64 children: [
74 { 65 {
75 path: 'page', 66 path: 'page',
76 name: 'BackAuthPage', 67 name: 'BackAuthPage',
77 component: '/demo/permission/back/index', 68 component: '/demo/permission/back/index',
78 meta: { 69 meta: {
79 - title: '页面权限', 70 + title: 'routes.demo.permission.backPage',
80 }, 71 },
81 }, 72 },
82 { 73 {
@@ -84,7 +75,7 @@ const backRoute = { @@ -84,7 +75,7 @@ const backRoute = {
84 name: 'BackAuthBtn', 75 name: 'BackAuthBtn',
85 component: '/demo/permission/back/Btn', 76 component: '/demo/permission/back/Btn',
86 meta: { 77 meta: {
87 - title: '按钮权限', 78 + title: 'routes.demo.permission.backBtn',
88 }, 79 },
89 }, 80 },
90 ], 81 ],
@@ -92,11 +83,11 @@ const backRoute = { @@ -92,11 +83,11 @@ const backRoute = {
92 const authRoute = { 83 const authRoute = {
93 path: '/permission', 84 path: '/permission',
94 name: 'Permission', 85 name: 'Permission',
95 - component: 'PAGE_LAYOUT', 86 + component: 'LAYOUT',
96 redirect: '/permission/front/page', 87 redirect: '/permission/front/page',
97 meta: { 88 meta: {
98 - icon: 'ant-design:home-outlined',  
99 - title: '权限管理', 89 + icon: 'carbon:user-role',
  90 + title: 'routes.demo.permission.permission',
100 }, 91 },
101 children: [frontRoute, backRoute], 92 children: [frontRoute, backRoute],
102 }; 93 };
@@ -104,14 +95,70 @@ const authRoute = { @@ -104,14 +95,70 @@ const authRoute = {
104 const authRoute1 = { 95 const authRoute1 = {
105 path: '/permission', 96 path: '/permission',
106 name: 'Permission', 97 name: 'Permission',
107 - component: 'PAGE_LAYOUT', 98 + component: 'LAYOUT',
108 redirect: '/permission/front/page', 99 redirect: '/permission/front/page',
109 meta: { 100 meta: {
110 - icon: 'ant-design:home-outlined',  
111 - title: '权限管理', 101 + icon: 'carbon:user-role',
  102 + title: 'routes.demo.permission.permission',
112 }, 103 },
113 children: [backRoute], 104 children: [backRoute],
114 }; 105 };
  106 +
  107 +const levelRoute = {
  108 + path: '/level',
  109 + name: 'Level',
  110 + component: 'LAYOUT',
  111 + redirect: '/level/menu1/menu1-1',
  112 + meta: {
  113 + icon: 'carbon:user-role',
  114 + title: 'routes.demo.level.level',
  115 + },
  116 +
  117 + children: [
  118 + {
  119 + path: 'menu1',
  120 + name: 'Menu1Demo',
  121 + meta: {
  122 + title: 'Menu1',
  123 + },
  124 + children: [
  125 + {
  126 + path: 'menu1-1',
  127 + name: 'Menu11Demo',
  128 + meta: {
  129 + title: 'Menu1-1',
  130 + },
  131 + children: [
  132 + {
  133 + path: 'menu1-1-1',
  134 + name: 'Menu111Demo',
  135 + component: '/demo/level/Menu111',
  136 + meta: {
  137 + title: 'Menu111',
  138 + },
  139 + },
  140 + ],
  141 + },
  142 + {
  143 + path: 'menu1-2',
  144 + name: 'Menu12Demo',
  145 + component: '/demo/level/Menu12',
  146 + meta: {
  147 + title: 'Menu1-2',
  148 + },
  149 + },
  150 + ],
  151 + },
  152 + {
  153 + path: 'menu2',
  154 + name: 'Menu2Demo',
  155 + component: '/demo/level/Menu2',
  156 + meta: {
  157 + title: 'Menu2',
  158 + },
  159 + },
  160 + ],
  161 +};
115 export default [ 162 export default [
116 { 163 {
117 url: '/api/getMenuListById', 164 url: '/api/getMenuListById',
@@ -120,10 +167,10 @@ export default [ @@ -120,10 +167,10 @@ export default [
120 response: ({ query }) => { 167 response: ({ query }) => {
121 const { id } = query; 168 const { id } = query;
122 if (!id || id === '1') { 169 if (!id || id === '1') {
123 - return resultSuccess([dashboardRoute, authRoute]); 170 + return resultSuccess([dashboardRoute, authRoute, levelRoute]);
124 } 171 }
125 if (id === '2') { 172 if (id === '2') {
126 - return resultSuccess([dashboardRoute, authRoute1]); 173 + return resultSuccess([dashboardRoute, authRoute1, levelRoute]);
127 } 174 }
128 }, 175 },
129 }, 176 },
package.json
@@ -35,7 +35,7 @@ @@ -35,7 +35,7 @@
35 "qrcode": "^1.4.4", 35 "qrcode": "^1.4.4",
36 "sortablejs": "^1.12.0", 36 "sortablejs": "^1.12.0",
37 "vditor": "^3.7.0", 37 "vditor": "^3.7.0",
38 - "vue": "^3.0.3", 38 + "vue": "^3.0.4",
39 "vue-i18n": "^9.0.0-beta.8", 39 "vue-i18n": "^9.0.0-beta.8",
40 "vue-router": "^4.0.0-rc.6", 40 "vue-router": "^4.0.0-rc.6",
41 "vue-types": "^3.0.1", 41 "vue-types": "^3.0.1",
@@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
47 "devDependencies": { 47 "devDependencies": {
48 "@commitlint/cli": "^11.0.0", 48 "@commitlint/cli": "^11.0.0",
49 "@commitlint/config-conventional": "^11.0.0", 49 "@commitlint/config-conventional": "^11.0.0",
50 - "@iconify/json": "^1.1.266", 50 + "@iconify/json": "^1.1.267",
51 "@ls-lint/ls-lint": "^1.9.2", 51 "@ls-lint/ls-lint": "^1.9.2",
52 "@purge-icons/generated": "^0.4.1", 52 "@purge-icons/generated": "^0.4.1",
53 "@types/echarts": "^4.9.2", 53 "@types/echarts": "^4.9.2",
@@ -60,25 +60,25 @@ @@ -60,25 +60,25 @@
60 "@types/qrcode": "^1.3.5", 60 "@types/qrcode": "^1.3.5",
61 "@types/rollup-plugin-visualizer": "^2.6.0", 61 "@types/rollup-plugin-visualizer": "^2.6.0",
62 "@types/sortablejs": "^1.10.6", 62 "@types/sortablejs": "^1.10.6",
63 - "@types/yargs": "^15.0.10", 63 + "@types/yargs": "^15.0.11",
64 "@types/zxcvbn": "^4.4.0", 64 "@types/zxcvbn": "^4.4.0",
65 "@typescript-eslint/eslint-plugin": "^4.9.0", 65 "@typescript-eslint/eslint-plugin": "^4.9.0",
66 "@typescript-eslint/parser": "^4.9.0", 66 "@typescript-eslint/parser": "^4.9.0",
67 - "@vue/compiler-sfc": "^3.0.3", 67 + "@vue/compiler-sfc": "^3.0.4",
68 "@vuedx/typecheck": "^0.2.4-0", 68 "@vuedx/typecheck": "^0.2.4-0",
69 "@vuedx/typescript-plugin-vue": "^0.2.4-0", 69 "@vuedx/typescript-plugin-vue": "^0.2.4-0",
70 "autoprefixer": "^9.8.6", 70 "autoprefixer": "^9.8.6",
71 "commitizen": "^4.2.2", 71 "commitizen": "^4.2.2",
72 "conventional-changelog-cli": "^2.1.1", 72 "conventional-changelog-cli": "^2.1.1",
73 "conventional-changelog-custom-config": "^0.3.1", 73 "conventional-changelog-custom-config": "^0.3.1",
74 - "cross-env": "^7.0.2", 74 + "cross-env": "^7.0.3",
75 "dot-prop": "^6.0.1", 75 "dot-prop": "^6.0.1",
76 "dotenv": "^8.2.0", 76 "dotenv": "^8.2.0",
77 "eslint": "^7.14.0", 77 "eslint": "^7.14.0",
78 "eslint-config-prettier": "^6.15.0", 78 "eslint-config-prettier": "^6.15.0",
79 "eslint-plugin-prettier": "^3.1.4", 79 "eslint-plugin-prettier": "^3.1.4",
80 "eslint-plugin-vue": "^7.1.0", 80 "eslint-plugin-vue": "^7.1.0",
81 - "esno": "^0.2.4", 81 + "esno": "^0.3.0",
82 "fs-extra": "^9.0.1", 82 "fs-extra": "^9.0.1",
83 "globrex": "^0.1.2", 83 "globrex": "^0.1.2",
84 "husky": "^4.3.0", 84 "husky": "^4.3.0",
src/components/Breadcrumb/index.ts deleted 100644 → 0
1 -import BreadcrumbLib from './src/Breadcrumb.vue';  
2 -import BreadcrumbItemLib from './src/BreadcrumbItem.vue';  
3 -import { withInstall } from '../util';  
4 -  
5 -export const Breadcrumb = withInstall(BreadcrumbLib);  
6 -export const BreadcrumbItem = withInstall(BreadcrumbItemLib);  
src/components/Breadcrumb/src/Breadcrumb.vue deleted 100644 → 0
1 -<template>  
2 - <div ref="breadcrumbRef" class="breadcrumb">  
3 - <slot />  
4 - </div>  
5 -</template>  
6 -  
7 -<script lang="ts">  
8 - import { defineComponent, provide, ref } from 'vue';  
9 - import { propTypes } from '/@/utils/propTypes';  
10 -  
11 - export default defineComponent({  
12 - name: 'Breadcrumb',  
13 - props: {  
14 - separator: propTypes.string.def('/'),  
15 - separatorClass: propTypes.string,  
16 - },  
17 - setup(props) {  
18 - const breadcrumbRef = ref<Nullable<HTMLElement>>(null);  
19 -  
20 - provide('breadcrumb', props);  
21 -  
22 - return {  
23 - breadcrumbRef,  
24 - };  
25 - },  
26 - });  
27 -</script>  
28 -<style lang="less">  
29 - @import (reference) '../../../design/index.less';  
30 -  
31 - .breadcrumb {  
32 - .unselect();  
33 -  
34 - height: @header-height;  
35 - padding-right: 20px;  
36 - font-size: 13px;  
37 - line-height: @header-height;  
38 - // line-height: 1;  
39 -  
40 - &::after,  
41 - &::before {  
42 - display: table;  
43 - content: '';  
44 - }  
45 -  
46 - &::after {  
47 - clear: both;  
48 - }  
49 -  
50 - &__separator {  
51 - margin: 0 9px;  
52 - font-weight: 700;  
53 - color: @breadcrumb-item-normal-color;  
54 -  
55 - &[class*='icon'] {  
56 - margin: 0 6px;  
57 - font-weight: 400;  
58 - }  
59 - }  
60 -  
61 - &__item {  
62 - float: left;  
63 - }  
64 -  
65 - &__inner {  
66 - color: @breadcrumb-item-normal-color;  
67 -  
68 - &.is-link,  
69 - a {  
70 - font-weight: 500;  
71 - color: @text-color-base;  
72 - text-decoration: none;  
73 - transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);  
74 - }  
75 -  
76 - a:hover,  
77 - &.is-link:hover {  
78 - color: @primary-color;  
79 - cursor: pointer;  
80 - }  
81 - }  
82 -  
83 - &__item:last-child .breadcrumb__inner,  
84 - &__item:last-child &__inner a,  
85 - &__item:last-child &__inner a:hover,  
86 - &__item:last-child &__inner:hover {  
87 - font-weight: 400;  
88 - color: @breadcrumb-item-normal-color;  
89 - cursor: text;  
90 - }  
91 -  
92 - &__item:last-child &__separator {  
93 - display: none;  
94 - }  
95 - }  
96 -</style>  
src/components/Breadcrumb/src/BreadcrumbItem.vue deleted 100644 → 0
1 -<template>  
2 - <span class="breadcrumb__item">  
3 - <span ref="linkRef" :class="['breadcrumb__inner', to || isLink ? 'is-link' : '']">  
4 - <slot />  
5 - </span>  
6 - <i v-if="separatorClass" class="breadcrumb__separator" :class="separatorClass"></i>  
7 - <span v-else class="breadcrumb__separator">{{ separator }}</span>  
8 - </span>  
9 -</template>  
10 -  
11 -<script lang="ts">  
12 - import { defineComponent, inject, ref, onMounted, unref } from 'vue';  
13 - import { useRouter } from 'vue-router';  
14 - import { useEventListener } from '/@/hooks/event/useEventListener';  
15 -  
16 - import { propTypes } from '/@/utils/propTypes';  
17 -  
18 - export default defineComponent({  
19 - name: 'BreadcrumbItem',  
20 - props: {  
21 - to: propTypes.oneOfType([propTypes.string, propTypes.object]),  
22 - replace: propTypes.bool,  
23 - isLink: propTypes.bool,  
24 - },  
25 - setup(props) {  
26 - const linkRef = ref<Nullable<HTMLElement>>(null);  
27 -  
28 - const parent = inject('breadcrumb') as {  
29 - separator: string;  
30 - separatorClass: string;  
31 - };  
32 -  
33 - const { push, replace } = useRouter();  
34 -  
35 - onMounted(() => {  
36 - const link = unref(linkRef);  
37 - if (!link) return;  
38 - useEventListener({  
39 - el: link,  
40 - listener: () => {  
41 - const { to } = props;  
42 - if (!props.to) return;  
43 - props.replace ? replace(to) : push(to);  
44 - },  
45 - name: 'click',  
46 - wait: 0,  
47 - });  
48 - });  
49 -  
50 - return {  
51 - linkRef,  
52 - separator: parent.separator && parent.separator,  
53 - separatorClass: parent.separatorClass && parent.separatorClass,  
54 - };  
55 - },  
56 - });  
57 -</script>  
src/components/Menu/src/BasicMenu.tsx
@@ -36,6 +36,7 @@ import { getCurrentParentPath } from &#39;/@/router/menus&#39;; @@ -36,6 +36,7 @@ import { getCurrentParentPath } from &#39;/@/router/menus&#39;;
36 36
37 import { basicProps } from './props'; 37 import { basicProps } from './props';
38 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; 38 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  39 +import { REDIRECT_NAME } from '/@/router/constant';
39 export default defineComponent({ 40 export default defineComponent({
40 name: 'BasicMenu', 41 name: 'BasicMenu',
41 props: basicProps, 42 props: basicProps,
@@ -120,7 +121,7 @@ export default defineComponent({ @@ -120,7 +121,7 @@ export default defineComponent({
120 watch( 121 watch(
121 () => currentRoute.value.name, 122 () => currentRoute.value.name,
122 (name: string) => { 123 (name: string) => {
123 - if (name === 'Redirect') return; 124 + if (name === REDIRECT_NAME) return;
124 handleMenuChange(); 125 handleMenuChange();
125 props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath(); 126 props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath();
126 } 127 }
src/components/Menu/src/hooks/useOpenKeys.ts
@@ -4,7 +4,7 @@ import type { MenuState } from &#39;../types&#39;; @@ -4,7 +4,7 @@ import type { MenuState } from &#39;../types&#39;;
4 import type { Ref } from 'vue'; 4 import type { Ref } from 'vue';
5 5
6 import { unref } from 'vue'; 6 import { unref } from 'vue';
7 -import { getAllParentPath } from '/@/utils/helper/menuHelper'; 7 +import { getAllParentPath } from '/@/router/helper/menuHelper';
8 import { es6Unique } from '/@/utils'; 8 import { es6Unique } from '/@/utils';
9 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; 9 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
10 10
src/components/Menu/src/hooks/useSearchInput.ts
@@ -5,7 +5,7 @@ import type { Ref } from &#39;vue&#39;; @@ -5,7 +5,7 @@ import type { Ref } from &#39;vue&#39;;
5 import { isString } from '/@/utils/is'; 5 import { isString } from '/@/utils/is';
6 import { unref } from 'vue'; 6 import { unref } from 'vue';
7 import { es6Unique } from '/@/utils'; 7 import { es6Unique } from '/@/utils';
8 -import { getAllParentPath } from '/@/utils/helper/menuHelper'; 8 +import { getAllParentPath } from '/@/router/helper/menuHelper';
9 9
10 interface UseSearchInputOptions { 10 interface UseSearchInputOptions {
11 menuState: MenuState; 11 menuState: MenuState;
src/components/Table/src/components/TableSetting.vue
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 33
34 <Tooltip placement="top" v-if="getSetting.setting"> 34 <Tooltip placement="top" v-if="getSetting.setting">
35 <template #title> 35 <template #title>
36 - <span>{{ t('settingColumn') }}</span> 36 + <span>{{ t('component.table.settingColumn') }}</span>
37 </template> 37 </template>
38 <Popover 38 <Popover
39 placement="bottomLeft" 39 placement="bottomLeft"
@@ -58,9 +58,11 @@ @@ -58,9 +58,11 @@
58 v-model:checked="checkAll" 58 v-model:checked="checkAll"
59 @change="onCheckAllChange" 59 @change="onCheckAllChange"
60 > 60 >
61 - {{ t('settingColumnShow') }} 61 + {{ t('component.table.settingColumnShow') }}
62 </Checkbox> 62 </Checkbox>
63 - <a-button size="small" type="link" @click="reset"> {{ t('settingReset') }}</a-button> 63 + <a-button size="small" type="link" @click="reset">
  64 + {{ t('component.table.settingReset') }}</a-button
  65 + >
64 </div> 66 </div>
65 </template> 67 </template>
66 <SettingOutlined /> 68 <SettingOutlined />
@@ -69,7 +71,7 @@ @@ -69,7 +71,7 @@
69 71
70 <Tooltip placement="top" v-if="getSetting.fullScreen"> 72 <Tooltip placement="top" v-if="getSetting.fullScreen">
71 <template #title> 73 <template #title>
72 - <span>{{ t('settingFullScreen') }}</span> 74 + <span>{{ t('component.table.settingFullScreen') }}</span>
73 </template> 75 </template>
74 <FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" /> 76 <FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" />
75 <FullscreenExitOutlined @click="handleFullScreen" v-else /> 77 <FullscreenExitOutlined @click="handleFullScreen" v-else />
src/components/registerGlobComp.ts
@@ -33,6 +33,7 @@ import { @@ -33,6 +33,7 @@ import {
33 Empty, 33 Empty,
34 Avatar, 34 Avatar,
35 Menu, 35 Menu,
  36 + Breadcrumb,
36 } from 'ant-design-vue'; 37 } from 'ant-design-vue';
37 import { getApp } from '/@/setup/App'; 38 import { getApp } from '/@/setup/App';
38 39
@@ -55,6 +56,7 @@ export function registerGlobComp() { @@ -55,6 +56,7 @@ export function registerGlobComp() {
55 getApp() 56 getApp()
56 .use(Select) 57 .use(Select)
57 .use(Alert) 58 .use(Alert)
  59 + .use(Breadcrumb)
58 .use(Checkbox) 60 .use(Checkbox)
59 .use(DatePicker) 61 .use(DatePicker)
60 .use(Radio) 62 .use(Radio)
src/design/transition/breadcrumb.less deleted 100644 → 0
1 -.breadcrumb-enter-active,  
2 -.breadcrumb-leave-active {  
3 - transition: all 0.24s;  
4 -}  
5 -  
6 -.breadcrumb-enter-from,  
7 -.breadcrumb-leave-active {  
8 - opacity: 0;  
9 - transform: translateX(16px);  
10 -}  
11 -  
12 -.breadcrumb-move {  
13 - transition: all 0.38s;  
14 -}  
15 -  
16 -.breadcrumb-leave-active {  
17 - position: absolute;  
18 -}  
src/design/transition/index.less
@@ -4,4 +4,3 @@ @@ -4,4 +4,3 @@
4 @import './slide.less'; 4 @import './slide.less';
5 @import './scroll.less'; 5 @import './scroll.less';
6 @import './zoom.less'; 6 @import './zoom.less';
7 -@import './breadcrumb.less';  
src/enums/pageEnum.ts
@@ -2,7 +2,7 @@ export enum PageEnum { @@ -2,7 +2,7 @@ export enum PageEnum {
2 // basic login path 2 // basic login path
3 BASE_LOGIN = '/login', 3 BASE_LOGIN = '/login',
4 // basic home path 4 // basic home path
5 - BASE_HOME = '/dashboard', 5 + BASE_HOME = '/home',
6 // error page path 6 // error page path
7 ERROR_PAGE = '/exception', 7 ERROR_PAGE = '/exception',
8 // error log page path 8 // error log page path
src/hooks/web/usePage.ts
1 import { appStore } from '/@/store/modules/app'; 1 import { appStore } from '/@/store/modules/app';
2 import type { RouteLocationRaw } from 'vue-router'; 2 import type { RouteLocationRaw } from 'vue-router';
3 3
4 -import { useRouter } from 'vue-router';  
5 import { PageEnum } from '/@/enums/pageEnum'; 4 import { PageEnum } from '/@/enums/pageEnum';
6 import { isString } from '/@/utils/is'; 5 import { isString } from '/@/utils/is';
7 import { unref } from 'vue'; 6 import { unref } from 'vue';
8 7
  8 +import router from '/@/router';
  9 +
9 export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum }; 10 export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
10 11
11 function handleError(e: Error) { 12 function handleError(e: Error) {
@@ -18,7 +19,7 @@ function handleError(e: Error) { @@ -18,7 +19,7 @@ function handleError(e: Error) {
18 19
19 // page switch 20 // page switch
20 export function useGo() { 21 export function useGo() {
21 - const { push, replace } = useRouter(); 22 + const { push, replace } = router;
22 function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) { 23 function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
23 if (!opt) return; 24 if (!opt) return;
24 if (isString(opt)) { 25 if (isString(opt)) {
@@ -35,7 +36,7 @@ export function useGo() { @@ -35,7 +36,7 @@ export function useGo() {
35 * @description: redo current page 36 * @description: redo current page
36 */ 37 */
37 export const useRedo = () => { 38 export const useRedo = () => {
38 - const { push, currentRoute } = useRouter(); 39 + const { push, currentRoute } = router;
39 const { query, params } = currentRoute.value; 40 const { query, params } = currentRoute.value;
40 function redo() { 41 function redo() {
41 push({ 42 push({
src/hooks/web/usePermission.ts
@@ -7,13 +7,14 @@ import { userStore } from &#39;/@/store/modules/user&#39;; @@ -7,13 +7,14 @@ import { userStore } from &#39;/@/store/modules/user&#39;;
7 import { useTabs } from './useTabs'; 7 import { useTabs } from './useTabs';
8 8
9 import router, { resetRouter } from '/@/router'; 9 import router, { resetRouter } from '/@/router';
10 -import { RootRoute } from '/@/router/routes'; 10 +// import { RootRoute } from '/@/router/routes';
11 11
12 import { PermissionModeEnum } from '/@/enums/appEnum'; 12 import { PermissionModeEnum } from '/@/enums/appEnum';
13 import { RoleEnum } from '/@/enums/roleEnum'; 13 import { RoleEnum } from '/@/enums/roleEnum';
14 14
15 import { intersection } from 'lodash-es'; 15 import { intersection } from 'lodash-es';
16 import { isArray } from '/@/utils/is'; 16 import { isArray } from '/@/utils/is';
  17 +import { tabStore } from '/@/store/modules/tab';
17 18
18 // User permissions related operations 19 // User permissions related operations
19 export function usePermission() { 20 export function usePermission() {
@@ -27,8 +28,7 @@ export function usePermission() { @@ -27,8 +28,7 @@ export function usePermission() {
27 ? PermissionModeEnum.ROLE 28 ? PermissionModeEnum.ROLE
28 : PermissionModeEnum.BACK, 29 : PermissionModeEnum.BACK,
29 }); 30 });
30 - resume();  
31 - // location.reload(); 31 + location.reload();
32 } 32 }
33 33
34 /** 34 /**
@@ -36,18 +36,15 @@ export function usePermission() { @@ -36,18 +36,15 @@ export function usePermission() {
36 * @param id 36 * @param id
37 */ 37 */
38 async function resume(id?: string | number) { 38 async function resume(id?: string | number) {
  39 + tabStore.commitClearCache();
39 resetRouter(); 40 resetRouter();
40 const routes = await permissionStore.buildRoutesAction(id); 41 const routes = await permissionStore.buildRoutesAction(id);
41 routes.forEach((route) => { 42 routes.forEach((route) => {
42 - router.addRoute(RootRoute.name!, route as RouteRecordRaw); 43 + router.addRoute(route as RouteRecordRaw);
43 }); 44 });
44 permissionStore.commitLastBuildMenuTimeState(); 45 permissionStore.commitLastBuildMenuTimeState();
45 - const {  
46 - // closeAll,  
47 - closeOther,  
48 - } = useTabs();  
49 - // closeAll();  
50 - closeOther(); 46 + const { closeAll } = useTabs();
  47 + closeAll();
51 } 48 }
52 49
53 /** 50 /**
src/hooks/web/useTabs.ts
1 -import { TabItem, tabStore } from '/@/store/modules/tab'; 1 +import { tabStore } from '/@/store/modules/tab';
2 import { appStore } from '/@/store/modules/app'; 2 import { appStore } from '/@/store/modules/app';
3 3
4 -type RouteFn = (tabItem: TabItem) => void;  
5 -  
6 -interface TabFn {  
7 - refreshPageFn: RouteFn;  
8 - closeAllFn: Fn;  
9 - closeLeftFn: RouteFn;  
10 - closeRightFn: RouteFn;  
11 - closeOtherFn: RouteFn;  
12 - closeCurrentFn: RouteFn;  
13 -}  
14 -  
15 -let refreshPage: RouteFn;  
16 -let closeAll: Fn;  
17 -let closeLeft: RouteFn;  
18 -let closeRight: RouteFn;  
19 -let closeOther: RouteFn;  
20 -let closeCurrent: RouteFn;  
21 -  
22 -export let isInitUseTab = false;  
23 -  
24 export function useTabs() { 4 export function useTabs() {
25 - function initTabFn({  
26 - refreshPageFn,  
27 - closeAllFn,  
28 - closeLeftFn,  
29 - closeRightFn,  
30 - closeOtherFn,  
31 - closeCurrentFn,  
32 - }: TabFn) {  
33 - if (isInitUseTab) return;  
34 -  
35 - refreshPageFn && (refreshPage = refreshPageFn);  
36 - closeAllFn && (closeAll = closeAllFn);  
37 - closeLeftFn && (closeLeft = closeLeftFn);  
38 - closeRightFn && (closeRight = closeRightFn);  
39 - closeOtherFn && (closeOther = closeOtherFn);  
40 - closeCurrentFn && (closeCurrent = closeCurrentFn);  
41 - isInitUseTab = true;  
42 - }  
43 -  
44 - function resetCache() {  
45 - const def = undefined as any;  
46 - refreshPage = def;  
47 - closeAll = def;  
48 - closeLeft = def;  
49 - closeRight = def;  
50 - closeOther = def;  
51 - closeCurrent = def;  
52 - }  
53 -  
54 function canIUseFn(): boolean { 5 function canIUseFn(): boolean {
55 const { multiTabsSetting: { show } = {} } = appStore.getProjectConfig; 6 const { multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
56 if (!show) { 7 if (!show) {
57 - throw new Error('当前未开启多标签页,请在设置中打开!'); 8 + throw new Error('The multi-tab page is currently not open, please open it in the settings!');
58 } 9 }
59 return !!show; 10 return !!show;
60 } 11 }
61 12
62 return { 13 return {
63 - initTabFn,  
64 - refreshPage: () => canIUseFn() && refreshPage(tabStore.getCurrentTab),  
65 - closeAll: () => canIUseFn() && closeAll(),  
66 - closeLeft: () => canIUseFn() && closeLeft(tabStore.getCurrentTab),  
67 - closeRight: () => canIUseFn() && closeRight(tabStore.getCurrentTab),  
68 - closeOther: () => canIUseFn() && closeOther(tabStore.getCurrentTab),  
69 - closeCurrent: () => canIUseFn() && closeCurrent(tabStore.getCurrentTab),  
70 - resetCache: () => canIUseFn() && resetCache(), 14 + refreshPage: () => canIUseFn() && tabStore.commitRedoPage(),
  15 + closeAll: () => canIUseFn() && tabStore.closeAllTabAction(),
  16 + closeLeft: () => canIUseFn() && tabStore.closeLeftTabAction(tabStore.getCurrentTab),
  17 + closeRight: () => canIUseFn() && tabStore.closeRightTabAction(tabStore.getCurrentTab),
  18 + closeOther: () => canIUseFn() && tabStore.closeOtherTabAction(tabStore.getCurrentTab),
  19 + closeCurrent: () => canIUseFn() && tabStore.closeTabAction(tabStore.getCurrentTab),
71 }; 20 };
72 } 21 }
src/layouts/default/content/index.tsx
@@ -3,11 +3,9 @@ import &#39;./index.less&#39;; @@ -3,11 +3,9 @@ import &#39;./index.less&#39;;
3 import { defineComponent, unref } from 'vue'; 3 import { defineComponent, unref } from 'vue';
4 import { Loading } from '/@/components/Loading'; 4 import { Loading } from '/@/components/Loading';
5 5
6 -import { RouterView } from 'vue-router';  
7 -  
8 import { useRootSetting } from '/@/hooks/setting/useRootSetting'; 6 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
9 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; 7 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
10 - 8 +import PageLayout from '/@/layouts/page/index.vue';
11 export default defineComponent({ 9 export default defineComponent({
12 name: 'LayoutContent', 10 name: 'LayoutContent',
13 setup() { 11 setup() {
@@ -20,7 +18,7 @@ export default defineComponent({ @@ -20,7 +18,7 @@ export default defineComponent({
20 {unref(getOpenPageLoading) && ( 18 {unref(getOpenPageLoading) && (
21 <Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" /> 19 <Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" />
22 )} 20 )}
23 - <RouterView /> 21 + <PageLayout />
24 </div> 22 </div>
25 ); 23 );
26 }; 24 };
src/layouts/default/header/LayoutBreadcrumb.tsx deleted 100644 → 0
1 -import type { AppRouteRecordRaw } from '/@/router/types';  
2 -import type { RouteLocationMatched } from 'vue-router';  
3 -import type { PropType } from 'vue';  
4 -  
5 -import { defineComponent, TransitionGroup, unref, watch, ref } from 'vue';  
6 -import Icon from '/@/components/Icon';  
7 -  
8 -import { Breadcrumb, BreadcrumbItem } from '/@/components/Breadcrumb';  
9 -  
10 -import { useRouter } from 'vue-router';  
11 -  
12 -import { isBoolean } from '/@/utils/is';  
13 -import { compile } from 'path-to-regexp';  
14 -  
15 -import router from '/@/router';  
16 -  
17 -import { PageEnum } from '/@/enums/pageEnum';  
18 -import { useI18n } from '/@/hooks/web/useI18n';  
19 -  
20 -export default defineComponent({  
21 - name: 'BasicBreadcrumb',  
22 - props: {  
23 - showIcon: {  
24 - type: Boolean as PropType<boolean>,  
25 - default: false,  
26 - },  
27 - },  
28 - setup(props) {  
29 - const itemList = ref<AppRouteRecordRaw[]>([]);  
30 -  
31 - const { currentRoute, push } = useRouter();  
32 - const { t } = useI18n();  
33 - watch(  
34 - () => currentRoute.value,  
35 - () => {  
36 - if (unref(currentRoute).name === 'Redirect') return;  
37 - getBreadcrumb();  
38 - },  
39 - { immediate: true }  
40 - );  
41 -  
42 - function getBreadcrumb() {  
43 - const { matched } = unref(currentRoute);  
44 - const matchedList = matched.filter((item) => item.meta && item.meta.title).slice(1);  
45 - const firstItem = matchedList[0];  
46 - const ret = getHomeRoute(firstItem);  
47 - if (!isBoolean(ret)) {  
48 - matchedList.unshift(ret);  
49 - }  
50 - itemList.value = ((matchedList as any) as AppRouteRecordRaw[]).filter(  
51 - (item) => item.meta && item.meta.title && !item.meta.hideBreadcrumb  
52 - );  
53 - }  
54 -  
55 - function getHomeRoute(firstItem: RouteLocationMatched) {  
56 - if (!firstItem || !firstItem.name) return false;  
57 - const routes = router.getRoutes();  
58 - const homeRoute = routes.find((item) => item.path === PageEnum.BASE_HOME);  
59 - if (!homeRoute) return false;  
60 - if (homeRoute.name === firstItem.name) return false;  
61 - return homeRoute;  
62 - }  
63 -  
64 - function pathCompile(path: string) {  
65 - const { params } = unref(currentRoute);  
66 - const toPath = compile(path);  
67 - return toPath(params);  
68 - }  
69 -  
70 - function handleItemClick(item: AppRouteRecordRaw) {  
71 - const { redirect, path, meta } = item;  
72 - if (meta.disabledRedirect) return;  
73 - if (redirect) {  
74 - push(redirect as string);  
75 - return;  
76 - }  
77 - return push(pathCompile(path));  
78 - }  
79 -  
80 - function renderItemContent(item: AppRouteRecordRaw) {  
81 - return (  
82 - <>  
83 - {props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && (  
84 - <Icon  
85 - icon={item.meta.icon}  
86 - class="icon mr-1 "  
87 - style={{  
88 - marginBottom: '2px',  
89 - }}  
90 - />  
91 - )}  
92 - {t(item.meta.title)}  
93 - </>  
94 - );  
95 - }  
96 -  
97 - function renderBreadcrumbItemList() {  
98 - return unref(itemList).map((item) => {  
99 - const isLink =  
100 - (!!item.redirect && !item.meta.disabledRedirect) ||  
101 - !item.children ||  
102 - item.children.length === 0;  
103 -  
104 - return (  
105 - <BreadcrumbItem  
106 - key={item.path}  
107 - isLink={isLink}  
108 - onClick={handleItemClick.bind(null, item)}  
109 - >  
110 - {() => renderItemContent(item as AppRouteRecordRaw)}  
111 - </BreadcrumbItem>  
112 - );  
113 - });  
114 - }  
115 -  
116 - function renderBreadcrumbDefault() {  
117 - return (  
118 - <TransitionGroup name="breadcrumb">{() => renderBreadcrumbItemList()}</TransitionGroup>  
119 - );  
120 - }  
121 -  
122 - return () => (  
123 - <Breadcrumb class={['layout-breadcrumb', unref(itemList).length === 0 ? 'hidden' : '']}>  
124 - {() => renderBreadcrumbDefault()}  
125 - </Breadcrumb>  
126 - );  
127 - },  
128 -});  
src/layouts/default/header/LayoutBreadcrumb.vue 0 → 100644
  1 +<template>
  2 + <div class="layout-breadcrumb">
  3 + <a-breadcrumb :routes="routes">
  4 + <template #itemRender="{ route, routes }">
  5 + <Icon :icon="route.meta.icon" v-if="showIcon && route.meta.icon" />
  6 + <span v-if="routes.indexOf(route) === routes.length - 1">
  7 + {{ t(route.meta.title) }}
  8 + </span>
  9 + <router-link v-else :to="route.path">
  10 + {{ t(route.meta.title) }}
  11 + </router-link>
  12 + </template>
  13 + </a-breadcrumb>
  14 + </div>
  15 +</template>
  16 +<script lang="ts">
  17 + import { PropType } from 'vue';
  18 + import { defineComponent, ref, toRaw, watchEffect } from 'vue';
  19 + import { useI18n } from 'vue-i18n';
  20 +
  21 + import type { RouteLocationMatched } from 'vue-router';
  22 + import { useRouter } from 'vue-router';
  23 + import { filter } from '/@/utils/helper/treeHelper';
  24 + import { REDIRECT_NAME } from '/@/router/constant';
  25 + import Icon from '/@/components/Icon';
  26 +
  27 + import { HomeOutlined } from '@ant-design/icons-vue';
  28 + import { PageEnum } from '/@/enums/pageEnum';
  29 + export default defineComponent({
  30 + name: 'LayoutBreadcrumb',
  31 + components: { HomeOutlined, Icon },
  32 + props: {
  33 + showIcon: {
  34 + type: Boolean as PropType<boolean>,
  35 + default: false,
  36 + },
  37 + },
  38 + setup() {
  39 + const routes = ref<RouteLocationMatched[]>([]);
  40 + const { currentRoute } = useRouter();
  41 +
  42 + const { t } = useI18n();
  43 + watchEffect(() => {
  44 + if (currentRoute.value.name === REDIRECT_NAME) {
  45 + return;
  46 + }
  47 + const matched = currentRoute.value.matched;
  48 + if (!matched || matched.length === 0) return;
  49 +
  50 + let breadcrumbList = filter(toRaw(matched), (item) => {
  51 + if (!item.meta) {
  52 + return false;
  53 + }
  54 + const { title, hideBreadcrumb } = item.meta;
  55 + if (!title || hideBreadcrumb) {
  56 + return false;
  57 + }
  58 + return true;
  59 + });
  60 +
  61 + const filterBreadcrumbList = breadcrumbList.filter(
  62 + (item) => item.path !== PageEnum.BASE_HOME
  63 + );
  64 +
  65 + if (filterBreadcrumbList.length === breadcrumbList.length) {
  66 + filterBreadcrumbList.unshift({
  67 + path: PageEnum.BASE_HOME,
  68 + meta: {
  69 + title: t('layout.header.home'),
  70 + },
  71 + });
  72 + }
  73 + routes.value = filterBreadcrumbList;
  74 + });
  75 +
  76 + return { routes, t };
  77 + },
  78 + });
  79 +</script>
src/layouts/default/header/LayoutHeader.tsx
@@ -9,7 +9,7 @@ import { Layout, Tooltip, Badge } from &#39;ant-design-vue&#39;; @@ -9,7 +9,7 @@ import { Layout, Tooltip, Badge } from &#39;ant-design-vue&#39;;
9 import { AppLogo } from '/@/components/Application'; 9 import { AppLogo } from '/@/components/Application';
10 import UserDropdown from './UserDropdown'; 10 import UserDropdown from './UserDropdown';
11 import LayoutMenu from '../menu'; 11 import LayoutMenu from '../menu';
12 -import LayoutBreadcrumb from './LayoutBreadcrumb'; 12 +import LayoutBreadcrumb from './LayoutBreadcrumb.vue';
13 import LockAction from '../lock/LockAction'; 13 import LockAction from '../lock/LockAction';
14 import LayoutTrigger from '../LayoutTrigger'; 14 import LayoutTrigger from '../LayoutTrigger';
15 import NoticeAction from './notice/NoticeActionItem.vue'; 15 import NoticeAction from './notice/NoticeActionItem.vue';
src/layouts/default/header/LayoutMultipleHeader.less
1 .multiple-tab-header { 1 .multiple-tab-header {
2 flex: 0 0 auto; 2 flex: 0 0 auto;
  3 + margin-left: -1px;
3 4
4 &.fixed { 5 &.fixed {
5 position: fixed; 6 position: fixed;
src/layouts/default/header/index.less
@@ -21,11 +21,15 @@ @@ -21,11 +21,15 @@
21 21
22 &__left { 22 &__left {
23 display: flex; 23 display: flex;
  24 + height: 100%;
24 align-items: center; 25 align-items: center;
25 26
26 .layout-trigger { 27 .layout-trigger {
  28 + display: flex;
  29 + height: 100%;
27 padding: 1px 10px 0 16px; 30 padding: 1px 10px 0 16px;
28 cursor: pointer; 31 cursor: pointer;
  32 + align-items: center;
29 33
30 .anticon { 34 .anticon {
31 font-size: 17px; 35 font-size: 17px;
@@ -49,12 +53,22 @@ @@ -49,12 +53,22 @@
49 } 53 }
50 54
51 .layout-breadcrumb { 55 .layout-breadcrumb {
  56 + display: flex;
52 padding: 0 8px; 57 padding: 0 8px;
  58 + align-items: center;
  59 +
  60 + .ant-breadcrumb-link {
  61 + .anticon {
  62 + margin-right: 4px;
  63 + margin-bottom: 2px;
  64 + }
  65 + }
53 } 66 }
54 } 67 }
55 68
56 &__content { 69 &__content {
57 display: flex; 70 display: flex;
  71 + height: 100%;
58 flex-grow: 1; 72 flex-grow: 1;
59 align-items: center; 73 align-items: center;
60 } 74 }
@@ -72,6 +86,24 @@ @@ -72,6 +86,24 @@
72 } 86 }
73 } 87 }
74 88
  89 + .layout-breadcrumb {
  90 + .ant-breadcrumb-link {
  91 + color: @breadcrumb-item-normal-color;
  92 +
  93 + a {
  94 + color: @text-color-base;
  95 +
  96 + &:hover {
  97 + color: @primary-color;
  98 + }
  99 + }
  100 + }
  101 +
  102 + .ant-breadcrumb-separator {
  103 + color: @breadcrumb-item-normal-color;
  104 + }
  105 + }
  106 +
75 .layout-header__logo { 107 .layout-header__logo {
76 height: @header-height; 108 height: @header-height;
77 color: @text-color-base; 109 color: @text-color-base;
@@ -152,20 +184,22 @@ @@ -152,20 +184,22 @@
152 } 184 }
153 } 185 }
154 186
155 - .breadcrumb {  
156 - &__item:last-child .breadcrumb__inner,  
157 - &__item:last-child &__inner a,  
158 - &__item:last-child &__inner a:hover,  
159 - &__item:last-child &__inner:hover {  
160 - font-weight: 400; 187 + .layout-breadcrumb {
  188 + .ant-breadcrumb-link {
161 color: rgba(255, 255, 255, 0.6); 189 color: rgba(255, 255, 255, 0.6);
162 - cursor: text; 190 +
  191 + a {
  192 + color: rgba(255, 255, 255, 0.8);
  193 +
  194 + &:hover {
  195 + color: @white;
  196 + }
  197 + }
163 } 198 }
164 199
165 - &__inner,  
166 - &__inner.is-link,  
167 - &__separator {  
168 - color: @white; 200 + .ant-breadcrumb-separator,
  201 + .anticon {
  202 + color: rgba(255, 255, 255, 0.8);
169 } 203 }
170 } 204 }
171 } 205 }
src/layouts/default/multitabs/TabContent.tsx
1 import type { PropType } from 'vue'; 1 import type { PropType } from 'vue';
  2 +import { Dropdown } from '/@/components/Dropdown/index';
2 3
3 -import { defineComponent, unref, computed, FunctionalComponent } from 'vue'; 4 +import { defineComponent, unref, FunctionalComponent } from 'vue';
4 5
5 -import { TabItem, tabStore } from '/@/store/modules/tab';  
6 -import { getScaleAction, TabContentProps } from './data'; 6 +import { TabContentProps } from './types';
7 7
8 -import { Dropdown } from '/@/components/Dropdown/index';  
9 import { RightOutlined } from '@ant-design/icons-vue'; 8 import { RightOutlined } from '@ant-design/icons-vue';
10 9
11 -import { TabContentEnum } from './data'; 10 +import { TabContentEnum } from './types';
  11 +
12 import { useTabDropdown } from './useTabDropdown'; 12 import { useTabDropdown } from './useTabDropdown';
13 -import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';  
14 -import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';  
15 -import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';  
16 import { useI18n } from '/@/hooks/web/useI18n'; 13 import { useI18n } from '/@/hooks/web/useI18n';
17 14
  15 +import { RouteLocationNormalized } from 'vue-router';
  16 +
18 const { t: titleT } = useI18n(); 17 const { t: titleT } = useI18n();
19 18
20 const ExtraContent: FunctionalComponent = () => { 19 const ExtraContent: FunctionalComponent = () => {
@@ -25,21 +24,13 @@ const ExtraContent: FunctionalComponent = () =&gt; { @@ -25,21 +24,13 @@ const ExtraContent: FunctionalComponent = () =&gt; {
25 ); 24 );
26 }; 25 };
27 26
28 -const TabContent: FunctionalComponent<{ tabItem: TabItem }> = (props) => { 27 +const TabContent: FunctionalComponent<{ tabItem: RouteLocationNormalized; handler: Fn }> = (
  28 + props
  29 +) => {
29 const { tabItem: { meta } = {} } = props; 30 const { tabItem: { meta } = {} } = props;
30 31
31 - function handleContextMenu(e: Event) {  
32 - if (!props.tabItem) return;  
33 - const tableItem = props.tabItem;  
34 - e?.preventDefault();  
35 - const index = unref(tabStore.getTabsState).findIndex((tab) => tab.path === tableItem.path);  
36 -  
37 - tabStore.commitCurrentContextMenuIndexState(index);  
38 - tabStore.commitCurrentContextMenuState(props.tabItem);  
39 - }  
40 -  
41 return ( 32 return (
42 - <div class={`multiple-tabs-content__content `} onContextmenu={handleContextMenu}> 33 + <div class={`multiple-tabs-content__content `} onContextmenu={props.handler(props.tabItem)}>
43 <span class="ml-1">{meta && titleT(meta.title)}</span> 34 <span class="ml-1">{meta && titleT(meta.title)}</span>
44 </div> 35 </div>
45 ); 36 );
@@ -49,7 +40,7 @@ export default defineComponent({ @@ -49,7 +40,7 @@ export default defineComponent({
49 name: 'TabContent', 40 name: 'TabContent',
50 props: { 41 props: {
51 tabItem: { 42 tabItem: {
52 - type: Object as PropType<TabItem>, 43 + type: Object as PropType<RouteLocationNormalized>,
53 default: null, 44 default: null,
54 }, 45 },
55 46
@@ -59,36 +50,27 @@ export default defineComponent({ @@ -59,36 +50,27 @@ export default defineComponent({
59 }, 50 },
60 }, 51 },
61 setup(props) { 52 setup(props) {
62 - const { t } = useI18n();  
63 - const { getShowMenu } = useMenuSetting();  
64 - const { getShowHeader } = useHeaderSetting();  
65 - const { getShowQuick } = useMultipleTabSetting();  
66 -  
67 - const getIsScale = computed(() => {  
68 - return !unref(getShowMenu) && !unref(getShowHeader);  
69 - });  
70 -  
71 - const getIsTab = computed(() => {  
72 - return !unref(getShowQuick) ? true : props.type === TabContentEnum.TAB_TYPE;  
73 - });  
74 -  
75 - const { getDropMenuList, handleMenuEvent } = useTabDropdown(props as TabContentProps); 53 + const {
  54 + getDropMenuList,
  55 + handleMenuEvent,
  56 + handleContextMenu,
  57 + getTrigger,
  58 + isTabs,
  59 + } = useTabDropdown(props as TabContentProps);
76 60
77 return () => { 61 return () => {
78 - const scaleAction = getScaleAction(  
79 - unref(getIsScale) ? t('layout.multipleTab.putAway') : t('layout.multipleTab.unfold'),  
80 - unref(getIsScale)  
81 - );  
82 - const dropMenuList = unref(getDropMenuList) || [];  
83 -  
84 - const isTab = unref(getIsTab);  
85 return ( 62 return (
86 <Dropdown 63 <Dropdown
87 - dropMenuList={!isTab ? [scaleAction, ...dropMenuList] : dropMenuList}  
88 - trigger={isTab ? ['contextmenu'] : ['click']} 64 + dropMenuList={unref(getDropMenuList)}
  65 + trigger={unref(getTrigger)}
89 onMenuEvent={handleMenuEvent} 66 onMenuEvent={handleMenuEvent}
90 > 67 >
91 - {() => (isTab ? <TabContent tabItem={props.tabItem} /> : <ExtraContent />)} 68 + {() => {
  69 + if (!unref(isTabs)) {
  70 + return <ExtraContent />;
  71 + }
  72 + return <TabContent handler={handleContextMenu} tabItem={props.tabItem} />;
  73 + }}
92 </Dropdown> 74 </Dropdown>
93 ); 75 );
94 }; 76 };
src/layouts/default/multitabs/data.ts deleted 100644 → 0
1 -import { DropMenu } from '/@/components/Dropdown/index';  
2 -import { AppRouteRecordRaw } from '/@/router/types';  
3 -import type { TabItem } from '/@/store/modules/tab';  
4 -  
5 -import { useI18n } from '/@/hooks/web/useI18n';  
6 -  
7 -const { t } = useI18n();  
8 -  
9 -export enum TabContentEnum {  
10 - TAB_TYPE,  
11 - EXTRA_TYPE,  
12 -}  
13 -  
14 -export interface TabContentProps {  
15 - tabItem: TabItem | AppRouteRecordRaw;  
16 - type?: TabContentEnum;  
17 - trigger?: Array<'click' | 'hover' | 'contextmenu'>;  
18 -}  
19 -  
20 -/**  
21 - * @description: 右键:下拉菜单文字  
22 - */  
23 -export enum MenuEventEnum {  
24 - // 刷新  
25 - REFRESH_PAGE,  
26 - // 关闭当前  
27 - CLOSE_CURRENT,  
28 - // 关闭左侧  
29 - CLOSE_LEFT,  
30 - // 关闭右侧  
31 - CLOSE_RIGHT,  
32 - // 关闭其他  
33 - CLOSE_OTHER,  
34 - // 关闭所有  
35 - CLOSE_ALL,  
36 - // 放大  
37 - SCALE,  
38 -}  
39 -  
40 -export function getActions() {  
41 - const REFRESH_PAGE: DropMenu = {  
42 - icon: 'ant-design:reload-outlined',  
43 - event: MenuEventEnum.REFRESH_PAGE,  
44 - text: t('layout.multipleTab.redo'),  
45 - disabled: false,  
46 - };  
47 - const CLOSE_CURRENT: DropMenu = {  
48 - icon: 'ant-design:close-outlined',  
49 - event: MenuEventEnum.CLOSE_CURRENT,  
50 - text: t('layout.multipleTab.close'),  
51 - disabled: false,  
52 - divider: true,  
53 - };  
54 - const CLOSE_LEFT: DropMenu = {  
55 - icon: 'ant-design:pic-left-outlined',  
56 - event: MenuEventEnum.CLOSE_LEFT,  
57 - text: t('layout.multipleTab.closeLeft'),  
58 - disabled: false,  
59 - divider: false,  
60 - };  
61 - const CLOSE_RIGHT: DropMenu = {  
62 - icon: 'ant-design:pic-right-outlined',  
63 - event: MenuEventEnum.CLOSE_RIGHT,  
64 - text: t('layout.multipleTab.closeRight'),  
65 - disabled: false,  
66 - divider: true,  
67 - };  
68 - const CLOSE_OTHER: DropMenu = {  
69 - icon: 'ant-design:pic-center-outlined',  
70 - event: MenuEventEnum.CLOSE_OTHER,  
71 - text: t('layout.multipleTab.closeOther'),  
72 - disabled: false,  
73 - };  
74 - const CLOSE_ALL: DropMenu = {  
75 - icon: 'ant-design:line-outlined',  
76 - event: MenuEventEnum.CLOSE_ALL,  
77 - text: t('layout.multipleTab.closeAll'),  
78 - disabled: false,  
79 - };  
80 - return [REFRESH_PAGE, CLOSE_CURRENT, CLOSE_LEFT, CLOSE_RIGHT, CLOSE_OTHER, CLOSE_ALL];  
81 -}  
82 -  
83 -export function getScaleAction(text: string, isZoom = false) {  
84 - return {  
85 - icon: isZoom ? 'codicon:screen-normal' : 'codicon:screen-full',  
86 - event: MenuEventEnum.SCALE,  
87 - text: text,  
88 - disabled: false,  
89 - };  
90 -}  
src/layouts/default/multitabs/index.tsx
1 import './index.less'; 1 import './index.less';
2 2
3 -import type { TabContentProps } from './data';  
4 -import type { TabItem } from '/@/store/modules/tab';  
5 -import type { AppRouteRecordRaw } from '/@/router/types';  
6 -  
7 -import { defineComponent, watch, computed, unref, ref, onMounted, nextTick } from 'vue';  
8 -import Sortable from 'sortablejs'; 3 +import type { TabContentProps } from './types';
9 4
  5 +import { defineComponent, watch, computed, unref, ref } from 'vue';
10 import { useRouter } from 'vue-router'; 6 import { useRouter } from 'vue-router';
11 7
12 import { Tabs } from 'ant-design-vue'; 8 import { Tabs } from 'ant-design-vue';
@@ -14,15 +10,12 @@ import TabContent from &#39;./TabContent&#39;; @@ -14,15 +10,12 @@ import TabContent from &#39;./TabContent&#39;;
14 10
15 import { useGo } from '/@/hooks/web/usePage'; 11 import { useGo } from '/@/hooks/web/usePage';
16 12
17 -import { TabContentEnum } from './data'; 13 +import { TabContentEnum } from './types';
18 14
19 import { tabStore } from '/@/store/modules/tab'; 15 import { tabStore } from '/@/store/modules/tab';
20 import { userStore } from '/@/store/modules/user'; 16 import { userStore } from '/@/store/modules/user';
21 17
22 -import { closeTab } from './useTabDropdown';  
23 -import { initAffixTabs } from './useMultipleTabs';  
24 -import { isNullAndUnDef } from '/@/utils/is';  
25 -import { useProjectSetting } from '/@/hooks/setting'; 18 +import { initAffixTabs, useTabsDrag } from './useMultipleTabs';
26 19
27 export default defineComponent({ 20 export default defineComponent({
28 name: 'MultipleTabs', 21 name: 'MultipleTabs',
@@ -31,28 +24,25 @@ export default defineComponent({ @@ -31,28 +24,25 @@ export default defineComponent({
31 24
32 const affixTextList = initAffixTabs(); 25 const affixTextList = initAffixTabs();
33 26
34 - const go = useGo(); 27 + useTabsDrag(affixTextList);
35 28
36 - const { multiTabsSetting } = useProjectSetting(); 29 + const go = useGo();
37 30
38 const { currentRoute } = useRouter(); 31 const { currentRoute } = useRouter();
39 32
40 const getTabsState = computed(() => tabStore.getTabsState); 33 const getTabsState = computed(() => tabStore.getTabsState);
41 34
42 - // If you monitor routing changes, tab switching will be stuck. So setting this method  
43 watch( 35 watch(
44 - () => tabStore.getLastChangeRouteState, 36 + () => tabStore.getLastChangeRouteState?.path,
45 () => { 37 () => {
46 const lastChangeRoute = unref(tabStore.getLastChangeRouteState); 38 const lastChangeRoute = unref(tabStore.getLastChangeRouteState);
47 -  
48 if (!lastChangeRoute || !userStore.getTokenState) return; 39 if (!lastChangeRoute || !userStore.getTokenState) return;
49 -  
50 - const { path, fullPath } = lastChangeRoute as AppRouteRecordRaw; 40 + const { path, fullPath } = lastChangeRoute;
51 const p = fullPath || path; 41 const p = fullPath || path;
52 if (activeKeyRef.value !== p) { 42 if (activeKeyRef.value !== p) {
53 activeKeyRef.value = p; 43 activeKeyRef.value = p;
54 } 44 }
55 - tabStore.commitAddTab(lastChangeRoute); 45 + tabStore.addTabAction(lastChangeRoute);
56 }, 46 },
57 { 47 {
58 immediate: true, 48 immediate: true,
@@ -67,22 +57,19 @@ export default defineComponent({ @@ -67,22 +57,19 @@ export default defineComponent({
67 // Close the current tab 57 // Close the current tab
68 function handleEdit(targetKey: string) { 58 function handleEdit(targetKey: string) {
69 // Added operation to hide, currently only use delete operation 59 // Added operation to hide, currently only use delete operation
70 - const index = unref(getTabsState).findIndex(  
71 - (item) => (item.fullPath || item.path) === targetKey  
72 - );  
73 - index !== -1 && closeTab(unref(getTabsState)[index]); 60 + tabStore.closeTabByKeyAction(targetKey);
74 } 61 }
75 62
76 function renderQuick() { 63 function renderQuick() {
77 const tabContentProps: TabContentProps = { 64 const tabContentProps: TabContentProps = {
78 - tabItem: (currentRoute as unknown) as AppRouteRecordRaw, 65 + tabItem: currentRoute.value,
79 type: TabContentEnum.EXTRA_TYPE, 66 type: TabContentEnum.EXTRA_TYPE,
80 }; 67 };
81 - return <TabContent {...(tabContentProps as any)} />; 68 + return <TabContent {...tabContentProps} />;
82 } 69 }
83 70
84 function renderTabs() { 71 function renderTabs() {
85 - return unref(getTabsState).map((item: TabItem) => { 72 + return unref(getTabsState).map((item) => {
86 const key = item.query ? item.fullPath : item.path; 73 const key = item.query ? item.fullPath : item.path;
87 const closable = !(item && item.meta && item.meta.affix); 74 const closable = !(item && item.meta && item.meta.affix);
88 75
@@ -97,40 +84,6 @@ export default defineComponent({ @@ -97,40 +84,6 @@ export default defineComponent({
97 }); 84 });
98 } 85 }
99 86
100 - function initSortableTabs() {  
101 - if (!multiTabsSetting.canDrag) return;  
102 - nextTick(() => {  
103 - const el = document.querySelectorAll(  
104 - '.multiple-tabs .ant-tabs-nav > div'  
105 - )?.[0] as HTMLElement;  
106 -  
107 - if (!el) return;  
108 - Sortable.create(el, {  
109 - animation: 500,  
110 - delay: 400,  
111 - delayOnTouchOnly: true,  
112 - filter: (e: ChangeEvent) => {  
113 - const text = e?.target?.innerText;  
114 - if (!text) return false;  
115 - return affixTextList.includes(text);  
116 - },  
117 - onEnd: (evt) => {  
118 - const { oldIndex, newIndex } = evt;  
119 -  
120 - if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {  
121 - return;  
122 - }  
123 -  
124 - tabStore.commitSortTabs({ oldIndex, newIndex });  
125 - },  
126 - });  
127 - });  
128 - }  
129 -  
130 - onMounted(() => {  
131 - initSortableTabs();  
132 - });  
133 -  
134 return () => { 87 return () => {
135 const slots = { 88 const slots = {
136 default: () => renderTabs(), 89 default: () => renderTabs(),
src/layouts/default/multitabs/types.ts 0 → 100644
  1 +import type { DropMenu } from '/@/components/Dropdown/index';
  2 +import type { RouteLocationNormalized } from 'vue-router';
  3 +
  4 +export enum TabContentEnum {
  5 + TAB_TYPE,
  6 + EXTRA_TYPE,
  7 +}
  8 +
  9 +export type { DropMenu };
  10 +
  11 +export interface TabContentProps {
  12 + tabItem: RouteLocationNormalized;
  13 + type?: TabContentEnum;
  14 + trigger?: ('click' | 'hover' | 'contextmenu')[];
  15 +}
  16 +
  17 +/**
  18 + * @description: 右键:下拉菜单文字
  19 + */
  20 +export enum MenuEventEnum {
  21 + // 刷新
  22 + REFRESH_PAGE,
  23 + // 关闭当前
  24 + CLOSE_CURRENT,
  25 + // 关闭左侧
  26 + CLOSE_LEFT,
  27 + // 关闭右侧
  28 + CLOSE_RIGHT,
  29 + // 关闭其他
  30 + CLOSE_OTHER,
  31 + // 关闭所有
  32 + CLOSE_ALL,
  33 + // 放大
  34 + SCALE,
  35 +}
src/layouts/default/multitabs/useMultipleTabs.ts
1 -import { toRaw, ref } from 'vue'; 1 +import Sortable from 'sortablejs';
  2 +import { toRaw, ref, nextTick, onMounted } from 'vue';
  3 +import { RouteLocationNormalized } from 'vue-router';
  4 +import { useProjectSetting } from '/@/hooks/setting';
2 import router from '/@/router'; 5 import router from '/@/router';
3 -import { AppRouteRecordRaw } from '/@/router/types';  
4 -import { TabItem, tabStore } from '/@/store/modules/tab'; 6 +import { tabStore } from '/@/store/modules/tab';
  7 +import { isNullAndUnDef } from '/@/utils/is';
5 8
6 -export function initAffixTabs() {  
7 - const affixList = ref<TabItem[]>([]); 9 +export function initAffixTabs(): string[] {
  10 + const affixList = ref<RouteLocationNormalized[]>([]);
8 /** 11 /**
9 * @description: Filter all fixed routes 12 * @description: Filter all fixed routes
10 */ 13 */
11 - function filterAffixTabs(routes: AppRouteRecordRaw[]) {  
12 - const tabs: TabItem[] = []; 14 + function filterAffixTabs(routes: RouteLocationNormalized[]) {
  15 + const tabs: RouteLocationNormalized[] = [];
13 routes && 16 routes &&
14 routes.forEach((route) => { 17 routes.forEach((route) => {
15 if (route.meta && route.meta.affix) { 18 if (route.meta && route.meta.affix) {
16 - tabs.push(toRaw(route) as TabItem); 19 + tabs.push(toRaw(route));
17 } 20 }
18 }); 21 });
19 return tabs; 22 return tabs;
@@ -23,10 +26,14 @@ export function initAffixTabs() { @@ -23,10 +26,14 @@ export function initAffixTabs() {
23 * @description: Set fixed tabs 26 * @description: Set fixed tabs
24 */ 27 */
25 function addAffixTabs(): void { 28 function addAffixTabs(): void {
26 - const affixTabs = filterAffixTabs((router.getRoutes() as unknown) as AppRouteRecordRaw[]); 29 + const affixTabs = filterAffixTabs((router.getRoutes() as unknown) as RouteLocationNormalized[]);
27 affixList.value = affixTabs; 30 affixList.value = affixTabs;
28 for (const tab of affixTabs) { 31 for (const tab of affixTabs) {
29 - tabStore.commitAddTab(tab); 32 + tabStore.addTabAction(({
  33 + meta: tab.meta,
  34 + name: tab.name,
  35 + path: tab.path,
  36 + } as unknown) as RouteLocationNormalized);
30 } 37 }
31 } 38 }
32 39
@@ -37,3 +44,41 @@ export function initAffixTabs() { @@ -37,3 +44,41 @@ export function initAffixTabs() {
37 } 44 }
38 return affixList.value.map((item) => item.meta?.title).filter(Boolean); 45 return affixList.value.map((item) => item.meta?.title).filter(Boolean);
39 } 46 }
  47 +
  48 +export function useTabsDrag(affixTextList: string[]) {
  49 + const { multiTabsSetting } = useProjectSetting();
  50 +
  51 + function initSortableTabs() {
  52 + if (!multiTabsSetting.canDrag) return;
  53 + nextTick(() => {
  54 + const el = document.querySelectorAll(
  55 + '.multiple-tabs .ant-tabs-nav > div'
  56 + )?.[0] as HTMLElement;
  57 +
  58 + if (!el) return;
  59 + Sortable.create(el, {
  60 + animation: 500,
  61 + delay: 400,
  62 + delayOnTouchOnly: true,
  63 + filter: (e: ChangeEvent) => {
  64 + const text = e?.target?.innerText;
  65 + if (!text) return false;
  66 + return affixTextList.includes(text);
  67 + },
  68 + onEnd: (evt) => {
  69 + const { oldIndex, newIndex } = evt;
  70 +
  71 + if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
  72 + return;
  73 + }
  74 +
  75 + tabStore.commitSortTabs({ oldIndex, newIndex });
  76 + },
  77 + });
  78 + });
  79 + }
  80 +
  81 + onMounted(() => {
  82 + initSortableTabs();
  83 + });
  84 +}
src/layouts/default/multitabs/useTabDropdown.ts
1 -import type { AppRouteRecordRaw } from '/@/router/types';  
2 -import type { TabContentProps } from './data';  
3 -import type { Ref } from 'vue';  
4 -import type { TabItem } from '/@/store/modules/tab'; 1 +import type { TabContentProps } from './types';
5 import type { DropMenu } from '/@/components/Dropdown'; 2 import type { DropMenu } from '/@/components/Dropdown';
6 3
7 -import { computed, unref } from 'vue';  
8 -import { TabContentEnum, MenuEventEnum, getActions } from './data'; 4 +import { computed, unref, reactive } from 'vue';
  5 +import { TabContentEnum, MenuEventEnum } from './types';
9 import { tabStore } from '/@/store/modules/tab'; 6 import { tabStore } from '/@/store/modules/tab';
10 -import { appStore } from '/@/store/modules/app';  
11 -import { PageEnum } from '/@/enums/pageEnum';  
12 -import { useGo, useRedo } from '/@/hooks/web/usePage';  
13 import router from '/@/router'; 7 import router from '/@/router';
14 -import { useTabs, isInitUseTab } from '/@/hooks/web/useTabs';  
15 -import { RouteLocationRaw } from 'vue-router'; 8 +import { RouteLocationNormalized } from 'vue-router';
  9 +import { useTabs } from '/@/hooks/web/useTabs';
  10 +import { useI18n } from '/@/hooks/web/useI18n';
  11 +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  12 +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  13 +import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
16 14
17 -const { initTabFn } = useTabs(); 15 +const { t } = useI18n();
18 16
19 export function useTabDropdown(tabContentProps: TabContentProps) { 17 export function useTabDropdown(tabContentProps: TabContentProps) {
20 - const { currentRoute } = router;  
21 - const redo = useRedo();  
22 - const go = useGo();  
23 -  
24 - const isTabsRef = computed(() => tabContentProps.type === TabContentEnum.TAB_TYPE);  
25 - const getCurrentTab: Ref<TabItem | AppRouteRecordRaw> = computed(() => {  
26 - return unref(isTabsRef)  
27 - ? tabContentProps.tabItem  
28 - : ((unref(currentRoute) as any) as AppRouteRecordRaw); 18 + const state = reactive({
  19 + current: null as Nullable<RouteLocationNormalized>,
  20 + currentIndex: 0,
29 }); 21 });
30 22
31 - // Current tab list  
32 - const getTabsState = computed(() => tabStore.getTabsState); 23 + const { currentRoute } = router;
  24 +
  25 + const { getShowMenu, setMenuSetting } = useMenuSetting();
  26 + const { getShowHeader, setHeaderSetting } = useHeaderSetting();
  27 + const { getShowQuick } = useMultipleTabSetting();
  28 +
  29 + const isTabs = computed(() =>
  30 + !unref(getShowQuick) ? true : tabContentProps.type === TabContentEnum.TAB_TYPE
  31 + );
  32 +
  33 + const getCurrentTab = computed(
  34 + (): RouteLocationNormalized => {
  35 + return unref(isTabs) ? tabContentProps.tabItem : unref(currentRoute);
  36 + }
  37 + );
  38 +
  39 + const getIsScale = computed(() => {
  40 + return !unref(getShowMenu) && !unref(getShowHeader);
  41 + });
33 42
34 /** 43 /**
35 * @description: drop-down list 44 * @description: drop-down list
36 */ 45 */
37 const getDropMenuList = computed(() => { 46 const getDropMenuList = computed(() => {
38 - const dropMenuList = getActions();  
39 - // Reset to initial state  
40 - for (const item of dropMenuList) {  
41 - item.disabled = false;  
42 - }  
43 -  
44 - // No tab  
45 - if (!unref(getTabsState) || unref(getTabsState).length <= 0) {  
46 - return dropMenuList;  
47 - } else if (unref(getTabsState).length === 1) {  
48 - // Only one tab  
49 - for (const item of dropMenuList) {  
50 - if (item.event !== MenuEventEnum.REFRESH_PAGE) {  
51 - item.disabled = true;  
52 - }  
53 - }  
54 - return dropMenuList;  
55 - }  
56 if (!unref(getCurrentTab)) return; 47 if (!unref(getCurrentTab)) return;
57 - const { meta, path } = unref(getCurrentTab); 48 + const { meta } = unref(getCurrentTab);
  49 + const { path } = unref(currentRoute);
58 50
59 // Refresh button 51 // Refresh button
60 - const curItem = tabStore.getCurrentContextMenuState;  
61 - const index = tabStore.getCurrentContextMenuIndexState; 52 + const curItem = state.current;
  53 + const index = state.currentIndex;
62 const refreshDisabled = curItem ? curItem.path !== path : true; 54 const refreshDisabled = curItem ? curItem.path !== path : true;
63 // Close left 55 // Close left
64 const closeLeftDisabled = index === 0; 56 const closeLeftDisabled = index === 0;
65 57
  58 + const disabled = tabStore.getTabsState.length === 1;
  59 +
66 // Close right 60 // Close right
67 - const closeRightDisabled = index === unref(getTabsState).length - 1;  
68 - // Currently fixed tab  
69 - // TODO PERf  
70 - dropMenuList[0].disabled = unref(isTabsRef) ? refreshDisabled : false;  
71 - if (meta && meta.affix) {  
72 - dropMenuList[1].disabled = true; 61 + const closeRightDisabled =
  62 + index === tabStore.getTabsState.length - 1 && tabStore.getLastDragEndIndexState >= 0;
  63 + const dropMenuList: DropMenu[] = [
  64 + {
  65 + icon: 'ant-design:reload-outlined',
  66 + event: MenuEventEnum.REFRESH_PAGE,
  67 + text: t('layout.multipleTab.redo'),
  68 + disabled: refreshDisabled,
  69 + },
  70 + {
  71 + icon: 'ant-design:close-outlined',
  72 + event: MenuEventEnum.CLOSE_CURRENT,
  73 + text: t('layout.multipleTab.close'),
  74 + disabled: meta?.affix || disabled,
  75 + divider: true,
  76 + },
  77 + {
  78 + icon: 'ant-design:pic-left-outlined',
  79 + event: MenuEventEnum.CLOSE_LEFT,
  80 + text: t('layout.multipleTab.closeLeft'),
  81 + disabled: closeLeftDisabled,
  82 + divider: false,
  83 + },
  84 + {
  85 + icon: 'ant-design:pic-right-outlined',
  86 + event: MenuEventEnum.CLOSE_RIGHT,
  87 + text: t('layout.multipleTab.closeRight'),
  88 + disabled: closeRightDisabled,
  89 + divider: true,
  90 + },
  91 + {
  92 + icon: 'ant-design:pic-center-outlined',
  93 + event: MenuEventEnum.CLOSE_OTHER,
  94 + text: t('layout.multipleTab.closeOther'),
  95 + disabled: disabled,
  96 + },
  97 + {
  98 + icon: 'ant-design:line-outlined',
  99 + event: MenuEventEnum.CLOSE_ALL,
  100 + text: t('layout.multipleTab.closeAll'),
  101 + disabled: disabled,
  102 + },
  103 + ];
  104 +
  105 + if (!unref(isTabs)) {
  106 + const isScale = unref(getIsScale);
  107 + dropMenuList.unshift({
  108 + icon: isScale ? 'codicon:screen-normal' : 'codicon:screen-full',
  109 + event: MenuEventEnum.SCALE,
  110 + text: isScale ? t('layout.multipleTab.putAway') : t('layout.multipleTab.unfold'),
  111 + disabled: false,
  112 + });
73 } 113 }
74 - dropMenuList[2].disabled = closeLeftDisabled;  
75 - dropMenuList[3].disabled = closeRightDisabled;  
76 114
77 return dropMenuList; 115 return dropMenuList;
78 }); 116 });
79 117
80 - /**  
81 - * @description: Jump to page when closing all pages  
82 - */  
83 - function gotoPage() {  
84 - const len = unref(getTabsState).length;  
85 - const { path } = unref(currentRoute);  
86 -  
87 - let toPath: PageEnum | string = PageEnum.BASE_HOME;  
88 -  
89 - if (len > 0) {  
90 - const page = unref(getTabsState)[len - 1];  
91 - const p = page.fullPath || page.path;  
92 - if (p) {  
93 - toPath = p;  
94 - }  
95 - }  
96 - // Jump to the current page and report an error  
97 - path !== toPath && go(toPath as PageEnum, true);  
98 - }  
99 -  
100 - function isGotoPage(currentTab?: TabItem) {  
101 - const { path } = unref(currentRoute);  
102 - const currentPath = (currentTab || unref(getCurrentTab)).path;  
103 - // Not the current tab, when you close the left/right side, you need to jump to the page  
104 - if (path !== currentPath) {  
105 - go(currentPath as PageEnum, true);  
106 - }  
107 - }  
108 - function refreshPage(tabItem?: TabItem) {  
109 - try {  
110 - tabStore.commitCloseTabKeepAlive(tabItem || unref(getCurrentTab));  
111 - } catch (error) {}  
112 - redo();  
113 - }  
114 -  
115 - function closeAll() {  
116 - tabStore.commitCloseAllTab();  
117 - gotoPage();  
118 - }  
119 -  
120 - function closeLeft(tabItem?: TabItem) {  
121 - tabStore.closeLeftTabAction(tabItem || unref(getCurrentTab));  
122 - isGotoPage(tabItem);  
123 - }  
124 -  
125 - function closeRight(tabItem?: TabItem) {  
126 - tabStore.closeRightTabAction(tabItem || unref(getCurrentTab));  
127 - isGotoPage(tabItem);  
128 - }  
129 -  
130 - function closeOther(tabItem?: TabItem) {  
131 - tabStore.closeOtherTabAction(tabItem || unref(getCurrentTab));  
132 - isGotoPage(tabItem);  
133 - } 118 + const getTrigger = computed(() => {
  119 + return unref(isTabs) ? ['contextmenu'] : ['click'];
  120 + });
134 121
135 - function closeCurrent(tabItem?: TabItem) {  
136 - closeTab(unref(tabItem || unref(getCurrentTab))); 122 + function handleContextMenu(tabItem: RouteLocationNormalized) {
  123 + return (e: Event) => {
  124 + if (!tabItem) return;
  125 + e?.preventDefault();
  126 + const index = tabStore.getTabsState.findIndex((tab) => tab.path === tabItem.path);
  127 + state.current = tabItem;
  128 + state.currentIndex = index;
  129 + };
137 } 130 }
138 131
139 function scaleScreen() { 132 function scaleScreen() {
140 - const {  
141 - headerSetting: { show: showHeader },  
142 - menuSetting: { show: showMenu },  
143 - } = appStore.getProjectConfig;  
144 - const isScale = !showHeader && !showMenu;  
145 - appStore.commitProjectConfigState({  
146 - headerSetting: { show: isScale },  
147 - menuSetting: { show: isScale }, 133 + const isScale = !unref(getShowMenu) && !unref(getShowHeader);
  134 + setMenuSetting({
  135 + show: isScale,
148 }); 136 });
149 - }  
150 -  
151 - if (!isInitUseTab) {  
152 - initTabFn({  
153 - refreshPageFn: refreshPage,  
154 - closeAllFn: closeAll,  
155 - closeCurrentFn: closeCurrent,  
156 - closeLeftFn: closeLeft,  
157 - closeOtherFn: closeOther,  
158 - closeRightFn: closeRight, 137 + setHeaderSetting({
  138 + show: isScale,
159 }); 139 });
160 } 140 }
161 141
162 // Handle right click event 142 // Handle right click event
163 function handleMenuEvent(menu: DropMenu): void { 143 function handleMenuEvent(menu: DropMenu): void {
  144 + const { refreshPage, closeAll, closeCurrent, closeLeft, closeOther, closeRight } = useTabs();
164 const { event } = menu; 145 const { event } = menu;
165 -  
166 switch (event) { 146 switch (event) {
167 case MenuEventEnum.SCALE: 147 case MenuEventEnum.SCALE:
168 scaleScreen(); 148 scaleScreen();
@@ -193,51 +173,5 @@ export function useTabDropdown(tabContentProps: TabContentProps) { @@ -193,51 +173,5 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
193 break; 173 break;
194 } 174 }
195 } 175 }
196 - return { getDropMenuList, handleMenuEvent };  
197 -}  
198 -  
199 -export function getObj(tabItem: TabItem) {  
200 - const { params, path, query } = tabItem;  
201 - return {  
202 - params: params || {},  
203 - path,  
204 - query: query || {},  
205 - };  
206 -}  
207 -  
208 -export function closeTab(closedTab: TabItem | AppRouteRecordRaw) {  
209 - const { currentRoute, replace } = router;  
210 - // Current tab list  
211 - const getTabsState = computed(() => tabStore.getTabsState);  
212 -  
213 - const { path } = unref(currentRoute);  
214 - if (path !== closedTab.path) {  
215 - // Closed is not the activation tab  
216 - tabStore.commitCloseTab(closedTab);  
217 - return;  
218 - }  
219 -  
220 - // Closed is activated atb  
221 - let toObj: RouteLocationRaw = {};  
222 -  
223 - const index = unref(getTabsState).findIndex((item) => item.path === path);  
224 -  
225 - // If the current is the leftmost tab  
226 - if (index === 0) {  
227 - // There is only one tab, then jump to the homepage, otherwise jump to the right tab  
228 - if (unref(getTabsState).length === 1) {  
229 - toObj = PageEnum.BASE_HOME;  
230 - } else {  
231 - // Jump to the right tab  
232 - const page = unref(getTabsState)[index + 1];  
233 - toObj = getObj(page);  
234 - }  
235 - } else {  
236 - // Close the current tab  
237 - const page = unref(getTabsState)[index - 1];  
238 - toObj = getObj(page);  
239 - }  
240 - const route = (unref(currentRoute) as unknown) as AppRouteRecordRaw;  
241 - tabStore.commitCloseTab(route);  
242 - replace(toObj); 176 + return { getDropMenuList, handleMenuEvent, handleContextMenu, getTrigger, isTabs };
243 } 177 }
src/layouts/iframe/index.vue
1 <template> 1 <template>
2 <template v-for="frame in getFramePages" :key="frame.path"> 2 <template v-for="frame in getFramePages" :key="frame.path">
3 <FramePage 3 <FramePage
4 - v-if="frame.meta.frameSrc && hasRenderFrame(frame.path)" 4 + v-if="frame.meta.frameSrc && hasRenderFrame(frame.name)"
5 v-show="showIframe(frame)" 5 v-show="showIframe(frame)"
6 :frameSrc="frame.meta.frameSrc" 6 :frameSrc="frame.meta.frameSrc"
7 /> 7 />
src/layouts/iframe/useFrameKeepAlive.ts
@@ -23,7 +23,7 @@ export function useFrameKeepAlive() { @@ -23,7 +23,7 @@ export function useFrameKeepAlive() {
23 const getOpenTabList = computed((): string[] => { 23 const getOpenTabList = computed((): string[] => {
24 return tabStore.getTabsState.reduce((prev: string[], next) => { 24 return tabStore.getTabsState.reduce((prev: string[], next) => {
25 if (next.meta && Reflect.has(next.meta, 'frameSrc')) { 25 if (next.meta && Reflect.has(next.meta, 'frameSrc')) {
26 - prev.push(next.path!); 26 + prev.push(next.name as string);
27 } 27 }
28 return prev; 28 return prev;
29 }, []); 29 }, []);
@@ -45,11 +45,14 @@ export function useFrameKeepAlive() { @@ -45,11 +45,14 @@ export function useFrameKeepAlive() {
45 } 45 }
46 46
47 function showIframe(item: AppRouteRecordRaw) { 47 function showIframe(item: AppRouteRecordRaw) {
48 - return item.path === unref(currentRoute).path; 48 + return item.name === unref(currentRoute).name;
49 } 49 }
50 50
51 - function hasRenderFrame(path: string) {  
52 - return unref(getShowMultipleTab) ? unref(getOpenTabList).includes(path) : true; 51 + function hasRenderFrame(name: string) {
  52 + if (!unref(getShowMultipleTab)) {
  53 + return true;
  54 + }
  55 + return unref(getOpenTabList).includes(name);
53 } 56 }
54 return { hasRenderFrame, getFramePages, showIframe, getAllFramePages }; 57 return { hasRenderFrame, getFramePages, showIframe, getAllFramePages };
55 } 58 }
src/layouts/page/index.tsx deleted 100644 → 0
1 -import type { FunctionalComponent } from 'vue';  
2 -  
3 -import { computed, defineComponent, unref, Transition, KeepAlive } from 'vue';  
4 -import { RouterView, RouteLocation } from 'vue-router';  
5 -  
6 -import FrameLayout from '/@/layouts/iframe/index.vue';  
7 -  
8 -import { useTransition } from './useTransition';  
9 -import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';  
10 -import { useRootSetting } from '/@/hooks/setting/useRootSetting';  
11 -import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';  
12 -  
13 -import { tabStore } from '/@/store/modules/tab';  
14 -import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';  
15 -  
16 -interface DefaultContext {  
17 - Component: FunctionalComponent;  
18 - route: RouteLocation;  
19 -}  
20 -  
21 -export default defineComponent({  
22 - name: 'PageLayout',  
23 - setup() {  
24 - const { getShowMenu } = useMenuSetting();  
25 -  
26 - const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();  
27 -  
28 - const { getBasicTransition, getEnableTransition } = useTransitionSetting();  
29 -  
30 - const { getMax } = useMultipleTabSetting();  
31 -  
32 - const transitionEvent = useTransition();  
33 -  
34 - const openCacheRef = computed(() => unref(getOpenKeepAlive) && unref(getShowMenu));  
35 -  
36 - const getCacheTabsRef = computed(() => tabStore.getKeepAliveTabsState as string[]);  
37 -  
38 - return () => {  
39 - return (  
40 - <div>  
41 - <RouterView>  
42 - {{  
43 - default: ({ Component, route }: DefaultContext) => {  
44 - // No longer show animations that are already in the tab  
45 - const cacheTabs = unref(getCacheTabsRef);  
46 - const isInCache = cacheTabs.includes(route.name as string);  
47 - const name = isInCache && route.meta.inTab ? 'fade-slide' : null;  
48 -  
49 - const renderComp = () => <Component key={route.fullPath} />;  
50 -  
51 - const PageContent = unref(openCacheRef) ? (  
52 - <KeepAlive max={unref(getMax)} include={cacheTabs}>  
53 - {renderComp()}  
54 - </KeepAlive>  
55 - ) : (  
56 - renderComp()  
57 - );  
58 -  
59 - return unref(getEnableTransition) ? (  
60 - <Transition  
61 - {...transitionEvent}  
62 - name={name || route.meta.transitionName || unref(getBasicTransition)}  
63 - mode="out-in"  
64 - appear={true}  
65 - >  
66 - {() => PageContent}  
67 - </Transition>  
68 - ) : (  
69 - PageContent  
70 - );  
71 - },  
72 - }}  
73 - </RouterView>  
74 - {unref(getCanEmbedIFramePage) && <FrameLayout />}  
75 - </div>  
76 - );  
77 - };  
78 - },  
79 -});  
src/layouts/page/index.vue 0 → 100644
  1 +<template>
  2 + <ParentLayout :isPage="true" />
  3 + <FrameLayout v-if="getCanEmbedIFramePage" />
  4 +</template>
  5 +<script lang="ts">
  6 + import { defineComponent } from 'vue';
  7 +
  8 + import FrameLayout from '/@/layouts/iframe/index.vue';
  9 +
  10 + import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  11 +
  12 + import ParentLayout from '/@/layouts/parent/index.vue';
  13 + export default defineComponent({
  14 + components: { ParentLayout, FrameLayout },
  15 + setup() {
  16 + const { getCanEmbedIFramePage } = useRootSetting();
  17 +
  18 + return { getCanEmbedIFramePage };
  19 + },
  20 + });
  21 +</script>
src/layouts/parent/index.vue 0 → 100644
  1 +<!--
  2 + * @Description: The reason is that tsx will report warnings under multi-level nesting.
  3 +-->
  4 +<template>
  5 + <div>
  6 + <router-view>
  7 + <template #default="{ Component, route }">
  8 + <transition v-bind="transitionEvent" :name="getName(route)" mode="out-in" appear>
  9 + <keep-alive v-if="openCache" :include="getCaches">
  10 + <component :max="getMax" :is="Component" :key="route.fullPath" />
  11 + </keep-alive>
  12 + <component v-else :max="getMax" :is="Component" :key="route.fullPath" />
  13 + </transition>
  14 + </template>
  15 + </router-view>
  16 + </div>
  17 +</template>
  18 +<script lang="ts">
  19 + import { computed, defineComponent, unref } from 'vue';
  20 + import { RouteLocationNormalized } from 'vue-router';
  21 +
  22 + import { useTransition } from './useTransition';
  23 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  24 + import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  25 + import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
  26 +
  27 + import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
  28 + import { useCache } from './useCache';
  29 +
  30 + export default defineComponent({
  31 + props: {
  32 + isPage: {
  33 + type: Boolean,
  34 + },
  35 + },
  36 + setup(props) {
  37 + const { getCaches } = useCache(props.isPage);
  38 +
  39 + const { getShowMenu } = useMenuSetting();
  40 +
  41 + const { getOpenKeepAlive } = useRootSetting();
  42 +
  43 + const { getBasicTransition, getEnableTransition } = useTransitionSetting();
  44 +
  45 + const { getMax } = useMultipleTabSetting();
  46 +
  47 + const transitionEvent = useTransition();
  48 +
  49 + const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMenu));
  50 +
  51 + function getName(route: RouteLocationNormalized) {
  52 + if (!unref(getEnableTransition)) {
  53 + return null;
  54 + }
  55 + const cacheTabs = unref(getCaches);
  56 + const isInCache = cacheTabs.includes(route.name as string);
  57 + const name = isInCache && route.meta.inTab ? 'fade-slide' : null;
  58 +
  59 + return name || route.meta.transitionName || unref(getBasicTransition);
  60 + }
  61 +
  62 + return {
  63 + getCaches,
  64 + getMax,
  65 + transitionEvent,
  66 + getBasicTransition,
  67 + getName,
  68 + openCache,
  69 + getEnableTransition,
  70 + };
  71 + },
  72 + });
  73 +</script>
src/layouts/parent/useCache.ts 0 → 100644
  1 +import { computed, ref, unref } from 'vue';
  2 +import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  3 +import { tryTsxEmit } from '/@/utils/helper/vueHelper';
  4 +import { tabStore, PAGE_LAYOUT_KEY } from '/@/store/modules/tab';
  5 +
  6 +import { useRouter } from 'vue-router';
  7 +
  8 +const ParentLayoutName = 'ParentLayout';
  9 +export function useCache(isPage: boolean) {
  10 + const name = ref('');
  11 + const { currentRoute } = useRouter();
  12 +
  13 + tryTsxEmit((instance: any) => {
  14 + const routeName = instance.ctx.$options.name;
  15 +
  16 + if (routeName && ![ParentLayoutName].includes(routeName)) {
  17 + name.value = routeName;
  18 + } else {
  19 + const matched = currentRoute.value.matched;
  20 + const len = matched.length;
  21 + if (len < 2) return;
  22 + name.value = matched[len - 2].name as string;
  23 + }
  24 + });
  25 + const { getOpenKeepAlive } = useRootSetting();
  26 +
  27 + const getCaches = computed((): string[] => {
  28 + if (!unref(getOpenKeepAlive)) {
  29 + return [];
  30 + }
  31 + const cached = tabStore.getCachedMapState;
  32 +
  33 + if (isPage) {
  34 + // page Layout
  35 + // not parent layout
  36 + return cached.get(PAGE_LAYOUT_KEY) || [];
  37 + }
  38 +
  39 + const cacheSet = new Set<string>();
  40 + cacheSet.add(unref(name));
  41 +
  42 + const list = cached.get(unref(name));
  43 + if (!list) {
  44 + return Array.from(cacheSet);
  45 + }
  46 + list.forEach((item) => {
  47 + cacheSet.add(item);
  48 + });
  49 + return Array.from(cacheSet);
  50 + });
  51 + return { getCaches };
  52 +}
src/layouts/page/useTransition.ts renamed to src/layouts/parent/useTransition.ts
src/locales/lang/en/component/form.ts
@@ -4,8 +4,8 @@ export default { @@ -4,8 +4,8 @@ export default {
4 putAway: 'Put away', 4 putAway: 'Put away',
5 unfold: 'Unfold', 5 unfold: 'Unfold',
6 6
7 - input: 'Please Input',  
8 - choose: 'Please Choose', 7 + input: 'Please Input ',
  8 + choose: 'Please Choose ',
9 9
10 maxTip: 'The number of characters should be less than {0}', 10 maxTip: 'The number of characters should be less than {0}',
11 }; 11 };
src/locales/lang/en/layout/header.ts
@@ -15,4 +15,6 @@ export default { @@ -15,4 +15,6 @@ export default {
15 lockScreen: 'Lock screen', 15 lockScreen: 'Lock screen',
16 lockScreenBtn: 'Locking', 16 lockScreenBtn: 'Locking',
17 notLockScreenPassword: 'No password lock screen', 17 notLockScreenPassword: 'No password lock screen',
  18 +
  19 + home: 'Home',
18 }; 20 };
src/locales/lang/en/routes/demo/level.ts 0 → 100644
  1 +export default {
  2 + level: 'Multi menu cache',
  3 +};
src/locales/lang/zh_CN/layout/header.ts
@@ -16,4 +16,6 @@ export default { @@ -16,4 +16,6 @@ export default {
16 lockScreen: '锁定屏幕', 16 lockScreen: '锁定屏幕',
17 lockScreenBtn: '锁定', 17 lockScreenBtn: '锁定',
18 notLockScreenPassword: '不设置密码锁屏', 18 notLockScreenPassword: '不设置密码锁屏',
  19 +
  20 + home: '首页',
19 }; 21 };
src/locales/lang/zh_CN/routes/demo/feat.ts
1 export default { 1 export default {
2 - feat: '页面功能', 2 + feat: '功能',
3 icon: '图标', 3 icon: '图标',
4 tabs: '标签页操作', 4 tabs: '标签页操作',
5 contextMenu: '右键菜单', 5 contextMenu: '右键菜单',
src/locales/lang/zh_CN/routes/demo/level.ts 0 → 100644
  1 +export default {
  2 + level: '多级菜单缓存',
  3 +};
src/router/constant.ts
1 import type { AppRouteRecordRaw } from '/@/router/types'; 1 import type { AppRouteRecordRaw } from '/@/router/types';
  2 +import ParentLayout from '/@/layouts/parent/index.vue';
2 3
3 const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception'); 4 const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception');
4 5
5 /** 6 /**
6 * @description: default layout 7 * @description: default layout
7 */ 8 */
8 -export const DEFAULT_LAYOUT_COMPONENT = () => import('/@/layouts/default/index'); 9 +export const LAYOUT = () => import('/@/layouts/default/index');
9 10
10 /** 11 /**
11 * @description: page-layout 12 * @description: page-layout
12 */ 13 */
13 -export const PAGE_LAYOUT_COMPONENT = () => import('/@/layouts/page/index'); 14 +export const PAGE_LAYOUT_COMPONENT = () => import('/@/layouts/page/index.vue');
  15 +
  16 +/**
  17 + * @description: page-layout
  18 + */
  19 +export const getParentLayout = (name: string) => {
  20 + return () =>
  21 + new Promise((resolve) => {
  22 + resolve({
  23 + ...ParentLayout,
  24 + name,
  25 + });
  26 + });
  27 +};
14 28
15 // 404 on a page 29 // 404 on a page
16 export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { 30 export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
@@ -23,12 +37,25 @@ export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { @@ -23,12 +37,25 @@ export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
23 }, 37 },
24 }; 38 };
25 39
  40 +export const REDIRECT_NAME = 'Redirect';
  41 +
26 export const REDIRECT_ROUTE: AppRouteRecordRaw = { 42 export const REDIRECT_ROUTE: AppRouteRecordRaw = {
27 - path: '/redirect/:path(.*)*',  
28 - name: 'Redirect',  
29 - component: () => import('/@/views/sys/redirect/index.vue'), 43 + path: '/redirect',
  44 + name: REDIRECT_NAME,
  45 + component: LAYOUT,
30 meta: { 46 meta: {
31 - title: 'Redirect', 47 + title: REDIRECT_NAME,
32 hideBreadcrumb: true, 48 hideBreadcrumb: true,
33 }, 49 },
  50 + children: [
  51 + {
  52 + path: '/redirect/:path(.*)',
  53 + name: REDIRECT_NAME,
  54 + component: () => import('/@/views/sys/redirect/index.vue'),
  55 + meta: {
  56 + title: REDIRECT_NAME,
  57 + hideBreadcrumb: true,
  58 + },
  59 + },
  60 + ],
34 }; 61 };
src/router/guard/index.ts
@@ -8,17 +8,19 @@ import { createPageLoadingGuard } from &#39;./pageLoadingGuard&#39;; @@ -8,17 +8,19 @@ import { createPageLoadingGuard } from &#39;./pageLoadingGuard&#39;;
8 8
9 import { useGlobSetting, useProjectSetting } from '/@/hooks/setting'; 9 import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
10 10
11 -import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper'; 11 +import { getIsOpenTab, getRoute } from '/@/router/helper/routeHelper';
12 import { setTitle } from '/@/utils/browser'; 12 import { setTitle } from '/@/utils/browser';
13 import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; 13 import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
14 14
15 import { tabStore } from '/@/store/modules/tab'; 15 import { tabStore } from '/@/store/modules/tab';
16 import { useI18n } from '/@/hooks/web/useI18n'; 16 import { useI18n } from '/@/hooks/web/useI18n';
  17 +import { REDIRECT_NAME } from '/@/router/constant';
17 18
18 const { closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting(); 19 const { closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting();
19 const globSetting = useGlobSetting(); 20 const globSetting = useGlobSetting();
  21 +
20 export function createGuard(router: Router) { 22 export function createGuard(router: Router) {
21 - let axiosCanceler: AxiosCanceler | null; 23 + let axiosCanceler: Nullable<AxiosCanceler>;
22 if (removeAllHttpPending) { 24 if (removeAllHttpPending) {
23 axiosCanceler = new AxiosCanceler(); 25 axiosCanceler = new AxiosCanceler();
24 } 26 }
@@ -30,15 +32,7 @@ export function createGuard(router: Router) { @@ -30,15 +32,7 @@ export function createGuard(router: Router) {
30 to.meta.inTab = isOpen; 32 to.meta.inTab = isOpen;
31 33
32 // Notify routing changes 34 // Notify routing changes
33 - const { fullPath, path, query, params, name, meta } = to;  
34 - tabStore.commitLastChangeRouteState({  
35 - fullPath,  
36 - path,  
37 - query,  
38 - params,  
39 - name,  
40 - meta,  
41 - } as any); 35 + tabStore.commitLastChangeRouteState(getRoute(to));
42 36
43 try { 37 try {
44 if (closeMessageOnSwitch) { 38 if (closeMessageOnSwitch) {
@@ -50,14 +44,13 @@ export function createGuard(router: Router) { @@ -50,14 +44,13 @@ export function createGuard(router: Router) {
50 } catch (error) { 44 } catch (error) {
51 console.warn('basic guard error:' + error); 45 console.warn('basic guard error:' + error);
52 } 46 }
53 - setCurrentTo(to);  
54 return true; 47 return true;
55 }); 48 });
56 49
57 router.afterEach((to) => { 50 router.afterEach((to) => {
58 const { t } = useI18n(); 51 const { t } = useI18n();
59 // change html title 52 // change html title
60 - to.name !== 'Redirect' && setTitle(t(to.meta.title), globSetting.title); 53 + to.name !== REDIRECT_NAME && setTitle(t(to.meta.title), globSetting.title);
61 }); 54 });
62 createProgressGuard(router); 55 createProgressGuard(router);
63 createPermissionGuard(router); 56 createPermissionGuard(router);
src/router/guard/pageLoadingGuard.ts
@@ -2,7 +2,7 @@ import type { Router } from &#39;vue-router&#39;; @@ -2,7 +2,7 @@ import type { Router } from &#39;vue-router&#39;;
2 import { tabStore } from '/@/store/modules/tab'; 2 import { tabStore } from '/@/store/modules/tab';
3 import { appStore } from '/@/store/modules/app'; 3 import { appStore } from '/@/store/modules/app';
4 import { userStore } from '/@/store/modules/user'; 4 import { userStore } from '/@/store/modules/user';
5 -import { getParams } from '/@/utils/helper/routeHelper'; 5 +import { getParams } from '/@/router/helper/routeHelper';
6 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; 6 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
7 import { unref } from 'vue'; 7 import { unref } from 'vue';
8 8
@@ -14,6 +14,7 @@ export function createPageLoadingGuard(router: Router) { @@ -14,6 +14,7 @@ export function createPageLoadingGuard(router: Router) {
14 if (!userStore.getTokenState) { 14 if (!userStore.getTokenState) {
15 return true; 15 return true;
16 } 16 }
  17 +
17 if (!unref(getEnableTransition) && unref(getOpenPageLoading)) { 18 if (!unref(getEnableTransition) && unref(getOpenPageLoading)) {
18 appStore.commitPageLoadingState(true); 19 appStore.commitPageLoadingState(true);
19 return true; 20 return true;
src/router/guard/permissionGuard.ts
@@ -7,7 +7,7 @@ import { PageEnum } from &#39;/@/enums/pageEnum&#39;; @@ -7,7 +7,7 @@ import { PageEnum } from &#39;/@/enums/pageEnum&#39;;
7 import { getToken } from '/@/utils/auth'; 7 import { getToken } from '/@/utils/auth';
8 8
9 import { PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; 9 import { PAGE_NOT_FOUND_ROUTE } from '/@/router/constant';
10 -import { RootRoute } from '../routes/index'; 10 +// import { RootRoute } from '../routes/index';
11 11
12 const LOGIN_PATH = PageEnum.BASE_LOGIN; 12 const LOGIN_PATH = PageEnum.BASE_LOGIN;
13 13
@@ -59,7 +59,8 @@ export function createPermissionGuard(router: Router) { @@ -59,7 +59,8 @@ export function createPermissionGuard(router: Router) {
59 } 59 }
60 const routes = await permissionStore.buildRoutesAction(); 60 const routes = await permissionStore.buildRoutesAction();
61 routes.forEach((route) => { 61 routes.forEach((route) => {
62 - router.addRoute(RootRoute.name!, route as RouteRecordRaw); 62 + // router.addRoute(RootRoute.name!, route as RouteRecordRaw);
  63 + router.addRoute(route as RouteRecordRaw);
63 }); 64 });
64 65
65 const redirectPath = (from.query.redirect || to.path) as string; 66 const redirectPath = (from.query.redirect || to.path) as string;
src/router/guard/progressGuard.ts
@@ -9,9 +9,6 @@ import { unref } from &#39;vue&#39;; @@ -9,9 +9,6 @@ import { unref } from &#39;vue&#39;;
9 const { getOpenNProgress } = useTransitionSetting(); 9 const { getOpenNProgress } = useTransitionSetting();
10 10
11 export function createProgressGuard(router: Router) { 11 export function createProgressGuard(router: Router) {
12 - // NProgress.inc(0.1);  
13 - // NProgress.configure({ easing: 'ease', speed: 200, showSpinner: false });  
14 -  
15 router.beforeEach(async (to) => { 12 router.beforeEach(async (to) => {
16 !to.meta.inTab && unref(getOpenNProgress) && NProgress.start(); 13 !to.meta.inTab && unref(getOpenNProgress) && NProgress.start();
17 return true; 14 return true;
src/router/helper/dynamicImport.ts 0 → 100644
  1 +// The content here is just for type approval. The actual file content is overwritten by transform
  2 +// For specific coverage, see build/vite/plugin/transform/dynamic-import/index.ts
  3 +export default function (name: string) {
  4 + return name as any;
  5 +}
src/utils/helper/menuHelper.ts renamed to src/router/helper/menuHelper.ts
1 -import { AppRouteModule, RouteModule } from '/@/router/types.d'; 1 +import { AppRouteModule } from '/@/router/types.d';
2 import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types'; 2 import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types';
3 3
4 -import { findPath, forEach, treeMap, treeToList } from './treeHelper'; 4 +import { findPath, forEach, treeMap, treeToList } from '/@/utils/helper/treeHelper';
5 import { cloneDeep } from 'lodash-es'; 5 import { cloneDeep } from 'lodash-es';
6 6
7 export function getAllParentPath(treeData: any[], path: string) { 7 export function getAllParentPath(treeData: any[], path: string) {
@@ -48,12 +48,11 @@ export function transformRouteToMenu(routeModList: AppRouteModule[]) { @@ -48,12 +48,11 @@ export function transformRouteToMenu(routeModList: AppRouteModule[]) {
48 const cloneRouteModList = cloneDeep(routeModList); 48 const cloneRouteModList = cloneDeep(routeModList);
49 const routeList: AppRouteRecordRaw[] = []; 49 const routeList: AppRouteRecordRaw[] = [];
50 cloneRouteModList.forEach((item) => { 50 cloneRouteModList.forEach((item) => {
51 - const { layout, routes, children } = item as RouteModule;  
52 - if (layout) {  
53 - layout.children = routes || children;  
54 - routeList.push(layout); 51 + if (item.meta?.single) {
  52 + const realItem = item?.children?.[0];
  53 + realItem && routeList.push(realItem);
55 } else { 54 } else {
56 - routes && routeList.push(...routes); 55 + routeList.push(item);
57 } 56 }
58 }); 57 });
59 return treeMap(routeList, { 58 return treeMap(routeList, {
src/router/helper/routeHelper.ts 0 → 100644
  1 +import type { AppRouteModule, AppRouteRecordRaw } from '/@/router/types';
  2 +import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
  3 +
  4 +import { appStore } from '/@/store/modules/app';
  5 +import { tabStore } from '/@/store/modules/tab';
  6 +import { getParentLayout, LAYOUT } from '/@/router/constant';
  7 +import dynamicImport from './dynamicImport';
  8 +import { cloneDeep } from 'lodash-es';
  9 +
  10 +// 动态引入
  11 +function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
  12 + if (!routes) return;
  13 + routes.forEach((item) => {
  14 + const { component, name } = item;
  15 + const { children } = item;
  16 + if (component) {
  17 + item.component = dynamicImport(component);
  18 + } else if (name) {
  19 + item.component = getParentLayout(name);
  20 + }
  21 + children && asyncImportRoute(children);
  22 + });
  23 +}
  24 +
  25 +function getLayoutComp(comp: string) {
  26 + return comp === 'LAYOUT' ? LAYOUT : '';
  27 +}
  28 +
  29 +// Turn background objects into routing objects
  30 +export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
  31 + routeList.forEach((route) => {
  32 + if (route.component) {
  33 + if ((route.component as string).toUpperCase() === 'LAYOUT') {
  34 + route.component = getLayoutComp(route.component);
  35 + } else {
  36 + route.children = [cloneDeep(route)];
  37 + route.component = LAYOUT;
  38 + route.name = `${route.name}Parent`;
  39 + route.path = '';
  40 + const meta = route.meta || {};
  41 + meta.single = true;
  42 + meta.affix = false;
  43 + route.meta = meta;
  44 + }
  45 + }
  46 + route.children && asyncImportRoute(route.children);
  47 + });
  48 + return (routeList as unknown) as T[];
  49 +}
  50 +
  51 +/**
  52 + * Determine whether the tab has been opened
  53 + * @param toPath
  54 + */
  55 +export function getIsOpenTab(toPath: string) {
  56 + const { openKeepAlive, multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
  57 +
  58 + if (show && openKeepAlive) {
  59 + const tabList = tabStore.getTabsState;
  60 + return tabList.some((tab) => tab.path === toPath);
  61 + }
  62 + return false;
  63 +}
  64 +
  65 +export function getParams(data: any = {}) {
  66 + const { params = {} } = data;
  67 + let ret = '';
  68 + Object.keys(params).forEach((key) => {
  69 + const p = params[key];
  70 + ret += `/${p}`;
  71 + });
  72 + return ret;
  73 +}
  74 +
  75 +// Return to the new routing structure, not affected by the original example
  76 +export function getRoute(route: RouteLocationNormalized): RouteLocationNormalized {
  77 + if (!route) return route;
  78 + const { matched, ...opt } = route;
  79 + return {
  80 + ...opt,
  81 + matched: (matched
  82 + ? matched.map((item) => ({
  83 + meta: item.meta,
  84 + name: item.name,
  85 + path: item.path,
  86 + }))
  87 + : undefined) as RouteRecordNormalized[],
  88 + };
  89 +}
src/router/menus/index.ts
@@ -2,7 +2,7 @@ import type { Menu, MenuModule } from &#39;/@/router/types&#39;; @@ -2,7 +2,7 @@ import type { Menu, MenuModule } from &#39;/@/router/types&#39;;
2 import type { RouteRecordNormalized } from 'vue-router'; 2 import type { RouteRecordNormalized } from 'vue-router';
3 import { appStore } from '/@/store/modules/app'; 3 import { appStore } from '/@/store/modules/app';
4 import { permissionStore } from '/@/store/modules/permission'; 4 import { permissionStore } from '/@/store/modules/permission';
5 -import { transformMenuModule, flatMenus, getAllParentPath } from '/@/utils/helper/menuHelper'; 5 +import { transformMenuModule, flatMenus, getAllParentPath } from '/@/router/helper/menuHelper';
6 import { filter } from '/@/utils/helper/treeHelper'; 6 import { filter } from '/@/utils/helper/treeHelper';
7 import router from '/@/router'; 7 import router from '/@/router';
8 import { PermissionModeEnum } from '/@/enums/appEnum'; 8 import { PermissionModeEnum } from '/@/enums/appEnum';
src/router/menus/modules/dashboard.ts
1 import type { MenuModule } from '/@/router/types.d'; 1 import type { MenuModule } from '/@/router/types.d';
2 2
3 -const menu: MenuModule[] = [  
4 - {  
5 - orderNo: 0,  
6 - menu: {  
7 - path: '/dashboard/welcome',  
8 - name: 'routes.dashboard.welcome',  
9 - }, 3 +const menu: MenuModule = {
  4 + orderNo: 10,
  5 + menu: {
  6 + name: 'routes.dashboard.dashboard',
  7 + path: '/dashboard',
  8 + children: [
  9 + {
  10 + path: '/workbench',
  11 + name: 'routes.dashboard.workbench',
  12 + },
  13 + {
  14 + path: '/analysis',
  15 + name: 'routes.dashboard.analysis',
  16 + },
  17 + ],
10 }, 18 },
11 - {  
12 - orderNo: 10,  
13 - menu: {  
14 - name: 'routes.dashboard.dashboard',  
15 - path: '/dashboard',  
16 - children: [  
17 - {  
18 - path: '/workbench',  
19 - name: 'routes.dashboard.workbench',  
20 - },  
21 - {  
22 - path: '/analysis',  
23 - name: 'routes.dashboard.analysis',  
24 - },  
25 - // {  
26 - // path: '/welcome',  
27 - // name: 'routes.dashboard.welcome',  
28 - // },  
29 - ],  
30 - },  
31 - },  
32 -]; 19 +};
33 export default menu; 20 export default menu;
src/router/menus/modules/demo/level.ts 0 → 100644
  1 +import type { MenuModule } from '/@/router/types.d';
  2 +
  3 +const menu: MenuModule = {
  4 + orderNo: 2000,
  5 + menu: {
  6 + name: 'routes.demo.level.level',
  7 + path: '/level',
  8 + tag: {
  9 + dot: true,
  10 + },
  11 + children: [
  12 + {
  13 + path: 'menu1',
  14 + name: 'Menu1',
  15 + children: [
  16 + {
  17 + path: 'menu1-1',
  18 + name: 'Menu1-1',
  19 + children: [
  20 + {
  21 + path: 'menu1-1-1',
  22 + name: 'Menu1-1-1',
  23 + },
  24 + ],
  25 + },
  26 + {
  27 + path: 'menu1-2',
  28 + name: 'Menu1-2',
  29 + },
  30 + ],
  31 + },
  32 + {
  33 + path: 'menu2',
  34 + name: 'Menu2',
  35 + },
  36 + ],
  37 + },
  38 +};
  39 +export default menu;
src/router/menus/modules/home.ts 0 → 100644
  1 +import type { MenuModule } from '/@/router/types.d';
  2 +
  3 +const menu: MenuModule = {
  4 + orderNo: 0,
  5 + menu: {
  6 + path: '/home/welcome',
  7 + name: 'routes.dashboard.welcome',
  8 + },
  9 +};
  10 +export default menu;
src/router/routes/index.ts
1 import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types'; 1 import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types';
2 2
3 -import { DEFAULT_LAYOUT_COMPONENT, PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '../constant';  
4 -import { genRouteModule } from '/@/utils/helper/routeHelper'; 3 +import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE, LAYOUT } from '../constant';
  4 +import { PageEnum } from '/@/enums/pageEnum';
  5 +
5 import modules from 'globby!/@/router/routes/modules/**/*.@(ts)'; 6 import modules from 'globby!/@/router/routes/modules/**/*.@(ts)';
6 7
7 const routeModuleList: AppRouteModule[] = []; 8 const routeModuleList: AppRouteModule[] = [];
8 9
9 Object.keys(modules).forEach((key) => { 10 Object.keys(modules).forEach((key) => {
10 - routeModuleList.push(modules[key]); 11 + const mod = Array.isArray(modules[key]) ? [...modules[key]] : [modules[key]];
  12 + routeModuleList.push(...mod);
11 }); 13 });
12 14
13 -export const asyncRoutes = [  
14 - REDIRECT_ROUTE,  
15 - PAGE_NOT_FOUND_ROUTE,  
16 - ...genRouteModule(routeModuleList),  
17 -]; 15 +export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
18 16
19 -// 主框架根路由  
20 -export const RootRoute: AppRouteRecordRaw = { 17 +const MainRoute: AppRouteModule = {
21 path: '/', 18 path: '/',
22 - name: 'Root',  
23 - component: DEFAULT_LAYOUT_COMPONENT,  
24 - redirect: '/dashboard', 19 + name: 'MainRoute',
  20 + component: LAYOUT,
  21 + redirect: PageEnum.BASE_HOME,
25 meta: { 22 meta: {
26 - title: 'Root', 23 + icon: 'ant-design:home-outlined',
  24 + title: 'routes.dashboard.dashboard',
27 }, 25 },
28 - children: [],  
29 }; 26 };
30 27
31 export const LoginRoute: AppRouteRecordRaw = { 28 export const LoginRoute: AppRouteRecordRaw = {
@@ -38,4 +35,4 @@ export const LoginRoute: AppRouteRecordRaw = { @@ -38,4 +35,4 @@ export const LoginRoute: AppRouteRecordRaw = {
38 }; 35 };
39 36
40 // 基础路由 不用权限 37 // 基础路由 不用权限
41 -export const basicRoutes = [LoginRoute, RootRoute]; 38 +export const basicRoutes = [LoginRoute, MainRoute, REDIRECT_ROUTE];
src/router/routes/modules/dashboard.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { LAYOUT } from '/@/router/constant';
4 4
5 const dashboard: AppRouteModule = { 5 const dashboard: AppRouteModule = {
6 - layout: {  
7 - path: '/dashboard',  
8 - name: 'Dashboard',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/dashboard/welcome',  
11 - meta: {  
12 - icon: 'ant-design:home-outlined',  
13 - title: 'routes.dashboard.dashboard',  
14 - }, 6 + path: '/dashboard',
  7 + name: 'Dashboard',
  8 + component: LAYOUT,
  9 + redirect: '/dashboard/welcome',
  10 + meta: {
  11 + icon: 'ant-design:home-outlined',
  12 + title: 'routes.dashboard.dashboard',
15 }, 13 },
16 -  
17 - routes: [  
18 - {  
19 - path: '/welcome',  
20 - name: 'Welcome',  
21 - component: () => import('/@/views/dashboard/welcome/index.vue'),  
22 - meta: {  
23 - title: 'routes.dashboard.welcome',  
24 - affix: true,  
25 - icon: 'ant-design:home-outlined',  
26 - },  
27 - }, 14 + children: [
28 { 15 {
29 - path: '/workbench', 16 + path: 'workbench',
30 name: 'Workbench', 17 name: 'Workbench',
31 component: () => import('/@/views/dashboard/workbench/index.vue'), 18 component: () => import('/@/views/dashboard/workbench/index.vue'),
32 meta: { 19 meta: {
@@ -34,7 +21,7 @@ const dashboard: AppRouteModule = { @@ -34,7 +21,7 @@ const dashboard: AppRouteModule = {
34 }, 21 },
35 }, 22 },
36 { 23 {
37 - path: '/analysis', 24 + path: 'analysis',
38 name: 'Analysis', 25 name: 'Analysis',
39 component: () => import('/@/views/dashboard/analysis/index.vue'), 26 component: () => import('/@/views/dashboard/analysis/index.vue'),
40 meta: { 27 meta: {
src/router/routes/modules/demo/charts.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { getParentLayout, LAYOUT } from '/@/router/constant';
4 4
5 const charts: AppRouteModule = { 5 const charts: AppRouteModule = {
6 - layout: {  
7 - path: '/charts',  
8 - name: 'Charts',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/charts/apexChart',  
11 - meta: {  
12 - icon: 'ant-design:area-chart-outlined',  
13 - title: 'routes.demo.charts.charts',  
14 - }, 6 + path: '/charts',
  7 + name: 'Charts',
  8 + component: LAYOUT,
  9 + redirect: '/charts/apexChart',
  10 + meta: {
  11 + icon: 'ant-design:area-chart-outlined',
  12 + title: 'routes.demo.charts.charts',
15 }, 13 },
16 -  
17 - routes: [ 14 + children: [
18 { 15 {
19 - path: '/echarts', 16 + path: 'echarts',
20 name: 'Echarts', 17 name: 'Echarts',
  18 + component: getParentLayout('Echarts'),
21 meta: { 19 meta: {
22 title: 'Echarts', 20 title: 'Echarts',
23 }, 21 },
@@ -49,7 +47,7 @@ const charts: AppRouteModule = { @@ -49,7 +47,7 @@ const charts: AppRouteModule = {
49 ], 47 ],
50 }, 48 },
51 { 49 {
52 - path: '/apexChart', 50 + path: 'apexChart',
53 name: 'ApexChart', 51 name: 'ApexChart',
54 meta: { 52 meta: {
55 title: 'routes.demo.charts.apexChart', 53 title: 'routes.demo.charts.apexChart',
src/router/routes/modules/demo/comp.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { getParentLayout, LAYOUT } from '/@/router/constant';
4 4
5 const comp: AppRouteModule = { 5 const comp: AppRouteModule = {
6 - layout: {  
7 - path: '/comp',  
8 - name: 'Comp',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/comp/basic',  
11 - meta: {  
12 - icon: 'ant-design:table-outlined',  
13 - title: 'routes.demo.comp.comp',  
14 - }, 6 + path: '/comp',
  7 + name: 'Comp',
  8 + component: LAYOUT,
  9 + redirect: '/comp/basic',
  10 + meta: {
  11 + icon: 'ant-design:table-outlined',
  12 + title: 'routes.demo.comp.comp',
15 }, 13 },
16 14
17 - routes: [ 15 + children: [
18 { 16 {
19 - path: '/basic', 17 + path: 'basic',
20 name: 'BasicDemo', 18 name: 'BasicDemo',
21 component: () => import('/@/views/demo/comp/button/index.vue'), 19 component: () => import('/@/views/demo/comp/button/index.vue'),
22 meta: { 20 meta: {
@@ -24,7 +22,7 @@ const comp: AppRouteModule = { @@ -24,7 +22,7 @@ const comp: AppRouteModule = {
24 }, 22 },
25 }, 23 },
26 { 24 {
27 - path: '/transition', 25 + path: 'transition',
28 name: 'transitionDemo', 26 name: 'transitionDemo',
29 component: () => import('/@/views/demo/comp/transition/index.vue'), 27 component: () => import('/@/views/demo/comp/transition/index.vue'),
30 meta: { 28 meta: {
@@ -32,7 +30,7 @@ const comp: AppRouteModule = { @@ -32,7 +30,7 @@ const comp: AppRouteModule = {
32 }, 30 },
33 }, 31 },
34 { 32 {
35 - path: '/countTo', 33 + path: 'countTo',
36 name: 'CountTo', 34 name: 'CountTo',
37 component: () => import('/@/views/demo/comp/count-to/index.vue'), 35 component: () => import('/@/views/demo/comp/count-to/index.vue'),
38 meta: { 36 meta: {
@@ -41,9 +39,10 @@ const comp: AppRouteModule = { @@ -41,9 +39,10 @@ const comp: AppRouteModule = {
41 }, 39 },
42 40
43 { 41 {
44 - path: '/scroll', 42 + path: 'scroll',
45 name: 'ScrollDemo', 43 name: 'ScrollDemo',
46 redirect: '/comp/scroll/basic', 44 redirect: '/comp/scroll/basic',
  45 + component: getParentLayout('ScrollDemo'),
47 meta: { 46 meta: {
48 title: 'routes.demo.comp.scroll', 47 title: 'routes.demo.comp.scroll',
49 }, 48 },
@@ -76,7 +75,7 @@ const comp: AppRouteModule = { @@ -76,7 +75,7 @@ const comp: AppRouteModule = {
76 }, 75 },
77 76
78 { 77 {
79 - path: '/modal', 78 + path: 'modal',
80 name: 'ModalDemo', 79 name: 'ModalDemo',
81 component: () => import('/@/views/demo/comp/modal/index.vue'), 80 component: () => import('/@/views/demo/comp/modal/index.vue'),
82 meta: { 81 meta: {
@@ -84,7 +83,7 @@ const comp: AppRouteModule = { @@ -84,7 +83,7 @@ const comp: AppRouteModule = {
84 }, 83 },
85 }, 84 },
86 { 85 {
87 - path: '/drawer', 86 + path: 'drawer',
88 name: 'DrawerDemo', 87 name: 'DrawerDemo',
89 component: () => import('/@/views/demo/comp/drawer/index.vue'), 88 component: () => import('/@/views/demo/comp/drawer/index.vue'),
90 meta: { 89 meta: {
@@ -92,7 +91,7 @@ const comp: AppRouteModule = { @@ -92,7 +91,7 @@ const comp: AppRouteModule = {
92 }, 91 },
93 }, 92 },
94 { 93 {
95 - path: '/desc', 94 + path: 'desc',
96 name: 'DescDemo', 95 name: 'DescDemo',
97 component: () => import('/@/views/demo/comp/desc/index.vue'), 96 component: () => import('/@/views/demo/comp/desc/index.vue'),
98 meta: { 97 meta: {
@@ -101,8 +100,9 @@ const comp: AppRouteModule = { @@ -101,8 +100,9 @@ const comp: AppRouteModule = {
101 }, 100 },
102 101
103 { 102 {
104 - path: '/lazy',  
105 - name: 'lazyDemo', 103 + path: 'lazy',
  104 + name: 'LazyDemo',
  105 + component: getParentLayout('LazyDemo'),
106 redirect: '/comp/lazy/basic', 106 redirect: '/comp/lazy/basic',
107 meta: { 107 meta: {
108 title: 'routes.demo.comp.lazy', 108 title: 'routes.demo.comp.lazy',
@@ -127,8 +127,9 @@ const comp: AppRouteModule = { @@ -127,8 +127,9 @@ const comp: AppRouteModule = {
127 ], 127 ],
128 }, 128 },
129 { 129 {
130 - path: '/verify', 130 + path: 'verify',
131 name: 'VerifyDemo', 131 name: 'VerifyDemo',
  132 + component: getParentLayout('VerifyDemo'),
132 redirect: '/comp/verify/drag', 133 redirect: '/comp/verify/drag',
133 meta: { 134 meta: {
134 title: 'routes.demo.comp.verify', 135 title: 'routes.demo.comp.verify',
@@ -155,7 +156,7 @@ const comp: AppRouteModule = { @@ -155,7 +156,7 @@ const comp: AppRouteModule = {
155 // 156 //
156 157
157 { 158 {
158 - path: '/qrcode', 159 + path: 'qrcode',
159 name: 'QrCodeDemo', 160 name: 'QrCodeDemo',
160 component: () => import('/@/views/demo/comp/qrcode/index.vue'), 161 component: () => import('/@/views/demo/comp/qrcode/index.vue'),
161 meta: { 162 meta: {
@@ -163,7 +164,7 @@ const comp: AppRouteModule = { @@ -163,7 +164,7 @@ const comp: AppRouteModule = {
163 }, 164 },
164 }, 165 },
165 { 166 {
166 - path: '/strength-meter', 167 + path: 'strength-meter',
167 name: 'StrengthMeterDemo', 168 name: 'StrengthMeterDemo',
168 component: () => import('/@/views/demo/comp/strength-meter/index.vue'), 169 component: () => import('/@/views/demo/comp/strength-meter/index.vue'),
169 meta: { 170 meta: {
@@ -171,7 +172,7 @@ const comp: AppRouteModule = { @@ -171,7 +172,7 @@ const comp: AppRouteModule = {
171 }, 172 },
172 }, 173 },
173 { 174 {
174 - path: '/upload', 175 + path: 'upload',
175 name: 'UploadDemo', 176 name: 'UploadDemo',
176 component: () => import('/@/views/demo/comp/upload/index.vue'), 177 component: () => import('/@/views/demo/comp/upload/index.vue'),
177 meta: { 178 meta: {
@@ -179,7 +180,7 @@ const comp: AppRouteModule = { @@ -179,7 +180,7 @@ const comp: AppRouteModule = {
179 }, 180 },
180 }, 181 },
181 { 182 {
182 - path: '/loading', 183 + path: 'loading',
183 name: 'LoadingDemo', 184 name: 'LoadingDemo',
184 component: () => import('/@/views/demo/comp/loading/index.vue'), 185 component: () => import('/@/views/demo/comp/loading/index.vue'),
185 meta: { 186 meta: {
src/router/routes/modules/demo/editor.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { getParentLayout, LAYOUT } from '/@/router/constant';
4 4
5 const editor: AppRouteModule = { 5 const editor: AppRouteModule = {
6 - layout: {  
7 - path: '/editor',  
8 - name: 'Editor',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/editor/markdown',  
11 - meta: {  
12 - icon: 'ant-design:table-outlined',  
13 - title: 'routes.demo.editor.editor',  
14 - }, 6 + path: '/editor',
  7 + name: 'Editor',
  8 + component: LAYOUT,
  9 + redirect: '/editor/markdown',
  10 + meta: {
  11 + icon: 'ant-design:table-outlined',
  12 + title: 'routes.demo.editor.editor',
15 }, 13 },
16 -  
17 - routes: [ 14 + children: [
18 { 15 {
19 - path: '/markdown', 16 + path: 'markdown',
20 name: 'MarkdownDemo', 17 name: 'MarkdownDemo',
21 component: () => import('/@/views/demo/editor/Markdown.vue'), 18 component: () => import('/@/views/demo/editor/Markdown.vue'),
22 meta: { 19 meta: {
@@ -24,7 +21,8 @@ const editor: AppRouteModule = { @@ -24,7 +21,8 @@ const editor: AppRouteModule = {
24 }, 21 },
25 }, 22 },
26 { 23 {
27 - path: '/tinymce', 24 + path: 'tinymce',
  25 + component: getParentLayout('TinymceDemo'),
28 name: 'TinymceDemo', 26 name: 'TinymceDemo',
29 meta: { 27 meta: {
30 title: 'routes.demo.editor.tinymce', 28 title: 'routes.demo.editor.tinymce',
@@ -39,7 +37,6 @@ const editor: AppRouteModule = { @@ -39,7 +37,6 @@ const editor: AppRouteModule = {
39 title: 'routes.demo.editor.tinymceBasic', 37 title: 'routes.demo.editor.tinymceBasic',
40 }, 38 },
41 }, 39 },
42 - // TODO  
43 { 40 {
44 path: 'editor', 41 path: 'editor',
45 name: 'TinymceFormDemo', 42 name: 'TinymceFormDemo',
src/router/routes/modules/demo/excel.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { LAYOUT } from '/@/router/constant';
4 4
5 const excel: AppRouteModule = { 5 const excel: AppRouteModule = {
6 - layout: {  
7 - path: '/excel',  
8 - name: 'Excel',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/excel/customExport',  
11 - meta: {  
12 - icon: 'mdi:microsoft-excel',  
13 - title: 'routes.demo.excel.excel',  
14 - }, 6 + path: '/excel',
  7 + name: 'Excel',
  8 + component: LAYOUT,
  9 + redirect: '/excel/customExport',
  10 + meta: {
  11 + icon: 'mdi:microsoft-excel',
  12 + title: 'routes.demo.excel.excel',
15 }, 13 },
16 14
17 - routes: [ 15 + children: [
18 { 16 {
19 - path: '/customExport', 17 + path: 'customExport',
20 name: 'CustomExport', 18 name: 'CustomExport',
21 component: () => import('/@/views/demo/excel/CustomExport.vue'), 19 component: () => import('/@/views/demo/excel/CustomExport.vue'),
22 meta: { 20 meta: {
@@ -24,7 +22,7 @@ const excel: AppRouteModule = { @@ -24,7 +22,7 @@ const excel: AppRouteModule = {
24 }, 22 },
25 }, 23 },
26 { 24 {
27 - path: '/jsonExport', 25 + path: 'jsonExport',
28 name: 'JsonExport', 26 name: 'JsonExport',
29 component: () => import('/@/views/demo/excel/JsonExport.vue'), 27 component: () => import('/@/views/demo/excel/JsonExport.vue'),
30 meta: { 28 meta: {
@@ -32,7 +30,7 @@ const excel: AppRouteModule = { @@ -32,7 +30,7 @@ const excel: AppRouteModule = {
32 }, 30 },
33 }, 31 },
34 { 32 {
35 - path: '/arrayExport', 33 + path: 'arrayExport',
36 name: 'ArrayExport', 34 name: 'ArrayExport',
37 component: () => import('/@/views/demo/excel/ArrayExport.vue'), 35 component: () => import('/@/views/demo/excel/ArrayExport.vue'),
38 meta: { 36 meta: {
@@ -40,7 +38,7 @@ const excel: AppRouteModule = { @@ -40,7 +38,7 @@ const excel: AppRouteModule = {
40 }, 38 },
41 }, 39 },
42 { 40 {
43 - path: '/importExcel', 41 + path: 'importExcel',
44 name: 'ImportExcel', 42 name: 'ImportExcel',
45 component: () => import('/@/views/demo/excel/ImportExcel.vue'), 43 component: () => import('/@/views/demo/excel/ImportExcel.vue'),
46 meta: { 44 meta: {
src/router/routes/modules/demo/feat.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { LAYOUT } from '/@/router/constant';
4 4
5 const feat: AppRouteModule = { 5 const feat: AppRouteModule = {
6 - layout: {  
7 - path: '/feat',  
8 - name: 'FeatDemo',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/feat/icon',  
11 - meta: {  
12 - icon: 'ic:outline-featured-play-list',  
13 - title: 'routes.demo.feat.feat',  
14 - }, 6 + path: '/feat',
  7 + name: 'FeatDemo',
  8 + component: LAYOUT,
  9 + redirect: '/feat/icon',
  10 + meta: {
  11 + icon: 'ic:outline-featured-play-list',
  12 + title: 'routes.demo.feat.feat',
15 }, 13 },
16 -  
17 - routes: [ 14 + children: [
18 { 15 {
19 - path: '/icon', 16 + path: 'icon',
20 name: 'IconDemo', 17 name: 'IconDemo',
21 component: () => import('/@/views/demo/feat/icon/index.vue'), 18 component: () => import('/@/views/demo/feat/icon/index.vue'),
22 meta: { 19 meta: {
@@ -24,7 +21,7 @@ const feat: AppRouteModule = { @@ -24,7 +21,7 @@ const feat: AppRouteModule = {
24 }, 21 },
25 }, 22 },
26 { 23 {
27 - path: '/tabs', 24 + path: 'tabs',
28 name: 'TabsDemo', 25 name: 'TabsDemo',
29 component: () => import('/@/views/demo/feat/tabs/index.vue'), 26 component: () => import('/@/views/demo/feat/tabs/index.vue'),
30 meta: { 27 meta: {
@@ -33,7 +30,7 @@ const feat: AppRouteModule = { @@ -33,7 +30,7 @@ const feat: AppRouteModule = {
33 }, 30 },
34 31
35 { 32 {
36 - path: '/context-menu', 33 + path: 'context-menu',
37 name: 'ContextMenuDemo', 34 name: 'ContextMenuDemo',
38 component: () => import('/@/views/demo/feat/context-menu/index.vue'), 35 component: () => import('/@/views/demo/feat/context-menu/index.vue'),
39 meta: { 36 meta: {
@@ -41,7 +38,7 @@ const feat: AppRouteModule = { @@ -41,7 +38,7 @@ const feat: AppRouteModule = {
41 }, 38 },
42 }, 39 },
43 { 40 {
44 - path: '/download', 41 + path: 'download',
45 name: 'DownLoadDemo', 42 name: 'DownLoadDemo',
46 component: () => import('/@/views/demo/feat/download/index.vue'), 43 component: () => import('/@/views/demo/feat/download/index.vue'),
47 meta: { 44 meta: {
@@ -49,7 +46,7 @@ const feat: AppRouteModule = { @@ -49,7 +46,7 @@ const feat: AppRouteModule = {
49 }, 46 },
50 }, 47 },
51 { 48 {
52 - path: '/click-out-side', 49 + path: 'click-out-side',
53 name: 'ClickOutSideDemo', 50 name: 'ClickOutSideDemo',
54 component: () => import('/@/views/demo/feat/click-out-side/index.vue'), 51 component: () => import('/@/views/demo/feat/click-out-side/index.vue'),
55 meta: { 52 meta: {
@@ -57,7 +54,7 @@ const feat: AppRouteModule = { @@ -57,7 +54,7 @@ const feat: AppRouteModule = {
57 }, 54 },
58 }, 55 },
59 { 56 {
60 - path: '/img-preview', 57 + path: 'img-preview',
61 name: 'ImgPreview', 58 name: 'ImgPreview',
62 component: () => import('/@/views/demo/feat/img-preview/index.vue'), 59 component: () => import('/@/views/demo/feat/img-preview/index.vue'),
63 meta: { 60 meta: {
@@ -65,7 +62,7 @@ const feat: AppRouteModule = { @@ -65,7 +62,7 @@ const feat: AppRouteModule = {
65 }, 62 },
66 }, 63 },
67 { 64 {
68 - path: '/copy', 65 + path: 'copy',
69 name: 'CopyDemo', 66 name: 'CopyDemo',
70 component: () => import('/@/views/demo/feat/copy/index.vue'), 67 component: () => import('/@/views/demo/feat/copy/index.vue'),
71 meta: { 68 meta: {
@@ -73,7 +70,7 @@ const feat: AppRouteModule = { @@ -73,7 +70,7 @@ const feat: AppRouteModule = {
73 }, 70 },
74 }, 71 },
75 { 72 {
76 - path: '/msg', 73 + path: 'msg',
77 name: 'MsgDemo', 74 name: 'MsgDemo',
78 component: () => import('/@/views/demo/feat/msg/index.vue'), 75 component: () => import('/@/views/demo/feat/msg/index.vue'),
79 meta: { 76 meta: {
@@ -81,7 +78,7 @@ const feat: AppRouteModule = { @@ -81,7 +78,7 @@ const feat: AppRouteModule = {
81 }, 78 },
82 }, 79 },
83 { 80 {
84 - path: '/watermark', 81 + path: 'watermark',
85 name: 'WatermarkDemo', 82 name: 'WatermarkDemo',
86 component: () => import('/@/views/demo/feat/watermark/index.vue'), 83 component: () => import('/@/views/demo/feat/watermark/index.vue'),
87 meta: { 84 meta: {
@@ -89,7 +86,7 @@ const feat: AppRouteModule = { @@ -89,7 +86,7 @@ const feat: AppRouteModule = {
89 }, 86 },
90 }, 87 },
91 { 88 {
92 - path: '/full-screen', 89 + path: 'full-screen',
93 name: 'FullScreenDemo', 90 name: 'FullScreenDemo',
94 component: () => import('/@/views/demo/feat/full-screen/index.vue'), 91 component: () => import('/@/views/demo/feat/full-screen/index.vue'),
95 meta: { 92 meta: {
@@ -97,7 +94,7 @@ const feat: AppRouteModule = { @@ -97,7 +94,7 @@ const feat: AppRouteModule = {
97 }, 94 },
98 }, 95 },
99 { 96 {
100 - path: '/error-log', 97 + path: 'error-log',
101 name: 'ErrorLog', 98 name: 'ErrorLog',
102 component: () => import('/@/views/sys/error-log/index.vue'), 99 component: () => import('/@/views/sys/error-log/index.vue'),
103 meta: { 100 meta: {
@@ -105,7 +102,7 @@ const feat: AppRouteModule = { @@ -105,7 +102,7 @@ const feat: AppRouteModule = {
105 }, 102 },
106 }, 103 },
107 { 104 {
108 - path: '/testTab/:id', 105 + path: 'testTab/:id',
109 name: 'TestTab', 106 name: 'TestTab',
110 component: () => import('/@/views/demo/feat/tab-params/index.vue'), 107 component: () => import('/@/views/demo/feat/tab-params/index.vue'),
111 meta: { 108 meta: {
src/router/routes/modules/demo/form.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { LAYOUT } from '/@/router/constant';
4 4
5 const form: AppRouteModule = { 5 const form: AppRouteModule = {
6 - layout: {  
7 - path: '/form',  
8 - name: 'FormDemo',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/form/basic',  
11 - meta: {  
12 - icon: 'ant-design:table-outlined',  
13 - title: 'routes.demo.form.form',  
14 - }, 6 + path: '/form',
  7 + name: 'FormDemo',
  8 + component: LAYOUT,
  9 + redirect: '/form/basic',
  10 + meta: {
  11 + icon: 'ant-design:table-outlined',
  12 + title: 'routes.demo.form.form',
15 }, 13 },
16 -  
17 - routes: [ 14 + children: [
18 { 15 {
19 - path: '/basic', 16 + path: 'basic',
20 name: 'FormBasicDemo', 17 name: 'FormBasicDemo',
21 component: () => import('/@/views/demo/form/index.vue'), 18 component: () => import('/@/views/demo/form/index.vue'),
22 meta: { 19 meta: {
@@ -24,7 +21,7 @@ const form: AppRouteModule = { @@ -24,7 +21,7 @@ const form: AppRouteModule = {
24 }, 21 },
25 }, 22 },
26 { 23 {
27 - path: '/useForm', 24 + path: 'useForm',
28 name: 'UseFormDemo', 25 name: 'UseFormDemo',
29 component: () => import('/@/views/demo/form/UseForm.vue'), 26 component: () => import('/@/views/demo/form/UseForm.vue'),
30 meta: { 27 meta: {
@@ -32,7 +29,7 @@ const form: AppRouteModule = { @@ -32,7 +29,7 @@ const form: AppRouteModule = {
32 }, 29 },
33 }, 30 },
34 { 31 {
35 - path: '/refForm', 32 + path: 'refForm',
36 name: 'RefFormDemo', 33 name: 'RefFormDemo',
37 component: () => import('/@/views/demo/form/RefForm.vue'), 34 component: () => import('/@/views/demo/form/RefForm.vue'),
38 meta: { 35 meta: {
@@ -40,7 +37,7 @@ const form: AppRouteModule = { @@ -40,7 +37,7 @@ const form: AppRouteModule = {
40 }, 37 },
41 }, 38 },
42 { 39 {
43 - path: '/advancedForm', 40 + path: 'advancedForm',
44 name: 'AdvancedFormDemo', 41 name: 'AdvancedFormDemo',
45 component: () => import('/@/views/demo/form/AdvancedForm.vue'), 42 component: () => import('/@/views/demo/form/AdvancedForm.vue'),
46 meta: { 43 meta: {
@@ -48,7 +45,7 @@ const form: AppRouteModule = { @@ -48,7 +45,7 @@ const form: AppRouteModule = {
48 }, 45 },
49 }, 46 },
50 { 47 {
51 - path: '/ruleForm', 48 + path: 'ruleForm',
52 name: 'RuleFormDemo', 49 name: 'RuleFormDemo',
53 component: () => import('/@/views/demo/form/RuleForm.vue'), 50 component: () => import('/@/views/demo/form/RuleForm.vue'),
54 meta: { 51 meta: {
@@ -56,7 +53,7 @@ const form: AppRouteModule = { @@ -56,7 +53,7 @@ const form: AppRouteModule = {
56 }, 53 },
57 }, 54 },
58 { 55 {
59 - path: '/dynamicForm', 56 + path: 'dynamicForm',
60 name: 'DynamicFormDemo', 57 name: 'DynamicFormDemo',
61 component: () => import('/@/views/demo/form/DynamicForm.vue'), 58 component: () => import('/@/views/demo/form/DynamicForm.vue'),
62 meta: { 59 meta: {
@@ -64,7 +61,7 @@ const form: AppRouteModule = { @@ -64,7 +61,7 @@ const form: AppRouteModule = {
64 }, 61 },
65 }, 62 },
66 { 63 {
67 - path: '/customerForm', 64 + path: 'customerForm',
68 name: 'CustomerFormDemo', 65 name: 'CustomerFormDemo',
69 component: () => import('/@/views/demo/form/CustomerForm.vue'), 66 component: () => import('/@/views/demo/form/CustomerForm.vue'),
70 meta: { 67 meta: {
src/router/routes/modules/demo/iframe.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { LAYOUT } from '/@/router/constant';
4 const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue'); 4 const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue');
5 5
6 const iframe: AppRouteModule = { 6 const iframe: AppRouteModule = {
7 - layout: {  
8 - path: '/frame',  
9 - name: 'Frame',  
10 - component: PAGE_LAYOUT_COMPONENT,  
11 - redirect: '/frame/antv',  
12 - meta: {  
13 - icon: 'mdi:page-next-outline',  
14 - title: 'routes.demo.iframe.frame',  
15 - }, 7 + path: '/frame',
  8 + name: 'Frame',
  9 + component: LAYOUT,
  10 + redirect: '/frame/antv',
  11 + meta: {
  12 + icon: 'mdi:page-next-outline',
  13 + title: 'routes.demo.iframe.frame',
16 }, 14 },
17 15
18 - routes: [ 16 + children: [
19 { 17 {
20 - path: '/antv', 18 + path: 'antv',
21 name: 'Antv', 19 name: 'Antv',
22 component: IFrame, 20 component: IFrame,
23 meta: { 21 meta: {
@@ -27,7 +25,7 @@ const iframe: AppRouteModule = { @@ -27,7 +25,7 @@ const iframe: AppRouteModule = {
27 }, 25 },
28 }, 26 },
29 { 27 {
30 - path: '/doc', 28 + path: 'doc',
31 name: 'Doc', 29 name: 'Doc',
32 component: IFrame, 30 component: IFrame,
33 meta: { 31 meta: {
@@ -37,7 +35,7 @@ const iframe: AppRouteModule = { @@ -37,7 +35,7 @@ const iframe: AppRouteModule = {
37 }, 35 },
38 }, 36 },
39 { 37 {
40 - path: '/docExternal', 38 + path: 'docExternal',
41 name: 'DocExternal', 39 name: 'DocExternal',
42 component: IFrame, 40 component: IFrame,
43 meta: { 41 meta: {
src/router/routes/modules/demo/level.ts 0 → 100644
  1 +import type { AppRouteModule } from '/@/router/types';
  2 +
  3 +import { getParentLayout, LAYOUT } from '/@/router/constant';
  4 +
  5 +const permission: AppRouteModule = {
  6 + path: '/level',
  7 + name: 'Level',
  8 + component: LAYOUT,
  9 + redirect: '/level/menu1/menu1-1',
  10 + meta: {
  11 + icon: 'carbon:user-role',
  12 + title: 'routes.demo.level.level',
  13 + },
  14 +
  15 + children: [
  16 + {
  17 + path: 'menu1',
  18 + name: 'Menu1Demo',
  19 + component: getParentLayout('Menu1Demo'),
  20 + meta: {
  21 + title: 'Menu1',
  22 + },
  23 + children: [
  24 + {
  25 + path: 'menu1-1',
  26 + name: 'Menu11Demo',
  27 + component: getParentLayout('Menu11Demo'),
  28 + meta: {
  29 + title: 'Menu1-1',
  30 + },
  31 + children: [
  32 + {
  33 + path: 'menu1-1-1',
  34 + name: 'Menu111Demo',
  35 + component: () => import('/@/views/demo/level/Menu111.vue'),
  36 + meta: {
  37 + title: 'Menu111',
  38 + },
  39 + },
  40 + ],
  41 + },
  42 + {
  43 + path: 'menu1-2',
  44 + name: 'Menu12Demo',
  45 + component: () => import('/@/views/demo/level/Menu12.vue'),
  46 + meta: {
  47 + title: 'Menu1-2',
  48 + },
  49 + },
  50 + ],
  51 + },
  52 + {
  53 + path: 'menu2',
  54 + name: 'Menu2Demo',
  55 + component: () => import('/@/views/demo/level/Menu2.vue'),
  56 + meta: {
  57 + title: 'Menu2',
  58 + },
  59 + },
  60 + ],
  61 +};
  62 +
  63 +export default permission;
src/router/routes/modules/demo/page.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { getParentLayout, LAYOUT } from '/@/router/constant';
4 import { ExceptionEnum } from '/@/enums/exceptionEnum'; 4 import { ExceptionEnum } from '/@/enums/exceptionEnum';
5 5
6 const ExceptionPage = () => import('/@/views/sys/exception/Exception'); 6 const ExceptionPage = () => import('/@/views/sys/exception/Exception');
@@ -8,7 +8,7 @@ const ExceptionPage = () =&gt; import(&#39;/@/views/sys/exception/Exception&#39;); @@ -8,7 +8,7 @@ const ExceptionPage = () =&gt; import(&#39;/@/views/sys/exception/Exception&#39;);
8 const page: AppRouteModule = { 8 const page: AppRouteModule = {
9 path: '/page-demo', 9 path: '/page-demo',
10 name: 'PageDemo', 10 name: 'PageDemo',
11 - component: PAGE_LAYOUT_COMPONENT, 11 + component: LAYOUT,
12 redirect: '/page-demo/exception', 12 redirect: '/page-demo/exception',
13 meta: { 13 meta: {
14 icon: 'mdi:page-next-outline', 14 icon: 'mdi:page-next-outline',
@@ -17,9 +17,10 @@ const page: AppRouteModule = { @@ -17,9 +17,10 @@ const page: AppRouteModule = {
17 children: [ 17 children: [
18 // =============================form start============================= 18 // =============================form start=============================
19 { 19 {
20 - path: '/form', 20 + path: 'form',
21 name: 'FormPage', 21 name: 'FormPage',
22 redirect: '/page-demo/form/basic', 22 redirect: '/page-demo/form/basic',
  23 + component: getParentLayout('FormPage'),
23 meta: { 24 meta: {
24 title: 'routes.demo.page.form', 25 title: 'routes.demo.page.form',
25 }, 26 },
@@ -53,8 +54,9 @@ const page: AppRouteModule = { @@ -53,8 +54,9 @@ const page: AppRouteModule = {
53 // =============================form end============================= 54 // =============================form end=============================
54 // =============================desc start============================= 55 // =============================desc start=============================
55 { 56 {
56 - path: '/desc', 57 + path: 'desc',
57 name: 'DescPage', 58 name: 'DescPage',
  59 + component: getParentLayout('DescPage'),
58 redirect: '/page-demo/desc/basic', 60 redirect: '/page-demo/desc/basic',
59 meta: { 61 meta: {
60 title: 'routes.demo.page.desc', 62 title: 'routes.demo.page.desc',
@@ -82,9 +84,11 @@ const page: AppRouteModule = { @@ -82,9 +84,11 @@ const page: AppRouteModule = {
82 84
83 // =============================result start============================= 85 // =============================result start=============================
84 { 86 {
85 - path: '/result', 87 + path: 'result',
86 name: 'ResultPage', 88 name: 'ResultPage',
87 redirect: '/page-demo/result/success', 89 redirect: '/page-demo/result/success',
  90 + component: getParentLayout('ResultPage'),
  91 +
88 meta: { 92 meta: {
89 title: 'routes.demo.page.result', 93 title: 'routes.demo.page.result',
90 }, 94 },
@@ -111,8 +115,9 @@ const page: AppRouteModule = { @@ -111,8 +115,9 @@ const page: AppRouteModule = {
111 115
112 // =============================account start============================= 116 // =============================account start=============================
113 { 117 {
114 - path: '/account', 118 + path: 'account',
115 name: 'AccountPage', 119 name: 'AccountPage',
  120 + component: getParentLayout('AccountPage'),
116 redirect: '/page-demo/account/setting', 121 redirect: '/page-demo/account/setting',
117 meta: { 122 meta: {
118 title: 'routes.demo.page.account', 123 title: 'routes.demo.page.account',
@@ -139,8 +144,9 @@ const page: AppRouteModule = { @@ -139,8 +144,9 @@ const page: AppRouteModule = {
139 // =============================account end============================= 144 // =============================account end=============================
140 // =============================exception start============================= 145 // =============================exception start=============================
141 { 146 {
142 - path: '/exception', 147 + path: 'exception',
143 name: 'ExceptionPage', 148 name: 'ExceptionPage',
  149 + component: getParentLayout('ExceptionPage'),
144 redirect: '/page-demo/exception/404', 150 redirect: '/page-demo/exception/404',
145 meta: { 151 meta: {
146 title: 'routes.demo.page.exception', 152 title: 'routes.demo.page.exception',
@@ -211,8 +217,9 @@ const page: AppRouteModule = { @@ -211,8 +217,9 @@ const page: AppRouteModule = {
211 // =============================exception end============================= 217 // =============================exception end=============================
212 // =============================list start============================= 218 // =============================list start=============================
213 { 219 {
214 - path: '/list', 220 + path: 'list',
215 name: 'ListPage', 221 name: 'ListPage',
  222 + component: getParentLayout('ListPage'),
216 redirect: '/page-demo/list/card', 223 redirect: '/page-demo/list/card',
217 meta: { 224 meta: {
218 title: 'routes.demo.page.list', 225 title: 'routes.demo.page.list',
src/router/routes/modules/demo/permission.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { getParentLayout, LAYOUT } from '/@/router/constant';
4 import { RoleEnum } from '/@/enums/roleEnum'; 4 import { RoleEnum } from '/@/enums/roleEnum';
5 5
6 const permission: AppRouteModule = { 6 const permission: AppRouteModule = {
7 - layout: {  
8 - path: '/permission',  
9 - name: 'Permission',  
10 - component: PAGE_LAYOUT_COMPONENT,  
11 - redirect: '/permission/front/page',  
12 - meta: {  
13 - icon: 'carbon:user-role',  
14 - title: 'routes.demo.permission.permission',  
15 - }, 7 + path: '/permission',
  8 + name: 'Permission',
  9 + component: LAYOUT,
  10 + redirect: '/permission/front/page',
  11 + meta: {
  12 + icon: 'carbon:user-role',
  13 + title: 'routes.demo.permission.permission',
16 }, 14 },
17 15
18 - routes: [ 16 + children: [
19 { 17 {
20 - path: '/front', 18 + path: 'front',
21 name: 'PermissionFrontDemo', 19 name: 'PermissionFrontDemo',
  20 + component: getParentLayout('PermissionFrontDemo'),
22 meta: { 21 meta: {
23 title: 'routes.demo.permission.front', 22 title: 'routes.demo.permission.front',
24 }, 23 },
@@ -60,8 +59,9 @@ const permission: AppRouteModule = { @@ -60,8 +59,9 @@ const permission: AppRouteModule = {
60 ], 59 ],
61 }, 60 },
62 { 61 {
63 - path: '/back', 62 + path: 'back',
64 name: 'PermissionBackDemo', 63 name: 'PermissionBackDemo',
  64 + component: getParentLayout('PermissionBackDemo'),
65 meta: { 65 meta: {
66 title: 'routes.demo.permission.back', 66 title: 'routes.demo.permission.back',
67 }, 67 },
src/router/routes/modules/demo/table.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { LAYOUT } from '/@/router/constant';
4 4
5 const table: AppRouteModule = { 5 const table: AppRouteModule = {
6 - layout: {  
7 - path: '/table',  
8 - name: 'TableDemo',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/table/basic',  
11 - meta: {  
12 - icon: 'ant-design:table-outlined',  
13 - title: 'routes.demo.table.table',  
14 - }, 6 + path: '/table',
  7 + name: 'TableDemo',
  8 + component: LAYOUT,
  9 + redirect: '/table/basic',
  10 + meta: {
  11 + icon: 'ant-design:table-outlined',
  12 + title: 'routes.demo.table.table',
15 }, 13 },
16 14
17 - routes: [ 15 + children: [
18 { 16 {
19 - path: '/basic', 17 + path: 'basic',
20 name: 'TableBasicDemo', 18 name: 'TableBasicDemo',
21 component: () => import('/@/views/demo/table/Basic.vue'), 19 component: () => import('/@/views/demo/table/Basic.vue'),
22 meta: { 20 meta: {
@@ -24,7 +22,7 @@ const table: AppRouteModule = { @@ -24,7 +22,7 @@ const table: AppRouteModule = {
24 }, 22 },
25 }, 23 },
26 { 24 {
27 - path: '/treeTable', 25 + path: 'treeTable',
28 name: 'TreeTableDemo', 26 name: 'TreeTableDemo',
29 component: () => import('/@/views/demo/table/TreeTable.vue'), 27 component: () => import('/@/views/demo/table/TreeTable.vue'),
30 meta: { 28 meta: {
@@ -32,7 +30,7 @@ const table: AppRouteModule = { @@ -32,7 +30,7 @@ const table: AppRouteModule = {
32 }, 30 },
33 }, 31 },
34 { 32 {
35 - path: '/fetchTable', 33 + path: 'fetchTable',
36 name: 'FetchTableDemo', 34 name: 'FetchTableDemo',
37 component: () => import('/@/views/demo/table/FetchTable.vue'), 35 component: () => import('/@/views/demo/table/FetchTable.vue'),
38 meta: { 36 meta: {
@@ -40,7 +38,7 @@ const table: AppRouteModule = { @@ -40,7 +38,7 @@ const table: AppRouteModule = {
40 }, 38 },
41 }, 39 },
42 { 40 {
43 - path: '/fixedColumn', 41 + path: 'fixedColumn',
44 name: 'FixedColumnDemo', 42 name: 'FixedColumnDemo',
45 component: () => import('/@/views/demo/table/FixedColumn.vue'), 43 component: () => import('/@/views/demo/table/FixedColumn.vue'),
46 meta: { 44 meta: {
@@ -48,7 +46,7 @@ const table: AppRouteModule = { @@ -48,7 +46,7 @@ const table: AppRouteModule = {
48 }, 46 },
49 }, 47 },
50 { 48 {
51 - path: '/customerCell', 49 + path: 'customerCell',
52 name: 'CustomerCellDemo', 50 name: 'CustomerCellDemo',
53 component: () => import('/@/views/demo/table/CustomerCell.vue'), 51 component: () => import('/@/views/demo/table/CustomerCell.vue'),
54 meta: { 52 meta: {
@@ -56,7 +54,7 @@ const table: AppRouteModule = { @@ -56,7 +54,7 @@ const table: AppRouteModule = {
56 }, 54 },
57 }, 55 },
58 { 56 {
59 - path: '/formTable', 57 + path: 'formTable',
60 name: 'FormTableDemo', 58 name: 'FormTableDemo',
61 component: () => import('/@/views/demo/table/FormTable.vue'), 59 component: () => import('/@/views/demo/table/FormTable.vue'),
62 meta: { 60 meta: {
@@ -64,7 +62,7 @@ const table: AppRouteModule = { @@ -64,7 +62,7 @@ const table: AppRouteModule = {
64 }, 62 },
65 }, 63 },
66 { 64 {
67 - path: '/useTable', 65 + path: 'useTable',
68 name: 'UseTableDemo', 66 name: 'UseTableDemo',
69 component: () => import('/@/views/demo/table/UseTable.vue'), 67 component: () => import('/@/views/demo/table/UseTable.vue'),
70 meta: { 68 meta: {
@@ -72,7 +70,7 @@ const table: AppRouteModule = { @@ -72,7 +70,7 @@ const table: AppRouteModule = {
72 }, 70 },
73 }, 71 },
74 { 72 {
75 - path: '/refTable', 73 + path: 'refTable',
76 name: 'RefTableDemo', 74 name: 'RefTableDemo',
77 component: () => import('/@/views/demo/table/RefTable.vue'), 75 component: () => import('/@/views/demo/table/RefTable.vue'),
78 meta: { 76 meta: {
@@ -80,7 +78,7 @@ const table: AppRouteModule = { @@ -80,7 +78,7 @@ const table: AppRouteModule = {
80 }, 78 },
81 }, 79 },
82 { 80 {
83 - path: '/multipleHeader', 81 + path: 'multipleHeader',
84 name: 'MultipleHeaderDemo', 82 name: 'MultipleHeaderDemo',
85 component: () => import('/@/views/demo/table/MultipleHeader.vue'), 83 component: () => import('/@/views/demo/table/MultipleHeader.vue'),
86 meta: { 84 meta: {
@@ -88,7 +86,7 @@ const table: AppRouteModule = { @@ -88,7 +86,7 @@ const table: AppRouteModule = {
88 }, 86 },
89 }, 87 },
90 { 88 {
91 - path: '/mergeHeader', 89 + path: 'mergeHeader',
92 name: 'MergeHeaderDemo', 90 name: 'MergeHeaderDemo',
93 component: () => import('/@/views/demo/table/MergeHeader.vue'), 91 component: () => import('/@/views/demo/table/MergeHeader.vue'),
94 meta: { 92 meta: {
@@ -96,7 +94,7 @@ const table: AppRouteModule = { @@ -96,7 +94,7 @@ const table: AppRouteModule = {
96 }, 94 },
97 }, 95 },
98 { 96 {
99 - path: '/expandTable', 97 + path: 'expandTable',
100 name: 'ExpandTableDemo', 98 name: 'ExpandTableDemo',
101 component: () => import('/@/views/demo/table/ExpandTable.vue'), 99 component: () => import('/@/views/demo/table/ExpandTable.vue'),
102 meta: { 100 meta: {
@@ -104,7 +102,7 @@ const table: AppRouteModule = { @@ -104,7 +102,7 @@ const table: AppRouteModule = {
104 }, 102 },
105 }, 103 },
106 { 104 {
107 - path: '/fixedHeight', 105 + path: 'fixedHeight',
108 name: 'FixedHeightDemo', 106 name: 'FixedHeightDemo',
109 component: () => import('/@/views/demo/table/FixedHeight.vue'), 107 component: () => import('/@/views/demo/table/FixedHeight.vue'),
110 meta: { 108 meta: {
@@ -112,7 +110,7 @@ const table: AppRouteModule = { @@ -112,7 +110,7 @@ const table: AppRouteModule = {
112 }, 110 },
113 }, 111 },
114 { 112 {
115 - path: '/footerTable', 113 + path: 'footerTable',
116 name: 'FooterTableDemo', 114 name: 'FooterTableDemo',
117 component: () => import('/@/views/demo/table/FooterTable.vue'), 115 component: () => import('/@/views/demo/table/FooterTable.vue'),
118 meta: { 116 meta: {
@@ -120,7 +118,7 @@ const table: AppRouteModule = { @@ -120,7 +118,7 @@ const table: AppRouteModule = {
120 }, 118 },
121 }, 119 },
122 { 120 {
123 - path: '/editCellTable', 121 + path: 'editCellTable',
124 name: 'EditCellTableDemo', 122 name: 'EditCellTableDemo',
125 component: () => import('/@/views/demo/table/EditCellTable.vue'), 123 component: () => import('/@/views/demo/table/EditCellTable.vue'),
126 meta: { 124 meta: {
@@ -128,7 +126,7 @@ const table: AppRouteModule = { @@ -128,7 +126,7 @@ const table: AppRouteModule = {
128 }, 126 },
129 }, 127 },
130 { 128 {
131 - path: '/editRowTable', 129 + path: 'editRowTable',
132 name: 'EditRowTableDemo', 130 name: 'EditRowTableDemo',
133 component: () => import('/@/views/demo/table/EditRowTable.vue'), 131 component: () => import('/@/views/demo/table/EditRowTable.vue'),
134 meta: { 132 meta: {
src/router/routes/modules/demo/tree.ts
1 import type { AppRouteModule } from '/@/router/types'; 1 import type { AppRouteModule } from '/@/router/types';
2 2
3 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; 3 +import { LAYOUT } from '/@/router/constant';
4 4
5 const tree: AppRouteModule = { 5 const tree: AppRouteModule = {
6 - layout: {  
7 - path: '/tree',  
8 - name: 'TreeDemo',  
9 - component: PAGE_LAYOUT_COMPONENT,  
10 - redirect: '/tree/basic',  
11 - meta: {  
12 - icon: 'clarity:tree-view-line',  
13 - title: 'routes.demo.tree.tree',  
14 - }, 6 + path: '/tree',
  7 + name: 'TreeDemo',
  8 + component: LAYOUT,
  9 + redirect: '/tree/basic',
  10 + meta: {
  11 + icon: 'clarity:tree-view-line',
  12 + title: 'routes.demo.tree.tree',
15 }, 13 },
16 - routes: [ 14 + children: [
17 { 15 {
18 - path: '/basic', 16 + path: 'basic',
19 name: 'BasicTreeDemo', 17 name: 'BasicTreeDemo',
20 component: () => import('/@/views/demo/tree/index.vue'), 18 component: () => import('/@/views/demo/tree/index.vue'),
21 meta: { 19 meta: {
@@ -23,7 +21,7 @@ const tree: AppRouteModule = { @@ -23,7 +21,7 @@ const tree: AppRouteModule = {
23 }, 21 },
24 }, 22 },
25 { 23 {
26 - path: '/editTree', 24 + path: 'editTree',
27 name: 'EditTreeDemo', 25 name: 'EditTreeDemo',
28 component: () => import('/@/views/demo/tree/EditTree.vue'), 26 component: () => import('/@/views/demo/tree/EditTree.vue'),
29 meta: { 27 meta: {
@@ -31,7 +29,7 @@ const tree: AppRouteModule = { @@ -31,7 +29,7 @@ const tree: AppRouteModule = {
31 }, 29 },
32 }, 30 },
33 { 31 {
34 - path: '/actionTree', 32 + path: 'actionTree',
35 name: 'ActionTreeDemo', 33 name: 'ActionTreeDemo',
36 component: () => import('/@/views/demo/tree/ActionTree.vue'), 34 component: () => import('/@/views/demo/tree/ActionTree.vue'),
37 meta: { 35 meta: {
src/router/routes/modules/home.ts 0 → 100644
  1 +import type { AppRouteModule } from '/@/router/types';
  2 +
  3 +import { LAYOUT } from '/@/router/constant';
  4 +
  5 +const dashboard: AppRouteModule = {
  6 + path: '/home',
  7 + name: 'Home',
  8 + component: LAYOUT,
  9 + redirect: '/home/welcome',
  10 + meta: {
  11 + icon: 'ant-design:home-outlined',
  12 + title: 'routes.dashboard.welcome',
  13 + },
  14 + children: [
  15 + {
  16 + path: 'welcome',
  17 + name: 'Welcome',
  18 + component: () => import('/@/views/dashboard/welcome/index.vue'),
  19 + meta: {
  20 + title: 'routes.dashboard.welcome',
  21 + affix: true,
  22 + icon: 'ant-design:home-outlined',
  23 + },
  24 + },
  25 + ],
  26 +};
  27 +
  28 +export default dashboard;
src/router/types.d.ts
1 import type { RouteRecordRaw } from 'vue-router'; 1 import type { RouteRecordRaw } from 'vue-router';
2 import { RoleEnum } from '/@/enums/roleEnum'; 2 import { RoleEnum } from '/@/enums/roleEnum';
  3 +import Component from '/@/components/types';
3 export interface RouteMeta { 4 export interface RouteMeta {
4 // title 5 // title
5 title: string; 6 title: string;
@@ -24,24 +25,23 @@ export interface RouteMeta { @@ -24,24 +25,23 @@ export interface RouteMeta {
24 // Whether the route has been dynamically added 25 // Whether the route has been dynamically added
25 hideBreadcrumb?: boolean; 26 hideBreadcrumb?: boolean;
26 27
27 - // disabled redirect  
28 - disabledRedirect?: boolean;  
29 -  
30 // close loading 28 // close loading
31 afterCloseLoading?: boolean; 29 afterCloseLoading?: boolean;
32 // Is it in the tab 30 // Is it in the tab
33 inTab?: boolean; 31 inTab?: boolean;
34 // Carrying parameters 32 // Carrying parameters
35 carryParam?: boolean; 33 carryParam?: boolean;
  34 +
  35 + single?: boolean;
36 } 36 }
37 37
38 export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> { 38 export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
39 name: string; 39 name: string;
40 meta: RouteMeta; 40 meta: RouteMeta;
41 - component?: any;  
42 - components?: any; 41 + component?: Component;
  42 + components?: Component;
43 children?: AppRouteRecordRaw[]; 43 children?: AppRouteRecordRaw[];
44 - props?: any; 44 + props?: Record<string, any>;
45 fullPath?: string; 45 fullPath?: string;
46 } 46 }
47 export interface MenuTag { 47 export interface MenuTag {
@@ -75,11 +75,12 @@ export interface MenuModule { @@ -75,11 +75,12 @@ export interface MenuModule {
75 menu: Menu; 75 menu: Menu;
76 } 76 }
77 77
78 -interface RouteModule {  
79 - layout: AppRouteRecordRaw;  
80 - routes: AppRouteRecordRaw[];  
81 - children?: AppRouteRecordRaw[];  
82 - component?: any;  
83 -} 78 +// interface RouteModule {
  79 +// layout: AppRouteRecordRaw;
  80 +// routes: AppRouteRecordRaw[];
  81 +// children?: AppRouteRecordRaw[];
  82 +// component?: Component;
  83 +// }
84 84
85 -export type AppRouteModule = RouteModule | AppRouteRecordRaw; 85 +// export type AppRouteModule = RouteModule | AppRouteRecordRaw;
  86 +export type AppRouteModule = AppRouteRecordRaw;
src/store/modules/permission.ts
1 -import { REDIRECT_ROUTE } from '/@/router/constant';  
2 import type { AppRouteRecordRaw, Menu } from '/@/router/types'; 1 import type { AppRouteRecordRaw, Menu } from '/@/router/types';
3 import store from '/@/store/index'; 2 import store from '/@/store/index';
4 import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; 3 import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
@@ -15,15 +14,13 @@ import { filter } from &#39;/@/utils/helper/treeHelper&#39;; @@ -15,15 +14,13 @@ import { filter } from &#39;/@/utils/helper/treeHelper&#39;;
15 import { toRaw } from 'vue'; 14 import { toRaw } from 'vue';
16 import { getMenuListById } from '/@/api/sys/menu'; 15 import { getMenuListById } from '/@/api/sys/menu';
17 16
18 -import { genRouteModule, transformObjToRoute } from '/@/utils/helper/routeHelper';  
19 -import { transformRouteToMenu } from '/@/utils/helper/menuHelper'; 17 +import { transformObjToRoute } from '/@/router/helper/routeHelper';
  18 +import { transformRouteToMenu } from '/@/router/helper/menuHelper';
20 19
21 import { useMessage } from '/@/hooks/web/useMessage'; 20 import { useMessage } from '/@/hooks/web/useMessage';
22 // import { warn } from '/@/utils/log'; 21 // import { warn } from '/@/utils/log';
23 import { useI18n } from '/@/hooks/web/useI18n'; 22 import { useI18n } from '/@/hooks/web/useI18n';
24 23
25 -const { t } = useI18n();  
26 -  
27 const { createMessage } = useMessage(); 24 const { createMessage } = useMessage();
28 const NAME = 'permission'; 25 const NAME = 'permission';
29 hotModuleUnregisterModule(NAME); 26 hotModuleUnregisterModule(NAME);
@@ -87,6 +84,7 @@ class Permission extends VuexModule { @@ -87,6 +84,7 @@ class Permission extends VuexModule {
87 84
88 @Action 85 @Action
89 async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> { 86 async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> {
  87 + const { t } = useI18n();
90 let routes: AppRouteRecordRaw[] = []; 88 let routes: AppRouteRecordRaw[] = [];
91 const roleList = toRaw(userStore.getRoleListState); 89 const roleList = toRaw(userStore.getRoleListState);
92 90
@@ -95,17 +93,15 @@ class Permission extends VuexModule { @@ -95,17 +93,15 @@ class Permission extends VuexModule {
95 // role permissions 93 // role permissions
96 if (permissionMode === PermissionModeEnum.ROLE) { 94 if (permissionMode === PermissionModeEnum.ROLE) {
97 routes = filter(asyncRoutes, (route) => { 95 routes = filter(asyncRoutes, (route) => {
98 - const { meta } = route;  
99 - const { roles } = meta!; 96 + const { meta } = route as AppRouteRecordRaw;
  97 + const { roles } = meta || {};
100 if (!roles) return true; 98 if (!roles) return true;
101 return roleList.some((role) => roles.includes(role)); 99 return roleList.some((role) => roles.includes(role));
102 }); 100 });
103 // 如果确定不需要做后台动态权限,请将下面整个判断注释 101 // 如果确定不需要做后台动态权限,请将下面整个判断注释
104 } else if (permissionMode === PermissionModeEnum.BACK) { 102 } else if (permissionMode === PermissionModeEnum.BACK) {
105 - const messageKey = 'loadMenu';  
106 createMessage.loading({ 103 createMessage.loading({
107 content: t('sys.app.menuLoading'), 104 content: t('sys.app.menuLoading'),
108 - key: messageKey,  
109 duration: 1, 105 duration: 1,
110 }); 106 });
111 // 这里获取后台路由菜单逻辑自行修改 107 // 这里获取后台路由菜单逻辑自行修改
@@ -118,10 +114,10 @@ class Permission extends VuexModule { @@ -118,10 +114,10 @@ class Permission extends VuexModule {
118 routeList = transformObjToRoute(routeList); 114 routeList = transformObjToRoute(routeList);
119 // 后台路由转菜单结构 115 // 后台路由转菜单结构
120 const backMenuList = transformRouteToMenu(routeList); 116 const backMenuList = transformRouteToMenu(routeList);
  117 +
121 this.commitBackMenuListState(backMenuList); 118 this.commitBackMenuListState(backMenuList);
122 - // 生成路由  
123 - routes = genRouteModule(routeList) as AppRouteRecordRaw[];  
124 - routes.push(REDIRECT_ROUTE); 119 +
  120 + routes = routeList;
125 } 121 }
126 return routes; 122 return routes;
127 } 123 }
src/store/modules/tab.ts
1 -import { computed, toRaw } from 'vue';  
2 -import type { AppRouteRecordRaw, RouteMeta } from '/@/router/types.d'; 1 +import { toRaw } from 'vue';
3 2
4 import { unref } from 'vue'; 3 import { unref } from 'vue';
5 import { Action, Module, Mutation, VuexModule, getModule } from 'vuex-module-decorators'; 4 import { Action, Module, Mutation, VuexModule, getModule } from 'vuex-module-decorators';
6 import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; 5 import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
7 6
8 import { PageEnum } from '/@/enums/pageEnum'; 7 import { PageEnum } from '/@/enums/pageEnum';
9 -import { appStore } from '/@/store/modules/app';  
10 import { userStore } from './user'; 8 import { userStore } from './user';
11 9
12 import store from '/@/store'; 10 import store from '/@/store';
13 import router from '/@/router'; 11 import router from '/@/router';
14 import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/constant'; 12 import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/constant';
15 -import { getCurrentTo } from '/@/utils/helper/routeHelper'; 13 +import { RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
  14 +import { getRoute } from '/@/router/helper/routeHelper';
  15 +import { useGo, useRedo } from '/@/hooks/web/usePage';
16 16
17 -type CacheName = string | symbol | null | undefined;  
18 -  
19 -/**  
20 - * @description: vuex Tab模块  
21 - */  
22 // declare namespace TabsStore { 17 // declare namespace TabsStore {
23 -export interface TabItem {  
24 - fullPath: string;  
25 - path?: string;  
26 - params?: any;  
27 - query?: any;  
28 - name?: CacheName;  
29 - meta?: RouteMeta;  
30 -}  
31 18
32 const NAME = 'tab'; 19 const NAME = 'tab';
33 20
34 hotModuleUnregisterModule(NAME); 21 hotModuleUnregisterModule(NAME);
35 22
36 -const getOpenKeepAliveRef = computed(() => appStore.getProjectConfig.openKeepAlive); 23 +export const PAGE_LAYOUT_KEY = '__PAGE_LAYOUT__';
  24 +
  25 +function isGotoPage() {
  26 + const go = useGo();
  27 + go(unref(router.currentRoute).path, true);
  28 +}
37 29
38 @Module({ namespaced: true, name: NAME, dynamic: true, store }) 30 @Module({ namespaced: true, name: NAME, dynamic: true, store })
39 class Tab extends VuexModule { 31 class Tab extends VuexModule {
40 - // tab list  
41 - tabsState: TabItem[] = [];  
42 - // tab cache list  
43 - keepAliveTabsState: CacheName[] = []; 32 + cachedMapState = new Map<string, string[]>();
44 33
45 - currentContextMenuIndexState = -1;  
46 -  
47 - currentContextMenuState: TabItem | null = null; 34 + // tab list
  35 + tabsState: RouteLocationNormalized[] = [];
48 36
49 // Last route change 37 // Last route change
50 - lastChangeRouteState: AppRouteRecordRaw | null = null; 38 + lastChangeRouteState: RouteLocationNormalized | null = null;
  39 +
  40 + lastDragEndIndexState = 0;
51 41
52 get getTabsState() { 42 get getTabsState() {
53 return this.tabsState; 43 return this.tabsState;
@@ -57,56 +47,93 @@ class Tab extends VuexModule { @@ -57,56 +47,93 @@ class Tab extends VuexModule {
57 return this.lastChangeRouteState; 47 return this.lastChangeRouteState;
58 } 48 }
59 49
60 - get getCurrentContextMenuIndexState() {  
61 - return this.currentContextMenuIndexState;  
62 - }  
63 -  
64 - get getCurrentContextMenuState() {  
65 - return this.currentContextMenuState; 50 + get getCurrentTab(): RouteLocationNormalized {
  51 + const route = unref(router.currentRoute);
  52 + return this.tabsState.find((item) => item.path === route.path)!;
66 } 53 }
67 54
68 - get getKeepAliveTabsState() {  
69 - return this.keepAliveTabsState; 55 + get getCachedMapState(): Map<string, string[]> {
  56 + return this.cachedMapState;
70 } 57 }
71 58
72 - get getCurrentTab(): TabItem {  
73 - const route = unref(router.currentRoute);  
74 - return this.tabsState.find((item) => item.path === route.path)!; 59 + get getLastDragEndIndexState(): number {
  60 + return this.lastDragEndIndexState;
75 } 61 }
76 62
77 @Mutation 63 @Mutation
78 - commitLastChangeRouteState(route: AppRouteRecordRaw): void { 64 + commitLastChangeRouteState(route: RouteLocationNormalized): void {
79 if (!userStore.getTokenState) return; 65 if (!userStore.getTokenState) return;
80 this.lastChangeRouteState = route; 66 this.lastChangeRouteState = route;
81 } 67 }
82 68
83 @Mutation 69 @Mutation
84 commitClearCache(): void { 70 commitClearCache(): void {
85 - this.keepAliveTabsState = []; 71 + this.cachedMapState = new Map();
86 } 72 }
87 73
88 @Mutation 74 @Mutation
89 - commitCurrentContextMenuIndexState(index: number): void {  
90 - this.currentContextMenuIndexState = index; 75 + goToPage() {
  76 + const go = useGo();
  77 + const len = this.tabsState.length;
  78 + const { path } = unref(router.currentRoute);
  79 +
  80 + let toPath: PageEnum | string = PageEnum.BASE_HOME;
  81 +
  82 + if (len > 0) {
  83 + const page = this.tabsState[len - 1];
  84 + const p = page.fullPath || page.path;
  85 + if (p) {
  86 + toPath = p;
  87 + }
  88 + }
  89 + // Jump to the current page and report an error
  90 + path !== toPath && go(toPath as PageEnum, true);
91 } 91 }
92 92
93 @Mutation 93 @Mutation
94 - commitCurrentContextMenuState(item: TabItem): void {  
95 - this.currentContextMenuState = item; 94 + commitCachedMapState(): void {
  95 + const cacheMap = new Map<string, string[]>();
  96 +
  97 + const pageCacheSet = new Set<string>();
  98 + this.tabsState.forEach((tab) => {
  99 + const item = getRoute(tab);
  100 + const needAuth = !item.meta.ignoreAuth;
  101 + if (item.meta.affix) {
  102 + const name = item.name as string;
  103 + pageCacheSet.add(name);
  104 + } else if (item.matched && needAuth) {
  105 + const matched = item.matched;
  106 + const len = matched.length;
  107 +
  108 + if (len < 2) return;
  109 +
  110 + for (let i = 0; i < matched.length; i++) {
  111 + const key = matched[i].name as string;
  112 +
  113 + if (i < 2) {
  114 + pageCacheSet.add(key);
  115 + }
  116 + if (i < len - 1) {
  117 + const { meta, name } = matched[i + 1];
  118 + if (meta && (meta.affix || needAuth)) {
  119 + const mapList = cacheMap.get(key) || [];
  120 + if (!mapList.includes(name as string)) {
  121 + mapList.push(name as string);
  122 + }
  123 + cacheMap.set(key, mapList);
  124 + }
  125 + }
  126 + }
  127 + }
  128 + });
  129 +
  130 + cacheMap.set(PAGE_LAYOUT_KEY, Array.from(pageCacheSet));
  131 + this.cachedMapState = cacheMap;
96 } 132 }
97 133
98 - /**  
99 - * @description: add tab  
100 - */  
101 @Mutation 134 @Mutation
102 - commitAddTab(route: AppRouteRecordRaw | TabItem): void {  
103 - const { path, name, meta, fullPath, params, query } = route as TabItem;  
104 - // 404 页面不需要添加tab  
105 - if (path === PageEnum.ERROR_PAGE || !name) {  
106 - return;  
107 - } else if ([REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)) {  
108 - return;  
109 - } 135 + commitTabRoutesState(route: RouteLocationNormalized) {
  136 + const { path, fullPath, params, query } = route;
110 137
111 let updateIndex = -1; 138 let updateIndex = -1;
112 // 已经存在的页面,不重复添加tab 139 // 已经存在的页面,不重复添加tab
@@ -123,39 +150,18 @@ class Tab extends VuexModule { @@ -123,39 +150,18 @@ class Tab extends VuexModule {
123 this.tabsState.splice(updateIndex, 1, curTab); 150 this.tabsState.splice(updateIndex, 1, curTab);
124 return; 151 return;
125 } 152 }
126 - this.tabsState.push({ path, fullPath, name, meta, params, query });  
127 - if (unref(getOpenKeepAliveRef) && name) {  
128 - const noKeepAlive = meta && meta.ignoreKeepAlive;  
129 - const hasName = this.keepAliveTabsState.includes(name);  
130 - !noKeepAlive && !hasName && this.keepAliveTabsState.push(name);  
131 - } 153 + this.tabsState.push(route);
132 } 154 }
133 155
134 /** 156 /**
135 * @description: close tab 157 * @description: close tab
136 */ 158 */
137 @Mutation 159 @Mutation
138 - commitCloseTab(route: AppRouteRecordRaw | TabItem): void {  
139 - try {  
140 - const { fullPath, name, meta: { affix } = {} } = route;  
141 - if (affix) return;  
142 - const index = this.tabsState.findIndex((item) => item.fullPath === fullPath);  
143 - index !== -1 && this.tabsState.splice(index, 1);  
144 -  
145 - if (unref(getOpenKeepAliveRef) && name) {  
146 - const i = this.keepAliveTabsState.findIndex((item) => item === name);  
147 - i !== -1 && this.keepAliveTabsState.splice(i, 1);  
148 - }  
149 - } catch (error) {}  
150 - }  
151 -  
152 - @Mutation  
153 - commitCloseTabKeepAlive(route: AppRouteRecordRaw | TabItem): void {  
154 - const { name } = route;  
155 - if (unref(getOpenKeepAliveRef) && name) {  
156 - const i = this.keepAliveTabsState.findIndex((item) => item === name);  
157 - i !== -1 && toRaw(this.keepAliveTabsState).splice(i, 1);  
158 - } 160 + commitCloseTab(route: RouteLocationNormalized): void {
  161 + const { fullPath, meta: { affix } = {} } = route;
  162 + if (affix) return;
  163 + const index = this.tabsState.findIndex((item) => item.fullPath === fullPath);
  164 + index !== -1 && this.tabsState.splice(index, 1);
159 } 165 }
160 166
161 @Mutation 167 @Mutation
@@ -163,16 +169,12 @@ class Tab extends VuexModule { @@ -163,16 +169,12 @@ class Tab extends VuexModule {
163 this.tabsState = this.tabsState.filter((item) => { 169 this.tabsState = this.tabsState.filter((item) => {
164 return item.meta && item.meta.affix; 170 return item.meta && item.meta.affix;
165 }); 171 });
166 - const names = this.tabsState.map((item) => item.name);  
167 - this.keepAliveTabsState = names as string[];  
168 } 172 }
169 173
170 @Mutation 174 @Mutation
171 commitResetState(): void { 175 commitResetState(): void {
172 this.tabsState = []; 176 this.tabsState = [];
173 - this.currentContextMenuState = null;  
174 - this.currentContextMenuIndexState = -1;  
175 - this.keepAliveTabsState = []; 177 + this.cachedMapState = new Map();
176 } 178 }
177 179
178 @Mutation 180 @Mutation
@@ -181,73 +183,149 @@ class Tab extends VuexModule { @@ -181,73 +183,149 @@ class Tab extends VuexModule {
181 183
182 this.tabsState.splice(oldIndex, 1); 184 this.tabsState.splice(oldIndex, 1);
183 this.tabsState.splice(newIndex, 0, currentTab); 185 this.tabsState.splice(newIndex, 0, currentTab);
  186 + this.lastDragEndIndexState = this.lastDragEndIndexState + 1;
184 } 187 }
185 188
186 @Mutation 189 @Mutation
187 - closeMultipleTab({ pathList, nameList }: { pathList: string[]; nameList: string[] }): void { 190 + closeMultipleTab({ pathList }: { pathList: string[] }): void {
188 this.tabsState = toRaw(this.tabsState).filter((item) => !pathList.includes(item.fullPath)); 191 this.tabsState = toRaw(this.tabsState).filter((item) => !pathList.includes(item.fullPath));
189 - if (unref(getOpenKeepAliveRef) && nameList) {  
190 - this.keepAliveTabsState = toRaw(this.keepAliveTabsState).filter(  
191 - (item) => !nameList.includes(item as string)  
192 - ); 192 + }
  193 +
  194 + @Action
  195 + addTabAction(route: RouteLocationNormalized) {
  196 + const { path, name } = route;
  197 + // 404 页面不需要添加tab
  198 + if (
  199 + path === PageEnum.ERROR_PAGE ||
  200 + !name ||
  201 + [REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)
  202 + ) {
  203 + return;
  204 + }
  205 + this.commitTabRoutesState(getRoute(route));
  206 +
  207 + this.commitCachedMapState();
  208 + }
  209 +
  210 + @Mutation
  211 + commitRedoPage() {
  212 + const route = router.currentRoute.value;
  213 +
  214 + for (const [key, value] of this.cachedMapState) {
  215 + const index = value.findIndex((item) => item === (route.name as string));
  216 + if (index === -1) {
  217 + continue;
  218 + }
  219 + if (value.length === 1) {
  220 + this.cachedMapState.delete(key);
  221 + continue;
  222 + }
  223 + value.splice(index, 1);
  224 + this.cachedMapState.set(key, value);
  225 + }
  226 + const redo = useRedo();
  227 + redo();
  228 + }
  229 +
  230 + @Action
  231 + closeAllTabAction() {
  232 + this.commitCloseAllTab();
  233 + this.commitClearCache();
  234 + this.goToPage();
  235 + }
  236 +
  237 + @Action
  238 + closeTabAction(tab: RouteLocationNormalized) {
  239 + function getObj(tabItem: RouteLocationNormalized) {
  240 + const { params, path, query } = tabItem;
  241 + return {
  242 + params: params || {},
  243 + path,
  244 + query: query || {},
  245 + };
  246 + }
  247 + const { currentRoute, replace } = router;
  248 +
  249 + const { path } = unref(currentRoute);
  250 + if (path !== tab.path) {
  251 + // Closed is not the activation tab
  252 + this.commitCloseTab(tab);
  253 + return;
  254 + }
  255 +
  256 + // Closed is activated atb
  257 + let toObj: RouteLocationRaw = {};
  258 +
  259 + const index = this.getTabsState.findIndex((item) => item.path === path);
  260 +
  261 + // If the current is the leftmost tab
  262 + if (index === 0) {
  263 + // There is only one tab, then jump to the homepage, otherwise jump to the right tab
  264 + if (this.getTabsState.length === 1) {
  265 + toObj = PageEnum.BASE_HOME;
  266 + } else {
  267 + // Jump to the right tab
  268 + const page = this.getTabsState[index + 1];
  269 + toObj = getObj(page);
  270 + }
  271 + } else {
  272 + // Close the current tab
  273 + const page = this.getTabsState[index - 1];
  274 + toObj = getObj(page);
193 } 275 }
  276 + this.commitCloseTab(currentRoute.value);
  277 + replace(toObj);
194 } 278 }
195 279
196 @Action 280 @Action
197 - closeLeftTabAction(route: AppRouteRecordRaw | TabItem): void { 281 + closeTabByKeyAction(key: string) {
  282 + const index = this.tabsState.findIndex((item) => (item.fullPath || item.path) === key);
  283 + index !== -1 && this.closeTabAction(this.tabsState[index]);
  284 + }
  285 +
  286 + @Action
  287 + closeLeftTabAction(route: RouteLocationNormalized): void {
198 const index = this.tabsState.findIndex((item) => item.path === route.path); 288 const index = this.tabsState.findIndex((item) => item.path === route.path);
199 289
200 if (index > 0) { 290 if (index > 0) {
201 const leftTabs = this.tabsState.slice(0, index); 291 const leftTabs = this.tabsState.slice(0, index);
202 const pathList: string[] = []; 292 const pathList: string[] = [];
203 - const nameList: string[] = [];  
204 for (const item of leftTabs) { 293 for (const item of leftTabs) {
205 const affix = item.meta ? item.meta.affix : false; 294 const affix = item.meta ? item.meta.affix : false;
206 if (!affix) { 295 if (!affix) {
207 pathList.push(item.fullPath); 296 pathList.push(item.fullPath);
208 - nameList.push(item.name as string);  
209 } 297 }
210 } 298 }
211 - this.closeMultipleTab({ pathList, nameList });  
212 - }  
213 - }  
214 -  
215 - @Action  
216 - addTabByPathAction(): void {  
217 - const toRoute = getCurrentTo();  
218 - if (!toRoute) return;  
219 - const { meta } = toRoute;  
220 - if (meta && meta.affix) {  
221 - return; 299 + this.closeMultipleTab({ pathList });
222 } 300 }
223 - this.commitAddTab((toRoute as unknown) as AppRouteRecordRaw); 301 + this.commitCachedMapState();
  302 + isGotoPage();
224 } 303 }
225 304
226 @Action 305 @Action
227 - closeRightTabAction(route: AppRouteRecordRaw | TabItem): void { 306 + closeRightTabAction(route: RouteLocationNormalized): void {
228 const index = this.tabsState.findIndex((item) => item.fullPath === route.fullPath); 307 const index = this.tabsState.findIndex((item) => item.fullPath === route.fullPath);
229 308
230 if (index >= 0 && index < this.tabsState.length - 1) { 309 if (index >= 0 && index < this.tabsState.length - 1) {
231 const rightTabs = this.tabsState.slice(index + 1, this.tabsState.length); 310 const rightTabs = this.tabsState.slice(index + 1, this.tabsState.length);
232 311
233 const pathList: string[] = []; 312 const pathList: string[] = [];
234 - const nameList: string[] = [];  
235 for (const item of rightTabs) { 313 for (const item of rightTabs) {
236 const affix = item.meta ? item.meta.affix : false; 314 const affix = item.meta ? item.meta.affix : false;
237 if (!affix) { 315 if (!affix) {
238 pathList.push(item.fullPath); 316 pathList.push(item.fullPath);
239 - nameList.push(item.name as string);  
240 } 317 }
241 } 318 }
242 - this.closeMultipleTab({ pathList, nameList }); 319 + this.closeMultipleTab({ pathList });
243 } 320 }
  321 + this.commitCachedMapState();
  322 + isGotoPage();
244 } 323 }
245 324
246 @Action 325 @Action
247 - closeOtherTabAction(route: AppRouteRecordRaw | TabItem): void { 326 + closeOtherTabAction(route: RouteLocationNormalized): void {
248 const closePathList = this.tabsState.map((item) => item.fullPath); 327 const closePathList = this.tabsState.map((item) => item.fullPath);
249 const pathList: string[] = []; 328 const pathList: string[] = [];
250 - const nameList: string[] = [];  
251 closePathList.forEach((path) => { 329 closePathList.forEach((path) => {
252 if (path !== route.fullPath) { 330 if (path !== route.fullPath) {
253 const closeItem = this.tabsState.find((item) => item.path === path); 331 const closeItem = this.tabsState.find((item) => item.path === path);
@@ -255,11 +333,12 @@ class Tab extends VuexModule { @@ -255,11 +333,12 @@ class Tab extends VuexModule {
255 const affix = closeItem.meta ? closeItem.meta.affix : false; 333 const affix = closeItem.meta ? closeItem.meta.affix : false;
256 if (!affix) { 334 if (!affix) {
257 pathList.push(closeItem.fullPath); 335 pathList.push(closeItem.fullPath);
258 - nameList.push(closeItem.name as string);  
259 } 336 }
260 } 337 }
261 }); 338 });
262 - this.closeMultipleTab({ pathList, nameList }); 339 + this.closeMultipleTab({ pathList });
  340 + this.commitCachedMapState();
  341 + isGotoPage();
263 } 342 }
264 } 343 }
265 export const tabStore = getModule<Tab>(Tab); 344 export const tabStore = getModule<Tab>(Tab);
src/utils/helper/dynamicImport.ts deleted 100644 → 0
1 -// The content here is just for type approval. The actual file content is overwritten by transform  
2 -export default function (id: string) {  
3 - const dynamicImportModule: any = id;  
4 - return dynamicImportModule;  
5 -}  
src/utils/helper/routeHelper.ts deleted 100644 → 0
1 -import type { AppRouteModule, AppRouteRecordRaw, RouteModule } from '/@/router/types';  
2 -import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';  
3 -import { createRouter, createWebHashHistory } from 'vue-router';  
4 -  
5 -import { appStore } from '/@/store/modules/app';  
6 -import { tabStore } from '/@/store/modules/tab';  
7 -import { toRaw } from 'vue';  
8 -import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant';  
9 -// import { isDevMode } from '/@/utils/env';  
10 -import dynamicImport from './dynamicImport';  
11 -import { omit } from 'lodash-es';  
12 -  
13 -let currentTo: RouteLocationNormalized | null = null;  
14 -  
15 -export function getCurrentTo() {  
16 - return currentTo;  
17 -}  
18 -  
19 -export function setCurrentTo(to: RouteLocationNormalized) {  
20 - currentTo = to;  
21 -}  
22 -// 转化路由模块  
23 -// 将多级转成2层。keepAlive问题  
24 -export function genRouteModule(moduleList: AppRouteModule[] | AppRouteRecordRaw[]) {  
25 - const ret: AppRouteRecordRaw[] = [];  
26 - for (const routeMod of moduleList) {  
27 - let routes: RouteRecordRaw[] = [];  
28 - let layout: AppRouteRecordRaw | undefined;  
29 - if (Reflect.has(routeMod, 'routes')) {  
30 - routes = (routeMod as RouteModule).routes as any;  
31 - layout = (routeMod as RouteModule).layout;  
32 - } else if (Reflect.has(routeMod, 'path')) {  
33 - layout = omit(routeMod, 'children') as any;  
34 - routes = (routeMod.children as RouteRecordRaw[]) || ([] as RouteRecordRaw[]);  
35 - }  
36 -  
37 - const router = createRouter({ routes, history: createWebHashHistory() });  
38 -  
39 - const flatList = (toRaw(router.getRoutes()).filter(  
40 - (item) => item.children.length === 0  
41 - ) as unknown) as AppRouteRecordRaw[];  
42 - flatList.forEach((item) => {  
43 - item.path = `${layout ? layout.path : ''}${item.path}`;  
44 - });  
45 - if (layout) {  
46 - layout.children = flatList;  
47 - ret.push(layout);  
48 - } else {  
49 - ret.push(...flatList);  
50 - }  
51 - }  
52 - return ret as RouteRecordRaw[];  
53 -}  
54 -  
55 -// 动态引入  
56 -function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {  
57 - if (!routes) return;  
58 - routes.forEach((item) => {  
59 - const { component } = item;  
60 - const { children } = item;  
61 - if (component) {  
62 - item.component = dynamicImport(component);  
63 - }  
64 -  
65 - children && asyncImportRoute(children);  
66 - });  
67 -}  
68 -  
69 -function getLayoutComp(comp: string) {  
70 - return comp === 'PAGE_LAYOUT' ? PAGE_LAYOUT_COMPONENT : '';  
71 -}  
72 -  
73 -// 将后台对象转成路由对象  
74 -export function transformObjToRoute<T = any>(routeList: AppRouteModule[]): T[] {  
75 - routeList.forEach((route) => {  
76 - asyncImportRoute(  
77 - Reflect.has(route, 'routes') ? (route as RouteModule).routes : route.children || []  
78 - );  
79 - if ((route as RouteModule).layout) {  
80 - (route as RouteModule).layout.component = getLayoutComp(  
81 - (route as RouteModule).layout.component  
82 - );  
83 - } else {  
84 - route.component = getLayoutComp(route.component);  
85 - (route as RouteModule).layout = omit(route, 'children') as any;  
86 - }  
87 - });  
88 - return (routeList as unknown) as T[];  
89 -}  
90 -  
91 -//  
92 -export function getIsOpenTab(toPath: string) {  
93 - const { openKeepAlive, multiTabsSetting: { show } = {} } = appStore.getProjectConfig;  
94 -  
95 - if (show && openKeepAlive) {  
96 - const tabList = tabStore.getTabsState;  
97 - return tabList.some((tab) => tab.path === toPath);  
98 - }  
99 - return false;  
100 -}  
101 -  
102 -export function getParams(data: any = {}) {  
103 - const { params = {} } = data;  
104 - let ret = '';  
105 - Object.keys(params).forEach((key) => {  
106 - const p = params[key];  
107 - ret += `/${p}`;  
108 - });  
109 - return ret;  
110 -}  
src/views/demo/feat/copy/index.vue
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 import { useMessage } from '/@/hooks/web/useMessage'; 15 import { useMessage } from '/@/hooks/web/useMessage';
16 16
17 export default defineComponent({ 17 export default defineComponent({
  18 + name: 'Copy',
18 components: { CollapseContainer }, 19 components: { CollapseContainer },
19 setup() { 20 setup() {
20 const valueRef = ref(''); 21 const valueRef = ref('');
src/views/demo/level/Menu111.vue 0 → 100644
  1 +<template>
  2 + <div class="p-5">
  3 + 多层级缓存-页面1-1-1
  4 + <br />
  5 + <input />
  6 + </div>
  7 +</template>
  8 +<script lang="ts">
  9 + import { defineComponent } from 'vue';
  10 + export default defineComponent({ name: 'Menu111Demo' });
  11 +</script>
src/views/demo/level/Menu12.vue 0 → 100644
  1 +<template>
  2 + <div class="p-5">
  3 + 多层级缓存-页面1-2
  4 + <br />
  5 + <input />
  6 + </div>
  7 +</template>
  8 +<script lang="ts">
  9 + import { defineComponent } from 'vue';
  10 + export default defineComponent({ name: 'Menu12Demo' });
  11 +</script>
src/views/demo/level/Menu2.vue 0 → 100644
  1 +<template>
  2 + <div class="p-5">
  3 + 多层级缓存-页面2
  4 + <br />
  5 + <input />
  6 + </div>
  7 +</template>
  8 +<script lang="ts">
  9 + import { defineComponent } from 'vue';
  10 + export default defineComponent({
  11 + name: 'Menu2Demo',
  12 + });
  13 +</script>
src/views/sys/redirect/index.vue
  1 +<template>
  2 + <div />
  3 +</template>
1 <script lang="ts"> 4 <script lang="ts">
2 import { defineComponent, unref } from 'vue'; 5 import { defineComponent, unref } from 'vue';
3 6
@@ -18,12 +21,13 @@ @@ -18,12 +21,13 @@
18 path: '/' + _path, 21 path: '/' + _path,
19 query, 22 query,
20 }); 23 });
  24 + // close loading
21 if (unref(getEnableTransition) && unref(getOpenPageLoading)) { 25 if (unref(getEnableTransition) && unref(getOpenPageLoading)) {
22 setTimeout(() => { 26 setTimeout(() => {
23 appStore.setPageLoadingAction(false); 27 appStore.setPageLoadingAction(false);
24 }, 0); 28 }, 0);
25 } 29 }
26 - return () => null; 30 + return {};
27 }, 31 },
28 }); 32 });
29 </script> 33 </script>
yarn.lock
@@ -1061,10 +1061,10 @@ @@ -1061,10 +1061,10 @@
1061 resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" 1061 resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b"
1062 integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw== 1062 integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw==
1063 1063
1064 -"@iconify/json@^1.1.266":  
1065 - version "1.1.266"  
1066 - resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.266.tgz#3537de808399652b3ca2c89a561216324121b785"  
1067 - integrity sha512-I8S9lChQATaRroMGccdOQkFbBtMt4C2V/PQGiSjDq9yzdyqDCrPNN9X1qM4FoQt84zfW/+JMHIgShi42E+SXeA== 1064 +"@iconify/json@^1.1.267":
  1065 + version "1.1.267"
  1066 + resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.267.tgz#52ab5390fcaf95e0d68260523a3a3fbc575dfe01"
  1067 + integrity sha512-VKNvyALvbuwsXO7r2XvdoqdctmvJzp1/XYOXRfhJ4w+sjtWYp8T3oRGDJ0AZTafzGiBBUaMwCZVP+j87rqgD3w==
1068 1068
1069 "@koa/cors@^3.1.0": 1069 "@koa/cors@^3.1.0":
1070 version "3.1.0" 1070 version "3.1.0"
@@ -1535,10 +1535,10 @@ @@ -1535,10 +1535,10 @@
1535 resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" 1535 resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
1536 integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== 1536 integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
1537 1537
1538 -"@types/yargs@^15.0.10":  
1539 - version "15.0.10"  
1540 - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz#0fe3c8173a0d5c3e780b389050140c3f5ea6ea74"  
1541 - integrity sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ== 1538 +"@types/yargs@^15.0.11":
  1539 + version "15.0.11"
  1540 + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.11.tgz#361d7579ecdac1527687bcebf9946621c12ab78c"
  1541 + integrity sha512-jfcNBxHFYJ4nPIacsi3woz1+kvUO6s1CyeEhtnDHBjHUMNj5UlW2GynmnSgiJJEdNg9yW5C8lfoNRZrHGv5EqA==
1542 dependencies: 1542 dependencies:
1543 "@types/yargs-parser" "*" 1543 "@types/yargs-parser" "*"
1544 1544
@@ -1644,6 +1644,17 @@ @@ -1644,6 +1644,17 @@
1644 estree-walker "^2.0.1" 1644 estree-walker "^2.0.1"
1645 source-map "^0.6.1" 1645 source-map "^0.6.1"
1646 1646
  1647 +"@vue/compiler-core@3.0.4":
  1648 + version "3.0.4"
  1649 + resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.4.tgz#0122aca6eada4cb28b39ed930af917444755e330"
  1650 + integrity sha512-snpMICsbWTZqBFnPB03qr4DtiSxVYfDF3DvbDSkN9Z9NTM8Chl8E/lYhKBSsvauq91DAWAh8PU3lr9vrLyQsug==
  1651 + dependencies:
  1652 + "@babel/parser" "^7.12.0"
  1653 + "@babel/types" "^7.12.0"
  1654 + "@vue/shared" "3.0.4"
  1655 + estree-walker "^2.0.1"
  1656 + source-map "^0.6.1"
  1657 +
1647 "@vue/compiler-dom@3.0.2": 1658 "@vue/compiler-dom@3.0.2":
1648 version "3.0.2" 1659 version "3.0.2"
1649 resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.2.tgz#1d40de04bcdf9aabb79fb6a802dd70a2f3c2992a" 1660 resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.2.tgz#1d40de04bcdf9aabb79fb6a802dd70a2f3c2992a"
@@ -1660,6 +1671,14 @@ @@ -1660,6 +1671,14 @@
1660 "@vue/compiler-core" "3.0.3" 1671 "@vue/compiler-core" "3.0.3"
1661 "@vue/shared" "3.0.3" 1672 "@vue/shared" "3.0.3"
1662 1673
  1674 +"@vue/compiler-dom@3.0.4":
  1675 + version "3.0.4"
  1676 + resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.4.tgz#834fd4b15c5698cf9f4505c2bfbccca058a843eb"
  1677 + integrity sha512-FOxbHBIkkGjYQeTz1DlXQjS1Ms8EPXQWsdTdTPeohoS0KzCz6RiOjiAG+jLtMi6Nr5GX2h0TlCvcnI8mcsicFQ==
  1678 + dependencies:
  1679 + "@vue/compiler-core" "3.0.4"
  1680 + "@vue/shared" "3.0.4"
  1681 +
1663 "@vue/compiler-sfc@*", "@vue/compiler-sfc@^3.0.0-rc.5": 1682 "@vue/compiler-sfc@*", "@vue/compiler-sfc@^3.0.0-rc.5":
1664 version "3.0.2" 1683 version "3.0.2"
1665 resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.0.2.tgz#22c70fed72c347a4d5fa2db2e80594b3193dce57" 1684 resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.0.2.tgz#22c70fed72c347a4d5fa2db2e80594b3193dce57"
@@ -1704,6 +1723,28 @@ @@ -1704,6 +1723,28 @@
1704 postcss-selector-parser "^6.0.4" 1723 postcss-selector-parser "^6.0.4"
1705 source-map "^0.6.1" 1724 source-map "^0.6.1"
1706 1725
  1726 +"@vue/compiler-sfc@^3.0.4":
  1727 + version "3.0.4"
  1728 + resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.0.4.tgz#2119fe1e68d2c268aafa20461c82c139a9adf8e0"
  1729 + integrity sha512-brDn6HTuK6R3oBCjtMPPsIpyJEZFinlnxjtBXww/goFJOJBAU9CrsdegwyZItNnixCFUIg4CLv4Nj1Eg/eKlfg==
  1730 + dependencies:
  1731 + "@babel/parser" "^7.12.0"
  1732 + "@babel/types" "^7.12.0"
  1733 + "@vue/compiler-core" "3.0.4"
  1734 + "@vue/compiler-dom" "3.0.4"
  1735 + "@vue/compiler-ssr" "3.0.4"
  1736 + "@vue/shared" "3.0.4"
  1737 + consolidate "^0.16.0"
  1738 + estree-walker "^2.0.1"
  1739 + hash-sum "^2.0.0"
  1740 + lru-cache "^5.1.1"
  1741 + magic-string "^0.25.7"
  1742 + merge-source-map "^1.1.0"
  1743 + postcss "^7.0.32"
  1744 + postcss-modules "^3.2.2"
  1745 + postcss-selector-parser "^6.0.4"
  1746 + source-map "^0.6.1"
  1747 +
1707 "@vue/compiler-ssr@3.0.2": 1748 "@vue/compiler-ssr@3.0.2":
1708 version "3.0.2" 1749 version "3.0.2"
1709 resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.0.2.tgz#73af4d274a79bfcc72a996a9b45f1072e7deaa26" 1750 resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.0.2.tgz#73af4d274a79bfcc72a996a9b45f1072e7deaa26"
@@ -1720,6 +1761,14 @@ @@ -1720,6 +1761,14 @@
1720 "@vue/compiler-dom" "3.0.3" 1761 "@vue/compiler-dom" "3.0.3"
1721 "@vue/shared" "3.0.3" 1762 "@vue/shared" "3.0.3"
1722 1763
  1764 +"@vue/compiler-ssr@3.0.4":
  1765 + version "3.0.4"
  1766 + resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.0.4.tgz#ccbd1f55734d51d1402fad825ac102002a7a07c7"
  1767 + integrity sha512-4aYWQEL4+LS4+D44K9Z7xMOWMEjBsz4Li9nMcj2rxRQ35ewK6uFPodvs6ORP60iBDSkwUFZoldFlNemQlu1BFw==
  1768 + dependencies:
  1769 + "@vue/compiler-dom" "3.0.4"
  1770 + "@vue/shared" "3.0.4"
  1771 +
1723 "@vue/reactivity@3.0.2": 1772 "@vue/reactivity@3.0.2":
1724 version "3.0.2" 1773 version "3.0.2"
1725 resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.2.tgz#42ed5af6025b494a5e69b05169fcddf04eebfe77" 1774 resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.2.tgz#42ed5af6025b494a5e69b05169fcddf04eebfe77"
@@ -1734,6 +1783,13 @@ @@ -1734,6 +1783,13 @@
1734 dependencies: 1783 dependencies:
1735 "@vue/shared" "3.0.3" 1784 "@vue/shared" "3.0.3"
1736 1785
  1786 +"@vue/reactivity@3.0.4":
  1787 + version "3.0.4"
  1788 + resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.4.tgz#b6599dd8271a745960a03f05744ccf7991ba5d8d"
  1789 + integrity sha512-AFTABrLhUYZY2on3ea9FxeXal7w3f6qIp9gT+/oG93H7dFTL5LvVnxygCopv7tvkIl/GSGQb/yK1D1gmXx1Pww==
  1790 + dependencies:
  1791 + "@vue/shared" "3.0.4"
  1792 +
1737 "@vue/runtime-core@3.0.2": 1793 "@vue/runtime-core@3.0.2":
1738 version "3.0.2" 1794 version "3.0.2"
1739 resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.2.tgz#d7ed462af1cb0bf9836668e4e6fab3f2f4b1bc00" 1795 resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.2.tgz#d7ed462af1cb0bf9836668e4e6fab3f2f4b1bc00"
@@ -1750,6 +1806,14 @@ @@ -1750,6 +1806,14 @@
1750 "@vue/reactivity" "3.0.3" 1806 "@vue/reactivity" "3.0.3"
1751 "@vue/shared" "3.0.3" 1807 "@vue/shared" "3.0.3"
1752 1808
  1809 +"@vue/runtime-core@3.0.4":
  1810 + version "3.0.4"
  1811 + resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.4.tgz#a5b9a001560b1fd8c01a43f68b764c555de7836c"
  1812 + integrity sha512-qH9e4kqU7b3u1JewvLmGmoAGY+mnuBqz7aEKb2mhpEgwa1yFv496BRuUfMXXMCix3+TndUVMJ8jt41FSdNppwg==
  1813 + dependencies:
  1814 + "@vue/reactivity" "3.0.4"
  1815 + "@vue/shared" "3.0.4"
  1816 +
1753 "@vue/runtime-dom@3.0.3": 1817 "@vue/runtime-dom@3.0.3":
1754 version "3.0.3" 1818 version "3.0.3"
1755 resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.3.tgz#5e3e5e5418b9defcac988d2be0cf65596fa2cc03" 1819 resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.3.tgz#5e3e5e5418b9defcac988d2be0cf65596fa2cc03"
@@ -1759,6 +1823,15 @@ @@ -1759,6 +1823,15 @@
1759 "@vue/shared" "3.0.3" 1823 "@vue/shared" "3.0.3"
1760 csstype "^2.6.8" 1824 csstype "^2.6.8"
1761 1825
  1826 +"@vue/runtime-dom@3.0.4":
  1827 + version "3.0.4"
  1828 + resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.4.tgz#6f81aec545f24511d2c28a315aa3391420b69c68"
  1829 + integrity sha512-BGIoiTSESzWUhN0Ofi2X/q+HN8f6IUFmUEyyBGKbmx7DTAJNZhFfjqsepfXQrM5IGeTfJLB1ZEVyroDQJNXq3g==
  1830 + dependencies:
  1831 + "@vue/runtime-core" "3.0.4"
  1832 + "@vue/shared" "3.0.4"
  1833 + csstype "^2.6.8"
  1834 +
1762 "@vue/runtime-dom@^3.0.0": 1835 "@vue/runtime-dom@^3.0.0":
1763 version "3.0.2" 1836 version "3.0.2"
1764 resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.2.tgz#9d166d03225558025d3d80f5039b646e0051b71c" 1837 resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.2.tgz#9d166d03225558025d3d80f5039b646e0051b71c"
@@ -1778,6 +1851,11 @@ @@ -1778,6 +1851,11 @@
1778 resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.0.3.tgz#ef12ebff93a446df281e8a0fd765b5aea8e7745b" 1851 resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.0.3.tgz#ef12ebff93a446df281e8a0fd765b5aea8e7745b"
1779 integrity sha512-yGgkF7u4W0Dmwri9XdeY50kOowN4UIX7aBQ///jbxx37itpzVjK7QzvD3ltQtPfWaJDGBfssGL0wpAgwX9OJpQ== 1852 integrity sha512-yGgkF7u4W0Dmwri9XdeY50kOowN4UIX7aBQ///jbxx37itpzVjK7QzvD3ltQtPfWaJDGBfssGL0wpAgwX9OJpQ==
1780 1853
  1854 +"@vue/shared@3.0.4":
  1855 + version "3.0.4"
  1856 + resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.0.4.tgz#6dc50f593bdfdeaa6183d1dbc15e2d45e7c6b8b3"
  1857 + integrity sha512-Swfbz31AaMX48CpFl+YmIrqOH9MgJMTrltG9e26A4ZxYx9LjGuMV+41WnxFzS3Bc9nbrc6sDPM37G6nIT8NJSg==
  1858 +
1781 "@vuedx/analyze@0.2.4-0": 1859 "@vuedx/analyze@0.2.4-0":
1782 version "0.2.4-0" 1860 version "0.2.4-0"
1783 resolved "https://registry.npmjs.org/@vuedx/analyze/-/analyze-0.2.4-0.tgz#52766a6dcd2867320409fe517540fd0bf0394d48" 1861 resolved "https://registry.npmjs.org/@vuedx/analyze/-/analyze-0.2.4-0.tgz#52766a6dcd2867320409fe517540fd0bf0394d48"
@@ -3013,10 +3091,10 @@ crc-32@~1.2.0: @@ -3013,10 +3091,10 @@ crc-32@~1.2.0:
3013 exit-on-epipe "~1.0.1" 3091 exit-on-epipe "~1.0.1"
3014 printj "~1.1.0" 3092 printj "~1.1.0"
3015 3093
3016 -cross-env@^7.0.2:  
3017 - version "7.0.2"  
3018 - resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9"  
3019 - integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw== 3094 +cross-env@^7.0.3:
  3095 + version "7.0.3"
  3096 + resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
  3097 + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
3020 dependencies: 3098 dependencies:
3021 cross-spawn "^7.0.1" 3099 cross-spawn "^7.0.1"
3022 3100
@@ -3449,17 +3527,17 @@ es-module-lexer@^0.3.25: @@ -3449,17 +3527,17 @@ es-module-lexer@^0.3.25:
3449 resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b" 3527 resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b"
3450 integrity sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA== 3528 integrity sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA==
3451 3529
3452 -esbuild-register@^1.1.0:  
3453 - version "1.1.0"  
3454 - resolved "https://registry.npmjs.org/esbuild-register/-/esbuild-register-1.1.0.tgz#8ec1fbf6b84f0d7654b87eec04029a383dcb539d"  
3455 - integrity sha512-A+KGHDc7me/ATyNqnVQKsHxt2A/ORVvV2gmukx5ZtVcy5HVf19QBbHdfdP5QHFA8rF/WHmcnDxaxewu+VUvUhQ== 3530 +esbuild-register@^1.1.1:
  3531 + version "1.1.1"
  3532 + resolved "https://registry.npmjs.org/esbuild-register/-/esbuild-register-1.1.1.tgz#7d50e87ac0b9000085d9e6d9a78e4c2223fcce83"
  3533 + integrity sha512-hAPWuaUkPDLXCENc/AigJZaaDCvCkpmghRw8XPyT+rk08JHcIgUrmw1uabbUTfa6B6J9Wo2bFufb01JjbmzcfQ==
3456 dependencies: 3534 dependencies:
3457 joycon "^2.2.5" 3535 joycon "^2.2.5"
3458 pirates "^4.0.1" 3536 pirates "^4.0.1"
3459 source-map-support "^0.5.19" 3537 source-map-support "^0.5.19"
3460 strip-json-comments "^3.1.1" 3538 strip-json-comments "^3.1.1"
3461 3539
3462 -esbuild@^0.7.17, esbuild@^0.7.19: 3540 +esbuild@^0.7.19:
3463 version "0.7.22" 3541 version "0.7.22"
3464 resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.22.tgz#9149b903f8128b7c45a754046c24199d76bbe08e" 3542 resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.22.tgz#9149b903f8128b7c45a754046c24199d76bbe08e"
3465 integrity sha512-B43SYg8LGWYTCv9Gs0RnuLNwjzpuWOoCaZHTWEDEf5AfrnuDMerPVMdCEu7xOdhFvQ+UqfP2MGU9lxEy0JzccA== 3543 integrity sha512-B43SYg8LGWYTCv9Gs0RnuLNwjzpuWOoCaZHTWEDEf5AfrnuDMerPVMdCEu7xOdhFvQ+UqfP2MGU9lxEy0JzccA==
@@ -3469,6 +3547,11 @@ esbuild@^0.8.12: @@ -3469,6 +3547,11 @@ esbuild@^0.8.12:
3469 resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.8.15.tgz#cbc4d82a7fc4571d455233456e6fba83fd0364f1" 3547 resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.8.15.tgz#cbc4d82a7fc4571d455233456e6fba83fd0364f1"
3470 integrity sha512-mSaLo9t/oYtQE6FRUEdO47Pr8PisSPzHtgr+LcihIcjBEhbYwjT6WLCQ7noDoTBfIatBCw229rtmIwl9u9UQwg== 3548 integrity sha512-mSaLo9t/oYtQE6FRUEdO47Pr8PisSPzHtgr+LcihIcjBEhbYwjT6WLCQ7noDoTBfIatBCw229rtmIwl9u9UQwg==
3471 3549
  3550 +esbuild@^0.8.17:
  3551 + version "0.8.17"
  3552 + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.8.17.tgz#1c16c6d5988dcfdcf27a7e1612b7fd05e1477c54"
  3553 + integrity sha512-ReHap+Iyn5BQF0B8F3xrLwu+j57ri5uDUw2ej9XTPAuFDebYiWwRzBY4jhF610bklveXLbCGim/8/2wQKQlu1w==
  3554 +
3472 escalade@^3.1.1: 3555 escalade@^3.1.1:
3473 version "3.1.1" 3556 version "3.1.1"
3474 resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 3557 resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -3581,13 +3664,13 @@ esm@^3.2.25: @@ -3581,13 +3664,13 @@ esm@^3.2.25:
3581 resolved "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" 3664 resolved "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
3582 integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== 3665 integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
3583 3666
3584 -esno@^0.2.4:  
3585 - version "0.2.4"  
3586 - resolved "https://registry.npmjs.org/esno/-/esno-0.2.4.tgz#b04a368181bc03e5d11d5147106bf0d0ac4f3a48"  
3587 - integrity sha512-XlgsQe2va257kc1xsZg/X22fRyLVRNkCKEXjONoltA7HeXtmhrQ3n19all0eK0X6YRNE8X9qiVyWV0vMLZvY3w== 3667 +esno@^0.3.0:
  3668 + version "0.3.0"
  3669 + resolved "https://registry.npmjs.org/esno/-/esno-0.3.0.tgz#c818996bdaaf2deaf81413d6f45538ffa6e41b42"
  3670 + integrity sha512-4sF/j8jruQv9jScU8tNkgoDFLjyGxTTB8bmjRmWHyNNygra3WS3X0U1Cc7GuOvfSEjn3NDS57P0LRnzgiupKJg==
3588 dependencies: 3671 dependencies:
3589 - esbuild "^0.7.17"  
3590 - esbuild-register "^1.1.0" 3672 + esbuild "^0.8.17"
  3673 + esbuild-register "^1.1.1"
3591 esm "^3.2.25" 3674 esm "^3.2.25"
3592 3675
3593 espree@^6.2.1: 3676 espree@^6.2.1:
@@ -8335,6 +8418,15 @@ vue@^3.0.3: @@ -8335,6 +8418,15 @@ vue@^3.0.3:
8335 "@vue/runtime-dom" "3.0.3" 8418 "@vue/runtime-dom" "3.0.3"
8336 "@vue/shared" "3.0.3" 8419 "@vue/shared" "3.0.3"
8337 8420
  8421 +vue@^3.0.4:
  8422 + version "3.0.4"
  8423 + resolved "https://registry.npmjs.org/vue/-/vue-3.0.4.tgz#872c65c143f5717bd5387c61613d9f55f4cc0f43"
  8424 + integrity sha512-2o+AiQF8sAupyhbyl3oxVCl3WCwC/n5NI7VMM+gVQ231qvSB8eI7sCBloloqDJK6yA367EEtmRSeSCf4sxCC+A==
  8425 + dependencies:
  8426 + "@vue/compiler-dom" "3.0.4"
  8427 + "@vue/runtime-dom" "3.0.4"
  8428 + "@vue/shared" "3.0.4"
  8429 +
8338 vuex-module-decorators@^1.0.1: 8430 vuex-module-decorators@^1.0.1:
8339 version "1.0.1" 8431 version "1.0.1"
8340 resolved "https://registry.npmjs.org/vuex-module-decorators/-/vuex-module-decorators-1.0.1.tgz#d34dafb5428a3636f1c26d3d014c15fc9659ccd0" 8432 resolved "https://registry.npmjs.org/vuex-module-decorators/-/vuex-module-decorators-1.0.1.tgz#d34dafb5428a3636f1c26d3d014c15fc9659ccd0"