Commit c303ec1a23c4b1fbad4fbda9007af2147dc327e2
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 | 2 | "version": "0.2.0", |
3 | 3 | "configurations": [ |
4 | - // node环境调试当前激活编辑器ts/js代码 | |
5 | 4 | { |
6 | - "type": "node", | |
5 | + "type": "chrome", | |
7 | 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 | 163 | "[typescriptreact]": { |
164 | 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 | 166 | "[html]": { |
173 | 167 | "editor.defaultFormatter": "esbenp.prettier-vscode" |
174 | 168 | }, |
... | ... | @@ -198,5 +192,8 @@ |
198 | 192 | "ts" |
199 | 193 | ], |
200 | 194 | "i18n-ally.sourceLanguage": "zh", |
201 | - "i18n-ally.enabledFrameworks":["vue","react"] | |
195 | + "i18n-ally.enabledFrameworks": [ | |
196 | + "vue", | |
197 | + "react" | |
198 | + ] | |
202 | 199 | } |
203 | 200 | \ No newline at end of file |
... | ... |
CHANGELOG.zh_CN.md
1 | 1 | ## Wip |
2 | 2 | |
3 | +## (破坏性更新) Breaking changes | |
4 | + | |
5 | +- 路由重构, 不再支持以前的格式。改为支持 vue-router 最初的默认结构,具体格式可以参考示例更改。实现多级路由缓存,不再将路由转化为 2 级。 | |
6 | +- 重构面包屑,使用 antd 的面包屑组件。之前的组件已删除 | |
7 | + | |
3 | 8 | ### ✨ Features |
4 | 9 | |
5 | 10 | - 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading`和`v-loading`指令。并增加示例 |
6 | 11 | - i18n 支持 vscode `i18n-ally`插件 |
12 | +- 新增多级路由缓存示例 | |
7 | 13 | |
8 | 14 | ### 🎫 Chores |
9 | 15 | |
10 | 16 | - 首屏 loading 修改 |
11 | 17 | |
18 | +### 🐛 Bug Fixes | |
19 | + | |
20 | +-修复表格 i18n 错误 | |
21 | + | |
12 | 22 | ## 2.0.0-rc.12 (2020-11-30) |
13 | 23 | |
14 | 24 | ## (破坏性更新) Breaking changes |
... | ... |
build/vite/plugin/transform/dynamic-import/index.ts
... | ... | @@ -17,8 +17,8 @@ const dynamicImportTransform = function (enableDynamicImport: boolean): Transfor |
17 | 17 | test({ path }) { |
18 | 18 | // Only convert the file |
19 | 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 | 24 | transform({ code }) { |
... | ... |
mock/sys/menu.ts
1 | 1 | import { resultSuccess } from '../_util'; |
2 | 2 | import { MockMethod } from 'vite-plugin-mock'; |
3 | 3 | |
4 | +// single | |
4 | 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 | 9 | meta: { |
10 | + title: 'routes.dashboard.welcome', | |
11 | + affix: true, | |
10 | 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 | 16 | const frontRoute = { |
27 | - path: '/front', | |
17 | + path: 'front', | |
28 | 18 | name: 'PermissionFrontDemo', |
29 | 19 | meta: { |
30 | - title: '基于前端权限', | |
20 | + title: 'routes.demo.permission.front', | |
31 | 21 | }, |
32 | 22 | children: [ |
33 | 23 | { |
... | ... | @@ -35,7 +25,7 @@ const frontRoute = { |
35 | 25 | name: 'FrontPageAuth', |
36 | 26 | component: '/demo/permission/front/index', |
37 | 27 | meta: { |
38 | - title: '页面权限', | |
28 | + title: 'routes.demo.permission.frontPage', | |
39 | 29 | }, |
40 | 30 | }, |
41 | 31 | { |
... | ... | @@ -43,7 +33,7 @@ const frontRoute = { |
43 | 33 | name: 'FrontBtnAuth', |
44 | 34 | component: '/demo/permission/front/Btn', |
45 | 35 | meta: { |
46 | - title: '按钮权限', | |
36 | + title: 'routes.demo.permission.frontBtn', | |
47 | 37 | }, |
48 | 38 | }, |
49 | 39 | { |
... | ... | @@ -51,7 +41,7 @@ const frontRoute = { |
51 | 41 | name: 'FrontAuthPageA', |
52 | 42 | component: '/demo/permission/front/AuthPageA', |
53 | 43 | meta: { |
54 | - title: '权限测试页A', | |
44 | + title: 'routes.demo.permission.frontTestA', | |
55 | 45 | }, |
56 | 46 | }, |
57 | 47 | { |
... | ... | @@ -59,24 +49,25 @@ const frontRoute = { |
59 | 49 | name: 'FrontAuthPageB', |
60 | 50 | component: '/demo/permission/front/AuthPageB', |
61 | 51 | meta: { |
62 | - title: '权限测试页B', | |
52 | + title: 'routes.demo.permission.frontTestB', | |
63 | 53 | }, |
64 | 54 | }, |
65 | 55 | ], |
66 | 56 | }; |
67 | 57 | const backRoute = { |
68 | - path: '/back', | |
58 | + path: 'back', | |
69 | 59 | name: 'PermissionBackDemo', |
70 | 60 | meta: { |
71 | - title: '基于后台权限', | |
61 | + title: 'routes.demo.permission.back', | |
72 | 62 | }, |
63 | + | |
73 | 64 | children: [ |
74 | 65 | { |
75 | 66 | path: 'page', |
76 | 67 | name: 'BackAuthPage', |
77 | 68 | component: '/demo/permission/back/index', |
78 | 69 | meta: { |
79 | - title: '页面权限', | |
70 | + title: 'routes.demo.permission.backPage', | |
80 | 71 | }, |
81 | 72 | }, |
82 | 73 | { |
... | ... | @@ -84,7 +75,7 @@ const backRoute = { |
84 | 75 | name: 'BackAuthBtn', |
85 | 76 | component: '/demo/permission/back/Btn', |
86 | 77 | meta: { |
87 | - title: '按钮权限', | |
78 | + title: 'routes.demo.permission.backBtn', | |
88 | 79 | }, |
89 | 80 | }, |
90 | 81 | ], |
... | ... | @@ -92,11 +83,11 @@ const backRoute = { |
92 | 83 | const authRoute = { |
93 | 84 | path: '/permission', |
94 | 85 | name: 'Permission', |
95 | - component: 'PAGE_LAYOUT', | |
86 | + component: 'LAYOUT', | |
96 | 87 | redirect: '/permission/front/page', |
97 | 88 | meta: { |
98 | - icon: 'ant-design:home-outlined', | |
99 | - title: '权限管理', | |
89 | + icon: 'carbon:user-role', | |
90 | + title: 'routes.demo.permission.permission', | |
100 | 91 | }, |
101 | 92 | children: [frontRoute, backRoute], |
102 | 93 | }; |
... | ... | @@ -104,14 +95,70 @@ const authRoute = { |
104 | 95 | const authRoute1 = { |
105 | 96 | path: '/permission', |
106 | 97 | name: 'Permission', |
107 | - component: 'PAGE_LAYOUT', | |
98 | + component: 'LAYOUT', | |
108 | 99 | redirect: '/permission/front/page', |
109 | 100 | meta: { |
110 | - icon: 'ant-design:home-outlined', | |
111 | - title: '权限管理', | |
101 | + icon: 'carbon:user-role', | |
102 | + title: 'routes.demo.permission.permission', | |
112 | 103 | }, |
113 | 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 | 162 | export default [ |
116 | 163 | { |
117 | 164 | url: '/api/getMenuListById', |
... | ... | @@ -120,10 +167,10 @@ export default [ |
120 | 167 | response: ({ query }) => { |
121 | 168 | const { id } = query; |
122 | 169 | if (!id || id === '1') { |
123 | - return resultSuccess([dashboardRoute, authRoute]); | |
170 | + return resultSuccess([dashboardRoute, authRoute, levelRoute]); | |
124 | 171 | } |
125 | 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 | 35 | "qrcode": "^1.4.4", |
36 | 36 | "sortablejs": "^1.12.0", |
37 | 37 | "vditor": "^3.7.0", |
38 | - "vue": "^3.0.3", | |
38 | + "vue": "^3.0.4", | |
39 | 39 | "vue-i18n": "^9.0.0-beta.8", |
40 | 40 | "vue-router": "^4.0.0-rc.6", |
41 | 41 | "vue-types": "^3.0.1", |
... | ... | @@ -47,7 +47,7 @@ |
47 | 47 | "devDependencies": { |
48 | 48 | "@commitlint/cli": "^11.0.0", |
49 | 49 | "@commitlint/config-conventional": "^11.0.0", |
50 | - "@iconify/json": "^1.1.266", | |
50 | + "@iconify/json": "^1.1.267", | |
51 | 51 | "@ls-lint/ls-lint": "^1.9.2", |
52 | 52 | "@purge-icons/generated": "^0.4.1", |
53 | 53 | "@types/echarts": "^4.9.2", |
... | ... | @@ -60,25 +60,25 @@ |
60 | 60 | "@types/qrcode": "^1.3.5", |
61 | 61 | "@types/rollup-plugin-visualizer": "^2.6.0", |
62 | 62 | "@types/sortablejs": "^1.10.6", |
63 | - "@types/yargs": "^15.0.10", | |
63 | + "@types/yargs": "^15.0.11", | |
64 | 64 | "@types/zxcvbn": "^4.4.0", |
65 | 65 | "@typescript-eslint/eslint-plugin": "^4.9.0", |
66 | 66 | "@typescript-eslint/parser": "^4.9.0", |
67 | - "@vue/compiler-sfc": "^3.0.3", | |
67 | + "@vue/compiler-sfc": "^3.0.4", | |
68 | 68 | "@vuedx/typecheck": "^0.2.4-0", |
69 | 69 | "@vuedx/typescript-plugin-vue": "^0.2.4-0", |
70 | 70 | "autoprefixer": "^9.8.6", |
71 | 71 | "commitizen": "^4.2.2", |
72 | 72 | "conventional-changelog-cli": "^2.1.1", |
73 | 73 | "conventional-changelog-custom-config": "^0.3.1", |
74 | - "cross-env": "^7.0.2", | |
74 | + "cross-env": "^7.0.3", | |
75 | 75 | "dot-prop": "^6.0.1", |
76 | 76 | "dotenv": "^8.2.0", |
77 | 77 | "eslint": "^7.14.0", |
78 | 78 | "eslint-config-prettier": "^6.15.0", |
79 | 79 | "eslint-plugin-prettier": "^3.1.4", |
80 | 80 | "eslint-plugin-vue": "^7.1.0", |
81 | - "esno": "^0.2.4", | |
81 | + "esno": "^0.3.0", | |
82 | 82 | "fs-extra": "^9.0.1", |
83 | 83 | "globrex": "^0.1.2", |
84 | 84 | "husky": "^4.3.0", |
... | ... |
src/components/Breadcrumb/index.ts deleted
100644 → 0
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 '/@/router/menus'; |
36 | 36 | |
37 | 37 | import { basicProps } from './props'; |
38 | 38 | import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
39 | +import { REDIRECT_NAME } from '/@/router/constant'; | |
39 | 40 | export default defineComponent({ |
40 | 41 | name: 'BasicMenu', |
41 | 42 | props: basicProps, |
... | ... | @@ -120,7 +121,7 @@ export default defineComponent({ |
120 | 121 | watch( |
121 | 122 | () => currentRoute.value.name, |
122 | 123 | (name: string) => { |
123 | - if (name === 'Redirect') return; | |
124 | + if (name === REDIRECT_NAME) return; | |
124 | 125 | handleMenuChange(); |
125 | 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 '../types'; |
4 | 4 | import type { Ref } from 'vue'; |
5 | 5 | |
6 | 6 | import { unref } from 'vue'; |
7 | -import { getAllParentPath } from '/@/utils/helper/menuHelper'; | |
7 | +import { getAllParentPath } from '/@/router/helper/menuHelper'; | |
8 | 8 | import { es6Unique } from '/@/utils'; |
9 | 9 | import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
10 | 10 | |
... | ... |
src/components/Menu/src/hooks/useSearchInput.ts
... | ... | @@ -5,7 +5,7 @@ import type { Ref } from 'vue'; |
5 | 5 | import { isString } from '/@/utils/is'; |
6 | 6 | import { unref } from 'vue'; |
7 | 7 | import { es6Unique } from '/@/utils'; |
8 | -import { getAllParentPath } from '/@/utils/helper/menuHelper'; | |
8 | +import { getAllParentPath } from '/@/router/helper/menuHelper'; | |
9 | 9 | |
10 | 10 | interface UseSearchInputOptions { |
11 | 11 | menuState: MenuState; |
... | ... |
src/components/Table/src/components/TableSetting.vue
... | ... | @@ -33,7 +33,7 @@ |
33 | 33 | |
34 | 34 | <Tooltip placement="top" v-if="getSetting.setting"> |
35 | 35 | <template #title> |
36 | - <span>{{ t('settingColumn') }}</span> | |
36 | + <span>{{ t('component.table.settingColumn') }}</span> | |
37 | 37 | </template> |
38 | 38 | <Popover |
39 | 39 | placement="bottomLeft" |
... | ... | @@ -58,9 +58,11 @@ |
58 | 58 | v-model:checked="checkAll" |
59 | 59 | @change="onCheckAllChange" |
60 | 60 | > |
61 | - {{ t('settingColumnShow') }} | |
61 | + {{ t('component.table.settingColumnShow') }} | |
62 | 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 | 66 | </div> |
65 | 67 | </template> |
66 | 68 | <SettingOutlined /> |
... | ... | @@ -69,7 +71,7 @@ |
69 | 71 | |
70 | 72 | <Tooltip placement="top" v-if="getSetting.fullScreen"> |
71 | 73 | <template #title> |
72 | - <span>{{ t('settingFullScreen') }}</span> | |
74 | + <span>{{ t('component.table.settingFullScreen') }}</span> | |
73 | 75 | </template> |
74 | 76 | <FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" /> |
75 | 77 | <FullscreenExitOutlined @click="handleFullScreen" v-else /> |
... | ... |
src/components/registerGlobComp.ts
... | ... | @@ -33,6 +33,7 @@ import { |
33 | 33 | Empty, |
34 | 34 | Avatar, |
35 | 35 | Menu, |
36 | + Breadcrumb, | |
36 | 37 | } from 'ant-design-vue'; |
37 | 38 | import { getApp } from '/@/setup/App'; |
38 | 39 | |
... | ... | @@ -55,6 +56,7 @@ export function registerGlobComp() { |
55 | 56 | getApp() |
56 | 57 | .use(Select) |
57 | 58 | .use(Alert) |
59 | + .use(Breadcrumb) | |
58 | 60 | .use(Checkbox) |
59 | 61 | .use(DatePicker) |
60 | 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
src/enums/pageEnum.ts
src/hooks/web/usePage.ts
1 | 1 | import { appStore } from '/@/store/modules/app'; |
2 | 2 | import type { RouteLocationRaw } from 'vue-router'; |
3 | 3 | |
4 | -import { useRouter } from 'vue-router'; | |
5 | 4 | import { PageEnum } from '/@/enums/pageEnum'; |
6 | 5 | import { isString } from '/@/utils/is'; |
7 | 6 | import { unref } from 'vue'; |
8 | 7 | |
8 | +import router from '/@/router'; | |
9 | + | |
9 | 10 | export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum }; |
10 | 11 | |
11 | 12 | function handleError(e: Error) { |
... | ... | @@ -18,7 +19,7 @@ function handleError(e: Error) { |
18 | 19 | |
19 | 20 | // page switch |
20 | 21 | export function useGo() { |
21 | - const { push, replace } = useRouter(); | |
22 | + const { push, replace } = router; | |
22 | 23 | function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) { |
23 | 24 | if (!opt) return; |
24 | 25 | if (isString(opt)) { |
... | ... | @@ -35,7 +36,7 @@ export function useGo() { |
35 | 36 | * @description: redo current page |
36 | 37 | */ |
37 | 38 | export const useRedo = () => { |
38 | - const { push, currentRoute } = useRouter(); | |
39 | + const { push, currentRoute } = router; | |
39 | 40 | const { query, params } = currentRoute.value; |
40 | 41 | function redo() { |
41 | 42 | push({ |
... | ... |
src/hooks/web/usePermission.ts
... | ... | @@ -7,13 +7,14 @@ import { userStore } from '/@/store/modules/user'; |
7 | 7 | import { useTabs } from './useTabs'; |
8 | 8 | |
9 | 9 | import router, { resetRouter } from '/@/router'; |
10 | -import { RootRoute } from '/@/router/routes'; | |
10 | +// import { RootRoute } from '/@/router/routes'; | |
11 | 11 | |
12 | 12 | import { PermissionModeEnum } from '/@/enums/appEnum'; |
13 | 13 | import { RoleEnum } from '/@/enums/roleEnum'; |
14 | 14 | |
15 | 15 | import { intersection } from 'lodash-es'; |
16 | 16 | import { isArray } from '/@/utils/is'; |
17 | +import { tabStore } from '/@/store/modules/tab'; | |
17 | 18 | |
18 | 19 | // User permissions related operations |
19 | 20 | export function usePermission() { |
... | ... | @@ -27,8 +28,7 @@ export function usePermission() { |
27 | 28 | ? PermissionModeEnum.ROLE |
28 | 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 | 36 | * @param id |
37 | 37 | */ |
38 | 38 | async function resume(id?: string | number) { |
39 | + tabStore.commitClearCache(); | |
39 | 40 | resetRouter(); |
40 | 41 | const routes = await permissionStore.buildRoutesAction(id); |
41 | 42 | routes.forEach((route) => { |
42 | - router.addRoute(RootRoute.name!, route as RouteRecordRaw); | |
43 | + router.addRoute(route as RouteRecordRaw); | |
43 | 44 | }); |
44 | 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 | 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 | 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 | 5 | function canIUseFn(): boolean { |
55 | 6 | const { multiTabsSetting: { show } = {} } = appStore.getProjectConfig; |
56 | 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 | 10 | return !!show; |
60 | 11 | } |
61 | 12 | |
62 | 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 './index.less'; |
3 | 3 | import { defineComponent, unref } from 'vue'; |
4 | 4 | import { Loading } from '/@/components/Loading'; |
5 | 5 | |
6 | -import { RouterView } from 'vue-router'; | |
7 | - | |
8 | 6 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
9 | 7 | import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; |
10 | - | |
8 | +import PageLayout from '/@/layouts/page/index.vue'; | |
11 | 9 | export default defineComponent({ |
12 | 10 | name: 'LayoutContent', |
13 | 11 | setup() { |
... | ... | @@ -20,7 +18,7 @@ export default defineComponent({ |
20 | 18 | {unref(getOpenPageLoading) && ( |
21 | 19 | <Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" /> |
22 | 20 | )} |
23 | - <RouterView /> | |
21 | + <PageLayout /> | |
24 | 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 'ant-design-vue'; |
9 | 9 | import { AppLogo } from '/@/components/Application'; |
10 | 10 | import UserDropdown from './UserDropdown'; |
11 | 11 | import LayoutMenu from '../menu'; |
12 | -import LayoutBreadcrumb from './LayoutBreadcrumb'; | |
12 | +import LayoutBreadcrumb from './LayoutBreadcrumb.vue'; | |
13 | 13 | import LockAction from '../lock/LockAction'; |
14 | 14 | import LayoutTrigger from '../LayoutTrigger'; |
15 | 15 | import NoticeAction from './notice/NoticeActionItem.vue'; |
... | ... |
src/layouts/default/header/LayoutMultipleHeader.less
src/layouts/default/header/index.less
... | ... | @@ -21,11 +21,15 @@ |
21 | 21 | |
22 | 22 | &__left { |
23 | 23 | display: flex; |
24 | + height: 100%; | |
24 | 25 | align-items: center; |
25 | 26 | |
26 | 27 | .layout-trigger { |
28 | + display: flex; | |
29 | + height: 100%; | |
27 | 30 | padding: 1px 10px 0 16px; |
28 | 31 | cursor: pointer; |
32 | + align-items: center; | |
29 | 33 | |
30 | 34 | .anticon { |
31 | 35 | font-size: 17px; |
... | ... | @@ -49,12 +53,22 @@ |
49 | 53 | } |
50 | 54 | |
51 | 55 | .layout-breadcrumb { |
56 | + display: flex; | |
52 | 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 | 69 | &__content { |
57 | 70 | display: flex; |
71 | + height: 100%; | |
58 | 72 | flex-grow: 1; |
59 | 73 | align-items: center; |
60 | 74 | } |
... | ... | @@ -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 | 107 | .layout-header__logo { |
76 | 108 | height: @header-height; |
77 | 109 | color: @text-color-base; |
... | ... | @@ -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 | 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 | 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 | 8 | import { RightOutlined } from '@ant-design/icons-vue'; |
10 | 9 | |
11 | -import { TabContentEnum } from './data'; | |
10 | +import { TabContentEnum } from './types'; | |
11 | + | |
12 | 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 | 13 | import { useI18n } from '/@/hooks/web/useI18n'; |
17 | 14 | |
15 | +import { RouteLocationNormalized } from 'vue-router'; | |
16 | + | |
18 | 17 | const { t: titleT } = useI18n(); |
19 | 18 | |
20 | 19 | const ExtraContent: FunctionalComponent = () => { |
... | ... | @@ -25,21 +24,13 @@ const ExtraContent: FunctionalComponent = () => { |
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 | 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 | 32 | return ( |
42 | - <div class={`multiple-tabs-content__content `} onContextmenu={handleContextMenu}> | |
33 | + <div class={`multiple-tabs-content__content `} onContextmenu={props.handler(props.tabItem)}> | |
43 | 34 | <span class="ml-1">{meta && titleT(meta.title)}</span> |
44 | 35 | </div> |
45 | 36 | ); |
... | ... | @@ -49,7 +40,7 @@ export default defineComponent({ |
49 | 40 | name: 'TabContent', |
50 | 41 | props: { |
51 | 42 | tabItem: { |
52 | - type: Object as PropType<TabItem>, | |
43 | + type: Object as PropType<RouteLocationNormalized>, | |
53 | 44 | default: null, |
54 | 45 | }, |
55 | 46 | |
... | ... | @@ -59,36 +50,27 @@ export default defineComponent({ |
59 | 50 | }, |
60 | 51 | }, |
61 | 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 | 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 | 62 | return ( |
86 | 63 | <Dropdown |
87 | - dropMenuList={!isTab ? [scaleAction, ...dropMenuList] : dropMenuList} | |
88 | - trigger={isTab ? ['contextmenu'] : ['click']} | |
64 | + dropMenuList={unref(getDropMenuList)} | |
65 | + trigger={unref(getTrigger)} | |
89 | 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 | 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 | 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 | 6 | import { useRouter } from 'vue-router'; |
11 | 7 | |
12 | 8 | import { Tabs } from 'ant-design-vue'; |
... | ... | @@ -14,15 +10,12 @@ import TabContent from './TabContent'; |
14 | 10 | |
15 | 11 | import { useGo } from '/@/hooks/web/usePage'; |
16 | 12 | |
17 | -import { TabContentEnum } from './data'; | |
13 | +import { TabContentEnum } from './types'; | |
18 | 14 | |
19 | 15 | import { tabStore } from '/@/store/modules/tab'; |
20 | 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 | 20 | export default defineComponent({ |
28 | 21 | name: 'MultipleTabs', |
... | ... | @@ -31,28 +24,25 @@ export default defineComponent({ |
31 | 24 | |
32 | 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 | 31 | const { currentRoute } = useRouter(); |
39 | 32 | |
40 | 33 | const getTabsState = computed(() => tabStore.getTabsState); |
41 | 34 | |
42 | - // If you monitor routing changes, tab switching will be stuck. So setting this method | |
43 | 35 | watch( |
44 | - () => tabStore.getLastChangeRouteState, | |
36 | + () => tabStore.getLastChangeRouteState?.path, | |
45 | 37 | () => { |
46 | 38 | const lastChangeRoute = unref(tabStore.getLastChangeRouteState); |
47 | - | |
48 | 39 | if (!lastChangeRoute || !userStore.getTokenState) return; |
49 | - | |
50 | - const { path, fullPath } = lastChangeRoute as AppRouteRecordRaw; | |
40 | + const { path, fullPath } = lastChangeRoute; | |
51 | 41 | const p = fullPath || path; |
52 | 42 | if (activeKeyRef.value !== p) { |
53 | 43 | activeKeyRef.value = p; |
54 | 44 | } |
55 | - tabStore.commitAddTab(lastChangeRoute); | |
45 | + tabStore.addTabAction(lastChangeRoute); | |
56 | 46 | }, |
57 | 47 | { |
58 | 48 | immediate: true, |
... | ... | @@ -67,22 +57,19 @@ export default defineComponent({ |
67 | 57 | // Close the current tab |
68 | 58 | function handleEdit(targetKey: string) { |
69 | 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 | 63 | function renderQuick() { |
77 | 64 | const tabContentProps: TabContentProps = { |
78 | - tabItem: (currentRoute as unknown) as AppRouteRecordRaw, | |
65 | + tabItem: currentRoute.value, | |
79 | 66 | type: TabContentEnum.EXTRA_TYPE, |
80 | 67 | }; |
81 | - return <TabContent {...(tabContentProps as any)} />; | |
68 | + return <TabContent {...tabContentProps} />; | |
82 | 69 | } |
83 | 70 | |
84 | 71 | function renderTabs() { |
85 | - return unref(getTabsState).map((item: TabItem) => { | |
72 | + return unref(getTabsState).map((item) => { | |
86 | 73 | const key = item.query ? item.fullPath : item.path; |
87 | 74 | const closable = !(item && item.meta && item.meta.affix); |
88 | 75 | |
... | ... | @@ -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 | 87 | return () => { |
135 | 88 | const slots = { |
136 | 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 | 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 | 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 | 16 | routes && |
14 | 17 | routes.forEach((route) => { |
15 | 18 | if (route.meta && route.meta.affix) { |
16 | - tabs.push(toRaw(route) as TabItem); | |
19 | + tabs.push(toRaw(route)); | |
17 | 20 | } |
18 | 21 | }); |
19 | 22 | return tabs; |
... | ... | @@ -23,10 +26,14 @@ export function initAffixTabs() { |
23 | 26 | * @description: Set fixed tabs |
24 | 27 | */ |
25 | 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 | 30 | affixList.value = affixTabs; |
28 | 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 | 44 | } |
38 | 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 | 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 | 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 | 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 | 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 | 44 | * @description: drop-down list |
36 | 45 | */ |
37 | 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 | 47 | if (!unref(getCurrentTab)) return; |
57 | - const { meta, path } = unref(getCurrentTab); | |
48 | + const { meta } = unref(getCurrentTab); | |
49 | + const { path } = unref(currentRoute); | |
58 | 50 | |
59 | 51 | // Refresh button |
60 | - const curItem = tabStore.getCurrentContextMenuState; | |
61 | - const index = tabStore.getCurrentContextMenuIndexState; | |
52 | + const curItem = state.current; | |
53 | + const index = state.currentIndex; | |
62 | 54 | const refreshDisabled = curItem ? curItem.path !== path : true; |
63 | 55 | // Close left |
64 | 56 | const closeLeftDisabled = index === 0; |
65 | 57 | |
58 | + const disabled = tabStore.getTabsState.length === 1; | |
59 | + | |
66 | 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 | 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 | 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 | 142 | // Handle right click event |
163 | 143 | function handleMenuEvent(menu: DropMenu): void { |
144 | + const { refreshPage, closeAll, closeCurrent, closeLeft, closeOther, closeRight } = useTabs(); | |
164 | 145 | const { event } = menu; |
165 | - | |
166 | 146 | switch (event) { |
167 | 147 | case MenuEventEnum.SCALE: |
168 | 148 | scaleScreen(); |
... | ... | @@ -193,51 +173,5 @@ export function useTabDropdown(tabContentProps: TabContentProps) { |
193 | 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
src/layouts/iframe/useFrameKeepAlive.ts
... | ... | @@ -23,7 +23,7 @@ export function useFrameKeepAlive() { |
23 | 23 | const getOpenTabList = computed((): string[] => { |
24 | 24 | return tabStore.getTabsState.reduce((prev: string[], next) => { |
25 | 25 | if (next.meta && Reflect.has(next.meta, 'frameSrc')) { |
26 | - prev.push(next.path!); | |
26 | + prev.push(next.name as string); | |
27 | 27 | } |
28 | 28 | return prev; |
29 | 29 | }, []); |
... | ... | @@ -45,11 +45,14 @@ export function useFrameKeepAlive() { |
45 | 45 | } |
46 | 46 | |
47 | 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 | 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
src/locales/lang/en/layout/header.ts
src/locales/lang/en/routes/demo/level.ts
0 → 100644
src/locales/lang/zh_CN/layout/header.ts
src/locales/lang/zh_CN/routes/demo/feat.ts
src/locales/lang/zh_CN/routes/demo/level.ts
0 → 100644
src/router/constant.ts
1 | 1 | import type { AppRouteRecordRaw } from '/@/router/types'; |
2 | +import ParentLayout from '/@/layouts/parent/index.vue'; | |
2 | 3 | |
3 | 4 | const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception'); |
4 | 5 | |
5 | 6 | /** |
6 | 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 | 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 | 29 | // 404 on a page |
16 | 30 | 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 | 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 | 46 | meta: { |
31 | - title: 'Redirect', | |
47 | + title: REDIRECT_NAME, | |
32 | 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 './pageLoadingGuard'; |
8 | 8 | |
9 | 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 | 12 | import { setTitle } from '/@/utils/browser'; |
13 | 13 | import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; |
14 | 14 | |
15 | 15 | import { tabStore } from '/@/store/modules/tab'; |
16 | 16 | import { useI18n } from '/@/hooks/web/useI18n'; |
17 | +import { REDIRECT_NAME } from '/@/router/constant'; | |
17 | 18 | |
18 | 19 | const { closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting(); |
19 | 20 | const globSetting = useGlobSetting(); |
21 | + | |
20 | 22 | export function createGuard(router: Router) { |
21 | - let axiosCanceler: AxiosCanceler | null; | |
23 | + let axiosCanceler: Nullable<AxiosCanceler>; | |
22 | 24 | if (removeAllHttpPending) { |
23 | 25 | axiosCanceler = new AxiosCanceler(); |
24 | 26 | } |
... | ... | @@ -30,15 +32,7 @@ export function createGuard(router: Router) { |
30 | 32 | to.meta.inTab = isOpen; |
31 | 33 | |
32 | 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 | 37 | try { |
44 | 38 | if (closeMessageOnSwitch) { |
... | ... | @@ -50,14 +44,13 @@ export function createGuard(router: Router) { |
50 | 44 | } catch (error) { |
51 | 45 | console.warn('basic guard error:' + error); |
52 | 46 | } |
53 | - setCurrentTo(to); | |
54 | 47 | return true; |
55 | 48 | }); |
56 | 49 | |
57 | 50 | router.afterEach((to) => { |
58 | 51 | const { t } = useI18n(); |
59 | 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 | 55 | createProgressGuard(router); |
63 | 56 | createPermissionGuard(router); |
... | ... |
src/router/guard/pageLoadingGuard.ts
... | ... | @@ -2,7 +2,7 @@ import type { Router } from 'vue-router'; |
2 | 2 | import { tabStore } from '/@/store/modules/tab'; |
3 | 3 | import { appStore } from '/@/store/modules/app'; |
4 | 4 | import { userStore } from '/@/store/modules/user'; |
5 | -import { getParams } from '/@/utils/helper/routeHelper'; | |
5 | +import { getParams } from '/@/router/helper/routeHelper'; | |
6 | 6 | import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; |
7 | 7 | import { unref } from 'vue'; |
8 | 8 | |
... | ... | @@ -14,6 +14,7 @@ export function createPageLoadingGuard(router: Router) { |
14 | 14 | if (!userStore.getTokenState) { |
15 | 15 | return true; |
16 | 16 | } |
17 | + | |
17 | 18 | if (!unref(getEnableTransition) && unref(getOpenPageLoading)) { |
18 | 19 | appStore.commitPageLoadingState(true); |
19 | 20 | return true; |
... | ... |
src/router/guard/permissionGuard.ts
... | ... | @@ -7,7 +7,7 @@ import { PageEnum } from '/@/enums/pageEnum'; |
7 | 7 | import { getToken } from '/@/utils/auth'; |
8 | 8 | |
9 | 9 | import { PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; |
10 | -import { RootRoute } from '../routes/index'; | |
10 | +// import { RootRoute } from '../routes/index'; | |
11 | 11 | |
12 | 12 | const LOGIN_PATH = PageEnum.BASE_LOGIN; |
13 | 13 | |
... | ... | @@ -59,7 +59,8 @@ export function createPermissionGuard(router: Router) { |
59 | 59 | } |
60 | 60 | const routes = await permissionStore.buildRoutesAction(); |
61 | 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 | 66 | const redirectPath = (from.query.redirect || to.path) as string; |
... | ... |
src/router/guard/progressGuard.ts
... | ... | @@ -9,9 +9,6 @@ import { unref } from 'vue'; |
9 | 9 | const { getOpenNProgress } = useTransitionSetting(); |
10 | 10 | |
11 | 11 | export function createProgressGuard(router: Router) { |
12 | - // NProgress.inc(0.1); | |
13 | - // NProgress.configure({ easing: 'ease', speed: 200, showSpinner: false }); | |
14 | - | |
15 | 12 | router.beforeEach(async (to) => { |
16 | 13 | !to.meta.inTab && unref(getOpenNProgress) && NProgress.start(); |
17 | 14 | return true; |
... | ... |
src/router/helper/dynamicImport.ts
0 → 100644
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 | 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 | 5 | import { cloneDeep } from 'lodash-es'; |
6 | 6 | |
7 | 7 | export function getAllParentPath(treeData: any[], path: string) { |
... | ... | @@ -48,12 +48,11 @@ export function transformRouteToMenu(routeModList: AppRouteModule[]) { |
48 | 48 | const cloneRouteModList = cloneDeep(routeModList); |
49 | 49 | const routeList: AppRouteRecordRaw[] = []; |
50 | 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 | 54 | } else { |
56 | - routes && routeList.push(...routes); | |
55 | + routeList.push(item); | |
57 | 56 | } |
58 | 57 | }); |
59 | 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 '/@/router/types'; |
2 | 2 | import type { RouteRecordNormalized } from 'vue-router'; |
3 | 3 | import { appStore } from '/@/store/modules/app'; |
4 | 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 | 6 | import { filter } from '/@/utils/helper/treeHelper'; |
7 | 7 | import router from '/@/router'; |
8 | 8 | import { PermissionModeEnum } from '/@/enums/appEnum'; |
... | ... |
src/router/menus/modules/dashboard.ts
1 | 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 | 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
src/router/routes/index.ts
1 | 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 | 6 | import modules from 'globby!/@/router/routes/modules/**/*.@(ts)'; |
6 | 7 | |
7 | 8 | const routeModuleList: AppRouteModule[] = []; |
8 | 9 | |
9 | 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 | 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 | 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 | 28 | 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 | 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 | 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 | 17 | name: 'Workbench', |
31 | 18 | component: () => import('/@/views/dashboard/workbench/index.vue'), |
32 | 19 | meta: { |
... | ... | @@ -34,7 +21,7 @@ const dashboard: AppRouteModule = { |
34 | 21 | }, |
35 | 22 | }, |
36 | 23 | { |
37 | - path: '/analysis', | |
24 | + path: 'analysis', | |
38 | 25 | name: 'Analysis', |
39 | 26 | component: () => import('/@/views/dashboard/analysis/index.vue'), |
40 | 27 | meta: { |
... | ... |
src/router/routes/modules/demo/charts.ts
1 | 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 | 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 | 17 | name: 'Echarts', |
18 | + component: getParentLayout('Echarts'), | |
21 | 19 | meta: { |
22 | 20 | title: 'Echarts', |
23 | 21 | }, |
... | ... | @@ -49,7 +47,7 @@ const charts: AppRouteModule = { |
49 | 47 | ], |
50 | 48 | }, |
51 | 49 | { |
52 | - path: '/apexChart', | |
50 | + path: 'apexChart', | |
53 | 51 | name: 'ApexChart', |
54 | 52 | meta: { |
55 | 53 | title: 'routes.demo.charts.apexChart', |
... | ... |
src/router/routes/modules/demo/comp.ts
1 | 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 | 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 | 18 | name: 'BasicDemo', |
21 | 19 | component: () => import('/@/views/demo/comp/button/index.vue'), |
22 | 20 | meta: { |
... | ... | @@ -24,7 +22,7 @@ const comp: AppRouteModule = { |
24 | 22 | }, |
25 | 23 | }, |
26 | 24 | { |
27 | - path: '/transition', | |
25 | + path: 'transition', | |
28 | 26 | name: 'transitionDemo', |
29 | 27 | component: () => import('/@/views/demo/comp/transition/index.vue'), |
30 | 28 | meta: { |
... | ... | @@ -32,7 +30,7 @@ const comp: AppRouteModule = { |
32 | 30 | }, |
33 | 31 | }, |
34 | 32 | { |
35 | - path: '/countTo', | |
33 | + path: 'countTo', | |
36 | 34 | name: 'CountTo', |
37 | 35 | component: () => import('/@/views/demo/comp/count-to/index.vue'), |
38 | 36 | meta: { |
... | ... | @@ -41,9 +39,10 @@ const comp: AppRouteModule = { |
41 | 39 | }, |
42 | 40 | |
43 | 41 | { |
44 | - path: '/scroll', | |
42 | + path: 'scroll', | |
45 | 43 | name: 'ScrollDemo', |
46 | 44 | redirect: '/comp/scroll/basic', |
45 | + component: getParentLayout('ScrollDemo'), | |
47 | 46 | meta: { |
48 | 47 | title: 'routes.demo.comp.scroll', |
49 | 48 | }, |
... | ... | @@ -76,7 +75,7 @@ const comp: AppRouteModule = { |
76 | 75 | }, |
77 | 76 | |
78 | 77 | { |
79 | - path: '/modal', | |
78 | + path: 'modal', | |
80 | 79 | name: 'ModalDemo', |
81 | 80 | component: () => import('/@/views/demo/comp/modal/index.vue'), |
82 | 81 | meta: { |
... | ... | @@ -84,7 +83,7 @@ const comp: AppRouteModule = { |
84 | 83 | }, |
85 | 84 | }, |
86 | 85 | { |
87 | - path: '/drawer', | |
86 | + path: 'drawer', | |
88 | 87 | name: 'DrawerDemo', |
89 | 88 | component: () => import('/@/views/demo/comp/drawer/index.vue'), |
90 | 89 | meta: { |
... | ... | @@ -92,7 +91,7 @@ const comp: AppRouteModule = { |
92 | 91 | }, |
93 | 92 | }, |
94 | 93 | { |
95 | - path: '/desc', | |
94 | + path: 'desc', | |
96 | 95 | name: 'DescDemo', |
97 | 96 | component: () => import('/@/views/demo/comp/desc/index.vue'), |
98 | 97 | meta: { |
... | ... | @@ -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 | 106 | redirect: '/comp/lazy/basic', |
107 | 107 | meta: { |
108 | 108 | title: 'routes.demo.comp.lazy', |
... | ... | @@ -127,8 +127,9 @@ const comp: AppRouteModule = { |
127 | 127 | ], |
128 | 128 | }, |
129 | 129 | { |
130 | - path: '/verify', | |
130 | + path: 'verify', | |
131 | 131 | name: 'VerifyDemo', |
132 | + component: getParentLayout('VerifyDemo'), | |
132 | 133 | redirect: '/comp/verify/drag', |
133 | 134 | meta: { |
134 | 135 | title: 'routes.demo.comp.verify', |
... | ... | @@ -155,7 +156,7 @@ const comp: AppRouteModule = { |
155 | 156 | // |
156 | 157 | |
157 | 158 | { |
158 | - path: '/qrcode', | |
159 | + path: 'qrcode', | |
159 | 160 | name: 'QrCodeDemo', |
160 | 161 | component: () => import('/@/views/demo/comp/qrcode/index.vue'), |
161 | 162 | meta: { |
... | ... | @@ -163,7 +164,7 @@ const comp: AppRouteModule = { |
163 | 164 | }, |
164 | 165 | }, |
165 | 166 | { |
166 | - path: '/strength-meter', | |
167 | + path: 'strength-meter', | |
167 | 168 | name: 'StrengthMeterDemo', |
168 | 169 | component: () => import('/@/views/demo/comp/strength-meter/index.vue'), |
169 | 170 | meta: { |
... | ... | @@ -171,7 +172,7 @@ const comp: AppRouteModule = { |
171 | 172 | }, |
172 | 173 | }, |
173 | 174 | { |
174 | - path: '/upload', | |
175 | + path: 'upload', | |
175 | 176 | name: 'UploadDemo', |
176 | 177 | component: () => import('/@/views/demo/comp/upload/index.vue'), |
177 | 178 | meta: { |
... | ... | @@ -179,7 +180,7 @@ const comp: AppRouteModule = { |
179 | 180 | }, |
180 | 181 | }, |
181 | 182 | { |
182 | - path: '/loading', | |
183 | + path: 'loading', | |
183 | 184 | name: 'LoadingDemo', |
184 | 185 | component: () => import('/@/views/demo/comp/loading/index.vue'), |
185 | 186 | meta: { |
... | ... |
src/router/routes/modules/demo/editor.ts
1 | 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 | 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 | 17 | name: 'MarkdownDemo', |
21 | 18 | component: () => import('/@/views/demo/editor/Markdown.vue'), |
22 | 19 | meta: { |
... | ... | @@ -24,7 +21,8 @@ const editor: AppRouteModule = { |
24 | 21 | }, |
25 | 22 | }, |
26 | 23 | { |
27 | - path: '/tinymce', | |
24 | + path: 'tinymce', | |
25 | + component: getParentLayout('TinymceDemo'), | |
28 | 26 | name: 'TinymceDemo', |
29 | 27 | meta: { |
30 | 28 | title: 'routes.demo.editor.tinymce', |
... | ... | @@ -39,7 +37,6 @@ const editor: AppRouteModule = { |
39 | 37 | title: 'routes.demo.editor.tinymceBasic', |
40 | 38 | }, |
41 | 39 | }, |
42 | - // TODO | |
43 | 40 | { |
44 | 41 | path: 'editor', |
45 | 42 | name: 'TinymceFormDemo', |
... | ... |
src/router/routes/modules/demo/excel.ts
1 | 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 | 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 | 18 | name: 'CustomExport', |
21 | 19 | component: () => import('/@/views/demo/excel/CustomExport.vue'), |
22 | 20 | meta: { |
... | ... | @@ -24,7 +22,7 @@ const excel: AppRouteModule = { |
24 | 22 | }, |
25 | 23 | }, |
26 | 24 | { |
27 | - path: '/jsonExport', | |
25 | + path: 'jsonExport', | |
28 | 26 | name: 'JsonExport', |
29 | 27 | component: () => import('/@/views/demo/excel/JsonExport.vue'), |
30 | 28 | meta: { |
... | ... | @@ -32,7 +30,7 @@ const excel: AppRouteModule = { |
32 | 30 | }, |
33 | 31 | }, |
34 | 32 | { |
35 | - path: '/arrayExport', | |
33 | + path: 'arrayExport', | |
36 | 34 | name: 'ArrayExport', |
37 | 35 | component: () => import('/@/views/demo/excel/ArrayExport.vue'), |
38 | 36 | meta: { |
... | ... | @@ -40,7 +38,7 @@ const excel: AppRouteModule = { |
40 | 38 | }, |
41 | 39 | }, |
42 | 40 | { |
43 | - path: '/importExcel', | |
41 | + path: 'importExcel', | |
44 | 42 | name: 'ImportExcel', |
45 | 43 | component: () => import('/@/views/demo/excel/ImportExcel.vue'), |
46 | 44 | meta: { |
... | ... |
src/router/routes/modules/demo/feat.ts
1 | 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 | 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 | 17 | name: 'IconDemo', |
21 | 18 | component: () => import('/@/views/demo/feat/icon/index.vue'), |
22 | 19 | meta: { |
... | ... | @@ -24,7 +21,7 @@ const feat: AppRouteModule = { |
24 | 21 | }, |
25 | 22 | }, |
26 | 23 | { |
27 | - path: '/tabs', | |
24 | + path: 'tabs', | |
28 | 25 | name: 'TabsDemo', |
29 | 26 | component: () => import('/@/views/demo/feat/tabs/index.vue'), |
30 | 27 | meta: { |
... | ... | @@ -33,7 +30,7 @@ const feat: AppRouteModule = { |
33 | 30 | }, |
34 | 31 | |
35 | 32 | { |
36 | - path: '/context-menu', | |
33 | + path: 'context-menu', | |
37 | 34 | name: 'ContextMenuDemo', |
38 | 35 | component: () => import('/@/views/demo/feat/context-menu/index.vue'), |
39 | 36 | meta: { |
... | ... | @@ -41,7 +38,7 @@ const feat: AppRouteModule = { |
41 | 38 | }, |
42 | 39 | }, |
43 | 40 | { |
44 | - path: '/download', | |
41 | + path: 'download', | |
45 | 42 | name: 'DownLoadDemo', |
46 | 43 | component: () => import('/@/views/demo/feat/download/index.vue'), |
47 | 44 | meta: { |
... | ... | @@ -49,7 +46,7 @@ const feat: AppRouteModule = { |
49 | 46 | }, |
50 | 47 | }, |
51 | 48 | { |
52 | - path: '/click-out-side', | |
49 | + path: 'click-out-side', | |
53 | 50 | name: 'ClickOutSideDemo', |
54 | 51 | component: () => import('/@/views/demo/feat/click-out-side/index.vue'), |
55 | 52 | meta: { |
... | ... | @@ -57,7 +54,7 @@ const feat: AppRouteModule = { |
57 | 54 | }, |
58 | 55 | }, |
59 | 56 | { |
60 | - path: '/img-preview', | |
57 | + path: 'img-preview', | |
61 | 58 | name: 'ImgPreview', |
62 | 59 | component: () => import('/@/views/demo/feat/img-preview/index.vue'), |
63 | 60 | meta: { |
... | ... | @@ -65,7 +62,7 @@ const feat: AppRouteModule = { |
65 | 62 | }, |
66 | 63 | }, |
67 | 64 | { |
68 | - path: '/copy', | |
65 | + path: 'copy', | |
69 | 66 | name: 'CopyDemo', |
70 | 67 | component: () => import('/@/views/demo/feat/copy/index.vue'), |
71 | 68 | meta: { |
... | ... | @@ -73,7 +70,7 @@ const feat: AppRouteModule = { |
73 | 70 | }, |
74 | 71 | }, |
75 | 72 | { |
76 | - path: '/msg', | |
73 | + path: 'msg', | |
77 | 74 | name: 'MsgDemo', |
78 | 75 | component: () => import('/@/views/demo/feat/msg/index.vue'), |
79 | 76 | meta: { |
... | ... | @@ -81,7 +78,7 @@ const feat: AppRouteModule = { |
81 | 78 | }, |
82 | 79 | }, |
83 | 80 | { |
84 | - path: '/watermark', | |
81 | + path: 'watermark', | |
85 | 82 | name: 'WatermarkDemo', |
86 | 83 | component: () => import('/@/views/demo/feat/watermark/index.vue'), |
87 | 84 | meta: { |
... | ... | @@ -89,7 +86,7 @@ const feat: AppRouteModule = { |
89 | 86 | }, |
90 | 87 | }, |
91 | 88 | { |
92 | - path: '/full-screen', | |
89 | + path: 'full-screen', | |
93 | 90 | name: 'FullScreenDemo', |
94 | 91 | component: () => import('/@/views/demo/feat/full-screen/index.vue'), |
95 | 92 | meta: { |
... | ... | @@ -97,7 +94,7 @@ const feat: AppRouteModule = { |
97 | 94 | }, |
98 | 95 | }, |
99 | 96 | { |
100 | - path: '/error-log', | |
97 | + path: 'error-log', | |
101 | 98 | name: 'ErrorLog', |
102 | 99 | component: () => import('/@/views/sys/error-log/index.vue'), |
103 | 100 | meta: { |
... | ... | @@ -105,7 +102,7 @@ const feat: AppRouteModule = { |
105 | 102 | }, |
106 | 103 | }, |
107 | 104 | { |
108 | - path: '/testTab/:id', | |
105 | + path: 'testTab/:id', | |
109 | 106 | name: 'TestTab', |
110 | 107 | component: () => import('/@/views/demo/feat/tab-params/index.vue'), |
111 | 108 | meta: { |
... | ... |
src/router/routes/modules/demo/form.ts
1 | 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 | 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 | 17 | name: 'FormBasicDemo', |
21 | 18 | component: () => import('/@/views/demo/form/index.vue'), |
22 | 19 | meta: { |
... | ... | @@ -24,7 +21,7 @@ const form: AppRouteModule = { |
24 | 21 | }, |
25 | 22 | }, |
26 | 23 | { |
27 | - path: '/useForm', | |
24 | + path: 'useForm', | |
28 | 25 | name: 'UseFormDemo', |
29 | 26 | component: () => import('/@/views/demo/form/UseForm.vue'), |
30 | 27 | meta: { |
... | ... | @@ -32,7 +29,7 @@ const form: AppRouteModule = { |
32 | 29 | }, |
33 | 30 | }, |
34 | 31 | { |
35 | - path: '/refForm', | |
32 | + path: 'refForm', | |
36 | 33 | name: 'RefFormDemo', |
37 | 34 | component: () => import('/@/views/demo/form/RefForm.vue'), |
38 | 35 | meta: { |
... | ... | @@ -40,7 +37,7 @@ const form: AppRouteModule = { |
40 | 37 | }, |
41 | 38 | }, |
42 | 39 | { |
43 | - path: '/advancedForm', | |
40 | + path: 'advancedForm', | |
44 | 41 | name: 'AdvancedFormDemo', |
45 | 42 | component: () => import('/@/views/demo/form/AdvancedForm.vue'), |
46 | 43 | meta: { |
... | ... | @@ -48,7 +45,7 @@ const form: AppRouteModule = { |
48 | 45 | }, |
49 | 46 | }, |
50 | 47 | { |
51 | - path: '/ruleForm', | |
48 | + path: 'ruleForm', | |
52 | 49 | name: 'RuleFormDemo', |
53 | 50 | component: () => import('/@/views/demo/form/RuleForm.vue'), |
54 | 51 | meta: { |
... | ... | @@ -56,7 +53,7 @@ const form: AppRouteModule = { |
56 | 53 | }, |
57 | 54 | }, |
58 | 55 | { |
59 | - path: '/dynamicForm', | |
56 | + path: 'dynamicForm', | |
60 | 57 | name: 'DynamicFormDemo', |
61 | 58 | component: () => import('/@/views/demo/form/DynamicForm.vue'), |
62 | 59 | meta: { |
... | ... | @@ -64,7 +61,7 @@ const form: AppRouteModule = { |
64 | 61 | }, |
65 | 62 | }, |
66 | 63 | { |
67 | - path: '/customerForm', | |
64 | + path: 'customerForm', | |
68 | 65 | name: 'CustomerFormDemo', |
69 | 66 | component: () => import('/@/views/demo/form/CustomerForm.vue'), |
70 | 67 | meta: { |
... | ... |
src/router/routes/modules/demo/iframe.ts
1 | 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 | const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue'); |
5 | 5 | |
6 | 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 | 19 | name: 'Antv', |
22 | 20 | component: IFrame, |
23 | 21 | meta: { |
... | ... | @@ -27,7 +25,7 @@ const iframe: AppRouteModule = { |
27 | 25 | }, |
28 | 26 | }, |
29 | 27 | { |
30 | - path: '/doc', | |
28 | + path: 'doc', | |
31 | 29 | name: 'Doc', |
32 | 30 | component: IFrame, |
33 | 31 | meta: { |
... | ... | @@ -37,7 +35,7 @@ const iframe: AppRouteModule = { |
37 | 35 | }, |
38 | 36 | }, |
39 | 37 | { |
40 | - path: '/docExternal', | |
38 | + path: 'docExternal', | |
41 | 39 | name: 'DocExternal', |
42 | 40 | component: IFrame, |
43 | 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 | 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 | import { ExceptionEnum } from '/@/enums/exceptionEnum'; |
5 | 5 | |
6 | 6 | const ExceptionPage = () => import('/@/views/sys/exception/Exception'); |
... | ... | @@ -8,7 +8,7 @@ const ExceptionPage = () => import('/@/views/sys/exception/Exception'); |
8 | 8 | const page: AppRouteModule = { |
9 | 9 | path: '/page-demo', |
10 | 10 | name: 'PageDemo', |
11 | - component: PAGE_LAYOUT_COMPONENT, | |
11 | + component: LAYOUT, | |
12 | 12 | redirect: '/page-demo/exception', |
13 | 13 | meta: { |
14 | 14 | icon: 'mdi:page-next-outline', |
... | ... | @@ -17,9 +17,10 @@ const page: AppRouteModule = { |
17 | 17 | children: [ |
18 | 18 | // =============================form start============================= |
19 | 19 | { |
20 | - path: '/form', | |
20 | + path: 'form', | |
21 | 21 | name: 'FormPage', |
22 | 22 | redirect: '/page-demo/form/basic', |
23 | + component: getParentLayout('FormPage'), | |
23 | 24 | meta: { |
24 | 25 | title: 'routes.demo.page.form', |
25 | 26 | }, |
... | ... | @@ -53,8 +54,9 @@ const page: AppRouteModule = { |
53 | 54 | // =============================form end============================= |
54 | 55 | // =============================desc start============================= |
55 | 56 | { |
56 | - path: '/desc', | |
57 | + path: 'desc', | |
57 | 58 | name: 'DescPage', |
59 | + component: getParentLayout('DescPage'), | |
58 | 60 | redirect: '/page-demo/desc/basic', |
59 | 61 | meta: { |
60 | 62 | title: 'routes.demo.page.desc', |
... | ... | @@ -82,9 +84,11 @@ const page: AppRouteModule = { |
82 | 84 | |
83 | 85 | // =============================result start============================= |
84 | 86 | { |
85 | - path: '/result', | |
87 | + path: 'result', | |
86 | 88 | name: 'ResultPage', |
87 | 89 | redirect: '/page-demo/result/success', |
90 | + component: getParentLayout('ResultPage'), | |
91 | + | |
88 | 92 | meta: { |
89 | 93 | title: 'routes.demo.page.result', |
90 | 94 | }, |
... | ... | @@ -111,8 +115,9 @@ const page: AppRouteModule = { |
111 | 115 | |
112 | 116 | // =============================account start============================= |
113 | 117 | { |
114 | - path: '/account', | |
118 | + path: 'account', | |
115 | 119 | name: 'AccountPage', |
120 | + component: getParentLayout('AccountPage'), | |
116 | 121 | redirect: '/page-demo/account/setting', |
117 | 122 | meta: { |
118 | 123 | title: 'routes.demo.page.account', |
... | ... | @@ -139,8 +144,9 @@ const page: AppRouteModule = { |
139 | 144 | // =============================account end============================= |
140 | 145 | // =============================exception start============================= |
141 | 146 | { |
142 | - path: '/exception', | |
147 | + path: 'exception', | |
143 | 148 | name: 'ExceptionPage', |
149 | + component: getParentLayout('ExceptionPage'), | |
144 | 150 | redirect: '/page-demo/exception/404', |
145 | 151 | meta: { |
146 | 152 | title: 'routes.demo.page.exception', |
... | ... | @@ -211,8 +217,9 @@ const page: AppRouteModule = { |
211 | 217 | // =============================exception end============================= |
212 | 218 | // =============================list start============================= |
213 | 219 | { |
214 | - path: '/list', | |
220 | + path: 'list', | |
215 | 221 | name: 'ListPage', |
222 | + component: getParentLayout('ListPage'), | |
216 | 223 | redirect: '/page-demo/list/card', |
217 | 224 | meta: { |
218 | 225 | title: 'routes.demo.page.list', |
... | ... |
src/router/routes/modules/demo/permission.ts
1 | 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 | import { RoleEnum } from '/@/enums/roleEnum'; |
5 | 5 | |
6 | 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 | 19 | name: 'PermissionFrontDemo', |
20 | + component: getParentLayout('PermissionFrontDemo'), | |
22 | 21 | meta: { |
23 | 22 | title: 'routes.demo.permission.front', |
24 | 23 | }, |
... | ... | @@ -60,8 +59,9 @@ const permission: AppRouteModule = { |
60 | 59 | ], |
61 | 60 | }, |
62 | 61 | { |
63 | - path: '/back', | |
62 | + path: 'back', | |
64 | 63 | name: 'PermissionBackDemo', |
64 | + component: getParentLayout('PermissionBackDemo'), | |
65 | 65 | meta: { |
66 | 66 | title: 'routes.demo.permission.back', |
67 | 67 | }, |
... | ... |
src/router/routes/modules/demo/table.ts
1 | 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 | 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 | 18 | name: 'TableBasicDemo', |
21 | 19 | component: () => import('/@/views/demo/table/Basic.vue'), |
22 | 20 | meta: { |
... | ... | @@ -24,7 +22,7 @@ const table: AppRouteModule = { |
24 | 22 | }, |
25 | 23 | }, |
26 | 24 | { |
27 | - path: '/treeTable', | |
25 | + path: 'treeTable', | |
28 | 26 | name: 'TreeTableDemo', |
29 | 27 | component: () => import('/@/views/demo/table/TreeTable.vue'), |
30 | 28 | meta: { |
... | ... | @@ -32,7 +30,7 @@ const table: AppRouteModule = { |
32 | 30 | }, |
33 | 31 | }, |
34 | 32 | { |
35 | - path: '/fetchTable', | |
33 | + path: 'fetchTable', | |
36 | 34 | name: 'FetchTableDemo', |
37 | 35 | component: () => import('/@/views/demo/table/FetchTable.vue'), |
38 | 36 | meta: { |
... | ... | @@ -40,7 +38,7 @@ const table: AppRouteModule = { |
40 | 38 | }, |
41 | 39 | }, |
42 | 40 | { |
43 | - path: '/fixedColumn', | |
41 | + path: 'fixedColumn', | |
44 | 42 | name: 'FixedColumnDemo', |
45 | 43 | component: () => import('/@/views/demo/table/FixedColumn.vue'), |
46 | 44 | meta: { |
... | ... | @@ -48,7 +46,7 @@ const table: AppRouteModule = { |
48 | 46 | }, |
49 | 47 | }, |
50 | 48 | { |
51 | - path: '/customerCell', | |
49 | + path: 'customerCell', | |
52 | 50 | name: 'CustomerCellDemo', |
53 | 51 | component: () => import('/@/views/demo/table/CustomerCell.vue'), |
54 | 52 | meta: { |
... | ... | @@ -56,7 +54,7 @@ const table: AppRouteModule = { |
56 | 54 | }, |
57 | 55 | }, |
58 | 56 | { |
59 | - path: '/formTable', | |
57 | + path: 'formTable', | |
60 | 58 | name: 'FormTableDemo', |
61 | 59 | component: () => import('/@/views/demo/table/FormTable.vue'), |
62 | 60 | meta: { |
... | ... | @@ -64,7 +62,7 @@ const table: AppRouteModule = { |
64 | 62 | }, |
65 | 63 | }, |
66 | 64 | { |
67 | - path: '/useTable', | |
65 | + path: 'useTable', | |
68 | 66 | name: 'UseTableDemo', |
69 | 67 | component: () => import('/@/views/demo/table/UseTable.vue'), |
70 | 68 | meta: { |
... | ... | @@ -72,7 +70,7 @@ const table: AppRouteModule = { |
72 | 70 | }, |
73 | 71 | }, |
74 | 72 | { |
75 | - path: '/refTable', | |
73 | + path: 'refTable', | |
76 | 74 | name: 'RefTableDemo', |
77 | 75 | component: () => import('/@/views/demo/table/RefTable.vue'), |
78 | 76 | meta: { |
... | ... | @@ -80,7 +78,7 @@ const table: AppRouteModule = { |
80 | 78 | }, |
81 | 79 | }, |
82 | 80 | { |
83 | - path: '/multipleHeader', | |
81 | + path: 'multipleHeader', | |
84 | 82 | name: 'MultipleHeaderDemo', |
85 | 83 | component: () => import('/@/views/demo/table/MultipleHeader.vue'), |
86 | 84 | meta: { |
... | ... | @@ -88,7 +86,7 @@ const table: AppRouteModule = { |
88 | 86 | }, |
89 | 87 | }, |
90 | 88 | { |
91 | - path: '/mergeHeader', | |
89 | + path: 'mergeHeader', | |
92 | 90 | name: 'MergeHeaderDemo', |
93 | 91 | component: () => import('/@/views/demo/table/MergeHeader.vue'), |
94 | 92 | meta: { |
... | ... | @@ -96,7 +94,7 @@ const table: AppRouteModule = { |
96 | 94 | }, |
97 | 95 | }, |
98 | 96 | { |
99 | - path: '/expandTable', | |
97 | + path: 'expandTable', | |
100 | 98 | name: 'ExpandTableDemo', |
101 | 99 | component: () => import('/@/views/demo/table/ExpandTable.vue'), |
102 | 100 | meta: { |
... | ... | @@ -104,7 +102,7 @@ const table: AppRouteModule = { |
104 | 102 | }, |
105 | 103 | }, |
106 | 104 | { |
107 | - path: '/fixedHeight', | |
105 | + path: 'fixedHeight', | |
108 | 106 | name: 'FixedHeightDemo', |
109 | 107 | component: () => import('/@/views/demo/table/FixedHeight.vue'), |
110 | 108 | meta: { |
... | ... | @@ -112,7 +110,7 @@ const table: AppRouteModule = { |
112 | 110 | }, |
113 | 111 | }, |
114 | 112 | { |
115 | - path: '/footerTable', | |
113 | + path: 'footerTable', | |
116 | 114 | name: 'FooterTableDemo', |
117 | 115 | component: () => import('/@/views/demo/table/FooterTable.vue'), |
118 | 116 | meta: { |
... | ... | @@ -120,7 +118,7 @@ const table: AppRouteModule = { |
120 | 118 | }, |
121 | 119 | }, |
122 | 120 | { |
123 | - path: '/editCellTable', | |
121 | + path: 'editCellTable', | |
124 | 122 | name: 'EditCellTableDemo', |
125 | 123 | component: () => import('/@/views/demo/table/EditCellTable.vue'), |
126 | 124 | meta: { |
... | ... | @@ -128,7 +126,7 @@ const table: AppRouteModule = { |
128 | 126 | }, |
129 | 127 | }, |
130 | 128 | { |
131 | - path: '/editRowTable', | |
129 | + path: 'editRowTable', | |
132 | 130 | name: 'EditRowTableDemo', |
133 | 131 | component: () => import('/@/views/demo/table/EditRowTable.vue'), |
134 | 132 | meta: { |
... | ... |
src/router/routes/modules/demo/tree.ts
1 | 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 | 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 | 17 | name: 'BasicTreeDemo', |
20 | 18 | component: () => import('/@/views/demo/tree/index.vue'), |
21 | 19 | meta: { |
... | ... | @@ -23,7 +21,7 @@ const tree: AppRouteModule = { |
23 | 21 | }, |
24 | 22 | }, |
25 | 23 | { |
26 | - path: '/editTree', | |
24 | + path: 'editTree', | |
27 | 25 | name: 'EditTreeDemo', |
28 | 26 | component: () => import('/@/views/demo/tree/EditTree.vue'), |
29 | 27 | meta: { |
... | ... | @@ -31,7 +29,7 @@ const tree: AppRouteModule = { |
31 | 29 | }, |
32 | 30 | }, |
33 | 31 | { |
34 | - path: '/actionTree', | |
32 | + path: 'actionTree', | |
35 | 33 | name: 'ActionTreeDemo', |
36 | 34 | component: () => import('/@/views/demo/tree/ActionTree.vue'), |
37 | 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 | 1 | import type { RouteRecordRaw } from 'vue-router'; |
2 | 2 | import { RoleEnum } from '/@/enums/roleEnum'; |
3 | +import Component from '/@/components/types'; | |
3 | 4 | export interface RouteMeta { |
4 | 5 | // title |
5 | 6 | title: string; |
... | ... | @@ -24,24 +25,23 @@ export interface RouteMeta { |
24 | 25 | // Whether the route has been dynamically added |
25 | 26 | hideBreadcrumb?: boolean; |
26 | 27 | |
27 | - // disabled redirect | |
28 | - disabledRedirect?: boolean; | |
29 | - | |
30 | 28 | // close loading |
31 | 29 | afterCloseLoading?: boolean; |
32 | 30 | // Is it in the tab |
33 | 31 | inTab?: boolean; |
34 | 32 | // Carrying parameters |
35 | 33 | carryParam?: boolean; |
34 | + | |
35 | + single?: boolean; | |
36 | 36 | } |
37 | 37 | |
38 | 38 | export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> { |
39 | 39 | name: string; |
40 | 40 | meta: RouteMeta; |
41 | - component?: any; | |
42 | - components?: any; | |
41 | + component?: Component; | |
42 | + components?: Component; | |
43 | 43 | children?: AppRouteRecordRaw[]; |
44 | - props?: any; | |
44 | + props?: Record<string, any>; | |
45 | 45 | fullPath?: string; |
46 | 46 | } |
47 | 47 | export interface MenuTag { |
... | ... | @@ -75,11 +75,12 @@ export interface MenuModule { |
75 | 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 | 1 | import type { AppRouteRecordRaw, Menu } from '/@/router/types'; |
3 | 2 | import store from '/@/store/index'; |
4 | 3 | import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; |
... | ... | @@ -15,15 +14,13 @@ import { filter } from '/@/utils/helper/treeHelper'; |
15 | 14 | import { toRaw } from 'vue'; |
16 | 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 | 20 | import { useMessage } from '/@/hooks/web/useMessage'; |
22 | 21 | // import { warn } from '/@/utils/log'; |
23 | 22 | import { useI18n } from '/@/hooks/web/useI18n'; |
24 | 23 | |
25 | -const { t } = useI18n(); | |
26 | - | |
27 | 24 | const { createMessage } = useMessage(); |
28 | 25 | const NAME = 'permission'; |
29 | 26 | hotModuleUnregisterModule(NAME); |
... | ... | @@ -87,6 +84,7 @@ class Permission extends VuexModule { |
87 | 84 | |
88 | 85 | @Action |
89 | 86 | async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> { |
87 | + const { t } = useI18n(); | |
90 | 88 | let routes: AppRouteRecordRaw[] = []; |
91 | 89 | const roleList = toRaw(userStore.getRoleListState); |
92 | 90 | |
... | ... | @@ -95,17 +93,15 @@ class Permission extends VuexModule { |
95 | 93 | // role permissions |
96 | 94 | if (permissionMode === PermissionModeEnum.ROLE) { |
97 | 95 | routes = filter(asyncRoutes, (route) => { |
98 | - const { meta } = route; | |
99 | - const { roles } = meta!; | |
96 | + const { meta } = route as AppRouteRecordRaw; | |
97 | + const { roles } = meta || {}; | |
100 | 98 | if (!roles) return true; |
101 | 99 | return roleList.some((role) => roles.includes(role)); |
102 | 100 | }); |
103 | 101 | // 如果确定不需要做后台动态权限,请将下面整个判断注释 |
104 | 102 | } else if (permissionMode === PermissionModeEnum.BACK) { |
105 | - const messageKey = 'loadMenu'; | |
106 | 103 | createMessage.loading({ |
107 | 104 | content: t('sys.app.menuLoading'), |
108 | - key: messageKey, | |
109 | 105 | duration: 1, |
110 | 106 | }); |
111 | 107 | // 这里获取后台路由菜单逻辑自行修改 |
... | ... | @@ -118,10 +114,10 @@ class Permission extends VuexModule { |
118 | 114 | routeList = transformObjToRoute(routeList); |
119 | 115 | // 后台路由转菜单结构 |
120 | 116 | const backMenuList = transformRouteToMenu(routeList); |
117 | + | |
121 | 118 | this.commitBackMenuListState(backMenuList); |
122 | - // 生成路由 | |
123 | - routes = genRouteModule(routeList) as AppRouteRecordRaw[]; | |
124 | - routes.push(REDIRECT_ROUTE); | |
119 | + | |
120 | + routes = routeList; | |
125 | 121 | } |
126 | 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 | 3 | import { unref } from 'vue'; |
5 | 4 | import { Action, Module, Mutation, VuexModule, getModule } from 'vuex-module-decorators'; |
6 | 5 | import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; |
7 | 6 | |
8 | 7 | import { PageEnum } from '/@/enums/pageEnum'; |
9 | -import { appStore } from '/@/store/modules/app'; | |
10 | 8 | import { userStore } from './user'; |
11 | 9 | |
12 | 10 | import store from '/@/store'; |
13 | 11 | import router from '/@/router'; |
14 | 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 | 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 | 19 | const NAME = 'tab'; |
33 | 20 | |
34 | 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 | 30 | @Module({ namespaced: true, name: NAME, dynamic: true, store }) |
39 | 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 | 37 | // Last route change |
50 | - lastChangeRouteState: AppRouteRecordRaw | null = null; | |
38 | + lastChangeRouteState: RouteLocationNormalized | null = null; | |
39 | + | |
40 | + lastDragEndIndexState = 0; | |
51 | 41 | |
52 | 42 | get getTabsState() { |
53 | 43 | return this.tabsState; |
... | ... | @@ -57,56 +47,93 @@ class Tab extends VuexModule { |
57 | 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 | 63 | @Mutation |
78 | - commitLastChangeRouteState(route: AppRouteRecordRaw): void { | |
64 | + commitLastChangeRouteState(route: RouteLocationNormalized): void { | |
79 | 65 | if (!userStore.getTokenState) return; |
80 | 66 | this.lastChangeRouteState = route; |
81 | 67 | } |
82 | 68 | |
83 | 69 | @Mutation |
84 | 70 | commitClearCache(): void { |
85 | - this.keepAliveTabsState = []; | |
71 | + this.cachedMapState = new Map(); | |
86 | 72 | } |
87 | 73 | |
88 | 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 | 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 | 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 | 138 | let updateIndex = -1; |
112 | 139 | // 已经存在的页面,不重复添加tab |
... | ... | @@ -123,39 +150,18 @@ class Tab extends VuexModule { |
123 | 150 | this.tabsState.splice(updateIndex, 1, curTab); |
124 | 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 | 157 | * @description: close tab |
136 | 158 | */ |
137 | 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 | 167 | @Mutation |
... | ... | @@ -163,16 +169,12 @@ class Tab extends VuexModule { |
163 | 169 | this.tabsState = this.tabsState.filter((item) => { |
164 | 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 | 174 | @Mutation |
171 | 175 | commitResetState(): void { |
172 | 176 | this.tabsState = []; |
173 | - this.currentContextMenuState = null; | |
174 | - this.currentContextMenuIndexState = -1; | |
175 | - this.keepAliveTabsState = []; | |
177 | + this.cachedMapState = new Map(); | |
176 | 178 | } |
177 | 179 | |
178 | 180 | @Mutation |
... | ... | @@ -181,73 +183,149 @@ class Tab extends VuexModule { |
181 | 183 | |
182 | 184 | this.tabsState.splice(oldIndex, 1); |
183 | 185 | this.tabsState.splice(newIndex, 0, currentTab); |
186 | + this.lastDragEndIndexState = this.lastDragEndIndexState + 1; | |
184 | 187 | } |
185 | 188 | |
186 | 189 | @Mutation |
187 | - closeMultipleTab({ pathList, nameList }: { pathList: string[]; nameList: string[] }): void { | |
190 | + closeMultipleTab({ pathList }: { pathList: string[] }): void { | |
188 | 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 | 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 | 288 | const index = this.tabsState.findIndex((item) => item.path === route.path); |
199 | 289 | |
200 | 290 | if (index > 0) { |
201 | 291 | const leftTabs = this.tabsState.slice(0, index); |
202 | 292 | const pathList: string[] = []; |
203 | - const nameList: string[] = []; | |
204 | 293 | for (const item of leftTabs) { |
205 | 294 | const affix = item.meta ? item.meta.affix : false; |
206 | 295 | if (!affix) { |
207 | 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 | 305 | @Action |
227 | - closeRightTabAction(route: AppRouteRecordRaw | TabItem): void { | |
306 | + closeRightTabAction(route: RouteLocationNormalized): void { | |
228 | 307 | const index = this.tabsState.findIndex((item) => item.fullPath === route.fullPath); |
229 | 308 | |
230 | 309 | if (index >= 0 && index < this.tabsState.length - 1) { |
231 | 310 | const rightTabs = this.tabsState.slice(index + 1, this.tabsState.length); |
232 | 311 | |
233 | 312 | const pathList: string[] = []; |
234 | - const nameList: string[] = []; | |
235 | 313 | for (const item of rightTabs) { |
236 | 314 | const affix = item.meta ? item.meta.affix : false; |
237 | 315 | if (!affix) { |
238 | 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 | 325 | @Action |
247 | - closeOtherTabAction(route: AppRouteRecordRaw | TabItem): void { | |
326 | + closeOtherTabAction(route: RouteLocationNormalized): void { | |
248 | 327 | const closePathList = this.tabsState.map((item) => item.fullPath); |
249 | 328 | const pathList: string[] = []; |
250 | - const nameList: string[] = []; | |
251 | 329 | closePathList.forEach((path) => { |
252 | 330 | if (path !== route.fullPath) { |
253 | 331 | const closeItem = this.tabsState.find((item) => item.path === path); |
... | ... | @@ -255,11 +333,12 @@ class Tab extends VuexModule { |
255 | 333 | const affix = closeItem.meta ? closeItem.meta.affix : false; |
256 | 334 | if (!affix) { |
257 | 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 | 344 | export const tabStore = getModule<Tab>(Tab); |
... | ... |
src/utils/helper/dynamicImport.ts deleted
100644 → 0
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
src/views/demo/level/Menu111.vue
0 → 100644
src/views/demo/level/Menu12.vue
0 → 100644
src/views/demo/level/Menu2.vue
0 → 100644
src/views/sys/redirect/index.vue
1 | +<template> | |
2 | + <div /> | |
3 | +</template> | |
1 | 4 | <script lang="ts"> |
2 | 5 | import { defineComponent, unref } from 'vue'; |
3 | 6 | |
... | ... | @@ -18,12 +21,13 @@ |
18 | 21 | path: '/' + _path, |
19 | 22 | query, |
20 | 23 | }); |
24 | + // close loading | |
21 | 25 | if (unref(getEnableTransition) && unref(getOpenPageLoading)) { |
22 | 26 | setTimeout(() => { |
23 | 27 | appStore.setPageLoadingAction(false); |
24 | 28 | }, 0); |
25 | 29 | } |
26 | - return () => null; | |
30 | + return {}; | |
27 | 31 | }, |
28 | 32 | }); |
29 | 33 | </script> |
... | ... |
yarn.lock
... | ... | @@ -1061,10 +1061,10 @@ |
1061 | 1061 | resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" |
1062 | 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 | 1069 | "@koa/cors@^3.1.0": |
1070 | 1070 | version "3.1.0" |
... | ... | @@ -1535,10 +1535,10 @@ |
1535 | 1535 | resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" |
1536 | 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 | 1542 | dependencies: |
1543 | 1543 | "@types/yargs-parser" "*" |
1544 | 1544 | |
... | ... | @@ -1644,6 +1644,17 @@ |
1644 | 1644 | estree-walker "^2.0.1" |
1645 | 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 | 1658 | "@vue/compiler-dom@3.0.2": |
1648 | 1659 | version "3.0.2" |
1649 | 1660 | resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.2.tgz#1d40de04bcdf9aabb79fb6a802dd70a2f3c2992a" |
... | ... | @@ -1660,6 +1671,14 @@ |
1660 | 1671 | "@vue/compiler-core" "3.0.3" |
1661 | 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 | 1682 | "@vue/compiler-sfc@*", "@vue/compiler-sfc@^3.0.0-rc.5": |
1664 | 1683 | version "3.0.2" |
1665 | 1684 | resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.0.2.tgz#22c70fed72c347a4d5fa2db2e80594b3193dce57" |
... | ... | @@ -1704,6 +1723,28 @@ |
1704 | 1723 | postcss-selector-parser "^6.0.4" |
1705 | 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 | 1748 | "@vue/compiler-ssr@3.0.2": |
1708 | 1749 | version "3.0.2" |
1709 | 1750 | resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.0.2.tgz#73af4d274a79bfcc72a996a9b45f1072e7deaa26" |
... | ... | @@ -1720,6 +1761,14 @@ |
1720 | 1761 | "@vue/compiler-dom" "3.0.3" |
1721 | 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 | 1772 | "@vue/reactivity@3.0.2": |
1724 | 1773 | version "3.0.2" |
1725 | 1774 | resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.2.tgz#42ed5af6025b494a5e69b05169fcddf04eebfe77" |
... | ... | @@ -1734,6 +1783,13 @@ |
1734 | 1783 | dependencies: |
1735 | 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 | 1793 | "@vue/runtime-core@3.0.2": |
1738 | 1794 | version "3.0.2" |
1739 | 1795 | resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.2.tgz#d7ed462af1cb0bf9836668e4e6fab3f2f4b1bc00" |
... | ... | @@ -1750,6 +1806,14 @@ |
1750 | 1806 | "@vue/reactivity" "3.0.3" |
1751 | 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 | 1817 | "@vue/runtime-dom@3.0.3": |
1754 | 1818 | version "3.0.3" |
1755 | 1819 | resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.3.tgz#5e3e5e5418b9defcac988d2be0cf65596fa2cc03" |
... | ... | @@ -1759,6 +1823,15 @@ |
1759 | 1823 | "@vue/shared" "3.0.3" |
1760 | 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 | 1835 | "@vue/runtime-dom@^3.0.0": |
1763 | 1836 | version "3.0.2" |
1764 | 1837 | resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.2.tgz#9d166d03225558025d3d80f5039b646e0051b71c" |
... | ... | @@ -1778,6 +1851,11 @@ |
1778 | 1851 | resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.0.3.tgz#ef12ebff93a446df281e8a0fd765b5aea8e7745b" |
1779 | 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 | 1859 | "@vuedx/analyze@0.2.4-0": |
1782 | 1860 | version "0.2.4-0" |
1783 | 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 | 3091 | exit-on-epipe "~1.0.1" |
3014 | 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 | 3098 | dependencies: |
3021 | 3099 | cross-spawn "^7.0.1" |
3022 | 3100 | |
... | ... | @@ -3449,17 +3527,17 @@ es-module-lexer@^0.3.25: |
3449 | 3527 | resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b" |
3450 | 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 | 3534 | dependencies: |
3457 | 3535 | joycon "^2.2.5" |
3458 | 3536 | pirates "^4.0.1" |
3459 | 3537 | source-map-support "^0.5.19" |
3460 | 3538 | strip-json-comments "^3.1.1" |
3461 | 3539 | |
3462 | -esbuild@^0.7.17, esbuild@^0.7.19: | |
3540 | +esbuild@^0.7.19: | |
3463 | 3541 | version "0.7.22" |
3464 | 3542 | resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.22.tgz#9149b903f8128b7c45a754046c24199d76bbe08e" |
3465 | 3543 | integrity sha512-B43SYg8LGWYTCv9Gs0RnuLNwjzpuWOoCaZHTWEDEf5AfrnuDMerPVMdCEu7xOdhFvQ+UqfP2MGU9lxEy0JzccA== |
... | ... | @@ -3469,6 +3547,11 @@ esbuild@^0.8.12: |
3469 | 3547 | resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.8.15.tgz#cbc4d82a7fc4571d455233456e6fba83fd0364f1" |
3470 | 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 | 3555 | escalade@^3.1.1: |
3473 | 3556 | version "3.1.1" |
3474 | 3557 | resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" |
... | ... | @@ -3581,13 +3664,13 @@ esm@^3.2.25: |
3581 | 3664 | resolved "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" |
3582 | 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 | 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 | 3674 | esm "^3.2.25" |
3592 | 3675 | |
3593 | 3676 | espree@^6.2.1: |
... | ... | @@ -8335,6 +8418,15 @@ vue@^3.0.3: |
8335 | 8418 | "@vue/runtime-dom" "3.0.3" |
8336 | 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 | 8430 | vuex-module-decorators@^1.0.1: |
8339 | 8431 | version "1.0.1" |
8340 | 8432 | resolved "https://registry.npmjs.org/vuex-module-decorators/-/vuex-module-decorators-1.0.1.tgz#d34dafb5428a3636f1c26d3d014c15fc9659ccd0" |
... | ... |