diff --git a/apps/backend-mock/utils/mock-data.ts b/apps/backend-mock/utils/mock-data.ts index 057588e36..3967b906c 100644 --- a/apps/backend-mock/utils/mock-data.ts +++ b/apps/backend-mock/utils/mock-data.ts @@ -53,7 +53,6 @@ export const MOCK_CODES = [ const dashboardMenus = [ { - component: 'BasicLayout', meta: { order: -1, title: 'page.dashboard.title', @@ -116,7 +115,6 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => { return [ { - component: 'BasicLayout', meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, diff --git a/apps/web-antd/src/router/routes/core.ts b/apps/web-antd/src/router/routes/core.ts index fe030a9a2..1d0a07fad 100644 --- a/apps/web-antd/src/router/routes/core.ts +++ b/apps/web-antd/src/router/routes/core.ts @@ -2,7 +2,7 @@ import type { RouteRecordRaw } from 'vue-router'; import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; -import { AuthPageLayout } from '#/layouts'; +import { AuthPageLayout, BasicLayout } from '#/layouts'; import { $t } from '#/locales'; import Login from '#/views/_core/authentication/login.vue'; @@ -21,13 +21,23 @@ const fallbackNotFoundRoute: RouteRecordRaw = { /** 基本路由,这些路由是必须存在的 */ const coreRoutes: RouteRecordRaw[] = [ + /** + * 根路由 + * 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。 + * 此路由必须存在,且不应修改 + */ { + component: BasicLayout, meta: { + hideInBreadcrumb: true, + hideInMenu: true, + hideInTab: true, title: 'Root', }, name: 'Root', path: '/', redirect: DEFAULT_HOME_PATH, + children: [], }, { component: AuthPageLayout, diff --git a/apps/web-antd/src/router/routes/modules/dashboard.ts b/apps/web-antd/src/router/routes/modules/dashboard.ts index 1bddab9db..f3a952b08 100644 --- a/apps/web-antd/src/router/routes/modules/dashboard.ts +++ b/apps/web-antd/src/router/routes/modules/dashboard.ts @@ -1,11 +1,9 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'lucide:layout-dashboard', order: -1, diff --git a/apps/web-antd/src/router/routes/modules/demos.ts b/apps/web-antd/src/router/routes/modules/demos.ts index 32bb338e0..55ade09c9 100644 --- a/apps/web-antd/src/router/routes/modules/demos.ts +++ b/apps/web-antd/src/router/routes/modules/demos.ts @@ -1,11 +1,9 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, diff --git a/apps/web-antd/src/router/routes/modules/vben.ts b/apps/web-antd/src/router/routes/modules/vben.ts index 210e8610f..98acf5821 100644 --- a/apps/web-antd/src/router/routes/modules/vben.ts +++ b/apps/web-antd/src/router/routes/modules/vben.ts @@ -8,30 +8,20 @@ import { VBEN_NAIVE_PREVIEW_URL, } from '@vben/constants'; -import { BasicLayout, IFrameView } from '#/layouts'; +import { IFrameView } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { badgeType: 'dot', icon: VBEN_LOGO_URL, - order: 9999, + order: 9998, title: $t('demos.vben.title'), }, name: 'VbenProject', path: '/vben-admin', children: [ - { - name: 'VbenAbout', - path: '/vben-admin/about', - component: () => import('#/views/_core/about/index.vue'), - meta: { - icon: 'lucide:copyright', - title: $t('demos.vben.about'), - }, - }, { name: 'VbenDocument', path: '/vben-admin/document', @@ -76,6 +66,16 @@ const routes: RouteRecordRaw[] = [ }, ], }, + { + name: 'VbenAbout', + path: '/vben-admin/about', + component: () => import('#/views/_core/about/index.vue'), + meta: { + icon: 'lucide:copyright', + title: $t('demos.vben.about'), + order: 9999, + }, + }, ]; export default routes; diff --git a/apps/web-ele/src/router/routes/core.ts b/apps/web-ele/src/router/routes/core.ts index fe030a9a2..1d0a07fad 100644 --- a/apps/web-ele/src/router/routes/core.ts +++ b/apps/web-ele/src/router/routes/core.ts @@ -2,7 +2,7 @@ import type { RouteRecordRaw } from 'vue-router'; import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; -import { AuthPageLayout } from '#/layouts'; +import { AuthPageLayout, BasicLayout } from '#/layouts'; import { $t } from '#/locales'; import Login from '#/views/_core/authentication/login.vue'; @@ -21,13 +21,23 @@ const fallbackNotFoundRoute: RouteRecordRaw = { /** 基本路由,这些路由是必须存在的 */ const coreRoutes: RouteRecordRaw[] = [ + /** + * 根路由 + * 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。 + * 此路由必须存在,且不应修改 + */ { + component: BasicLayout, meta: { + hideInBreadcrumb: true, + hideInMenu: true, + hideInTab: true, title: 'Root', }, name: 'Root', path: '/', redirect: DEFAULT_HOME_PATH, + children: [], }, { component: AuthPageLayout, diff --git a/apps/web-ele/src/router/routes/modules/dashboard.ts b/apps/web-ele/src/router/routes/modules/dashboard.ts index 1bddab9db..f3a952b08 100644 --- a/apps/web-ele/src/router/routes/modules/dashboard.ts +++ b/apps/web-ele/src/router/routes/modules/dashboard.ts @@ -1,11 +1,9 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'lucide:layout-dashboard', order: -1, diff --git a/apps/web-ele/src/router/routes/modules/demos.ts b/apps/web-ele/src/router/routes/modules/demos.ts index 90cc2f11c..907ea3f45 100644 --- a/apps/web-ele/src/router/routes/modules/demos.ts +++ b/apps/web-ele/src/router/routes/modules/demos.ts @@ -1,11 +1,9 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, diff --git a/apps/web-ele/src/router/routes/modules/vben.ts b/apps/web-ele/src/router/routes/modules/vben.ts index 2cfef5498..20fbc96c0 100644 --- a/apps/web-ele/src/router/routes/modules/vben.ts +++ b/apps/web-ele/src/router/routes/modules/vben.ts @@ -9,30 +9,20 @@ import { } from '@vben/constants'; import { SvgAntdvLogoIcon } from '@vben/icons'; -import { BasicLayout, IFrameView } from '#/layouts'; +import { IFrameView } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { badgeType: 'dot', icon: VBEN_LOGO_URL, - order: 9999, + order: 9998, title: $t('demos.vben.title'), }, name: 'VbenProject', path: '/vben-admin', children: [ - { - name: 'VbenAbout', - path: '/vben-admin/about', - component: () => import('#/views/_core/about/index.vue'), - meta: { - icon: 'lucide:copyright', - title: $t('demos.vben.about'), - }, - }, { name: 'VbenDocument', path: '/vben-admin/document', @@ -77,6 +67,16 @@ const routes: RouteRecordRaw[] = [ }, ], }, + { + name: 'VbenAbout', + path: '/vben-admin/about', + component: () => import('#/views/_core/about/index.vue'), + meta: { + icon: 'lucide:copyright', + title: $t('demos.vben.about'), + order: 9999, + }, + }, ]; export default routes; diff --git a/apps/web-naive/src/router/routes/core.ts b/apps/web-naive/src/router/routes/core.ts index fe030a9a2..1d0a07fad 100644 --- a/apps/web-naive/src/router/routes/core.ts +++ b/apps/web-naive/src/router/routes/core.ts @@ -2,7 +2,7 @@ import type { RouteRecordRaw } from 'vue-router'; import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; -import { AuthPageLayout } from '#/layouts'; +import { AuthPageLayout, BasicLayout } from '#/layouts'; import { $t } from '#/locales'; import Login from '#/views/_core/authentication/login.vue'; @@ -21,13 +21,23 @@ const fallbackNotFoundRoute: RouteRecordRaw = { /** 基本路由,这些路由是必须存在的 */ const coreRoutes: RouteRecordRaw[] = [ + /** + * 根路由 + * 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。 + * 此路由必须存在,且不应修改 + */ { + component: BasicLayout, meta: { + hideInBreadcrumb: true, + hideInMenu: true, + hideInTab: true, title: 'Root', }, name: 'Root', path: '/', redirect: DEFAULT_HOME_PATH, + children: [], }, { component: AuthPageLayout, diff --git a/apps/web-naive/src/router/routes/modules/dashboard.ts b/apps/web-naive/src/router/routes/modules/dashboard.ts index 1bddab9db..f3a952b08 100644 --- a/apps/web-naive/src/router/routes/modules/dashboard.ts +++ b/apps/web-naive/src/router/routes/modules/dashboard.ts @@ -1,11 +1,9 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'lucide:layout-dashboard', order: -1, diff --git a/apps/web-naive/src/router/routes/modules/demos.ts b/apps/web-naive/src/router/routes/modules/demos.ts index d0631cb53..5e49ffa01 100644 --- a/apps/web-naive/src/router/routes/modules/demos.ts +++ b/apps/web-naive/src/router/routes/modules/demos.ts @@ -1,11 +1,9 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, diff --git a/apps/web-naive/src/router/routes/modules/vben.ts b/apps/web-naive/src/router/routes/modules/vben.ts index 44461a7fd..169de855b 100644 --- a/apps/web-naive/src/router/routes/modules/vben.ts +++ b/apps/web-naive/src/router/routes/modules/vben.ts @@ -9,30 +9,20 @@ import { } from '@vben/constants'; import { SvgAntdvLogoIcon } from '@vben/icons'; -import { BasicLayout, IFrameView } from '#/layouts'; +import { IFrameView } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { badgeType: 'dot', icon: VBEN_LOGO_URL, - order: 9999, + order: 9998, title: $t('demos.vben.title'), }, name: 'VbenProject', path: '/vben-admin', children: [ - { - name: 'VbenAbout', - path: '/vben-admin/about', - component: () => import('#/views/_core/about/index.vue'), - meta: { - icon: 'lucide:copyright', - title: $t('demos.vben.about'), - }, - }, { name: 'VbenDocument', path: '/vben-admin/document', @@ -77,6 +67,16 @@ const routes: RouteRecordRaw[] = [ }, ], }, + { + name: 'VbenAbout', + path: '/vben-admin/about', + component: () => import('#/views/_core/about/index.vue'), + meta: { + icon: 'lucide:copyright', + title: $t('demos.vben.about'), + order: 9999, + }, + }, ]; export default routes; diff --git a/docs/src/en/guide/essentials/route.md b/docs/src/en/guide/essentials/route.md index bef40d691..8fb0a6d1c 100644 --- a/docs/src/en/guide/essentials/route.md +++ b/docs/src/en/guide/essentials/route.md @@ -73,7 +73,6 @@ import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { badgeType: 'dot', badgeVariants: 'destructive', @@ -124,7 +123,6 @@ import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, @@ -249,7 +247,6 @@ import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'mdi:home', title: $t('page.home.title'), diff --git a/docs/src/guide/essentials/route.md b/docs/src/guide/essentials/route.md index 0eb74aea4..d8a938dda 100644 --- a/docs/src/guide/essentials/route.md +++ b/docs/src/guide/essentials/route.md @@ -62,12 +62,10 @@ import type { RouteRecordRaw } from 'vue-router'; import { VBEN_LOGO_URL } from '@vben/constants'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { badgeType: 'dot', badgeVariants: 'destructive', @@ -103,7 +101,6 @@ export default routes; ::: tip -- 多级路由的父级路由无需设置 `component` 属性,只需设置 `children` 属性即可。除非你真的需要在父级路由嵌套下显示内容。 - 如果没有特殊情况,父级路由的 `redirect` 属性,不需要指定,默认会指向第一个子路由。 ::: @@ -113,12 +110,10 @@ export default routes; ```ts import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, @@ -238,12 +233,10 @@ import type { RouteRecordRaw } from 'vue-router'; import { VBEN_LOGO_URL } from '@vben/constants'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'mdi:home', title: $t('page.home.title'), @@ -400,6 +393,10 @@ interface RouteMeta { * 菜单可以看到,但是访问会被重定向到403 */ menuVisibleWithForbidden?: boolean; + /** + * 当前路由不使用基础布局(仅在顶级生效) + */ + noBasicLayout?: boolean; /** * 在新窗口打开 */ @@ -584,6 +581,13 @@ _注意:_ 排序仅针对一级菜单有效,二级菜单的排序需要在对 用于配置页面的菜单参数,会在菜单中传递给页面。 +### noBasicLayout + +- 类型:`boolean` +- 默认值:`false` + +用于配置当前路由不使用基础布局,仅在顶级时生效。默认情况下,所有的路由都会被包裹在基础布局中(包含顶部以及侧边等导航部件),如果你的页面不需要这些部件,可以设置 `noBasicLayout` 为 `true`。 + ## 路由刷新 路由刷新方式如下: diff --git a/packages/@core/base/typings/src/vue-router.d.ts b/packages/@core/base/typings/src/vue-router.d.ts index b5809f0ed..099e8171a 100644 --- a/packages/@core/base/typings/src/vue-router.d.ts +++ b/packages/@core/base/typings/src/vue-router.d.ts @@ -97,6 +97,10 @@ interface RouteMeta { * 菜单可以看到,但是访问会被重定向到403 */ menuVisibleWithForbidden?: boolean; + /** + * 不使用基础布局(仅在顶级生效) + */ + noBasicLayout?: boolean; /** * 在新窗口打开 */ diff --git a/packages/effects/access/src/accessible.ts b/packages/effects/access/src/accessible.ts index c951d92a5..960af3fbc 100644 --- a/packages/effects/access/src/accessible.ts +++ b/packages/effects/access/src/accessible.ts @@ -22,11 +22,29 @@ async function generateAccessible( // 生成路由 const accessibleRoutes = await generateRoutes(mode, options); + const root = router.getRoutes().find((item) => item.path === '/'); + // 动态添加到router实例内 accessibleRoutes.forEach((route) => { - router.addRoute(route); + if (root && !route.meta?.noBasicLayout) { + // 为了兼容之前的版本用法,如果包含子路由,则将component移除,以免出现多层BasicLayout + // 如果你的项目已经跟进了本次修改,移除了所有自定义菜单首级的BasicLayout,可以将这段if代码删除 + if (route.children && route.children.length > 0) { + delete route.component; + } + root.children?.push(route); + } else { + router.addRoute(route); + } }); + if (root) { + if (root.name) { + router.removeRoute(root.name); + } + router.addRoute(root); + } + // 生成菜单 const accessibleMenus = await generateMenus(accessibleRoutes, options.router); diff --git a/playground/src/router/routes/core.ts b/playground/src/router/routes/core.ts index fe030a9a2..1d0a07fad 100644 --- a/playground/src/router/routes/core.ts +++ b/playground/src/router/routes/core.ts @@ -2,7 +2,7 @@ import type { RouteRecordRaw } from 'vue-router'; import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; -import { AuthPageLayout } from '#/layouts'; +import { AuthPageLayout, BasicLayout } from '#/layouts'; import { $t } from '#/locales'; import Login from '#/views/_core/authentication/login.vue'; @@ -21,13 +21,23 @@ const fallbackNotFoundRoute: RouteRecordRaw = { /** 基本路由,这些路由是必须存在的 */ const coreRoutes: RouteRecordRaw[] = [ + /** + * 根路由 + * 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。 + * 此路由必须存在,且不应修改 + */ { + component: BasicLayout, meta: { + hideInBreadcrumb: true, + hideInMenu: true, + hideInTab: true, title: 'Root', }, name: 'Root', path: '/', redirect: DEFAULT_HOME_PATH, + children: [], }, { component: AuthPageLayout, diff --git a/playground/src/router/routes/modules/dashboard.ts b/playground/src/router/routes/modules/dashboard.ts index 1bddab9db..5254dc65d 100644 --- a/playground/src/router/routes/modules/dashboard.ts +++ b/playground/src/router/routes/modules/dashboard.ts @@ -1,18 +1,16 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'lucide:layout-dashboard', order: -1, title: $t('page.dashboard.title'), }, name: 'Dashboard', - path: '/', + path: '/dashboard', children: [ { name: 'Analytics', diff --git a/playground/src/router/routes/modules/demos.ts b/playground/src/router/routes/modules/demos.ts index 46ebeeb9f..422e5bf13 100644 --- a/playground/src/router/routes/modules/demos.ts +++ b/playground/src/router/routes/modules/demos.ts @@ -1,11 +1,10 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout, IFrameView } from '#/layouts'; +import { IFrameView } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'ic:baseline-view-in-ar', keepAlive: true, diff --git a/playground/src/router/routes/modules/examples.ts b/playground/src/router/routes/modules/examples.ts index 449bbc2c1..7287f0ddc 100644 --- a/playground/src/router/routes/modules/examples.ts +++ b/playground/src/router/routes/modules/examples.ts @@ -1,11 +1,9 @@ import type { RouteRecordRaw } from 'vue-router'; -import { BasicLayout } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { icon: 'ion:layers-outline', keepAlive: true, diff --git a/playground/src/router/routes/modules/vben.ts b/playground/src/router/routes/modules/vben.ts index 2f2eb6604..ec44d58ab 100644 --- a/playground/src/router/routes/modules/vben.ts +++ b/playground/src/router/routes/modules/vben.ts @@ -10,30 +10,20 @@ import { } from '@vben/constants'; import { SvgAntdvLogoIcon } from '@vben/icons'; -import { BasicLayout, IFrameView } from '#/layouts'; +import { IFrameView } from '#/layouts'; import { $t } from '#/locales'; const routes: RouteRecordRaw[] = [ { - component: BasicLayout, meta: { badgeType: 'dot', icon: VBEN_LOGO_URL, - order: 9999, + order: 9998, title: $t('demos.vben.title'), }, name: 'VbenProject', path: '/vben-admin', children: [ - { - name: 'VbenAbout', - path: '/vben-admin/about', - component: () => import('#/views/_core/about/index.vue'), - meta: { - icon: 'lucide:copyright', - title: $t('demos.vben.about'), - }, - }, { name: 'VbenDocument', path: '/vben-admin/document', @@ -89,6 +79,16 @@ const routes: RouteRecordRaw[] = [ }, ], }, + { + component: () => import('#/views/_core/about/index.vue'), + meta: { + icon: 'lucide:copyright', + order: 9999, + title: $t('demos.vben.about'), + }, + name: 'VbenAbout', + path: '/vben-admin/about', + }, ]; export default routes;