提交 c303ec1a 作者: vben

refactor: refactor route

上级 7bfe5f75
name: deploy
on:
push:
branches:
- main
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- run: npm install
- run: npm run build
- name: Deploy
uses: peaceiris/actions-gh-pages@v2.5.0
env:
ACTIONS_DEPLOY_KEY: ${{secrets.ACTIONS_DEPLOY_KEY}}
PUBLISH_BRANCH: gh-pages
PUBLISH_DIR: dist
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
// node环境调试当前激活编辑器ts/js代码
{ {
"type": "node", "type": "chrome",
"request": "launch", "request": "launch",
"name": "file", "name": "Launch Chrome",
"cwd": "${workspaceFolder}", "url": "http://localhost:3100",
"program": "${file}", "webRoot": "${workspaceFolder}/src",
// .vscode 目录又不认识了??? "sourceMaps": true
"preLaunchTask": "tsc: 监视 - build/tsconfig.json", // cn
// "preLaunchTask": "tsc: watch - build/tsconfig.json", // en
"outFiles": ["${workspaceFolder}/compile/**/*.js"]
// "args": ["--experimental-modules", "--loader", "./loader.mjs"]
}, },
// 调试开发环境脚本
{
"type": "node",
"request": "launch",
"name": "dev",
// "stopOnEntry": true,
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/node_modules/@vue/cli-service/bin/vue-cli-service.js",
"args": ["serve", "--open"]
},
// 调试生产环境脚本
{
"type": "node",
"request": "launch",
"name": "build",
// "stopOnEntry": true,
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/node_modules/@vue/cli-service/bin/vue-cli-service.js",
"args": ["build"]
},
// 调试单元测试脚本
{
"type": "node",
"request": "launch",
"name": "test:unit",
// "stopOnEntry": true,
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/node_modules/@vue/cli-service/bin/vue-cli-service.js",
"args": ["test:unit", "--detectOpenHandles"]
}
] ]
} }
...@@ -163,12 +163,6 @@ ...@@ -163,12 +163,6 @@
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[html]": { "[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
...@@ -198,5 +192,8 @@ ...@@ -198,5 +192,8 @@
"ts" "ts"
], ],
"i18n-ally.sourceLanguage": "zh", "i18n-ally.sourceLanguage": "zh",
"i18n-ally.enabledFrameworks":["vue","react"] "i18n-ally.enabledFrameworks": [
"vue",
"react"
]
} }
\ No newline at end of file
## Wip ## Wip
## (破坏性更新) Breaking changes
- 路由重构, 不再支持以前的格式。改为支持 vue-router 最初的默认结构,具体格式可以参考示例更改。实现多级路由缓存,不再将路由转化为 2 级。
- 重构面包屑,使用 antd 的面包屑组件。之前的组件已删除
### ✨ Features ### ✨ Features
- 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading``v-loading`指令。并增加示例 - 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading``v-loading`指令。并增加示例
- i18n 支持 vscode `i18n-ally`插件 - i18n 支持 vscode `i18n-ally`插件
- 新增多级路由缓存示例
### 🎫 Chores ### 🎫 Chores
- 首屏 loading 修改 - 首屏 loading 修改
### 🐛 Bug Fixes
-修复表格 i18n 错误
## 2.0.0-rc.12 (2020-11-30) ## 2.0.0-rc.12 (2020-11-30)
## (破坏性更新) Breaking changes ## (破坏性更新) Breaking changes
......
...@@ -17,8 +17,8 @@ const dynamicImportTransform = function (enableDynamicImport: boolean): Transfor ...@@ -17,8 +17,8 @@ const dynamicImportTransform = function (enableDynamicImport: boolean): Transfor
test({ path }) { test({ path }) {
// Only convert the file // Only convert the file
return ( return (
path.includes('/src/utils/helper/dynamicImport.ts') || path.includes('/src/router/helper/dynamicImport.ts') ||
path.includes(`\\src\\utils\\helper\\dynamicImport.ts`) path.includes(`\\src\\router\\helper\\dynamicImport.ts`)
); );
}, },
transform({ code }) { transform({ code }) {
......
import { resultSuccess } from '../_util'; import { resultSuccess } from '../_util';
import { MockMethod } from 'vite-plugin-mock'; import { MockMethod } from 'vite-plugin-mock';
// single
const dashboardRoute = { const dashboardRoute = {
path: '/dashboard', path: '/home',
name: 'Dashboard', name: 'Home',
component: 'PAGE_LAYOUT', component: '/dashboard/welcome/index',
redirect: '/dashboard/welcome',
meta: { meta: {
title: 'routes.dashboard.welcome',
affix: true,
icon: 'ant-design:home-outlined', icon: 'ant-design:home-outlined',
title: 'Dashboard',
}, },
children: [
{
path: '/welcome',
name: 'Welcome',
component: '/dashboard/welcome/index',
meta: {
title: '欢迎页',
affix: true,
},
},
],
}; };
const frontRoute = { const frontRoute = {
path: '/front', path: 'front',
name: 'PermissionFrontDemo', name: 'PermissionFrontDemo',
meta: { meta: {
title: '基于前端权限', title: 'routes.demo.permission.front',
}, },
children: [ children: [
{ {
...@@ -35,7 +25,7 @@ const frontRoute = { ...@@ -35,7 +25,7 @@ const frontRoute = {
name: 'FrontPageAuth', name: 'FrontPageAuth',
component: '/demo/permission/front/index', component: '/demo/permission/front/index',
meta: { meta: {
title: '页面权限', title: 'routes.demo.permission.frontPage',
}, },
}, },
{ {
...@@ -43,7 +33,7 @@ const frontRoute = { ...@@ -43,7 +33,7 @@ const frontRoute = {
name: 'FrontBtnAuth', name: 'FrontBtnAuth',
component: '/demo/permission/front/Btn', component: '/demo/permission/front/Btn',
meta: { meta: {
title: '按钮权限', title: 'routes.demo.permission.frontBtn',
}, },
}, },
{ {
...@@ -51,7 +41,7 @@ const frontRoute = { ...@@ -51,7 +41,7 @@ const frontRoute = {
name: 'FrontAuthPageA', name: 'FrontAuthPageA',
component: '/demo/permission/front/AuthPageA', component: '/demo/permission/front/AuthPageA',
meta: { meta: {
title: '权限测试页A', title: 'routes.demo.permission.frontTestA',
}, },
}, },
{ {
...@@ -59,24 +49,25 @@ const frontRoute = { ...@@ -59,24 +49,25 @@ const frontRoute = {
name: 'FrontAuthPageB', name: 'FrontAuthPageB',
component: '/demo/permission/front/AuthPageB', component: '/demo/permission/front/AuthPageB',
meta: { meta: {
title: '权限测试页B', title: 'routes.demo.permission.frontTestB',
}, },
}, },
], ],
}; };
const backRoute = { const backRoute = {
path: '/back', path: 'back',
name: 'PermissionBackDemo', name: 'PermissionBackDemo',
meta: { meta: {
title: '基于后台权限', title: 'routes.demo.permission.back',
}, },
children: [ children: [
{ {
path: 'page', path: 'page',
name: 'BackAuthPage', name: 'BackAuthPage',
component: '/demo/permission/back/index', component: '/demo/permission/back/index',
meta: { meta: {
title: '页面权限', title: 'routes.demo.permission.backPage',
}, },
}, },
{ {
...@@ -84,7 +75,7 @@ const backRoute = { ...@@ -84,7 +75,7 @@ const backRoute = {
name: 'BackAuthBtn', name: 'BackAuthBtn',
component: '/demo/permission/back/Btn', component: '/demo/permission/back/Btn',
meta: { meta: {
title: '按钮权限', title: 'routes.demo.permission.backBtn',
}, },
}, },
], ],
...@@ -92,11 +83,11 @@ const backRoute = { ...@@ -92,11 +83,11 @@ const backRoute = {
const authRoute = { const authRoute = {
path: '/permission', path: '/permission',
name: 'Permission', name: 'Permission',
component: 'PAGE_LAYOUT', component: 'LAYOUT',
redirect: '/permission/front/page', redirect: '/permission/front/page',
meta: { meta: {
icon: 'ant-design:home-outlined', icon: 'carbon:user-role',
title: '权限管理', title: 'routes.demo.permission.permission',
}, },
children: [frontRoute, backRoute], children: [frontRoute, backRoute],
}; };
...@@ -104,14 +95,70 @@ const authRoute = { ...@@ -104,14 +95,70 @@ const authRoute = {
const authRoute1 = { const authRoute1 = {
path: '/permission', path: '/permission',
name: 'Permission', name: 'Permission',
component: 'PAGE_LAYOUT', component: 'LAYOUT',
redirect: '/permission/front/page', redirect: '/permission/front/page',
meta: { meta: {
icon: 'ant-design:home-outlined', icon: 'carbon:user-role',
title: '权限管理', title: 'routes.demo.permission.permission',
}, },
children: [backRoute], children: [backRoute],
}; };
const levelRoute = {
path: '/level',
name: 'Level',
component: 'LAYOUT',
redirect: '/level/menu1/menu1-1',
meta: {
icon: 'carbon:user-role',
title: 'routes.demo.level.level',
},
children: [
{
path: 'menu1',
name: 'Menu1Demo',
meta: {
title: 'Menu1',
},
children: [
{
path: 'menu1-1',
name: 'Menu11Demo',
meta: {
title: 'Menu1-1',
},
children: [
{
path: 'menu1-1-1',
name: 'Menu111Demo',
component: '/demo/level/Menu111',
meta: {
title: 'Menu111',
},
},
],
},
{
path: 'menu1-2',
name: 'Menu12Demo',
component: '/demo/level/Menu12',
meta: {
title: 'Menu1-2',
},
},
],
},
{
path: 'menu2',
name: 'Menu2Demo',
component: '/demo/level/Menu2',
meta: {
title: 'Menu2',
},
},
],
};
export default [ export default [
{ {
url: '/api/getMenuListById', url: '/api/getMenuListById',
...@@ -120,10 +167,10 @@ export default [ ...@@ -120,10 +167,10 @@ export default [
response: ({ query }) => { response: ({ query }) => {
const { id } = query; const { id } = query;
if (!id || id === '1') { if (!id || id === '1') {
return resultSuccess([dashboardRoute, authRoute]); return resultSuccess([dashboardRoute, authRoute, levelRoute]);
} }
if (id === '2') { if (id === '2') {
return resultSuccess([dashboardRoute, authRoute1]); return resultSuccess([dashboardRoute, authRoute1, levelRoute]);
} }
}, },
}, },
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
"qrcode": "^1.4.4", "qrcode": "^1.4.4",
"sortablejs": "^1.12.0", "sortablejs": "^1.12.0",
"vditor": "^3.7.0", "vditor": "^3.7.0",
"vue": "^3.0.3", "vue": "^3.0.4",
"vue-i18n": "^9.0.0-beta.8", "vue-i18n": "^9.0.0-beta.8",
"vue-router": "^4.0.0-rc.6", "vue-router": "^4.0.0-rc.6",
"vue-types": "^3.0.1", "vue-types": "^3.0.1",
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^11.0.0", "@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0", "@commitlint/config-conventional": "^11.0.0",
"@iconify/json": "^1.1.266", "@iconify/json": "^1.1.267",
"@ls-lint/ls-lint": "^1.9.2", "@ls-lint/ls-lint": "^1.9.2",
"@purge-icons/generated": "^0.4.1", "@purge-icons/generated": "^0.4.1",
"@types/echarts": "^4.9.2", "@types/echarts": "^4.9.2",
...@@ -60,25 +60,25 @@ ...@@ -60,25 +60,25 @@
"@types/qrcode": "^1.3.5", "@types/qrcode": "^1.3.5",
"@types/rollup-plugin-visualizer": "^2.6.0", "@types/rollup-plugin-visualizer": "^2.6.0",
"@types/sortablejs": "^1.10.6", "@types/sortablejs": "^1.10.6",
"@types/yargs": "^15.0.10", "@types/yargs": "^15.0.11",
"@types/zxcvbn": "^4.4.0", "@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^4.9.0", "@typescript-eslint/eslint-plugin": "^4.9.0",
"@typescript-eslint/parser": "^4.9.0", "@typescript-eslint/parser": "^4.9.0",
"@vue/compiler-sfc": "^3.0.3", "@vue/compiler-sfc": "^3.0.4",
"@vuedx/typecheck": "^0.2.4-0", "@vuedx/typecheck": "^0.2.4-0",
"@vuedx/typescript-plugin-vue": "^0.2.4-0", "@vuedx/typescript-plugin-vue": "^0.2.4-0",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
"commitizen": "^4.2.2", "commitizen": "^4.2.2",
"conventional-changelog-cli": "^2.1.1", "conventional-changelog-cli": "^2.1.1",
"conventional-changelog-custom-config": "^0.3.1", "conventional-changelog-custom-config": "^0.3.1",
"cross-env": "^7.0.2", "cross-env": "^7.0.3",
"dot-prop": "^6.0.1", "dot-prop": "^6.0.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"eslint": "^7.14.0", "eslint": "^7.14.0",
"eslint-config-prettier": "^6.15.0", "eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-vue": "^7.1.0", "eslint-plugin-vue": "^7.1.0",
"esno": "^0.2.4", "esno": "^0.3.0",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"globrex": "^0.1.2", "globrex": "^0.1.2",
"husky": "^4.3.0", "husky": "^4.3.0",
......
import BreadcrumbLib from './src/Breadcrumb.vue';
import BreadcrumbItemLib from './src/BreadcrumbItem.vue';
import { withInstall } from '../util';
export const Breadcrumb = withInstall(BreadcrumbLib);
export const BreadcrumbItem = withInstall(BreadcrumbItemLib);
<template>
<div ref="breadcrumbRef" class="breadcrumb">
<slot />
</div>
</template>
<script lang="ts">
import { defineComponent, provide, ref } from 'vue';
import { propTypes } from '/@/utils/propTypes';
export default defineComponent({
name: 'Breadcrumb',
props: {
separator: propTypes.string.def('/'),
separatorClass: propTypes.string,
},
setup(props) {
const breadcrumbRef = ref<Nullable<HTMLElement>>(null);
provide('breadcrumb', props);
return {
breadcrumbRef,
};
},
});
</script>
<style lang="less">
@import (reference) '../../../design/index.less';
.breadcrumb {
.unselect();
height: @header-height;
padding-right: 20px;
font-size: 13px;
line-height: @header-height;
// line-height: 1;
&::after,
&::before {
display: table;
content: '';
}
&::after {
clear: both;
}
&__separator {
margin: 0 9px;
font-weight: 700;
color: @breadcrumb-item-normal-color;
&[class*='icon'] {
margin: 0 6px;
font-weight: 400;
}
}
&__item {
float: left;
}
&__inner {
color: @breadcrumb-item-normal-color;
&.is-link,
a {
font-weight: 500;
color: @text-color-base;
text-decoration: none;
transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
a:hover,
&.is-link:hover {
color: @primary-color;
cursor: pointer;
}
}
&__item:last-child .breadcrumb__inner,
&__item:last-child &__inner a,
&__item:last-child &__inner a:hover,
&__item:last-child &__inner:hover {
font-weight: 400;
color: @breadcrumb-item-normal-color;
cursor: text;
}
&__item:last-child &__separator {
display: none;
}
}
</style>
<template>
<span class="breadcrumb__item">
<span ref="linkRef" :class="['breadcrumb__inner', to || isLink ? 'is-link' : '']">
<slot />
</span>
<i v-if="separatorClass" class="breadcrumb__separator" :class="separatorClass"></i>
<span v-else class="breadcrumb__separator">{{ separator }}</span>
</span>
</template>
<script lang="ts">
import { defineComponent, inject, ref, onMounted, unref } from 'vue';
import { useRouter } from 'vue-router';
import { useEventListener } from '/@/hooks/event/useEventListener';
import { propTypes } from '/@/utils/propTypes';
export default defineComponent({
name: 'BreadcrumbItem',
props: {
to: propTypes.oneOfType([propTypes.string, propTypes.object]),
replace: propTypes.bool,
isLink: propTypes.bool,
},
setup(props) {
const linkRef = ref<Nullable<HTMLElement>>(null);
const parent = inject('breadcrumb') as {
separator: string;
separatorClass: string;
};
const { push, replace } = useRouter();
onMounted(() => {
const link = unref(linkRef);
if (!link) return;
useEventListener({
el: link,
listener: () => {
const { to } = props;
if (!props.to) return;
props.replace ? replace(to) : push(to);
},
name: 'click',
wait: 0,
});
});
return {
linkRef,
separator: parent.separator && parent.separator,
separatorClass: parent.separatorClass && parent.separatorClass,
};
},
});
</script>
...@@ -36,6 +36,7 @@ import { getCurrentParentPath } from '/@/router/menus'; ...@@ -36,6 +36,7 @@ import { getCurrentParentPath } from '/@/router/menus';
import { basicProps } from './props'; import { basicProps } from './props';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { REDIRECT_NAME } from '/@/router/constant';
export default defineComponent({ export default defineComponent({
name: 'BasicMenu', name: 'BasicMenu',
props: basicProps, props: basicProps,
...@@ -120,7 +121,7 @@ export default defineComponent({ ...@@ -120,7 +121,7 @@ export default defineComponent({
watch( watch(
() => currentRoute.value.name, () => currentRoute.value.name,
(name: string) => { (name: string) => {
if (name === 'Redirect') return; if (name === REDIRECT_NAME) return;
handleMenuChange(); handleMenuChange();
props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath(); props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath();
} }
......
...@@ -4,7 +4,7 @@ import type { MenuState } from '../types'; ...@@ -4,7 +4,7 @@ import type { MenuState } from '../types';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { unref } from 'vue'; import { unref } from 'vue';
import { getAllParentPath } from '/@/utils/helper/menuHelper'; import { getAllParentPath } from '/@/router/helper/menuHelper';
import { es6Unique } from '/@/utils'; import { es6Unique } from '/@/utils';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
......
...@@ -5,7 +5,7 @@ import type { Ref } from 'vue'; ...@@ -5,7 +5,7 @@ import type { Ref } from 'vue';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
import { unref } from 'vue'; import { unref } from 'vue';
import { es6Unique } from '/@/utils'; import { es6Unique } from '/@/utils';
import { getAllParentPath } from '/@/utils/helper/menuHelper'; import { getAllParentPath } from '/@/router/helper/menuHelper';
interface UseSearchInputOptions { interface UseSearchInputOptions {
menuState: MenuState; menuState: MenuState;
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<Tooltip placement="top" v-if="getSetting.setting"> <Tooltip placement="top" v-if="getSetting.setting">
<template #title> <template #title>
<span>{{ t('settingColumn') }}</span> <span>{{ t('component.table.settingColumn') }}</span>
</template> </template>
<Popover <Popover
placement="bottomLeft" placement="bottomLeft"
...@@ -58,9 +58,11 @@ ...@@ -58,9 +58,11 @@
v-model:checked="checkAll" v-model:checked="checkAll"
@change="onCheckAllChange" @change="onCheckAllChange"
> >
{{ t('settingColumnShow') }} {{ t('component.table.settingColumnShow') }}
</Checkbox> </Checkbox>
<a-button size="small" type="link" @click="reset"> {{ t('settingReset') }}</a-button> <a-button size="small" type="link" @click="reset">
{{ t('component.table.settingReset') }}</a-button
>
</div> </div>
</template> </template>
<SettingOutlined /> <SettingOutlined />
...@@ -69,7 +71,7 @@ ...@@ -69,7 +71,7 @@
<Tooltip placement="top" v-if="getSetting.fullScreen"> <Tooltip placement="top" v-if="getSetting.fullScreen">
<template #title> <template #title>
<span>{{ t('settingFullScreen') }}</span> <span>{{ t('component.table.settingFullScreen') }}</span>
</template> </template>
<FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" /> <FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" />
<FullscreenExitOutlined @click="handleFullScreen" v-else /> <FullscreenExitOutlined @click="handleFullScreen" v-else />
......
...@@ -33,6 +33,7 @@ import { ...@@ -33,6 +33,7 @@ import {
Empty, Empty,
Avatar, Avatar,
Menu, Menu,
Breadcrumb,
} from 'ant-design-vue'; } from 'ant-design-vue';
import { getApp } from '/@/setup/App'; import { getApp } from '/@/setup/App';
...@@ -55,6 +56,7 @@ export function registerGlobComp() { ...@@ -55,6 +56,7 @@ export function registerGlobComp() {
getApp() getApp()
.use(Select) .use(Select)
.use(Alert) .use(Alert)
.use(Breadcrumb)
.use(Checkbox) .use(Checkbox)
.use(DatePicker) .use(DatePicker)
.use(Radio) .use(Radio)
......
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all 0.24s;
}
.breadcrumb-enter-from,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(16px);
}
.breadcrumb-move {
transition: all 0.38s;
}
.breadcrumb-leave-active {
position: absolute;
}
...@@ -4,4 +4,3 @@ ...@@ -4,4 +4,3 @@
@import './slide.less'; @import './slide.less';
@import './scroll.less'; @import './scroll.less';
@import './zoom.less'; @import './zoom.less';
@import './breadcrumb.less';
...@@ -2,7 +2,7 @@ export enum PageEnum { ...@@ -2,7 +2,7 @@ export enum PageEnum {
// basic login path // basic login path
BASE_LOGIN = '/login', BASE_LOGIN = '/login',
// basic home path // basic home path
BASE_HOME = '/dashboard', BASE_HOME = '/home',
// error page path // error page path
ERROR_PAGE = '/exception', ERROR_PAGE = '/exception',
// error log page path // error log page path
......
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
import type { RouteLocationRaw } from 'vue-router'; import type { RouteLocationRaw } from 'vue-router';
import { useRouter } from 'vue-router';
import { PageEnum } from '/@/enums/pageEnum'; import { PageEnum } from '/@/enums/pageEnum';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
import { unref } from 'vue'; import { unref } from 'vue';
import router from '/@/router';
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum }; export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
function handleError(e: Error) { function handleError(e: Error) {
...@@ -18,7 +19,7 @@ function handleError(e: Error) { ...@@ -18,7 +19,7 @@ function handleError(e: Error) {
// page switch // page switch
export function useGo() { export function useGo() {
const { push, replace } = useRouter(); const { push, replace } = router;
function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) { function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
if (!opt) return; if (!opt) return;
if (isString(opt)) { if (isString(opt)) {
...@@ -35,7 +36,7 @@ export function useGo() { ...@@ -35,7 +36,7 @@ export function useGo() {
* @description: redo current page * @description: redo current page
*/ */
export const useRedo = () => { export const useRedo = () => {
const { push, currentRoute } = useRouter(); const { push, currentRoute } = router;
const { query, params } = currentRoute.value; const { query, params } = currentRoute.value;
function redo() { function redo() {
push({ push({
......
...@@ -7,13 +7,14 @@ import { userStore } from '/@/store/modules/user'; ...@@ -7,13 +7,14 @@ import { userStore } from '/@/store/modules/user';
import { useTabs } from './useTabs'; import { useTabs } from './useTabs';
import router, { resetRouter } from '/@/router'; import router, { resetRouter } from '/@/router';
import { RootRoute } from '/@/router/routes'; // import { RootRoute } from '/@/router/routes';
import { PermissionModeEnum } from '/@/enums/appEnum'; import { PermissionModeEnum } from '/@/enums/appEnum';
import { RoleEnum } from '/@/enums/roleEnum'; import { RoleEnum } from '/@/enums/roleEnum';
import { intersection } from 'lodash-es'; import { intersection } from 'lodash-es';
import { isArray } from '/@/utils/is'; import { isArray } from '/@/utils/is';
import { tabStore } from '/@/store/modules/tab';
// User permissions related operations // User permissions related operations
export function usePermission() { export function usePermission() {
...@@ -27,8 +28,7 @@ export function usePermission() { ...@@ -27,8 +28,7 @@ export function usePermission() {
? PermissionModeEnum.ROLE ? PermissionModeEnum.ROLE
: PermissionModeEnum.BACK, : PermissionModeEnum.BACK,
}); });
resume(); location.reload();
// location.reload();
} }
/** /**
...@@ -36,18 +36,15 @@ export function usePermission() { ...@@ -36,18 +36,15 @@ export function usePermission() {
* @param id * @param id
*/ */
async function resume(id?: string | number) { async function resume(id?: string | number) {
tabStore.commitClearCache();
resetRouter(); resetRouter();
const routes = await permissionStore.buildRoutesAction(id); const routes = await permissionStore.buildRoutesAction(id);
routes.forEach((route) => { routes.forEach((route) => {
router.addRoute(RootRoute.name!, route as RouteRecordRaw); router.addRoute(route as RouteRecordRaw);
}); });
permissionStore.commitLastBuildMenuTimeState(); permissionStore.commitLastBuildMenuTimeState();
const { const { closeAll } = useTabs();
// closeAll, closeAll();
closeOther,
} = useTabs();
// closeAll();
closeOther();
} }
/** /**
......
import { TabItem, tabStore } from '/@/store/modules/tab'; import { tabStore } from '/@/store/modules/tab';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
type RouteFn = (tabItem: TabItem) => void;
interface TabFn {
refreshPageFn: RouteFn;
closeAllFn: Fn;
closeLeftFn: RouteFn;
closeRightFn: RouteFn;
closeOtherFn: RouteFn;
closeCurrentFn: RouteFn;
}
let refreshPage: RouteFn;
let closeAll: Fn;
let closeLeft: RouteFn;
let closeRight: RouteFn;
let closeOther: RouteFn;
let closeCurrent: RouteFn;
export let isInitUseTab = false;
export function useTabs() { export function useTabs() {
function initTabFn({
refreshPageFn,
closeAllFn,
closeLeftFn,
closeRightFn,
closeOtherFn,
closeCurrentFn,
}: TabFn) {
if (isInitUseTab) return;
refreshPageFn && (refreshPage = refreshPageFn);
closeAllFn && (closeAll = closeAllFn);
closeLeftFn && (closeLeft = closeLeftFn);
closeRightFn && (closeRight = closeRightFn);
closeOtherFn && (closeOther = closeOtherFn);
closeCurrentFn && (closeCurrent = closeCurrentFn);
isInitUseTab = true;
}
function resetCache() {
const def = undefined as any;
refreshPage = def;
closeAll = def;
closeLeft = def;
closeRight = def;
closeOther = def;
closeCurrent = def;
}
function canIUseFn(): boolean { function canIUseFn(): boolean {
const { multiTabsSetting: { show } = {} } = appStore.getProjectConfig; const { multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
if (!show) { if (!show) {
throw new Error('当前未开启多标签页,请在设置中打开!'); throw new Error('The multi-tab page is currently not open, please open it in the settings!');
} }
return !!show; return !!show;
} }
return { return {
initTabFn, refreshPage: () => canIUseFn() && tabStore.commitRedoPage(),
refreshPage: () => canIUseFn() && refreshPage(tabStore.getCurrentTab), closeAll: () => canIUseFn() && tabStore.closeAllTabAction(),
closeAll: () => canIUseFn() && closeAll(), closeLeft: () => canIUseFn() && tabStore.closeLeftTabAction(tabStore.getCurrentTab),
closeLeft: () => canIUseFn() && closeLeft(tabStore.getCurrentTab), closeRight: () => canIUseFn() && tabStore.closeRightTabAction(tabStore.getCurrentTab),
closeRight: () => canIUseFn() && closeRight(tabStore.getCurrentTab), closeOther: () => canIUseFn() && tabStore.closeOtherTabAction(tabStore.getCurrentTab),
closeOther: () => canIUseFn() && closeOther(tabStore.getCurrentTab), closeCurrent: () => canIUseFn() && tabStore.closeTabAction(tabStore.getCurrentTab),
closeCurrent: () => canIUseFn() && closeCurrent(tabStore.getCurrentTab),
resetCache: () => canIUseFn() && resetCache(),
}; };
} }
...@@ -3,11 +3,9 @@ import './index.less'; ...@@ -3,11 +3,9 @@ import './index.less';
import { defineComponent, unref } from 'vue'; import { defineComponent, unref } from 'vue';
import { Loading } from '/@/components/Loading'; import { Loading } from '/@/components/Loading';
import { RouterView } from 'vue-router';
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import PageLayout from '/@/layouts/page/index.vue';
export default defineComponent({ export default defineComponent({
name: 'LayoutContent', name: 'LayoutContent',
setup() { setup() {
...@@ -20,7 +18,7 @@ export default defineComponent({ ...@@ -20,7 +18,7 @@ export default defineComponent({
{unref(getOpenPageLoading) && ( {unref(getOpenPageLoading) && (
<Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" /> <Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" />
)} )}
<RouterView /> <PageLayout />
</div> </div>
); );
}; };
......
import type { AppRouteRecordRaw } from '/@/router/types';
import type { RouteLocationMatched } from 'vue-router';
import type { PropType } from 'vue';
import { defineComponent, TransitionGroup, unref, watch, ref } from 'vue';
import Icon from '/@/components/Icon';
import { Breadcrumb, BreadcrumbItem } from '/@/components/Breadcrumb';
import { useRouter } from 'vue-router';
import { isBoolean } from '/@/utils/is';
import { compile } from 'path-to-regexp';
import router from '/@/router';
import { PageEnum } from '/@/enums/pageEnum';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({
name: 'BasicBreadcrumb',
props: {
showIcon: {
type: Boolean as PropType<boolean>,
default: false,
},
},
setup(props) {
const itemList = ref<AppRouteRecordRaw[]>([]);
const { currentRoute, push } = useRouter();
const { t } = useI18n();
watch(
() => currentRoute.value,
() => {
if (unref(currentRoute).name === 'Redirect') return;
getBreadcrumb();
},
{ immediate: true }
);
function getBreadcrumb() {
const { matched } = unref(currentRoute);
const matchedList = matched.filter((item) => item.meta && item.meta.title).slice(1);
const firstItem = matchedList[0];
const ret = getHomeRoute(firstItem);
if (!isBoolean(ret)) {
matchedList.unshift(ret);
}
itemList.value = ((matchedList as any) as AppRouteRecordRaw[]).filter(
(item) => item.meta && item.meta.title && !item.meta.hideBreadcrumb
);
}
function getHomeRoute(firstItem: RouteLocationMatched) {
if (!firstItem || !firstItem.name) return false;
const routes = router.getRoutes();
const homeRoute = routes.find((item) => item.path === PageEnum.BASE_HOME);
if (!homeRoute) return false;
if (homeRoute.name === firstItem.name) return false;
return homeRoute;
}
function pathCompile(path: string) {
const { params } = unref(currentRoute);
const toPath = compile(path);
return toPath(params);
}
function handleItemClick(item: AppRouteRecordRaw) {
const { redirect, path, meta } = item;
if (meta.disabledRedirect) return;
if (redirect) {
push(redirect as string);
return;
}
return push(pathCompile(path));
}
function renderItemContent(item: AppRouteRecordRaw) {
return (
<>
{props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && (
<Icon
icon={item.meta.icon}
class="icon mr-1 "
style={{
marginBottom: '2px',
}}
/>
)}
{t(item.meta.title)}
</>
);
}
function renderBreadcrumbItemList() {
return unref(itemList).map((item) => {
const isLink =
(!!item.redirect && !item.meta.disabledRedirect) ||
!item.children ||
item.children.length === 0;
return (
<BreadcrumbItem
key={item.path}
isLink={isLink}
onClick={handleItemClick.bind(null, item)}
>
{() => renderItemContent(item as AppRouteRecordRaw)}
</BreadcrumbItem>
);
});
}
function renderBreadcrumbDefault() {
return (
<TransitionGroup name="breadcrumb">{() => renderBreadcrumbItemList()}</TransitionGroup>
);
}
return () => (
<Breadcrumb class={['layout-breadcrumb', unref(itemList).length === 0 ? 'hidden' : '']}>
{() => renderBreadcrumbDefault()}
</Breadcrumb>
);
},
});
<template>
<div class="layout-breadcrumb">
<a-breadcrumb :routes="routes">
<template #itemRender="{ route, routes }">
<Icon :icon="route.meta.icon" v-if="showIcon && route.meta.icon" />
<span v-if="routes.indexOf(route) === routes.length - 1">
{{ t(route.meta.title) }}
</span>
<router-link v-else :to="route.path">
{{ t(route.meta.title) }}
</router-link>
</template>
</a-breadcrumb>
</div>
</template>
<script lang="ts">
import { PropType } from 'vue';
import { defineComponent, ref, toRaw, watchEffect } from 'vue';
import { useI18n } from 'vue-i18n';
import type { RouteLocationMatched } from 'vue-router';
import { useRouter } from 'vue-router';
import { filter } from '/@/utils/helper/treeHelper';
import { REDIRECT_NAME } from '/@/router/constant';
import Icon from '/@/components/Icon';
import { HomeOutlined } from '@ant-design/icons-vue';
import { PageEnum } from '/@/enums/pageEnum';
export default defineComponent({
name: 'LayoutBreadcrumb',
components: { HomeOutlined, Icon },
props: {
showIcon: {
type: Boolean as PropType<boolean>,
default: false,
},
},
setup() {
const routes = ref<RouteLocationMatched[]>([]);
const { currentRoute } = useRouter();
const { t } = useI18n();
watchEffect(() => {
if (currentRoute.value.name === REDIRECT_NAME) {
return;
}
const matched = currentRoute.value.matched;
if (!matched || matched.length === 0) return;
let breadcrumbList = filter(toRaw(matched), (item) => {
if (!item.meta) {
return false;
}
const { title, hideBreadcrumb } = item.meta;
if (!title || hideBreadcrumb) {
return false;
}
return true;
});
const filterBreadcrumbList = breadcrumbList.filter(
(item) => item.path !== PageEnum.BASE_HOME
);
if (filterBreadcrumbList.length === breadcrumbList.length) {
filterBreadcrumbList.unshift({
path: PageEnum.BASE_HOME,
meta: {
title: t('layout.header.home'),
},
});
}
routes.value = filterBreadcrumbList;
});
return { routes, t };
},
});
</script>
...@@ -9,7 +9,7 @@ import { Layout, Tooltip, Badge } from 'ant-design-vue'; ...@@ -9,7 +9,7 @@ import { Layout, Tooltip, Badge } from 'ant-design-vue';
import { AppLogo } from '/@/components/Application'; import { AppLogo } from '/@/components/Application';
import UserDropdown from './UserDropdown'; import UserDropdown from './UserDropdown';
import LayoutMenu from '../menu'; import LayoutMenu from '../menu';
import LayoutBreadcrumb from './LayoutBreadcrumb'; import LayoutBreadcrumb from './LayoutBreadcrumb.vue';
import LockAction from '../lock/LockAction'; import LockAction from '../lock/LockAction';
import LayoutTrigger from '../LayoutTrigger'; import LayoutTrigger from '../LayoutTrigger';
import NoticeAction from './notice/NoticeActionItem.vue'; import NoticeAction from './notice/NoticeActionItem.vue';
......
.multiple-tab-header { .multiple-tab-header {
flex: 0 0 auto; flex: 0 0 auto;
margin-left: -1px;
&.fixed { &.fixed {
position: fixed; position: fixed;
......
...@@ -21,11 +21,15 @@ ...@@ -21,11 +21,15 @@
&__left { &__left {
display: flex; display: flex;
height: 100%;
align-items: center; align-items: center;
.layout-trigger { .layout-trigger {
display: flex;
height: 100%;
padding: 1px 10px 0 16px; padding: 1px 10px 0 16px;
cursor: pointer; cursor: pointer;
align-items: center;
.anticon { .anticon {
font-size: 17px; font-size: 17px;
...@@ -49,12 +53,22 @@ ...@@ -49,12 +53,22 @@
} }
.layout-breadcrumb { .layout-breadcrumb {
display: flex;
padding: 0 8px; padding: 0 8px;
align-items: center;
.ant-breadcrumb-link {
.anticon {
margin-right: 4px;
margin-bottom: 2px;
}
}
} }
} }
&__content { &__content {
display: flex; display: flex;
height: 100%;
flex-grow: 1; flex-grow: 1;
align-items: center; align-items: center;
} }
...@@ -72,6 +86,24 @@ ...@@ -72,6 +86,24 @@
} }
} }
.layout-breadcrumb {
.ant-breadcrumb-link {
color: @breadcrumb-item-normal-color;
a {
color: @text-color-base;
&:hover {
color: @primary-color;
}
}
}
.ant-breadcrumb-separator {
color: @breadcrumb-item-normal-color;
}
}
.layout-header__logo { .layout-header__logo {
height: @header-height; height: @header-height;
color: @text-color-base; color: @text-color-base;
...@@ -152,20 +184,22 @@ ...@@ -152,20 +184,22 @@
} }
} }
.breadcrumb { .layout-breadcrumb {
&__item:last-child .breadcrumb__inner, .ant-breadcrumb-link {
&__item:last-child &__inner a,
&__item:last-child &__inner a:hover,
&__item:last-child &__inner:hover {
font-weight: 400;
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
cursor: text;
a {
color: rgba(255, 255, 255, 0.8);
&:hover {
color: @white;
}
}
} }
&__inner, .ant-breadcrumb-separator,
&__inner.is-link, .anticon {
&__separator { color: rgba(255, 255, 255, 0.8);
color: @white;
} }
} }
} }
......
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { Dropdown } from '/@/components/Dropdown/index';
import { defineComponent, unref, computed, FunctionalComponent } from 'vue'; import { defineComponent, unref, FunctionalComponent } from 'vue';
import { TabItem, tabStore } from '/@/store/modules/tab'; import { TabContentProps } from './types';
import { getScaleAction, TabContentProps } from './data';
import { Dropdown } from '/@/components/Dropdown/index';
import { RightOutlined } from '@ant-design/icons-vue'; import { RightOutlined } from '@ant-design/icons-vue';
import { TabContentEnum } from './data'; import { TabContentEnum } from './types';
import { useTabDropdown } from './useTabDropdown'; import { useTabDropdown } from './useTabDropdown';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { RouteLocationNormalized } from 'vue-router';
const { t: titleT } = useI18n(); const { t: titleT } = useI18n();
const ExtraContent: FunctionalComponent = () => { const ExtraContent: FunctionalComponent = () => {
...@@ -25,21 +24,13 @@ const ExtraContent: FunctionalComponent = () => { ...@@ -25,21 +24,13 @@ const ExtraContent: FunctionalComponent = () => {
); );
}; };
const TabContent: FunctionalComponent<{ tabItem: TabItem }> = (props) => { const TabContent: FunctionalComponent<{ tabItem: RouteLocationNormalized; handler: Fn }> = (
props
) => {
const { tabItem: { meta } = {} } = props; const { tabItem: { meta } = {} } = props;
function handleContextMenu(e: Event) {
if (!props.tabItem) return;
const tableItem = props.tabItem;
e?.preventDefault();
const index = unref(tabStore.getTabsState).findIndex((tab) => tab.path === tableItem.path);
tabStore.commitCurrentContextMenuIndexState(index);
tabStore.commitCurrentContextMenuState(props.tabItem);
}
return ( return (
<div class={`multiple-tabs-content__content `} onContextmenu={handleContextMenu}> <div class={`multiple-tabs-content__content `} onContextmenu={props.handler(props.tabItem)}>
<span class="ml-1">{meta && titleT(meta.title)}</span> <span class="ml-1">{meta && titleT(meta.title)}</span>
</div> </div>
); );
...@@ -49,7 +40,7 @@ export default defineComponent({ ...@@ -49,7 +40,7 @@ export default defineComponent({
name: 'TabContent', name: 'TabContent',
props: { props: {
tabItem: { tabItem: {
type: Object as PropType<TabItem>, type: Object as PropType<RouteLocationNormalized>,
default: null, default: null,
}, },
...@@ -59,36 +50,27 @@ export default defineComponent({ ...@@ -59,36 +50,27 @@ export default defineComponent({
}, },
}, },
setup(props) { setup(props) {
const { t } = useI18n(); const {
const { getShowMenu } = useMenuSetting(); getDropMenuList,
const { getShowHeader } = useHeaderSetting(); handleMenuEvent,
const { getShowQuick } = useMultipleTabSetting(); handleContextMenu,
getTrigger,
const getIsScale = computed(() => { isTabs,
return !unref(getShowMenu) && !unref(getShowHeader); } = useTabDropdown(props as TabContentProps);
});
const getIsTab = computed(() => {
return !unref(getShowQuick) ? true : props.type === TabContentEnum.TAB_TYPE;
});
const { getDropMenuList, handleMenuEvent } = useTabDropdown(props as TabContentProps);
return () => { return () => {
const scaleAction = getScaleAction(
unref(getIsScale) ? t('layout.multipleTab.putAway') : t('layout.multipleTab.unfold'),
unref(getIsScale)
);
const dropMenuList = unref(getDropMenuList) || [];
const isTab = unref(getIsTab);
return ( return (
<Dropdown <Dropdown
dropMenuList={!isTab ? [scaleAction, ...dropMenuList] : dropMenuList} dropMenuList={unref(getDropMenuList)}
trigger={isTab ? ['contextmenu'] : ['click']} trigger={unref(getTrigger)}
onMenuEvent={handleMenuEvent} onMenuEvent={handleMenuEvent}
> >
{() => (isTab ? <TabContent tabItem={props.tabItem} /> : <ExtraContent />)} {() => {
if (!unref(isTabs)) {
return <ExtraContent />;
}
return <TabContent handler={handleContextMenu} tabItem={props.tabItem} />;
}}
</Dropdown> </Dropdown>
); );
}; };
......
import { DropMenu } from '/@/components/Dropdown/index';
import { AppRouteRecordRaw } from '/@/router/types';
import type { TabItem } from '/@/store/modules/tab';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
export enum TabContentEnum {
TAB_TYPE,
EXTRA_TYPE,
}
export interface TabContentProps {
tabItem: TabItem | AppRouteRecordRaw;
type?: TabContentEnum;
trigger?: Array<'click' | 'hover' | 'contextmenu'>;
}
/**
* @description: 右键:下拉菜单文字
*/
export enum MenuEventEnum {
// 刷新
REFRESH_PAGE,
// 关闭当前
CLOSE_CURRENT,
// 关闭左侧
CLOSE_LEFT,
// 关闭右侧
CLOSE_RIGHT,
// 关闭其他
CLOSE_OTHER,
// 关闭所有
CLOSE_ALL,
// 放大
SCALE,
}
export function getActions() {
const REFRESH_PAGE: DropMenu = {
icon: 'ant-design:reload-outlined',
event: MenuEventEnum.REFRESH_PAGE,
text: t('layout.multipleTab.redo'),
disabled: false,
};
const CLOSE_CURRENT: DropMenu = {
icon: 'ant-design:close-outlined',
event: MenuEventEnum.CLOSE_CURRENT,
text: t('layout.multipleTab.close'),
disabled: false,
divider: true,
};
const CLOSE_LEFT: DropMenu = {
icon: 'ant-design:pic-left-outlined',
event: MenuEventEnum.CLOSE_LEFT,
text: t('layout.multipleTab.closeLeft'),
disabled: false,
divider: false,
};
const CLOSE_RIGHT: DropMenu = {
icon: 'ant-design:pic-right-outlined',
event: MenuEventEnum.CLOSE_RIGHT,
text: t('layout.multipleTab.closeRight'),
disabled: false,
divider: true,
};
const CLOSE_OTHER: DropMenu = {
icon: 'ant-design:pic-center-outlined',
event: MenuEventEnum.CLOSE_OTHER,
text: t('layout.multipleTab.closeOther'),
disabled: false,
};
const CLOSE_ALL: DropMenu = {
icon: 'ant-design:line-outlined',
event: MenuEventEnum.CLOSE_ALL,
text: t('layout.multipleTab.closeAll'),
disabled: false,
};
return [REFRESH_PAGE, CLOSE_CURRENT, CLOSE_LEFT, CLOSE_RIGHT, CLOSE_OTHER, CLOSE_ALL];
}
export function getScaleAction(text: string, isZoom = false) {
return {
icon: isZoom ? 'codicon:screen-normal' : 'codicon:screen-full',
event: MenuEventEnum.SCALE,
text: text,
disabled: false,
};
}
import './index.less'; import './index.less';
import type { TabContentProps } from './data'; import type { TabContentProps } from './types';
import type { TabItem } from '/@/store/modules/tab';
import type { AppRouteRecordRaw } from '/@/router/types';
import { defineComponent, watch, computed, unref, ref, onMounted, nextTick } from 'vue';
import Sortable from 'sortablejs';
import { defineComponent, watch, computed, unref, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Tabs } from 'ant-design-vue'; import { Tabs } from 'ant-design-vue';
...@@ -14,15 +10,12 @@ import TabContent from './TabContent'; ...@@ -14,15 +10,12 @@ import TabContent from './TabContent';
import { useGo } from '/@/hooks/web/usePage'; import { useGo } from '/@/hooks/web/usePage';
import { TabContentEnum } from './data'; import { TabContentEnum } from './types';
import { tabStore } from '/@/store/modules/tab'; import { tabStore } from '/@/store/modules/tab';
import { userStore } from '/@/store/modules/user'; import { userStore } from '/@/store/modules/user';
import { closeTab } from './useTabDropdown'; import { initAffixTabs, useTabsDrag } from './useMultipleTabs';
import { initAffixTabs } from './useMultipleTabs';
import { isNullAndUnDef } from '/@/utils/is';
import { useProjectSetting } from '/@/hooks/setting';
export default defineComponent({ export default defineComponent({
name: 'MultipleTabs', name: 'MultipleTabs',
...@@ -31,28 +24,25 @@ export default defineComponent({ ...@@ -31,28 +24,25 @@ export default defineComponent({
const affixTextList = initAffixTabs(); const affixTextList = initAffixTabs();
const go = useGo(); useTabsDrag(affixTextList);
const { multiTabsSetting } = useProjectSetting(); const go = useGo();
const { currentRoute } = useRouter(); const { currentRoute } = useRouter();
const getTabsState = computed(() => tabStore.getTabsState); const getTabsState = computed(() => tabStore.getTabsState);
// If you monitor routing changes, tab switching will be stuck. So setting this method
watch( watch(
() => tabStore.getLastChangeRouteState, () => tabStore.getLastChangeRouteState?.path,
() => { () => {
const lastChangeRoute = unref(tabStore.getLastChangeRouteState); const lastChangeRoute = unref(tabStore.getLastChangeRouteState);
if (!lastChangeRoute || !userStore.getTokenState) return; if (!lastChangeRoute || !userStore.getTokenState) return;
const { path, fullPath } = lastChangeRoute;
const { path, fullPath } = lastChangeRoute as AppRouteRecordRaw;
const p = fullPath || path; const p = fullPath || path;
if (activeKeyRef.value !== p) { if (activeKeyRef.value !== p) {
activeKeyRef.value = p; activeKeyRef.value = p;
} }
tabStore.commitAddTab(lastChangeRoute); tabStore.addTabAction(lastChangeRoute);
}, },
{ {
immediate: true, immediate: true,
...@@ -67,22 +57,19 @@ export default defineComponent({ ...@@ -67,22 +57,19 @@ export default defineComponent({
// Close the current tab // Close the current tab
function handleEdit(targetKey: string) { function handleEdit(targetKey: string) {
// Added operation to hide, currently only use delete operation // Added operation to hide, currently only use delete operation
const index = unref(getTabsState).findIndex( tabStore.closeTabByKeyAction(targetKey);
(item) => (item.fullPath || item.path) === targetKey
);
index !== -1 && closeTab(unref(getTabsState)[index]);
} }
function renderQuick() { function renderQuick() {
const tabContentProps: TabContentProps = { const tabContentProps: TabContentProps = {
tabItem: (currentRoute as unknown) as AppRouteRecordRaw, tabItem: currentRoute.value,
type: TabContentEnum.EXTRA_TYPE, type: TabContentEnum.EXTRA_TYPE,
}; };
return <TabContent {...(tabContentProps as any)} />; return <TabContent {...tabContentProps} />;
} }
function renderTabs() { function renderTabs() {
return unref(getTabsState).map((item: TabItem) => { return unref(getTabsState).map((item) => {
const key = item.query ? item.fullPath : item.path; const key = item.query ? item.fullPath : item.path;
const closable = !(item && item.meta && item.meta.affix); const closable = !(item && item.meta && item.meta.affix);
...@@ -97,40 +84,6 @@ export default defineComponent({ ...@@ -97,40 +84,6 @@ export default defineComponent({
}); });
} }
function initSortableTabs() {
if (!multiTabsSetting.canDrag) return;
nextTick(() => {
const el = document.querySelectorAll(
'.multiple-tabs .ant-tabs-nav > div'
)?.[0] as HTMLElement;
if (!el) return;
Sortable.create(el, {
animation: 500,
delay: 400,
delayOnTouchOnly: true,
filter: (e: ChangeEvent) => {
const text = e?.target?.innerText;
if (!text) return false;
return affixTextList.includes(text);
},
onEnd: (evt) => {
const { oldIndex, newIndex } = evt;
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
return;
}
tabStore.commitSortTabs({ oldIndex, newIndex });
},
});
});
}
onMounted(() => {
initSortableTabs();
});
return () => { return () => {
const slots = { const slots = {
default: () => renderTabs(), default: () => renderTabs(),
......
import type { DropMenu } from '/@/components/Dropdown/index';
import type { RouteLocationNormalized } from 'vue-router';
export enum TabContentEnum {
TAB_TYPE,
EXTRA_TYPE,
}
export type { DropMenu };
export interface TabContentProps {
tabItem: RouteLocationNormalized;
type?: TabContentEnum;
trigger?: ('click' | 'hover' | 'contextmenu')[];
}
/**
* @description: 右键:下拉菜单文字
*/
export enum MenuEventEnum {
// 刷新
REFRESH_PAGE,
// 关闭当前
CLOSE_CURRENT,
// 关闭左侧
CLOSE_LEFT,
// 关闭右侧
CLOSE_RIGHT,
// 关闭其他
CLOSE_OTHER,
// 关闭所有
CLOSE_ALL,
// 放大
SCALE,
}
import { toRaw, ref } from 'vue'; import Sortable from 'sortablejs';
import { toRaw, ref, nextTick, onMounted } from 'vue';
import { RouteLocationNormalized } from 'vue-router';
import { useProjectSetting } from '/@/hooks/setting';
import router from '/@/router'; import router from '/@/router';
import { AppRouteRecordRaw } from '/@/router/types'; import { tabStore } from '/@/store/modules/tab';
import { TabItem, tabStore } from '/@/store/modules/tab'; import { isNullAndUnDef } from '/@/utils/is';
export function initAffixTabs() { export function initAffixTabs(): string[] {
const affixList = ref<TabItem[]>([]); const affixList = ref<RouteLocationNormalized[]>([]);
/** /**
* @description: Filter all fixed routes * @description: Filter all fixed routes
*/ */
function filterAffixTabs(routes: AppRouteRecordRaw[]) { function filterAffixTabs(routes: RouteLocationNormalized[]) {
const tabs: TabItem[] = []; const tabs: RouteLocationNormalized[] = [];
routes && routes &&
routes.forEach((route) => { routes.forEach((route) => {
if (route.meta && route.meta.affix) { if (route.meta && route.meta.affix) {
tabs.push(toRaw(route) as TabItem); tabs.push(toRaw(route));
} }
}); });
return tabs; return tabs;
...@@ -23,10 +26,14 @@ export function initAffixTabs() { ...@@ -23,10 +26,14 @@ export function initAffixTabs() {
* @description: Set fixed tabs * @description: Set fixed tabs
*/ */
function addAffixTabs(): void { function addAffixTabs(): void {
const affixTabs = filterAffixTabs((router.getRoutes() as unknown) as AppRouteRecordRaw[]); const affixTabs = filterAffixTabs((router.getRoutes() as unknown) as RouteLocationNormalized[]);
affixList.value = affixTabs; affixList.value = affixTabs;
for (const tab of affixTabs) { for (const tab of affixTabs) {
tabStore.commitAddTab(tab); tabStore.addTabAction(({
meta: tab.meta,
name: tab.name,
path: tab.path,
} as unknown) as RouteLocationNormalized);
} }
} }
...@@ -37,3 +44,41 @@ export function initAffixTabs() { ...@@ -37,3 +44,41 @@ export function initAffixTabs() {
} }
return affixList.value.map((item) => item.meta?.title).filter(Boolean); return affixList.value.map((item) => item.meta?.title).filter(Boolean);
} }
export function useTabsDrag(affixTextList: string[]) {
const { multiTabsSetting } = useProjectSetting();
function initSortableTabs() {
if (!multiTabsSetting.canDrag) return;
nextTick(() => {
const el = document.querySelectorAll(
'.multiple-tabs .ant-tabs-nav > div'
)?.[0] as HTMLElement;
if (!el) return;
Sortable.create(el, {
animation: 500,
delay: 400,
delayOnTouchOnly: true,
filter: (e: ChangeEvent) => {
const text = e?.target?.innerText;
if (!text) return false;
return affixTextList.includes(text);
},
onEnd: (evt) => {
const { oldIndex, newIndex } = evt;
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
return;
}
tabStore.commitSortTabs({ oldIndex, newIndex });
},
});
});
}
onMounted(() => {
initSortableTabs();
});
}
<template> <template>
<template v-for="frame in getFramePages" :key="frame.path"> <template v-for="frame in getFramePages" :key="frame.path">
<FramePage <FramePage
v-if="frame.meta.frameSrc && hasRenderFrame(frame.path)" v-if="frame.meta.frameSrc && hasRenderFrame(frame.name)"
v-show="showIframe(frame)" v-show="showIframe(frame)"
:frameSrc="frame.meta.frameSrc" :frameSrc="frame.meta.frameSrc"
/> />
......
...@@ -23,7 +23,7 @@ export function useFrameKeepAlive() { ...@@ -23,7 +23,7 @@ export function useFrameKeepAlive() {
const getOpenTabList = computed((): string[] => { const getOpenTabList = computed((): string[] => {
return tabStore.getTabsState.reduce((prev: string[], next) => { return tabStore.getTabsState.reduce((prev: string[], next) => {
if (next.meta && Reflect.has(next.meta, 'frameSrc')) { if (next.meta && Reflect.has(next.meta, 'frameSrc')) {
prev.push(next.path!); prev.push(next.name as string);
} }
return prev; return prev;
}, []); }, []);
...@@ -45,11 +45,14 @@ export function useFrameKeepAlive() { ...@@ -45,11 +45,14 @@ export function useFrameKeepAlive() {
} }
function showIframe(item: AppRouteRecordRaw) { function showIframe(item: AppRouteRecordRaw) {
return item.path === unref(currentRoute).path; return item.name === unref(currentRoute).name;
} }
function hasRenderFrame(path: string) { function hasRenderFrame(name: string) {
return unref(getShowMultipleTab) ? unref(getOpenTabList).includes(path) : true; if (!unref(getShowMultipleTab)) {
return true;
}
return unref(getOpenTabList).includes(name);
} }
return { hasRenderFrame, getFramePages, showIframe, getAllFramePages }; return { hasRenderFrame, getFramePages, showIframe, getAllFramePages };
} }
import type { FunctionalComponent } from 'vue';
import { computed, defineComponent, unref, Transition, KeepAlive } from 'vue';
import { RouterView, RouteLocation } from 'vue-router';
import FrameLayout from '/@/layouts/iframe/index.vue';
import { useTransition } from './useTransition';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
import { tabStore } from '/@/store/modules/tab';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
interface DefaultContext {
Component: FunctionalComponent;
route: RouteLocation;
}
export default defineComponent({
name: 'PageLayout',
setup() {
const { getShowMenu } = useMenuSetting();
const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();
const { getBasicTransition, getEnableTransition } = useTransitionSetting();
const { getMax } = useMultipleTabSetting();
const transitionEvent = useTransition();
const openCacheRef = computed(() => unref(getOpenKeepAlive) && unref(getShowMenu));
const getCacheTabsRef = computed(() => tabStore.getKeepAliveTabsState as string[]);
return () => {
return (
<div>
<RouterView>
{{
default: ({ Component, route }: DefaultContext) => {
// No longer show animations that are already in the tab
const cacheTabs = unref(getCacheTabsRef);
const isInCache = cacheTabs.includes(route.name as string);
const name = isInCache && route.meta.inTab ? 'fade-slide' : null;
const renderComp = () => <Component key={route.fullPath} />;
const PageContent = unref(openCacheRef) ? (
<KeepAlive max={unref(getMax)} include={cacheTabs}>
{renderComp()}
</KeepAlive>
) : (
renderComp()
);
return unref(getEnableTransition) ? (
<Transition
{...transitionEvent}
name={name || route.meta.transitionName || unref(getBasicTransition)}
mode="out-in"
appear={true}
>
{() => PageContent}
</Transition>
) : (
PageContent
);
},
}}
</RouterView>
{unref(getCanEmbedIFramePage) && <FrameLayout />}
</div>
);
};
},
});
<template>
<ParentLayout :isPage="true" />
<FrameLayout v-if="getCanEmbedIFramePage" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import FrameLayout from '/@/layouts/iframe/index.vue';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import ParentLayout from '/@/layouts/parent/index.vue';
export default defineComponent({
components: { ParentLayout, FrameLayout },
setup() {
const { getCanEmbedIFramePage } = useRootSetting();
return { getCanEmbedIFramePage };
},
});
</script>
<!--
* @Description: The reason is that tsx will report warnings under multi-level nesting.
-->
<template>
<div>
<router-view>
<template #default="{ Component, route }">
<transition v-bind="transitionEvent" :name="getName(route)" mode="out-in" appear>
<keep-alive v-if="openCache" :include="getCaches">
<component :max="getMax" :is="Component" :key="route.fullPath" />
</keep-alive>
<component v-else :max="getMax" :is="Component" :key="route.fullPath" />
</transition>
</template>
</router-view>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, unref } from 'vue';
import { RouteLocationNormalized } from 'vue-router';
import { useTransition } from './useTransition';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { useCache } from './useCache';
export default defineComponent({
props: {
isPage: {
type: Boolean,
},
},
setup(props) {
const { getCaches } = useCache(props.isPage);
const { getShowMenu } = useMenuSetting();
const { getOpenKeepAlive } = useRootSetting();
const { getBasicTransition, getEnableTransition } = useTransitionSetting();
const { getMax } = useMultipleTabSetting();
const transitionEvent = useTransition();
const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMenu));
function getName(route: RouteLocationNormalized) {
if (!unref(getEnableTransition)) {
return null;
}
const cacheTabs = unref(getCaches);
const isInCache = cacheTabs.includes(route.name as string);
const name = isInCache && route.meta.inTab ? 'fade-slide' : null;
return name || route.meta.transitionName || unref(getBasicTransition);
}
return {
getCaches,
getMax,
transitionEvent,
getBasicTransition,
getName,
openCache,
getEnableTransition,
};
},
});
</script>
import { computed, ref, unref } from 'vue';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { tryTsxEmit } from '/@/utils/helper/vueHelper';
import { tabStore, PAGE_LAYOUT_KEY } from '/@/store/modules/tab';
import { useRouter } from 'vue-router';
const ParentLayoutName = 'ParentLayout';
export function useCache(isPage: boolean) {
const name = ref('');
const { currentRoute } = useRouter();
tryTsxEmit((instance: any) => {
const routeName = instance.ctx.$options.name;
if (routeName && ![ParentLayoutName].includes(routeName)) {
name.value = routeName;
} else {
const matched = currentRoute.value.matched;
const len = matched.length;
if (len < 2) return;
name.value = matched[len - 2].name as string;
}
});
const { getOpenKeepAlive } = useRootSetting();
const getCaches = computed((): string[] => {
if (!unref(getOpenKeepAlive)) {
return [];
}
const cached = tabStore.getCachedMapState;
if (isPage) {
// page Layout
// not parent layout
return cached.get(PAGE_LAYOUT_KEY) || [];
}
const cacheSet = new Set<string>();
cacheSet.add(unref(name));
const list = cached.get(unref(name));
if (!list) {
return Array.from(cacheSet);
}
list.forEach((item) => {
cacheSet.add(item);
});
return Array.from(cacheSet);
});
return { getCaches };
}
...@@ -4,8 +4,8 @@ export default { ...@@ -4,8 +4,8 @@ export default {
putAway: 'Put away', putAway: 'Put away',
unfold: 'Unfold', unfold: 'Unfold',
input: 'Please Input', input: 'Please Input ',
choose: 'Please Choose', choose: 'Please Choose ',
maxTip: 'The number of characters should be less than {0}', maxTip: 'The number of characters should be less than {0}',
}; };
...@@ -15,4 +15,6 @@ export default { ...@@ -15,4 +15,6 @@ export default {
lockScreen: 'Lock screen', lockScreen: 'Lock screen',
lockScreenBtn: 'Locking', lockScreenBtn: 'Locking',
notLockScreenPassword: 'No password lock screen', notLockScreenPassword: 'No password lock screen',
home: 'Home',
}; };
export default {
level: 'Multi menu cache',
};
...@@ -16,4 +16,6 @@ export default { ...@@ -16,4 +16,6 @@ export default {
lockScreen: '锁定屏幕', lockScreen: '锁定屏幕',
lockScreenBtn: '锁定', lockScreenBtn: '锁定',
notLockScreenPassword: '不设置密码锁屏', notLockScreenPassword: '不设置密码锁屏',
home: '首页',
}; };
export default { export default {
feat: '页面功能', feat: '功能',
icon: '图标', icon: '图标',
tabs: '标签页操作', tabs: '标签页操作',
contextMenu: '右键菜单', contextMenu: '右键菜单',
......
export default {
level: '多级菜单缓存',
};
import type { AppRouteRecordRaw } from '/@/router/types'; import type { AppRouteRecordRaw } from '/@/router/types';
import ParentLayout from '/@/layouts/parent/index.vue';
const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception'); const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception');
/** /**
* @description: default layout * @description: default layout
*/ */
export const DEFAULT_LAYOUT_COMPONENT = () => import('/@/layouts/default/index'); export const LAYOUT = () => import('/@/layouts/default/index');
/** /**
* @description: page-layout * @description: page-layout
*/ */
export const PAGE_LAYOUT_COMPONENT = () => import('/@/layouts/page/index'); export const PAGE_LAYOUT_COMPONENT = () => import('/@/layouts/page/index.vue');
/**
* @description: page-layout
*/
export const getParentLayout = (name: string) => {
return () =>
new Promise((resolve) => {
resolve({
...ParentLayout,
name,
});
});
};
// 404 on a page // 404 on a page
export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
...@@ -23,12 +37,25 @@ export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { ...@@ -23,12 +37,25 @@ export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
}, },
}; };
export const REDIRECT_NAME = 'Redirect';
export const REDIRECT_ROUTE: AppRouteRecordRaw = { export const REDIRECT_ROUTE: AppRouteRecordRaw = {
path: '/redirect/:path(.*)*', path: '/redirect',
name: 'Redirect', name: REDIRECT_NAME,
component: () => import('/@/views/sys/redirect/index.vue'), component: LAYOUT,
meta: { meta: {
title: 'Redirect', title: REDIRECT_NAME,
hideBreadcrumb: true, hideBreadcrumb: true,
}, },
children: [
{
path: '/redirect/:path(.*)',
name: REDIRECT_NAME,
component: () => import('/@/views/sys/redirect/index.vue'),
meta: {
title: REDIRECT_NAME,
hideBreadcrumb: true,
},
},
],
}; };
...@@ -8,17 +8,19 @@ import { createPageLoadingGuard } from './pageLoadingGuard'; ...@@ -8,17 +8,19 @@ import { createPageLoadingGuard } from './pageLoadingGuard';
import { useGlobSetting, useProjectSetting } from '/@/hooks/setting'; import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper'; import { getIsOpenTab, getRoute } from '/@/router/helper/routeHelper';
import { setTitle } from '/@/utils/browser'; import { setTitle } from '/@/utils/browser';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
import { tabStore } from '/@/store/modules/tab'; import { tabStore } from '/@/store/modules/tab';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { REDIRECT_NAME } from '/@/router/constant';
const { closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting(); const { closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting();
const globSetting = useGlobSetting(); const globSetting = useGlobSetting();
export function createGuard(router: Router) { export function createGuard(router: Router) {
let axiosCanceler: AxiosCanceler | null; let axiosCanceler: Nullable<AxiosCanceler>;
if (removeAllHttpPending) { if (removeAllHttpPending) {
axiosCanceler = new AxiosCanceler(); axiosCanceler = new AxiosCanceler();
} }
...@@ -30,15 +32,7 @@ export function createGuard(router: Router) { ...@@ -30,15 +32,7 @@ export function createGuard(router: Router) {
to.meta.inTab = isOpen; to.meta.inTab = isOpen;
// Notify routing changes // Notify routing changes
const { fullPath, path, query, params, name, meta } = to; tabStore.commitLastChangeRouteState(getRoute(to));
tabStore.commitLastChangeRouteState({
fullPath,
path,
query,
params,
name,
meta,
} as any);
try { try {
if (closeMessageOnSwitch) { if (closeMessageOnSwitch) {
...@@ -50,14 +44,13 @@ export function createGuard(router: Router) { ...@@ -50,14 +44,13 @@ export function createGuard(router: Router) {
} catch (error) { } catch (error) {
console.warn('basic guard error:' + error); console.warn('basic guard error:' + error);
} }
setCurrentTo(to);
return true; return true;
}); });
router.afterEach((to) => { router.afterEach((to) => {
const { t } = useI18n(); const { t } = useI18n();
// change html title // change html title
to.name !== 'Redirect' && setTitle(t(to.meta.title), globSetting.title); to.name !== REDIRECT_NAME && setTitle(t(to.meta.title), globSetting.title);
}); });
createProgressGuard(router); createProgressGuard(router);
createPermissionGuard(router); createPermissionGuard(router);
......
...@@ -2,7 +2,7 @@ import type { Router } from 'vue-router'; ...@@ -2,7 +2,7 @@ import type { Router } from 'vue-router';
import { tabStore } from '/@/store/modules/tab'; import { tabStore } from '/@/store/modules/tab';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
import { userStore } from '/@/store/modules/user'; import { userStore } from '/@/store/modules/user';
import { getParams } from '/@/utils/helper/routeHelper'; import { getParams } from '/@/router/helper/routeHelper';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { unref } from 'vue'; import { unref } from 'vue';
...@@ -14,6 +14,7 @@ export function createPageLoadingGuard(router: Router) { ...@@ -14,6 +14,7 @@ export function createPageLoadingGuard(router: Router) {
if (!userStore.getTokenState) { if (!userStore.getTokenState) {
return true; return true;
} }
if (!unref(getEnableTransition) && unref(getOpenPageLoading)) { if (!unref(getEnableTransition) && unref(getOpenPageLoading)) {
appStore.commitPageLoadingState(true); appStore.commitPageLoadingState(true);
return true; return true;
......
...@@ -7,7 +7,7 @@ import { PageEnum } from '/@/enums/pageEnum'; ...@@ -7,7 +7,7 @@ import { PageEnum } from '/@/enums/pageEnum';
import { getToken } from '/@/utils/auth'; import { getToken } from '/@/utils/auth';
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; import { PAGE_NOT_FOUND_ROUTE } from '/@/router/constant';
import { RootRoute } from '../routes/index'; // import { RootRoute } from '../routes/index';
const LOGIN_PATH = PageEnum.BASE_LOGIN; const LOGIN_PATH = PageEnum.BASE_LOGIN;
...@@ -59,7 +59,8 @@ export function createPermissionGuard(router: Router) { ...@@ -59,7 +59,8 @@ export function createPermissionGuard(router: Router) {
} }
const routes = await permissionStore.buildRoutesAction(); const routes = await permissionStore.buildRoutesAction();
routes.forEach((route) => { routes.forEach((route) => {
router.addRoute(RootRoute.name!, route as RouteRecordRaw); // router.addRoute(RootRoute.name!, route as RouteRecordRaw);
router.addRoute(route as RouteRecordRaw);
}); });
const redirectPath = (from.query.redirect || to.path) as string; const redirectPath = (from.query.redirect || to.path) as string;
......
...@@ -9,9 +9,6 @@ import { unref } from 'vue'; ...@@ -9,9 +9,6 @@ import { unref } from 'vue';
const { getOpenNProgress } = useTransitionSetting(); const { getOpenNProgress } = useTransitionSetting();
export function createProgressGuard(router: Router) { export function createProgressGuard(router: Router) {
// NProgress.inc(0.1);
// NProgress.configure({ easing: 'ease', speed: 200, showSpinner: false });
router.beforeEach(async (to) => { router.beforeEach(async (to) => {
!to.meta.inTab && unref(getOpenNProgress) && NProgress.start(); !to.meta.inTab && unref(getOpenNProgress) && NProgress.start();
return true; return true;
......
// The content here is just for type approval. The actual file content is overwritten by transform
// For specific coverage, see build/vite/plugin/transform/dynamic-import/index.ts
export default function (name: string) {
return name as any;
}
import { AppRouteModule, RouteModule } from '/@/router/types.d'; import { AppRouteModule } from '/@/router/types.d';
import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types'; import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types';
import { findPath, forEach, treeMap, treeToList } from './treeHelper'; import { findPath, forEach, treeMap, treeToList } from '/@/utils/helper/treeHelper';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
export function getAllParentPath(treeData: any[], path: string) { export function getAllParentPath(treeData: any[], path: string) {
...@@ -48,12 +48,11 @@ export function transformRouteToMenu(routeModList: AppRouteModule[]) { ...@@ -48,12 +48,11 @@ export function transformRouteToMenu(routeModList: AppRouteModule[]) {
const cloneRouteModList = cloneDeep(routeModList); const cloneRouteModList = cloneDeep(routeModList);
const routeList: AppRouteRecordRaw[] = []; const routeList: AppRouteRecordRaw[] = [];
cloneRouteModList.forEach((item) => { cloneRouteModList.forEach((item) => {
const { layout, routes, children } = item as RouteModule; if (item.meta?.single) {
if (layout) { const realItem = item?.children?.[0];
layout.children = routes || children; realItem && routeList.push(realItem);
routeList.push(layout);
} else { } else {
routes && routeList.push(...routes); routeList.push(item);
} }
}); });
return treeMap(routeList, { return treeMap(routeList, {
......
import type { AppRouteModule, AppRouteRecordRaw } from '/@/router/types';
import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
import { appStore } from '/@/store/modules/app';
import { tabStore } from '/@/store/modules/tab';
import { getParentLayout, LAYOUT } from '/@/router/constant';
import dynamicImport from './dynamicImport';
import { cloneDeep } from 'lodash-es';
// 动态引入
function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
if (!routes) return;
routes.forEach((item) => {
const { component, name } = item;
const { children } = item;
if (component) {
item.component = dynamicImport(component);
} else if (name) {
item.component = getParentLayout(name);
}
children && asyncImportRoute(children);
});
}
function getLayoutComp(comp: string) {
return comp === 'LAYOUT' ? LAYOUT : '';
}
// Turn background objects into routing objects
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
routeList.forEach((route) => {
if (route.component) {
if ((route.component as string).toUpperCase() === 'LAYOUT') {
route.component = getLayoutComp(route.component);
} else {
route.children = [cloneDeep(route)];
route.component = LAYOUT;
route.name = `${route.name}Parent`;
route.path = '';
const meta = route.meta || {};
meta.single = true;
meta.affix = false;
route.meta = meta;
}
}
route.children && asyncImportRoute(route.children);
});
return (routeList as unknown) as T[];
}
/**
* Determine whether the tab has been opened
* @param toPath
*/
export function getIsOpenTab(toPath: string) {
const { openKeepAlive, multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
if (show && openKeepAlive) {
const tabList = tabStore.getTabsState;
return tabList.some((tab) => tab.path === toPath);
}
return false;
}
export function getParams(data: any = {}) {
const { params = {} } = data;
let ret = '';
Object.keys(params).forEach((key) => {
const p = params[key];
ret += `/${p}`;
});
return ret;
}
// Return to the new routing structure, not affected by the original example
export function getRoute(route: RouteLocationNormalized): RouteLocationNormalized {
if (!route) return route;
const { matched, ...opt } = route;
return {
...opt,
matched: (matched
? matched.map((item) => ({
meta: item.meta,
name: item.name,
path: item.path,
}))
: undefined) as RouteRecordNormalized[],
};
}
...@@ -2,7 +2,7 @@ import type { Menu, MenuModule } from '/@/router/types'; ...@@ -2,7 +2,7 @@ import type { Menu, MenuModule } from '/@/router/types';
import type { RouteRecordNormalized } from 'vue-router'; import type { RouteRecordNormalized } from 'vue-router';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
import { permissionStore } from '/@/store/modules/permission'; import { permissionStore } from '/@/store/modules/permission';
import { transformMenuModule, flatMenus, getAllParentPath } from '/@/utils/helper/menuHelper'; import { transformMenuModule, flatMenus, getAllParentPath } from '/@/router/helper/menuHelper';
import { filter } from '/@/utils/helper/treeHelper'; import { filter } from '/@/utils/helper/treeHelper';
import router from '/@/router'; import router from '/@/router';
import { PermissionModeEnum } from '/@/enums/appEnum'; import { PermissionModeEnum } from '/@/enums/appEnum';
......
import type { MenuModule } from '/@/router/types.d'; import type { MenuModule } from '/@/router/types.d';
const menu: MenuModule[] = [ const menu: MenuModule = {
{ orderNo: 10,
orderNo: 0, menu: {
menu: { name: 'routes.dashboard.dashboard',
path: '/dashboard/welcome', path: '/dashboard',
name: 'routes.dashboard.welcome', children: [
}, {
path: '/workbench',
name: 'routes.dashboard.workbench',
},
{
path: '/analysis',
name: 'routes.dashboard.analysis',
},
],
}, },
{ };
orderNo: 10,
menu: {
name: 'routes.dashboard.dashboard',
path: '/dashboard',
children: [
{
path: '/workbench',
name: 'routes.dashboard.workbench',
},
{
path: '/analysis',
name: 'routes.dashboard.analysis',
},
// {
// path: '/welcome',
// name: 'routes.dashboard.welcome',
// },
],
},
},
];
export default menu; export default menu;
import type { MenuModule } from '/@/router/types.d';
const menu: MenuModule = {
orderNo: 2000,
menu: {
name: 'routes.demo.level.level',
path: '/level',
tag: {
dot: true,
},
children: [
{
path: 'menu1',
name: 'Menu1',
children: [
{
path: 'menu1-1',
name: 'Menu1-1',
children: [
{
path: 'menu1-1-1',
name: 'Menu1-1-1',
},
],
},
{
path: 'menu1-2',
name: 'Menu1-2',
},
],
},
{
path: 'menu2',
name: 'Menu2',
},
],
},
};
export default menu;
import type { MenuModule } from '/@/router/types.d';
const menu: MenuModule = {
orderNo: 0,
menu: {
path: '/home/welcome',
name: 'routes.dashboard.welcome',
},
};
export default menu;
import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types'; import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types';
import { DEFAULT_LAYOUT_COMPONENT, PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '../constant'; import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE, LAYOUT } from '../constant';
import { genRouteModule } from '/@/utils/helper/routeHelper'; import { PageEnum } from '/@/enums/pageEnum';
import modules from 'globby!/@/router/routes/modules/**/*.@(ts)'; import modules from 'globby!/@/router/routes/modules/**/*.@(ts)';
const routeModuleList: AppRouteModule[] = []; const routeModuleList: AppRouteModule[] = [];
Object.keys(modules).forEach((key) => { Object.keys(modules).forEach((key) => {
routeModuleList.push(modules[key]); const mod = Array.isArray(modules[key]) ? [...modules[key]] : [modules[key]];
routeModuleList.push(...mod);
}); });
export const asyncRoutes = [ export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
REDIRECT_ROUTE,
PAGE_NOT_FOUND_ROUTE,
...genRouteModule(routeModuleList),
];
// 主框架根路由 const MainRoute: AppRouteModule = {
export const RootRoute: AppRouteRecordRaw = {
path: '/', path: '/',
name: 'Root', name: 'MainRoute',
component: DEFAULT_LAYOUT_COMPONENT, component: LAYOUT,
redirect: '/dashboard', redirect: PageEnum.BASE_HOME,
meta: { meta: {
title: 'Root', icon: 'ant-design:home-outlined',
title: 'routes.dashboard.dashboard',
}, },
children: [],
}; };
export const LoginRoute: AppRouteRecordRaw = { export const LoginRoute: AppRouteRecordRaw = {
...@@ -38,4 +35,4 @@ export const LoginRoute: AppRouteRecordRaw = { ...@@ -38,4 +35,4 @@ export const LoginRoute: AppRouteRecordRaw = {
}; };
// 基础路由 不用权限 // 基础路由 不用权限
export const basicRoutes = [LoginRoute, RootRoute]; export const basicRoutes = [LoginRoute, MainRoute, REDIRECT_ROUTE];
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { LAYOUT } from '/@/router/constant';
const dashboard: AppRouteModule = { const dashboard: AppRouteModule = {
layout: { path: '/dashboard',
path: '/dashboard', name: 'Dashboard',
name: 'Dashboard', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/dashboard/welcome',
redirect: '/dashboard/welcome', meta: {
meta: { icon: 'ant-design:home-outlined',
icon: 'ant-design:home-outlined', title: 'routes.dashboard.dashboard',
title: 'routes.dashboard.dashboard',
},
}, },
children: [
routes: [
{
path: '/welcome',
name: 'Welcome',
component: () => import('/@/views/dashboard/welcome/index.vue'),
meta: {
title: 'routes.dashboard.welcome',
affix: true,
icon: 'ant-design:home-outlined',
},
},
{ {
path: '/workbench', path: 'workbench',
name: 'Workbench', name: 'Workbench',
component: () => import('/@/views/dashboard/workbench/index.vue'), component: () => import('/@/views/dashboard/workbench/index.vue'),
meta: { meta: {
...@@ -34,7 +21,7 @@ const dashboard: AppRouteModule = { ...@@ -34,7 +21,7 @@ const dashboard: AppRouteModule = {
}, },
}, },
{ {
path: '/analysis', path: 'analysis',
name: 'Analysis', name: 'Analysis',
component: () => import('/@/views/dashboard/analysis/index.vue'), component: () => import('/@/views/dashboard/analysis/index.vue'),
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { getParentLayout, LAYOUT } from '/@/router/constant';
const charts: AppRouteModule = { const charts: AppRouteModule = {
layout: { path: '/charts',
path: '/charts', name: 'Charts',
name: 'Charts', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/charts/apexChart',
redirect: '/charts/apexChart', meta: {
meta: { icon: 'ant-design:area-chart-outlined',
icon: 'ant-design:area-chart-outlined', title: 'routes.demo.charts.charts',
title: 'routes.demo.charts.charts',
},
}, },
children: [
routes: [
{ {
path: '/echarts', path: 'echarts',
name: 'Echarts', name: 'Echarts',
component: getParentLayout('Echarts'),
meta: { meta: {
title: 'Echarts', title: 'Echarts',
}, },
...@@ -49,7 +47,7 @@ const charts: AppRouteModule = { ...@@ -49,7 +47,7 @@ const charts: AppRouteModule = {
], ],
}, },
{ {
path: '/apexChart', path: 'apexChart',
name: 'ApexChart', name: 'ApexChart',
meta: { meta: {
title: 'routes.demo.charts.apexChart', title: 'routes.demo.charts.apexChart',
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { getParentLayout, LAYOUT } from '/@/router/constant';
const comp: AppRouteModule = { const comp: AppRouteModule = {
layout: { path: '/comp',
path: '/comp', name: 'Comp',
name: 'Comp', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/comp/basic',
redirect: '/comp/basic', meta: {
meta: { icon: 'ant-design:table-outlined',
icon: 'ant-design:table-outlined', title: 'routes.demo.comp.comp',
title: 'routes.demo.comp.comp',
},
}, },
routes: [ children: [
{ {
path: '/basic', path: 'basic',
name: 'BasicDemo', name: 'BasicDemo',
component: () => import('/@/views/demo/comp/button/index.vue'), component: () => import('/@/views/demo/comp/button/index.vue'),
meta: { meta: {
...@@ -24,7 +22,7 @@ const comp: AppRouteModule = { ...@@ -24,7 +22,7 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: '/transition', path: 'transition',
name: 'transitionDemo', name: 'transitionDemo',
component: () => import('/@/views/demo/comp/transition/index.vue'), component: () => import('/@/views/demo/comp/transition/index.vue'),
meta: { meta: {
...@@ -32,7 +30,7 @@ const comp: AppRouteModule = { ...@@ -32,7 +30,7 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: '/countTo', path: 'countTo',
name: 'CountTo', name: 'CountTo',
component: () => import('/@/views/demo/comp/count-to/index.vue'), component: () => import('/@/views/demo/comp/count-to/index.vue'),
meta: { meta: {
...@@ -41,9 +39,10 @@ const comp: AppRouteModule = { ...@@ -41,9 +39,10 @@ const comp: AppRouteModule = {
}, },
{ {
path: '/scroll', path: 'scroll',
name: 'ScrollDemo', name: 'ScrollDemo',
redirect: '/comp/scroll/basic', redirect: '/comp/scroll/basic',
component: getParentLayout('ScrollDemo'),
meta: { meta: {
title: 'routes.demo.comp.scroll', title: 'routes.demo.comp.scroll',
}, },
...@@ -76,7 +75,7 @@ const comp: AppRouteModule = { ...@@ -76,7 +75,7 @@ const comp: AppRouteModule = {
}, },
{ {
path: '/modal', path: 'modal',
name: 'ModalDemo', name: 'ModalDemo',
component: () => import('/@/views/demo/comp/modal/index.vue'), component: () => import('/@/views/demo/comp/modal/index.vue'),
meta: { meta: {
...@@ -84,7 +83,7 @@ const comp: AppRouteModule = { ...@@ -84,7 +83,7 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: '/drawer', path: 'drawer',
name: 'DrawerDemo', name: 'DrawerDemo',
component: () => import('/@/views/demo/comp/drawer/index.vue'), component: () => import('/@/views/demo/comp/drawer/index.vue'),
meta: { meta: {
...@@ -92,7 +91,7 @@ const comp: AppRouteModule = { ...@@ -92,7 +91,7 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: '/desc', path: 'desc',
name: 'DescDemo', name: 'DescDemo',
component: () => import('/@/views/demo/comp/desc/index.vue'), component: () => import('/@/views/demo/comp/desc/index.vue'),
meta: { meta: {
...@@ -101,8 +100,9 @@ const comp: AppRouteModule = { ...@@ -101,8 +100,9 @@ const comp: AppRouteModule = {
}, },
{ {
path: '/lazy', path: 'lazy',
name: 'lazyDemo', name: 'LazyDemo',
component: getParentLayout('LazyDemo'),
redirect: '/comp/lazy/basic', redirect: '/comp/lazy/basic',
meta: { meta: {
title: 'routes.demo.comp.lazy', title: 'routes.demo.comp.lazy',
...@@ -127,8 +127,9 @@ const comp: AppRouteModule = { ...@@ -127,8 +127,9 @@ const comp: AppRouteModule = {
], ],
}, },
{ {
path: '/verify', path: 'verify',
name: 'VerifyDemo', name: 'VerifyDemo',
component: getParentLayout('VerifyDemo'),
redirect: '/comp/verify/drag', redirect: '/comp/verify/drag',
meta: { meta: {
title: 'routes.demo.comp.verify', title: 'routes.demo.comp.verify',
...@@ -155,7 +156,7 @@ const comp: AppRouteModule = { ...@@ -155,7 +156,7 @@ const comp: AppRouteModule = {
// //
{ {
path: '/qrcode', path: 'qrcode',
name: 'QrCodeDemo', name: 'QrCodeDemo',
component: () => import('/@/views/demo/comp/qrcode/index.vue'), component: () => import('/@/views/demo/comp/qrcode/index.vue'),
meta: { meta: {
...@@ -163,7 +164,7 @@ const comp: AppRouteModule = { ...@@ -163,7 +164,7 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: '/strength-meter', path: 'strength-meter',
name: 'StrengthMeterDemo', name: 'StrengthMeterDemo',
component: () => import('/@/views/demo/comp/strength-meter/index.vue'), component: () => import('/@/views/demo/comp/strength-meter/index.vue'),
meta: { meta: {
...@@ -171,7 +172,7 @@ const comp: AppRouteModule = { ...@@ -171,7 +172,7 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: '/upload', path: 'upload',
name: 'UploadDemo', name: 'UploadDemo',
component: () => import('/@/views/demo/comp/upload/index.vue'), component: () => import('/@/views/demo/comp/upload/index.vue'),
meta: { meta: {
...@@ -179,7 +180,7 @@ const comp: AppRouteModule = { ...@@ -179,7 +180,7 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: '/loading', path: 'loading',
name: 'LoadingDemo', name: 'LoadingDemo',
component: () => import('/@/views/demo/comp/loading/index.vue'), component: () => import('/@/views/demo/comp/loading/index.vue'),
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { getParentLayout, LAYOUT } from '/@/router/constant';
const editor: AppRouteModule = { const editor: AppRouteModule = {
layout: { path: '/editor',
path: '/editor', name: 'Editor',
name: 'Editor', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/editor/markdown',
redirect: '/editor/markdown', meta: {
meta: { icon: 'ant-design:table-outlined',
icon: 'ant-design:table-outlined', title: 'routes.demo.editor.editor',
title: 'routes.demo.editor.editor',
},
}, },
children: [
routes: [
{ {
path: '/markdown', path: 'markdown',
name: 'MarkdownDemo', name: 'MarkdownDemo',
component: () => import('/@/views/demo/editor/Markdown.vue'), component: () => import('/@/views/demo/editor/Markdown.vue'),
meta: { meta: {
...@@ -24,7 +21,8 @@ const editor: AppRouteModule = { ...@@ -24,7 +21,8 @@ const editor: AppRouteModule = {
}, },
}, },
{ {
path: '/tinymce', path: 'tinymce',
component: getParentLayout('TinymceDemo'),
name: 'TinymceDemo', name: 'TinymceDemo',
meta: { meta: {
title: 'routes.demo.editor.tinymce', title: 'routes.demo.editor.tinymce',
...@@ -39,7 +37,6 @@ const editor: AppRouteModule = { ...@@ -39,7 +37,6 @@ const editor: AppRouteModule = {
title: 'routes.demo.editor.tinymceBasic', title: 'routes.demo.editor.tinymceBasic',
}, },
}, },
// TODO
{ {
path: 'editor', path: 'editor',
name: 'TinymceFormDemo', name: 'TinymceFormDemo',
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { LAYOUT } from '/@/router/constant';
const excel: AppRouteModule = { const excel: AppRouteModule = {
layout: { path: '/excel',
path: '/excel', name: 'Excel',
name: 'Excel', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/excel/customExport',
redirect: '/excel/customExport', meta: {
meta: { icon: 'mdi:microsoft-excel',
icon: 'mdi:microsoft-excel', title: 'routes.demo.excel.excel',
title: 'routes.demo.excel.excel',
},
}, },
routes: [ children: [
{ {
path: '/customExport', path: 'customExport',
name: 'CustomExport', name: 'CustomExport',
component: () => import('/@/views/demo/excel/CustomExport.vue'), component: () => import('/@/views/demo/excel/CustomExport.vue'),
meta: { meta: {
...@@ -24,7 +22,7 @@ const excel: AppRouteModule = { ...@@ -24,7 +22,7 @@ const excel: AppRouteModule = {
}, },
}, },
{ {
path: '/jsonExport', path: 'jsonExport',
name: 'JsonExport', name: 'JsonExport',
component: () => import('/@/views/demo/excel/JsonExport.vue'), component: () => import('/@/views/demo/excel/JsonExport.vue'),
meta: { meta: {
...@@ -32,7 +30,7 @@ const excel: AppRouteModule = { ...@@ -32,7 +30,7 @@ const excel: AppRouteModule = {
}, },
}, },
{ {
path: '/arrayExport', path: 'arrayExport',
name: 'ArrayExport', name: 'ArrayExport',
component: () => import('/@/views/demo/excel/ArrayExport.vue'), component: () => import('/@/views/demo/excel/ArrayExport.vue'),
meta: { meta: {
...@@ -40,7 +38,7 @@ const excel: AppRouteModule = { ...@@ -40,7 +38,7 @@ const excel: AppRouteModule = {
}, },
}, },
{ {
path: '/importExcel', path: 'importExcel',
name: 'ImportExcel', name: 'ImportExcel',
component: () => import('/@/views/demo/excel/ImportExcel.vue'), component: () => import('/@/views/demo/excel/ImportExcel.vue'),
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { LAYOUT } from '/@/router/constant';
const feat: AppRouteModule = { const feat: AppRouteModule = {
layout: { path: '/feat',
path: '/feat', name: 'FeatDemo',
name: 'FeatDemo', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/feat/icon',
redirect: '/feat/icon', meta: {
meta: { icon: 'ic:outline-featured-play-list',
icon: 'ic:outline-featured-play-list', title: 'routes.demo.feat.feat',
title: 'routes.demo.feat.feat',
},
}, },
children: [
routes: [
{ {
path: '/icon', path: 'icon',
name: 'IconDemo', name: 'IconDemo',
component: () => import('/@/views/demo/feat/icon/index.vue'), component: () => import('/@/views/demo/feat/icon/index.vue'),
meta: { meta: {
...@@ -24,7 +21,7 @@ const feat: AppRouteModule = { ...@@ -24,7 +21,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/tabs', path: 'tabs',
name: 'TabsDemo', name: 'TabsDemo',
component: () => import('/@/views/demo/feat/tabs/index.vue'), component: () => import('/@/views/demo/feat/tabs/index.vue'),
meta: { meta: {
...@@ -33,7 +30,7 @@ const feat: AppRouteModule = { ...@@ -33,7 +30,7 @@ const feat: AppRouteModule = {
}, },
{ {
path: '/context-menu', path: 'context-menu',
name: 'ContextMenuDemo', name: 'ContextMenuDemo',
component: () => import('/@/views/demo/feat/context-menu/index.vue'), component: () => import('/@/views/demo/feat/context-menu/index.vue'),
meta: { meta: {
...@@ -41,7 +38,7 @@ const feat: AppRouteModule = { ...@@ -41,7 +38,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/download', path: 'download',
name: 'DownLoadDemo', name: 'DownLoadDemo',
component: () => import('/@/views/demo/feat/download/index.vue'), component: () => import('/@/views/demo/feat/download/index.vue'),
meta: { meta: {
...@@ -49,7 +46,7 @@ const feat: AppRouteModule = { ...@@ -49,7 +46,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/click-out-side', path: 'click-out-side',
name: 'ClickOutSideDemo', name: 'ClickOutSideDemo',
component: () => import('/@/views/demo/feat/click-out-side/index.vue'), component: () => import('/@/views/demo/feat/click-out-side/index.vue'),
meta: { meta: {
...@@ -57,7 +54,7 @@ const feat: AppRouteModule = { ...@@ -57,7 +54,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/img-preview', path: 'img-preview',
name: 'ImgPreview', name: 'ImgPreview',
component: () => import('/@/views/demo/feat/img-preview/index.vue'), component: () => import('/@/views/demo/feat/img-preview/index.vue'),
meta: { meta: {
...@@ -65,7 +62,7 @@ const feat: AppRouteModule = { ...@@ -65,7 +62,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/copy', path: 'copy',
name: 'CopyDemo', name: 'CopyDemo',
component: () => import('/@/views/demo/feat/copy/index.vue'), component: () => import('/@/views/demo/feat/copy/index.vue'),
meta: { meta: {
...@@ -73,7 +70,7 @@ const feat: AppRouteModule = { ...@@ -73,7 +70,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/msg', path: 'msg',
name: 'MsgDemo', name: 'MsgDemo',
component: () => import('/@/views/demo/feat/msg/index.vue'), component: () => import('/@/views/demo/feat/msg/index.vue'),
meta: { meta: {
...@@ -81,7 +78,7 @@ const feat: AppRouteModule = { ...@@ -81,7 +78,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/watermark', path: 'watermark',
name: 'WatermarkDemo', name: 'WatermarkDemo',
component: () => import('/@/views/demo/feat/watermark/index.vue'), component: () => import('/@/views/demo/feat/watermark/index.vue'),
meta: { meta: {
...@@ -89,7 +86,7 @@ const feat: AppRouteModule = { ...@@ -89,7 +86,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/full-screen', path: 'full-screen',
name: 'FullScreenDemo', name: 'FullScreenDemo',
component: () => import('/@/views/demo/feat/full-screen/index.vue'), component: () => import('/@/views/demo/feat/full-screen/index.vue'),
meta: { meta: {
...@@ -97,7 +94,7 @@ const feat: AppRouteModule = { ...@@ -97,7 +94,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/error-log', path: 'error-log',
name: 'ErrorLog', name: 'ErrorLog',
component: () => import('/@/views/sys/error-log/index.vue'), component: () => import('/@/views/sys/error-log/index.vue'),
meta: { meta: {
...@@ -105,7 +102,7 @@ const feat: AppRouteModule = { ...@@ -105,7 +102,7 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: '/testTab/:id', path: 'testTab/:id',
name: 'TestTab', name: 'TestTab',
component: () => import('/@/views/demo/feat/tab-params/index.vue'), component: () => import('/@/views/demo/feat/tab-params/index.vue'),
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { LAYOUT } from '/@/router/constant';
const form: AppRouteModule = { const form: AppRouteModule = {
layout: { path: '/form',
path: '/form', name: 'FormDemo',
name: 'FormDemo', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/form/basic',
redirect: '/form/basic', meta: {
meta: { icon: 'ant-design:table-outlined',
icon: 'ant-design:table-outlined', title: 'routes.demo.form.form',
title: 'routes.demo.form.form',
},
}, },
children: [
routes: [
{ {
path: '/basic', path: 'basic',
name: 'FormBasicDemo', name: 'FormBasicDemo',
component: () => import('/@/views/demo/form/index.vue'), component: () => import('/@/views/demo/form/index.vue'),
meta: { meta: {
...@@ -24,7 +21,7 @@ const form: AppRouteModule = { ...@@ -24,7 +21,7 @@ const form: AppRouteModule = {
}, },
}, },
{ {
path: '/useForm', path: 'useForm',
name: 'UseFormDemo', name: 'UseFormDemo',
component: () => import('/@/views/demo/form/UseForm.vue'), component: () => import('/@/views/demo/form/UseForm.vue'),
meta: { meta: {
...@@ -32,7 +29,7 @@ const form: AppRouteModule = { ...@@ -32,7 +29,7 @@ const form: AppRouteModule = {
}, },
}, },
{ {
path: '/refForm', path: 'refForm',
name: 'RefFormDemo', name: 'RefFormDemo',
component: () => import('/@/views/demo/form/RefForm.vue'), component: () => import('/@/views/demo/form/RefForm.vue'),
meta: { meta: {
...@@ -40,7 +37,7 @@ const form: AppRouteModule = { ...@@ -40,7 +37,7 @@ const form: AppRouteModule = {
}, },
}, },
{ {
path: '/advancedForm', path: 'advancedForm',
name: 'AdvancedFormDemo', name: 'AdvancedFormDemo',
component: () => import('/@/views/demo/form/AdvancedForm.vue'), component: () => import('/@/views/demo/form/AdvancedForm.vue'),
meta: { meta: {
...@@ -48,7 +45,7 @@ const form: AppRouteModule = { ...@@ -48,7 +45,7 @@ const form: AppRouteModule = {
}, },
}, },
{ {
path: '/ruleForm', path: 'ruleForm',
name: 'RuleFormDemo', name: 'RuleFormDemo',
component: () => import('/@/views/demo/form/RuleForm.vue'), component: () => import('/@/views/demo/form/RuleForm.vue'),
meta: { meta: {
...@@ -56,7 +53,7 @@ const form: AppRouteModule = { ...@@ -56,7 +53,7 @@ const form: AppRouteModule = {
}, },
}, },
{ {
path: '/dynamicForm', path: 'dynamicForm',
name: 'DynamicFormDemo', name: 'DynamicFormDemo',
component: () => import('/@/views/demo/form/DynamicForm.vue'), component: () => import('/@/views/demo/form/DynamicForm.vue'),
meta: { meta: {
...@@ -64,7 +61,7 @@ const form: AppRouteModule = { ...@@ -64,7 +61,7 @@ const form: AppRouteModule = {
}, },
}, },
{ {
path: '/customerForm', path: 'customerForm',
name: 'CustomerFormDemo', name: 'CustomerFormDemo',
component: () => import('/@/views/demo/form/CustomerForm.vue'), component: () => import('/@/views/demo/form/CustomerForm.vue'),
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { LAYOUT } from '/@/router/constant';
const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue'); const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue');
const iframe: AppRouteModule = { const iframe: AppRouteModule = {
layout: { path: '/frame',
path: '/frame', name: 'Frame',
name: 'Frame', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/frame/antv',
redirect: '/frame/antv', meta: {
meta: { icon: 'mdi:page-next-outline',
icon: 'mdi:page-next-outline', title: 'routes.demo.iframe.frame',
title: 'routes.demo.iframe.frame',
},
}, },
routes: [ children: [
{ {
path: '/antv', path: 'antv',
name: 'Antv', name: 'Antv',
component: IFrame, component: IFrame,
meta: { meta: {
...@@ -27,7 +25,7 @@ const iframe: AppRouteModule = { ...@@ -27,7 +25,7 @@ const iframe: AppRouteModule = {
}, },
}, },
{ {
path: '/doc', path: 'doc',
name: 'Doc', name: 'Doc',
component: IFrame, component: IFrame,
meta: { meta: {
...@@ -37,7 +35,7 @@ const iframe: AppRouteModule = { ...@@ -37,7 +35,7 @@ const iframe: AppRouteModule = {
}, },
}, },
{ {
path: '/docExternal', path: 'docExternal',
name: 'DocExternal', name: 'DocExternal',
component: IFrame, component: IFrame,
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types';
import { getParentLayout, LAYOUT } from '/@/router/constant';
const permission: AppRouteModule = {
path: '/level',
name: 'Level',
component: LAYOUT,
redirect: '/level/menu1/menu1-1',
meta: {
icon: 'carbon:user-role',
title: 'routes.demo.level.level',
},
children: [
{
path: 'menu1',
name: 'Menu1Demo',
component: getParentLayout('Menu1Demo'),
meta: {
title: 'Menu1',
},
children: [
{
path: 'menu1-1',
name: 'Menu11Demo',
component: getParentLayout('Menu11Demo'),
meta: {
title: 'Menu1-1',
},
children: [
{
path: 'menu1-1-1',
name: 'Menu111Demo',
component: () => import('/@/views/demo/level/Menu111.vue'),
meta: {
title: 'Menu111',
},
},
],
},
{
path: 'menu1-2',
name: 'Menu12Demo',
component: () => import('/@/views/demo/level/Menu12.vue'),
meta: {
title: 'Menu1-2',
},
},
],
},
{
path: 'menu2',
name: 'Menu2Demo',
component: () => import('/@/views/demo/level/Menu2.vue'),
meta: {
title: 'Menu2',
},
},
],
};
export default permission;
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { getParentLayout, LAYOUT } from '/@/router/constant';
import { ExceptionEnum } from '/@/enums/exceptionEnum'; import { ExceptionEnum } from '/@/enums/exceptionEnum';
const ExceptionPage = () => import('/@/views/sys/exception/Exception'); const ExceptionPage = () => import('/@/views/sys/exception/Exception');
...@@ -8,7 +8,7 @@ const ExceptionPage = () => import('/@/views/sys/exception/Exception'); ...@@ -8,7 +8,7 @@ const ExceptionPage = () => import('/@/views/sys/exception/Exception');
const page: AppRouteModule = { const page: AppRouteModule = {
path: '/page-demo', path: '/page-demo',
name: 'PageDemo', name: 'PageDemo',
component: PAGE_LAYOUT_COMPONENT, component: LAYOUT,
redirect: '/page-demo/exception', redirect: '/page-demo/exception',
meta: { meta: {
icon: 'mdi:page-next-outline', icon: 'mdi:page-next-outline',
...@@ -17,9 +17,10 @@ const page: AppRouteModule = { ...@@ -17,9 +17,10 @@ const page: AppRouteModule = {
children: [ children: [
// =============================form start============================= // =============================form start=============================
{ {
path: '/form', path: 'form',
name: 'FormPage', name: 'FormPage',
redirect: '/page-demo/form/basic', redirect: '/page-demo/form/basic',
component: getParentLayout('FormPage'),
meta: { meta: {
title: 'routes.demo.page.form', title: 'routes.demo.page.form',
}, },
...@@ -53,8 +54,9 @@ const page: AppRouteModule = { ...@@ -53,8 +54,9 @@ const page: AppRouteModule = {
// =============================form end============================= // =============================form end=============================
// =============================desc start============================= // =============================desc start=============================
{ {
path: '/desc', path: 'desc',
name: 'DescPage', name: 'DescPage',
component: getParentLayout('DescPage'),
redirect: '/page-demo/desc/basic', redirect: '/page-demo/desc/basic',
meta: { meta: {
title: 'routes.demo.page.desc', title: 'routes.demo.page.desc',
...@@ -82,9 +84,11 @@ const page: AppRouteModule = { ...@@ -82,9 +84,11 @@ const page: AppRouteModule = {
// =============================result start============================= // =============================result start=============================
{ {
path: '/result', path: 'result',
name: 'ResultPage', name: 'ResultPage',
redirect: '/page-demo/result/success', redirect: '/page-demo/result/success',
component: getParentLayout('ResultPage'),
meta: { meta: {
title: 'routes.demo.page.result', title: 'routes.demo.page.result',
}, },
...@@ -111,8 +115,9 @@ const page: AppRouteModule = { ...@@ -111,8 +115,9 @@ const page: AppRouteModule = {
// =============================account start============================= // =============================account start=============================
{ {
path: '/account', path: 'account',
name: 'AccountPage', name: 'AccountPage',
component: getParentLayout('AccountPage'),
redirect: '/page-demo/account/setting', redirect: '/page-demo/account/setting',
meta: { meta: {
title: 'routes.demo.page.account', title: 'routes.demo.page.account',
...@@ -139,8 +144,9 @@ const page: AppRouteModule = { ...@@ -139,8 +144,9 @@ const page: AppRouteModule = {
// =============================account end============================= // =============================account end=============================
// =============================exception start============================= // =============================exception start=============================
{ {
path: '/exception', path: 'exception',
name: 'ExceptionPage', name: 'ExceptionPage',
component: getParentLayout('ExceptionPage'),
redirect: '/page-demo/exception/404', redirect: '/page-demo/exception/404',
meta: { meta: {
title: 'routes.demo.page.exception', title: 'routes.demo.page.exception',
...@@ -211,8 +217,9 @@ const page: AppRouteModule = { ...@@ -211,8 +217,9 @@ const page: AppRouteModule = {
// =============================exception end============================= // =============================exception end=============================
// =============================list start============================= // =============================list start=============================
{ {
path: '/list', path: 'list',
name: 'ListPage', name: 'ListPage',
component: getParentLayout('ListPage'),
redirect: '/page-demo/list/card', redirect: '/page-demo/list/card',
meta: { meta: {
title: 'routes.demo.page.list', title: 'routes.demo.page.list',
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { getParentLayout, LAYOUT } from '/@/router/constant';
import { RoleEnum } from '/@/enums/roleEnum'; import { RoleEnum } from '/@/enums/roleEnum';
const permission: AppRouteModule = { const permission: AppRouteModule = {
layout: { path: '/permission',
path: '/permission', name: 'Permission',
name: 'Permission', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/permission/front/page',
redirect: '/permission/front/page', meta: {
meta: { icon: 'carbon:user-role',
icon: 'carbon:user-role', title: 'routes.demo.permission.permission',
title: 'routes.demo.permission.permission',
},
}, },
routes: [ children: [
{ {
path: '/front', path: 'front',
name: 'PermissionFrontDemo', name: 'PermissionFrontDemo',
component: getParentLayout('PermissionFrontDemo'),
meta: { meta: {
title: 'routes.demo.permission.front', title: 'routes.demo.permission.front',
}, },
...@@ -60,8 +59,9 @@ const permission: AppRouteModule = { ...@@ -60,8 +59,9 @@ const permission: AppRouteModule = {
], ],
}, },
{ {
path: '/back', path: 'back',
name: 'PermissionBackDemo', name: 'PermissionBackDemo',
component: getParentLayout('PermissionBackDemo'),
meta: { meta: {
title: 'routes.demo.permission.back', title: 'routes.demo.permission.back',
}, },
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { LAYOUT } from '/@/router/constant';
const table: AppRouteModule = { const table: AppRouteModule = {
layout: { path: '/table',
path: '/table', name: 'TableDemo',
name: 'TableDemo', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/table/basic',
redirect: '/table/basic', meta: {
meta: { icon: 'ant-design:table-outlined',
icon: 'ant-design:table-outlined', title: 'routes.demo.table.table',
title: 'routes.demo.table.table',
},
}, },
routes: [ children: [
{ {
path: '/basic', path: 'basic',
name: 'TableBasicDemo', name: 'TableBasicDemo',
component: () => import('/@/views/demo/table/Basic.vue'), component: () => import('/@/views/demo/table/Basic.vue'),
meta: { meta: {
...@@ -24,7 +22,7 @@ const table: AppRouteModule = { ...@@ -24,7 +22,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/treeTable', path: 'treeTable',
name: 'TreeTableDemo', name: 'TreeTableDemo',
component: () => import('/@/views/demo/table/TreeTable.vue'), component: () => import('/@/views/demo/table/TreeTable.vue'),
meta: { meta: {
...@@ -32,7 +30,7 @@ const table: AppRouteModule = { ...@@ -32,7 +30,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/fetchTable', path: 'fetchTable',
name: 'FetchTableDemo', name: 'FetchTableDemo',
component: () => import('/@/views/demo/table/FetchTable.vue'), component: () => import('/@/views/demo/table/FetchTable.vue'),
meta: { meta: {
...@@ -40,7 +38,7 @@ const table: AppRouteModule = { ...@@ -40,7 +38,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/fixedColumn', path: 'fixedColumn',
name: 'FixedColumnDemo', name: 'FixedColumnDemo',
component: () => import('/@/views/demo/table/FixedColumn.vue'), component: () => import('/@/views/demo/table/FixedColumn.vue'),
meta: { meta: {
...@@ -48,7 +46,7 @@ const table: AppRouteModule = { ...@@ -48,7 +46,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/customerCell', path: 'customerCell',
name: 'CustomerCellDemo', name: 'CustomerCellDemo',
component: () => import('/@/views/demo/table/CustomerCell.vue'), component: () => import('/@/views/demo/table/CustomerCell.vue'),
meta: { meta: {
...@@ -56,7 +54,7 @@ const table: AppRouteModule = { ...@@ -56,7 +54,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/formTable', path: 'formTable',
name: 'FormTableDemo', name: 'FormTableDemo',
component: () => import('/@/views/demo/table/FormTable.vue'), component: () => import('/@/views/demo/table/FormTable.vue'),
meta: { meta: {
...@@ -64,7 +62,7 @@ const table: AppRouteModule = { ...@@ -64,7 +62,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/useTable', path: 'useTable',
name: 'UseTableDemo', name: 'UseTableDemo',
component: () => import('/@/views/demo/table/UseTable.vue'), component: () => import('/@/views/demo/table/UseTable.vue'),
meta: { meta: {
...@@ -72,7 +70,7 @@ const table: AppRouteModule = { ...@@ -72,7 +70,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/refTable', path: 'refTable',
name: 'RefTableDemo', name: 'RefTableDemo',
component: () => import('/@/views/demo/table/RefTable.vue'), component: () => import('/@/views/demo/table/RefTable.vue'),
meta: { meta: {
...@@ -80,7 +78,7 @@ const table: AppRouteModule = { ...@@ -80,7 +78,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/multipleHeader', path: 'multipleHeader',
name: 'MultipleHeaderDemo', name: 'MultipleHeaderDemo',
component: () => import('/@/views/demo/table/MultipleHeader.vue'), component: () => import('/@/views/demo/table/MultipleHeader.vue'),
meta: { meta: {
...@@ -88,7 +86,7 @@ const table: AppRouteModule = { ...@@ -88,7 +86,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/mergeHeader', path: 'mergeHeader',
name: 'MergeHeaderDemo', name: 'MergeHeaderDemo',
component: () => import('/@/views/demo/table/MergeHeader.vue'), component: () => import('/@/views/demo/table/MergeHeader.vue'),
meta: { meta: {
...@@ -96,7 +94,7 @@ const table: AppRouteModule = { ...@@ -96,7 +94,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/expandTable', path: 'expandTable',
name: 'ExpandTableDemo', name: 'ExpandTableDemo',
component: () => import('/@/views/demo/table/ExpandTable.vue'), component: () => import('/@/views/demo/table/ExpandTable.vue'),
meta: { meta: {
...@@ -104,7 +102,7 @@ const table: AppRouteModule = { ...@@ -104,7 +102,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/fixedHeight', path: 'fixedHeight',
name: 'FixedHeightDemo', name: 'FixedHeightDemo',
component: () => import('/@/views/demo/table/FixedHeight.vue'), component: () => import('/@/views/demo/table/FixedHeight.vue'),
meta: { meta: {
...@@ -112,7 +110,7 @@ const table: AppRouteModule = { ...@@ -112,7 +110,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/footerTable', path: 'footerTable',
name: 'FooterTableDemo', name: 'FooterTableDemo',
component: () => import('/@/views/demo/table/FooterTable.vue'), component: () => import('/@/views/demo/table/FooterTable.vue'),
meta: { meta: {
...@@ -120,7 +118,7 @@ const table: AppRouteModule = { ...@@ -120,7 +118,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/editCellTable', path: 'editCellTable',
name: 'EditCellTableDemo', name: 'EditCellTableDemo',
component: () => import('/@/views/demo/table/EditCellTable.vue'), component: () => import('/@/views/demo/table/EditCellTable.vue'),
meta: { meta: {
...@@ -128,7 +126,7 @@ const table: AppRouteModule = { ...@@ -128,7 +126,7 @@ const table: AppRouteModule = {
}, },
}, },
{ {
path: '/editRowTable', path: 'editRowTable',
name: 'EditRowTableDemo', name: 'EditRowTableDemo',
component: () => import('/@/views/demo/table/EditRowTable.vue'), component: () => import('/@/views/demo/table/EditRowTable.vue'),
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; import { LAYOUT } from '/@/router/constant';
const tree: AppRouteModule = { const tree: AppRouteModule = {
layout: { path: '/tree',
path: '/tree', name: 'TreeDemo',
name: 'TreeDemo', component: LAYOUT,
component: PAGE_LAYOUT_COMPONENT, redirect: '/tree/basic',
redirect: '/tree/basic', meta: {
meta: { icon: 'clarity:tree-view-line',
icon: 'clarity:tree-view-line', title: 'routes.demo.tree.tree',
title: 'routes.demo.tree.tree',
},
}, },
routes: [ children: [
{ {
path: '/basic', path: 'basic',
name: 'BasicTreeDemo', name: 'BasicTreeDemo',
component: () => import('/@/views/demo/tree/index.vue'), component: () => import('/@/views/demo/tree/index.vue'),
meta: { meta: {
...@@ -23,7 +21,7 @@ const tree: AppRouteModule = { ...@@ -23,7 +21,7 @@ const tree: AppRouteModule = {
}, },
}, },
{ {
path: '/editTree', path: 'editTree',
name: 'EditTreeDemo', name: 'EditTreeDemo',
component: () => import('/@/views/demo/tree/EditTree.vue'), component: () => import('/@/views/demo/tree/EditTree.vue'),
meta: { meta: {
...@@ -31,7 +29,7 @@ const tree: AppRouteModule = { ...@@ -31,7 +29,7 @@ const tree: AppRouteModule = {
}, },
}, },
{ {
path: '/actionTree', path: 'actionTree',
name: 'ActionTreeDemo', name: 'ActionTreeDemo',
component: () => import('/@/views/demo/tree/ActionTree.vue'), component: () => import('/@/views/demo/tree/ActionTree.vue'),
meta: { meta: {
......
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
const dashboard: AppRouteModule = {
path: '/home',
name: 'Home',
component: LAYOUT,
redirect: '/home/welcome',
meta: {
icon: 'ant-design:home-outlined',
title: 'routes.dashboard.welcome',
},
children: [
{
path: 'welcome',
name: 'Welcome',
component: () => import('/@/views/dashboard/welcome/index.vue'),
meta: {
title: 'routes.dashboard.welcome',
affix: true,
icon: 'ant-design:home-outlined',
},
},
],
};
export default dashboard;
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import { RoleEnum } from '/@/enums/roleEnum'; import { RoleEnum } from '/@/enums/roleEnum';
import Component from '/@/components/types';
export interface RouteMeta { export interface RouteMeta {
// title // title
title: string; title: string;
...@@ -24,24 +25,23 @@ export interface RouteMeta { ...@@ -24,24 +25,23 @@ export interface RouteMeta {
// Whether the route has been dynamically added // Whether the route has been dynamically added
hideBreadcrumb?: boolean; hideBreadcrumb?: boolean;
// disabled redirect
disabledRedirect?: boolean;
// close loading // close loading
afterCloseLoading?: boolean; afterCloseLoading?: boolean;
// Is it in the tab // Is it in the tab
inTab?: boolean; inTab?: boolean;
// Carrying parameters // Carrying parameters
carryParam?: boolean; carryParam?: boolean;
single?: boolean;
} }
export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> { export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
name: string; name: string;
meta: RouteMeta; meta: RouteMeta;
component?: any; component?: Component;
components?: any; components?: Component;
children?: AppRouteRecordRaw[]; children?: AppRouteRecordRaw[];
props?: any; props?: Record<string, any>;
fullPath?: string; fullPath?: string;
} }
export interface MenuTag { export interface MenuTag {
...@@ -75,11 +75,12 @@ export interface MenuModule { ...@@ -75,11 +75,12 @@ export interface MenuModule {
menu: Menu; menu: Menu;
} }
interface RouteModule { // interface RouteModule {
layout: AppRouteRecordRaw; // layout: AppRouteRecordRaw;
routes: AppRouteRecordRaw[]; // routes: AppRouteRecordRaw[];
children?: AppRouteRecordRaw[]; // children?: AppRouteRecordRaw[];
component?: any; // component?: Component;
} // }
export type AppRouteModule = RouteModule | AppRouteRecordRaw; // export type AppRouteModule = RouteModule | AppRouteRecordRaw;
export type AppRouteModule = AppRouteRecordRaw;
import { REDIRECT_ROUTE } from '/@/router/constant';
import type { AppRouteRecordRaw, Menu } from '/@/router/types'; import type { AppRouteRecordRaw, Menu } from '/@/router/types';
import store from '/@/store/index'; import store from '/@/store/index';
import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
...@@ -15,15 +14,13 @@ import { filter } from '/@/utils/helper/treeHelper'; ...@@ -15,15 +14,13 @@ import { filter } from '/@/utils/helper/treeHelper';
import { toRaw } from 'vue'; import { toRaw } from 'vue';
import { getMenuListById } from '/@/api/sys/menu'; import { getMenuListById } from '/@/api/sys/menu';
import { genRouteModule, transformObjToRoute } from '/@/utils/helper/routeHelper'; import { transformObjToRoute } from '/@/router/helper/routeHelper';
import { transformRouteToMenu } from '/@/utils/helper/menuHelper'; import { transformRouteToMenu } from '/@/router/helper/menuHelper';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
// import { warn } from '/@/utils/log'; // import { warn } from '/@/utils/log';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const { createMessage } = useMessage(); const { createMessage } = useMessage();
const NAME = 'permission'; const NAME = 'permission';
hotModuleUnregisterModule(NAME); hotModuleUnregisterModule(NAME);
...@@ -87,6 +84,7 @@ class Permission extends VuexModule { ...@@ -87,6 +84,7 @@ class Permission extends VuexModule {
@Action @Action
async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> { async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> {
const { t } = useI18n();
let routes: AppRouteRecordRaw[] = []; let routes: AppRouteRecordRaw[] = [];
const roleList = toRaw(userStore.getRoleListState); const roleList = toRaw(userStore.getRoleListState);
...@@ -95,17 +93,15 @@ class Permission extends VuexModule { ...@@ -95,17 +93,15 @@ class Permission extends VuexModule {
// role permissions // role permissions
if (permissionMode === PermissionModeEnum.ROLE) { if (permissionMode === PermissionModeEnum.ROLE) {
routes = filter(asyncRoutes, (route) => { routes = filter(asyncRoutes, (route) => {
const { meta } = route; const { meta } = route as AppRouteRecordRaw;
const { roles } = meta!; const { roles } = meta || {};
if (!roles) return true; if (!roles) return true;
return roleList.some((role) => roles.includes(role)); return roleList.some((role) => roles.includes(role));
}); });
// 如果确定不需要做后台动态权限,请将下面整个判断注释 // 如果确定不需要做后台动态权限,请将下面整个判断注释
} else if (permissionMode === PermissionModeEnum.BACK) { } else if (permissionMode === PermissionModeEnum.BACK) {
const messageKey = 'loadMenu';
createMessage.loading({ createMessage.loading({
content: t('sys.app.menuLoading'), content: t('sys.app.menuLoading'),
key: messageKey,
duration: 1, duration: 1,
}); });
// 这里获取后台路由菜单逻辑自行修改 // 这里获取后台路由菜单逻辑自行修改
...@@ -118,10 +114,10 @@ class Permission extends VuexModule { ...@@ -118,10 +114,10 @@ class Permission extends VuexModule {
routeList = transformObjToRoute(routeList); routeList = transformObjToRoute(routeList);
// 后台路由转菜单结构 // 后台路由转菜单结构
const backMenuList = transformRouteToMenu(routeList); const backMenuList = transformRouteToMenu(routeList);
this.commitBackMenuListState(backMenuList); this.commitBackMenuListState(backMenuList);
// 生成路由
routes = genRouteModule(routeList) as AppRouteRecordRaw[]; routes = routeList;
routes.push(REDIRECT_ROUTE);
} }
return routes; return routes;
} }
......
// The content here is just for type approval. The actual file content is overwritten by transform
export default function (id: string) {
const dynamicImportModule: any = id;
return dynamicImportModule;
}
import type { AppRouteModule, AppRouteRecordRaw, RouteModule } from '/@/router/types';
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
import { createRouter, createWebHashHistory } from 'vue-router';
import { appStore } from '/@/store/modules/app';
import { tabStore } from '/@/store/modules/tab';
import { toRaw } from 'vue';
import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant';
// import { isDevMode } from '/@/utils/env';
import dynamicImport from './dynamicImport';
import { omit } from 'lodash-es';
let currentTo: RouteLocationNormalized | null = null;
export function getCurrentTo() {
return currentTo;
}
export function setCurrentTo(to: RouteLocationNormalized) {
currentTo = to;
}
// 转化路由模块
// 将多级转成2层。keepAlive问题
export function genRouteModule(moduleList: AppRouteModule[] | AppRouteRecordRaw[]) {
const ret: AppRouteRecordRaw[] = [];
for (const routeMod of moduleList) {
let routes: RouteRecordRaw[] = [];
let layout: AppRouteRecordRaw | undefined;
if (Reflect.has(routeMod, 'routes')) {
routes = (routeMod as RouteModule).routes as any;
layout = (routeMod as RouteModule).layout;
} else if (Reflect.has(routeMod, 'path')) {
layout = omit(routeMod, 'children') as any;
routes = (routeMod.children as RouteRecordRaw[]) || ([] as RouteRecordRaw[]);
}
const router = createRouter({ routes, history: createWebHashHistory() });
const flatList = (toRaw(router.getRoutes()).filter(
(item) => item.children.length === 0
) as unknown) as AppRouteRecordRaw[];
flatList.forEach((item) => {
item.path = `${layout ? layout.path : ''}${item.path}`;
});
if (layout) {
layout.children = flatList;
ret.push(layout);
} else {
ret.push(...flatList);
}
}
return ret as RouteRecordRaw[];
}
// 动态引入
function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
if (!routes) return;
routes.forEach((item) => {
const { component } = item;
const { children } = item;
if (component) {
item.component = dynamicImport(component);
}
children && asyncImportRoute(children);
});
}
function getLayoutComp(comp: string) {
return comp === 'PAGE_LAYOUT' ? PAGE_LAYOUT_COMPONENT : '';
}
// 将后台对象转成路由对象
export function transformObjToRoute<T = any>(routeList: AppRouteModule[]): T[] {
routeList.forEach((route) => {
asyncImportRoute(
Reflect.has(route, 'routes') ? (route as RouteModule).routes : route.children || []
);
if ((route as RouteModule).layout) {
(route as RouteModule).layout.component = getLayoutComp(
(route as RouteModule).layout.component
);
} else {
route.component = getLayoutComp(route.component);
(route as RouteModule).layout = omit(route, 'children') as any;
}
});
return (routeList as unknown) as T[];
}
//
export function getIsOpenTab(toPath: string) {
const { openKeepAlive, multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
if (show && openKeepAlive) {
const tabList = tabStore.getTabsState;
return tabList.some((tab) => tab.path === toPath);
}
return false;
}
export function getParams(data: any = {}) {
const { params = {} } = data;
let ret = '';
Object.keys(params).forEach((key) => {
const p = params[key];
ret += `/${p}`;
});
return ret;
}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({ export default defineComponent({
name: 'Copy',
components: { CollapseContainer }, components: { CollapseContainer },
setup() { setup() {
const valueRef = ref(''); const valueRef = ref('');
......
<template>
<div class="p-5">
多层级缓存-页面1-1-1
<br />
<input />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({ name: 'Menu111Demo' });
</script>
<template>
<div class="p-5">
多层级缓存-页面1-2
<br />
<input />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({ name: 'Menu12Demo' });
</script>
<template>
<div class="p-5">
多层级缓存-页面2
<br />
<input />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Menu2Demo',
});
</script>
<template>
<div />
</template>
<script lang="ts"> <script lang="ts">
import { defineComponent, unref } from 'vue'; import { defineComponent, unref } from 'vue';
...@@ -18,12 +21,13 @@ ...@@ -18,12 +21,13 @@
path: '/' + _path, path: '/' + _path,
query, query,
}); });
// close loading
if (unref(getEnableTransition) && unref(getOpenPageLoading)) { if (unref(getEnableTransition) && unref(getOpenPageLoading)) {
setTimeout(() => { setTimeout(() => {
appStore.setPageLoadingAction(false); appStore.setPageLoadingAction(false);
}, 0); }, 0);
} }
return () => null; return {};
}, },
}); });
</script> </script>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论