feat: 回退页面完善

pull/48/MERGE
vben 2024-06-08 20:14:04 +08:00
parent 7bcd7746ca
commit 1d6b1f4926
31 changed files with 791 additions and 201 deletions

View File

@ -1,4 +1,4 @@
import type { RouteLocationNormalized, Router } from 'vue-router'; import type { Router } from 'vue-router';
import { LOGIN_PATH } from '@vben/constants'; import { LOGIN_PATH } from '@vben/constants';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
@ -9,7 +9,9 @@ import { useAccessStore } from '@vben-core/stores';
import { useTitle } from '@vueuse/core'; import { useTitle } from '@vueuse/core';
import { dynamicRoutes } from '@/router/routes'; import { dynamicRoutes, essentialsRouteNames } from '@/router/routes';
const forbiddenPage = () => import('@/views/_essential/fallback/forbidden.vue');
/** /**
* *
@ -56,18 +58,24 @@ function setupAccessGuard(router: Router) {
// accessToken 检查 // accessToken 检查
if (!accessToken) { if (!accessToken) {
if (to.path === '/') { if (
return loginPageMeta(to); // 基本路由,这些路由不需要进入权限拦截
} essentialsRouteNames.includes(to.name as string) ||
// 明确声明忽略权限访问权限,则可以访问
// 明确声明忽略权限访问权限,则可以访问 to.meta.ignoreAccess
if (to.meta.ignoreAccess) { ) {
return true; return true;
} }
// 没有访问权限,跳转登录页面 // 没有访问权限,跳转登录页面
if (to.fullPath !== LOGIN_PATH) { if (to.fullPath !== LOGIN_PATH) {
return loginPageMeta(to); return {
path: LOGIN_PATH,
// 如不需要,直接删除 query
query: { redirect: encodeURIComponent(to.fullPath) },
// 携带当前跳转的页面,登录后重新跳转该页面
replace: true,
};
} }
return to; return to;
} }
@ -82,7 +90,15 @@ function setupAccessGuard(router: Router) {
// 生成路由表 // 生成路由表
// 当前登录用户拥有的角色标识列表 // 当前登录用户拥有的角色标识列表
const userRoles = accessStore.getUserRoles; const userRoles = accessStore.getUserRoles;
const accessibleRoutes = await generatorRoutes(dynamicRoutes, userRoles);
const accessibleRoutes = await generatorRoutes(
dynamicRoutes,
userRoles,
// 如果 route.meta.menuVisibleWithForbidden = true
// 则会在菜单中显示但是访问会被重定向到403
// 这里可以指定403页面
forbiddenPage,
);
// 动态添加到router实例内 // 动态添加到router实例内
accessibleRoutes.forEach((route) => router.addRoute(route)); accessibleRoutes.forEach((route) => router.addRoute(route));
@ -101,20 +117,6 @@ function setupAccessGuard(router: Router) {
}); });
} }
/**
*
* @param to
*/
function loginPageMeta(to: RouteLocationNormalized) {
return {
path: LOGIN_PATH,
// 如不需要,直接删除 query
query: { redirect: encodeURIComponent(to.fullPath) },
// 携带当前跳转的页面,登录后重新跳转该页面
replace: true,
};
}
/** /**
* *
* @param router * @param router

View File

@ -14,15 +14,9 @@ const router = createRouter({
history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH), history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH),
// 应该添加到路由的初始路由列表。 // 应该添加到路由的初始路由列表。
routes, routes,
scrollBehavior: (_to, _from, savedPosition) => { scrollBehavior: () => ({ left: 0, top: 0 }),
// if (to.path !== from.path) { // 是否应该禁止尾部斜杠。默认为假
// const app = document.querySelector('#app'); // strict: true,
// if (app) {
// app.scrollTop = 0;
// }
// }
return savedPosition || { left: 0, top: 0 };
},
}); });
/** /**

View File

@ -1,13 +1,35 @@
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import { DEFAULT_HOME_PATH } from '@vben/constants';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { AuthPageLayoutType } from '@/layouts'; import { AuthPageLayoutType } from '@/layouts';
import Login from '@/views/_essential/authentication/login.vue'; import Login from '@/views/_essential/authentication/login.vue';
/** 全局404页面 */
const fallbackNotFoundRoute: RouteRecordRaw = {
component: () => import('@/views/_essential/fallback/not-found.vue'),
meta: {
hideInBreadcrumb: true,
hideInMenu: true,
hideInTab: true,
title: '404',
},
name: 'Fallback',
path: '/:path(.*)*',
};
/** 基本路由,这些路由是必须存在的 */ /** 基本路由,这些路由是必须存在的 */
const essentialsRoutes: RouteRecordRaw[] = [ const essentialsRoutes: RouteRecordRaw[] = [
{
meta: {
title: 'Root',
},
name: 'Root',
path: '/',
redirect: DEFAULT_HOME_PATH,
},
{ {
component: AuthPageLayoutType, component: AuthPageLayoutType,
meta: { meta: {
@ -21,8 +43,7 @@ const essentialsRoutes: RouteRecordRaw[] = [
path: 'login', path: 'login',
component: Login, component: Login,
meta: { meta: {
ignoreAccess: true, title: $t('page.essentials.login'),
title: $t('page.login'),
}, },
}, },
{ {
@ -31,8 +52,7 @@ const essentialsRoutes: RouteRecordRaw[] = [
component: () => component: () =>
import('@/views/_essential/authentication/code-login.vue'), import('@/views/_essential/authentication/code-login.vue'),
meta: { meta: {
ignoreAccess: true, title: $t('page.essentials.code-login'),
title: $t('page.code-login'),
}, },
}, },
{ {
@ -41,8 +61,7 @@ const essentialsRoutes: RouteRecordRaw[] = [
component: () => component: () =>
import('@/views/_essential/authentication/qrcode-login.vue'), import('@/views/_essential/authentication/qrcode-login.vue'),
meta: { meta: {
ignoreAccess: true, title: $t('page.essentials.qrcode-login'),
title: $t('page.qrcode-login'),
}, },
}, },
{ {
@ -51,8 +70,7 @@ const essentialsRoutes: RouteRecordRaw[] = [
component: () => component: () =>
import('@/views/_essential/authentication/forget-password.vue'), import('@/views/_essential/authentication/forget-password.vue'),
meta: { meta: {
ignoreAccess: true, title: $t('page.essentials.forget-password'),
title: $t('page.forget-password'),
}, },
}, },
{ {
@ -61,25 +79,11 @@ const essentialsRoutes: RouteRecordRaw[] = [
component: () => component: () =>
import('@/views/_essential/authentication/register.vue'), import('@/views/_essential/authentication/register.vue'),
meta: { meta: {
ignoreAccess: true, title: $t('page.essentials.register'),
title: $t('page.register'),
}, },
}, },
], ],
}, },
// 错误页
{
component: () => import('@/views/_essential/fallback/not-found.vue'),
meta: {
hideInBreadcrumb: true,
hideInMenu: true,
hideInTab: true,
// ignoreAccess: true,
title: 'Fallback',
},
name: 'Fallback',
path: '/:path(.*)*',
},
]; ];
export { essentialsRoutes }; export { essentialsRoutes, fallbackNotFoundRoute };

View File

@ -1,39 +0,0 @@
import type { RouteRecordRaw } from 'vue-router';
import { BasicLayout, IFrameView } from '@/layouts';
const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
title: '外部页面',
},
name: 'Outside',
path: '/outside',
redirect: '/outside/document',
children: [
{
name: 'Document',
path: 'document',
component: IFrameView,
meta: {
iframeSrc: 'https://doc.vvbin.cn/',
// keepAlive: true,
title: '项目文档',
},
},
{
name: 'IFrameView',
path: 'vue-document',
component: IFrameView,
meta: {
iframeSrc: 'https://cn.vuejs.org/',
keepAlive: true,
title: 'Vue 文档(缓存)',
},
},
],
},
];
export default routes;

View File

@ -1,29 +1,35 @@
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import { traverseTreeValues } from '@vben/utils';
import { mergeRouteModules } from '@vben-core/helpers'; import { mergeRouteModules } from '@vben-core/helpers';
import { essentialsRoutes } from './_essentials'; import { essentialsRoutes, fallbackNotFoundRoute } from './_essentials';
const dynamicRouteFiles = import.meta.glob('./dynamic/**/*.ts', { const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', {
eager: true, eager: true,
}); });
const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true }); // 有需要可以自行打开注释,并创建文件夹
// const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true });
const externalRouteFiles = import.meta.glob('./external/**/*.ts', {
eager: true,
});
/** 动态路由 */ /** 动态路由 */
const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles); const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
/** 静态路由列表,访问这些页面可以不需要权限 */ /** 静态路由列表,访问这些页面可以不需要权限 */
const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles); // const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles);
const staticRoutes: RouteRecordRaw[] = [];
/** 排除在主框架外的路由,这些路由没有菜单和顶部及其他框架内容 */
const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles);
/** 路由列表,由基本路由+静态路由组成 */ /** 路由列表,由基本路由+静态路由组成 */
const routes: RouteRecordRaw[] = [...essentialsRoutes, ...staticRoutes]; const routes: RouteRecordRaw[] = [
...essentialsRoutes,
...staticRoutes,
fallbackNotFoundRoute,
];
export { dynamicRoutes, externalRoutes, routes }; /** 基本路由列表,这些路由不需要进入权限拦截 */
const essentialsRouteNames = traverseTreeValues(
essentialsRoutes,
(route) => route.name,
);
export { dynamicRoutes, essentialsRouteNames, routes };

View File

@ -0,0 +1,49 @@
import type { RouteRecordRaw } from 'vue-router';
import { BasicLayout } from '@/layouts';
import { $t } from '@vben/locales/helper';
const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
icon: 'mdi:lightbulb-error-outline',
title: $t('page.fallback.page'),
},
name: 'FallbackLayout',
path: '/fallback',
redirect: '/fallback/403',
children: [
{
name: 'Fallback403',
path: '403',
component: () => import('@/views/_essential/fallback/forbidden.vue'),
meta: {
icon: 'mdi:do-not-disturb-alt',
title: '403',
},
},
{
name: 'Fallback404',
path: '404',
component: () => import('@/views/_essential/fallback/not-found.vue'),
meta: {
icon: 'mdi:table-off',
title: '404',
},
},
{
name: 'Fallback500',
path: '500',
component: () =>
import('@/views/_essential/fallback/internal-error.vue'),
meta: {
icon: 'mdi:server-network-off',
title: '500',
},
},
],
},
];
export default routes;

View File

@ -1,24 +1,29 @@
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import { BasicLayout } from '@/layouts'; import { BasicLayout } from '@/layouts';
import { $t } from '@vben/locales/helper';
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
{ {
component: BasicLayout, component: BasicLayout,
meta: { meta: {
icon: 'ic:round-menu',
keepAlive: true, keepAlive: true,
title: '多级菜单', order: 1000,
title: $t('page.nested.page'),
}, },
name: 'Nested', name: 'Nested',
path: '/nested', path: '/nested',
redirect: '/nested/menu1',
children: [ children: [
{ {
name: 'Menu1', name: 'Menu1',
path: 'menu1', path: 'menu1',
component: () => import('@/views/nested/menu-1.vue'), component: () => import('@/views/nested/menu-1.vue'),
meta: { meta: {
icon: 'ic:round-menu',
keepAlive: true, keepAlive: true,
title: '菜单1', title: $t('page.nested.menu1'),
}, },
}, },
{ {
@ -26,40 +31,47 @@ const routes: RouteRecordRaw[] = [
path: 'menu2', path: 'menu2',
component: () => import('@/views/nested/menu-2.vue'), component: () => import('@/views/nested/menu-2.vue'),
meta: { meta: {
icon: 'ic:round-menu',
keepAlive: true, keepAlive: true,
title: '菜单2', title: $t('page.nested.menu2'),
}, },
}, },
{ {
name: 'Menu3', name: 'Menu3',
path: 'menu3', path: 'menu3',
meta: { meta: {
title: '菜单3', icon: 'ic:round-menu',
title: $t('page.nested.menu3'),
}, },
redirect: '/nested/menu3/menu3-1',
children: [ children: [
{ {
name: 'Menu31', name: 'Menu31',
path: 'menu3-1', path: 'menu3-1',
component: () => import('@/views/nested/menu-3-1.vue'), component: () => import('@/views/nested/menu-3-1.vue'),
meta: { meta: {
icon: 'ic:round-menu',
keepAlive: true, keepAlive: true,
title: '菜单3-1', title: $t('page.nested.menu31'),
}, },
}, },
{ {
name: 'Menu32', name: 'Menu32',
path: 'menu3-2', path: 'menu3-2',
meta: { meta: {
title: '菜单3-2', icon: 'ic:round-menu',
title: $t('page.nested.menu32'),
}, },
redirect: '/nested/menu3/menu3-2/menu3-2-1',
children: [ children: [
{ {
name: 'Menu321', name: 'Menu321',
path: 'menu3-2-1', path: 'menu3-2-1',
component: () => import('@/views/nested/menu-3-2-1.vue'), component: () => import('@/views/nested/menu-3-2-1.vue'),
meta: { meta: {
icon: 'ic:round-menu',
keepAlive: true, keepAlive: true,
title: '菜单3-2-1', title: $t('page.nested.menu321'),
}, },
}, },
], ],

View File

@ -0,0 +1,85 @@
import type { RouteRecordRaw } from 'vue-router';
import { BasicLayout, IFrameView } from '@/layouts';
import { $t } from '@vben/locales/helper';
const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
icon: 'ic:round-settings-input-composite',
title: $t('page.outside.page'),
},
name: 'Outside',
path: '/outside',
redirect: '/outside/iframe',
children: [
{
name: 'iframe',
path: 'iframe',
meta: {
icon: 'mdi:newspaper-variant-outline',
title: $t('page.outside.embedded'),
},
redirect: '/outside/iframe/vue-document',
children: [
{
name: 'VueDocument',
path: 'vue-document',
component: IFrameView,
meta: {
icon: 'logos:vue',
iframeSrc: 'https://cn.vuejs.org/',
keepAlive: true,
title: 'Vue',
},
},
{
name: 'Tailwindcss',
path: 'tailwindcss',
component: IFrameView,
meta: {
icon: 'devicon:tailwindcss',
iframeSrc: 'https://tailwindcss.com/',
// keepAlive: true,
title: 'Tailwindcss',
},
},
],
},
{
name: 'ExternalLink',
path: 'external-link',
meta: {
icon: 'mdi:newspaper-variant-multiple-outline',
title: $t('page.outside.external-link'),
},
redirect: '/outside/external-link/vite',
children: [
{
name: 'Vite',
path: 'vite',
component: IFrameView,
meta: {
icon: 'logos:vitejs',
link: 'https://vitejs.dev/',
title: 'Vite',
},
},
{
name: 'VueUse',
path: 'vue-use',
component: IFrameView,
meta: {
icon: 'logos:vueuse',
link: 'https://vueuse.org',
title: 'VueUse',
},
},
],
},
],
},
];
export default routes;

View File

@ -1,7 +1,6 @@
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import { VBEN_GITHUB_URL } from '@vben/constants'; import { VBEN_GITHUB_URL, VBEN_LOGO } from '@vben/constants';
import { preferences } from '@vben-core/preferences';
import { BasicLayout, IFrameView } from '@/layouts'; import { BasicLayout, IFrameView } from '@/layouts';
import { $t } from '@vben/locales/helper'; import { $t } from '@vben/locales/helper';
@ -10,7 +9,8 @@ const routes: RouteRecordRaw[] = [
{ {
component: BasicLayout, component: BasicLayout,
meta: { meta: {
icon: preferences.logo.source, icon: VBEN_LOGO,
order: 9999,
title: 'Vben', title: 'Vben',
}, },
name: 'AboutLayout', name: 'AboutLayout',
@ -18,32 +18,32 @@ const routes: RouteRecordRaw[] = [
redirect: '/vben-admin/about', redirect: '/vben-admin/about',
children: [ children: [
{ {
name: 'About', name: 'VbenAbout',
path: 'about', path: 'about',
component: () => import('@/views/about/index.vue'), component: () => import('@/views/_essential/vben/about/index.vue'),
meta: { meta: {
icon: 'mdi:creative-commons', icon: 'mdi:creative-commons',
title: $t('page.about'), title: $t('page.vben.about'),
}, },
}, },
{ {
name: 'AboutDocument', name: 'VbenDocument',
path: 'document', path: 'document',
component: IFrameView, component: IFrameView,
meta: { meta: {
icon: 'mdi:flame-circle', icon: 'mdi:flame-circle',
iframeSrc: 'https://doc.vvbin.cn/', iframeSrc: 'https://doc.vvbin.cn/',
keepAlive: true, keepAlive: true,
title: $t('page.document'), title: $t('page.vben.document'),
}, },
}, },
{ {
name: 'Github', name: 'VbenGithub',
path: 'github', path: 'github',
component: IFrameView, component: IFrameView,
meta: { meta: {
icon: 'mdi:github', icon: 'mdi:github',
target: VBEN_GITHUB_URL, link: VBEN_GITHUB_URL,
title: 'Github', title: 'Github',
}, },
}, },

View File

@ -0,0 +1,7 @@
<script lang="ts" setup>
import { Fallback } from '@vben/common-ui';
</script>
<template>
<Fallback status="403" />
</template>

View File

@ -0,0 +1,7 @@
<script lang="ts" setup>
import { Fallback } from '@vben/common-ui';
</script>
<template>
<Fallback status="500" :show-back="false" />
</template>

View File

@ -3,5 +3,5 @@ import { Fallback } from '@vben/common-ui';
</script> </script>
<template> <template>
<Fallback /> <Fallback status="404" />
</template> </template>

View File

@ -23,6 +23,8 @@
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden;
pointer-events: none;
background-color: #f4f7f9; background-color: #f4f7f9;
} }

View File

@ -15,12 +15,14 @@
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden;
background-color: #f4f7f9; background-color: #f4f7f9;
/* transition: all 0.8s ease-out; */ /* transition: all 0.8s ease-out; */
} }
.loading.hidden { .loading.hidden {
pointer-events: none;
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
transition: all 0.6s ease-out; transition: all 0.6s ease-out;

View File

@ -30,8 +30,8 @@ async function generatorMenus(
badgeVariants, badgeVariants,
hideChildrenInMenu = false, hideChildrenInMenu = false,
icon, icon,
link,
order, order,
target,
title = '', title = '',
} = meta || {}; } = meta || {};
@ -50,7 +50,7 @@ async function generatorMenus(
}); });
} }
// 隐藏子菜单 // 隐藏子菜单
const resultPath = hideChildrenInMenu ? redirect || path : target || path; const resultPath = hideChildrenInMenu ? redirect || path : link || path;
return { return {
badge, badge,
badgeType, badgeType,

View File

@ -68,6 +68,10 @@ interface RouteMeta {
* KeepAlive * KeepAlive
*/ */
keepAlive?: boolean; keepAlive?: boolean;
/**
* -
*/
link?: string;
/** /**
* *
*/ */
@ -80,10 +84,6 @@ interface RouteMeta {
* -> * ->
*/ */
order?: number; order?: number;
/**
* -
*/
target?: string;
/** /**
* *

View File

@ -0,0 +1,31 @@
interface FallbackProps {
/**
*
*/
description?: string;
/**
* @zh_CN
* @default /
*/
homePath?: string;
/**
* @zh_CN
* @default pageNotFoundSvg
*/
image?: string;
/**
* @zh_CN
* @default true
*/
showBack?: boolean;
/**
* @zh_CN
*/
status?: '403' | '404' | '500';
/**
* @zh_CN
*/
title?: string;
}
export type { FallbackProps };

View File

@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { FallbackProps } from './fallback';
import { computed } from 'vue'; import { computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
@ -6,29 +8,11 @@ import { $t } from '@vben/locales';
import { IcRoundArrowBackIosNew } from '@vben-core/iconify'; import { IcRoundArrowBackIosNew } from '@vben-core/iconify';
import { VbenButton } from '@vben-core/shadcn-ui'; import { VbenButton } from '@vben-core/shadcn-ui';
import FeedbackIcon from './icons/fallback-icon.vue'; import Icon403 from './icons/icon-403.vue';
import Icon404 from './icons/icon-404.vue';
import Icon500 from './icons/icon-500.vue';
interface Props { interface Props extends FallbackProps {}
/**
* 描述
*/
description?: string;
/**
* @zh_CN 首页路由地址
* @default /
*/
homePath?: string;
/**
* @zh_CN 默认显示的图片
* @default pageNotFoundSvg
*/
image?: string;
/**
* @zh_CN 页面提示语
*/
title?: string;
}
defineOptions({ defineOptions({
name: 'Fallback', name: 'Fallback',
@ -38,15 +22,58 @@ const props = withDefaults(defineProps<Props>(), {
description: '', description: '',
homePath: '/', homePath: '/',
image: '', image: '',
showBack: true,
status: '404',
title: '', title: '',
}); });
const titleText = computed(() => { const titleText = computed(() => {
return props.title || $t('fallback.page-not-found'); if (props.title) {
return props.title;
}
switch (props.status) {
case '403': {
return $t('fallback.forbidden');
}
case '500': {
return $t('fallback.internal-error');
}
default: {
return $t('fallback.page-not-found');
}
}
}); });
const descText = computed(() => { const descText = computed(() => {
return props.description || $t('fallback.page-not-found-desc'); if (props.description) {
return props.description;
}
switch (props.status) {
case '403': {
return $t('fallback.forbidden-desc');
}
case '500': {
return $t('fallback.internal-error-desc');
}
default: {
return $t('fallback.page-not-found-desc');
}
}
});
const fallbackIcon = computed(() => {
switch (props.status) {
case '403': {
return Icon403;
}
case '500': {
return Icon500;
}
default: {
return Icon404;
}
}
}); });
const { push } = useRouter(); const { push } = useRouter();
@ -58,19 +85,23 @@ function back() {
</script> </script>
<template> <template>
<div <div class="flex size-full flex-col items-center justify-center duration-300">
class="animate-in zoom-in-50 flex h-screen w-full flex-col items-center justify-center duration-300"
>
<img v-if="image" :src="image" class="md:1/3 w-1/2 lg:w-1/4" /> <img v-if="image" :src="image" class="md:1/3 w-1/2 lg:w-1/4" />
<FeedbackIcon v-else class="md:1/3 h-1/3 w-1/2 lg:w-1/4" /> <component :is="fallbackIcon" v-else class="md:1/3 h-1/3 w-1/2 lg:w-1/4" />
<div class="flex-col-center"> <div class="flex-col-center">
<p class="text-foreground mt-12 text-3xl md:text-4xl lg:text-5xl"> <p
v-if="titleText"
class="text-foreground mt-12 text-3xl md:text-4xl lg:text-5xl"
>
{{ titleText }} {{ titleText }}
</p> </p>
<p class="text-muted-foreground my-8 md:text-lg lg:text-xl"> <p
v-if="descText"
class="text-muted-foreground md:text-md my-6 lg:text-lg"
>
{{ descText }} {{ descText }}
</p> </p>
<VbenButton size="lg" @click="back"> <VbenButton v-if="showBack" size="lg" @click="back">
<IcRoundArrowBackIosNew class="mr-2" /> <IcRoundArrowBackIosNew class="mr-2" />
{{ $t('common.back-to-home') }} {{ $t('common.back-to-home') }}
</VbenButton> </VbenButton>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -1 +1,2 @@
export type * from './fallback';
export { default as Fallback } from './fallback.vue'; export { default as Fallback } from './fallback.vue';

View File

@ -52,7 +52,6 @@ function getTransitionName(route: RouteLocationNormalizedLoaded) {
v-if="renderRouteView" v-if="renderRouteView"
v-show="!route.meta.iframeSrc" v-show="!route.meta.iframeSrc"
:key="route.fullPath" :key="route.fullPath"
class="h-[1000px]"
/> />
</KeepAlive> </KeepAlive>
<component :is="Component" v-else :key="route.fullPath" /> <component :is="Component" v-else :key="route.fullPath" />

View File

@ -4,8 +4,8 @@
const LOGIN_PATH = '/auth/login'; const LOGIN_PATH = '/auth/login';
/** /**
* @zh_CN * @zh_CN
*/ */
const LOGIN_NAME = 'Login'; const DEFAULT_HOME_PATH = '/welcome';
export { LOGIN_NAME, LOGIN_PATH }; export { DEFAULT_HOME_PATH, LOGIN_PATH };

View File

@ -3,4 +3,9 @@
*/ */
const VBEN_GITHUB_URL = 'https://github.com/vbenjs/vue-vben-admin'; const VBEN_GITHUB_URL = 'https://github.com/vbenjs/vue-vben-admin';
export { VBEN_GITHUB_URL }; /**
* @zh_CN Vben Logo
*/
const VBEN_LOGO =
'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.0/source/logo-v1.webp';
export { VBEN_GITHUB_URL, VBEN_LOGO };

View File

@ -15,8 +15,12 @@ layout:
align-right: Align Right align-right: Align Right
fallback: fallback:
page-not-found: Page Not Found page-not-found: Oops! Page Not Found
page-not-found-desc: Sorry, We couldn't find the page you are looking for. page-not-found-desc: Sorry, we couldn't find the page you were looking for.
forbidden: Oops! Access Denied
forbidden-desc: Sorry, but you don't have permission to access this page.
internal-error: Oops! Something Went Wrong
internal-error-desc: Sorry, but the server encountered an error.
widgets: widgets:
document: Document document: Document
@ -83,13 +87,29 @@ authentication:
third-party-login: Or continue with third-party-login: Or continue with
page: page:
about: About essentials:
document: Document login: Login
login: Login register: Register
register: Register code-login: Code Login
code-login: Code Login qrcode-login: Qrcode Login
qrcode-login: Qrcode Login forget-password: Forget Password
forget-password: Forget Password vben:
about: About
document: Document
outside:
page: External Page
embedded: embedded Page
external-link: External Link
nested:
page: Nested Menu
menu1: Menu 1
menu2: Menu 2
menu3: Menu 3
menu31: Menu 3-1
menu32: Menu 3-2
menu321: Menu 3-2-11
fallback:
page: Exception Page
preference: preference:
preferences: Preferences preferences: Preferences

View File

@ -14,8 +14,12 @@ layout:
align-right: 居右 align-right: 居右
fallback: fallback:
page-not-found: 未找到页面 page-not-found: 哎呀!未找到页面
page-not-found-desc: 抱歉,我们找不到您要找的页面。 page-not-found-desc: 抱歉,我们无法找到您要找的页面。
forbidden: 哎呀!访问被拒绝
forbidden-desc: 抱歉,您没有权限访问此页面。
internal-error: 哎呀!出错了
internal-error-desc: 抱歉,服务器遇到错误。
widgets: widgets:
document: 文档 document: 文档
@ -82,13 +86,29 @@ authentication:
third-party-login: 其他登录方式 third-party-login: 其他登录方式
page: page:
about: 关于 essentials:
document: 文档 login: 登陆
login: 登陆 register: 注册
register: 注册 code-login: 验证码登陆
code-login: 验证码登陆 qrcode-login: 二维码登陆
qrcode-login: 二维码登陆 forget-password: 忘记密码
forget-password: 忘记密码 vben:
about: 关于
document: 文档
outside:
page: 外部页面
embedded: 内嵌
external-link: 外链
nested:
page: 嵌套菜单
menu1: 菜单1
menu2: 菜单2
menu3: 菜单3
menu31: 菜单31
menu32: 菜单32
menu321: 菜单321
fallback:
page: 异常页面
preference: preference:
preferences: 偏好设置 preferences: 偏好设置