init
This commit is contained in:
42
web/playground/src/router/access.ts
Normal file
42
web/playground/src/router/access.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type {
|
||||
ComponentRecordType,
|
||||
GenerateMenuAndRoutesOptions,
|
||||
} from '@vben/types';
|
||||
|
||||
import { generateAccessible } from '@vben/access';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { getAllMenusApi } from '#/api';
|
||||
import { BasicLayout, IFrameView } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
|
||||
|
||||
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue');
|
||||
|
||||
const layoutMap: ComponentRecordType = {
|
||||
BasicLayout,
|
||||
IFrameView,
|
||||
};
|
||||
|
||||
return await generateAccessible(preferences.app.accessMode, {
|
||||
...options,
|
||||
fetchMenuListAsync: async () => {
|
||||
message.loading({
|
||||
content: `${$t('common.loadingMenu')}...`,
|
||||
duration: 1.5,
|
||||
});
|
||||
return await getAllMenusApi();
|
||||
},
|
||||
// 可以指定没有权限跳转403页面
|
||||
forbiddenComponent,
|
||||
// 如果 route.meta.menuVisibleWithForbidden = true
|
||||
layoutMap,
|
||||
pageMap,
|
||||
});
|
||||
}
|
||||
|
||||
export { generateAccess };
|
||||
136
web/playground/src/router/guard.ts
Normal file
136
web/playground/src/router/guard.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import type { Router } from 'vue-router';
|
||||
|
||||
import { LOGIN_PATH } from '@vben/constants';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||
import { startProgress, stopProgress } from '@vben/utils';
|
||||
|
||||
import { accessRoutes, coreRouteNames } from '#/router/routes';
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
import { generateAccess } from './access';
|
||||
|
||||
/**
|
||||
* 通用守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function setupCommonGuard(router: Router) {
|
||||
// 记录已经加载的页面
|
||||
const loadedPaths = new Set<string>();
|
||||
|
||||
router.beforeEach((to) => {
|
||||
to.meta.loaded = loadedPaths.has(to.path);
|
||||
|
||||
// 页面加载进度条
|
||||
if (!to.meta.loaded && preferences.transition.progress) {
|
||||
startProgress();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
router.afterEach((to) => {
|
||||
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
|
||||
loadedPaths.add(to.path);
|
||||
|
||||
// 关闭页面加载进度条
|
||||
if (preferences.transition.progress) {
|
||||
stopProgress();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限访问守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function setupAccessGuard(router: Router) {
|
||||
router.beforeEach(async (to, from) => {
|
||||
const accessStore = useAccessStore();
|
||||
const userStore = useUserStore();
|
||||
const authStore = useAuthStore();
|
||||
// 基本路由,这些路由不需要进入权限拦截
|
||||
if (coreRouteNames.includes(to.name as string)) {
|
||||
if (to.path === LOGIN_PATH && accessStore.accessToken) {
|
||||
return decodeURIComponent(
|
||||
(to.query?.redirect as string) ||
|
||||
userStore.userInfo?.homePath ||
|
||||
preferences.app.defaultHomePath,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// accessToken 检查
|
||||
if (!accessStore.accessToken) {
|
||||
// 明确声明忽略权限访问权限,则可以访问
|
||||
if (to.meta.ignoreAccess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 没有访问权限,跳转登录页面
|
||||
if (to.fullPath !== LOGIN_PATH) {
|
||||
return {
|
||||
path: LOGIN_PATH,
|
||||
// 如不需要,直接删除 query
|
||||
query:
|
||||
to.fullPath === preferences.app.defaultHomePath
|
||||
? {}
|
||||
: { redirect: encodeURIComponent(to.fullPath) },
|
||||
// 携带当前跳转的页面,登录后重新跳转该页面
|
||||
replace: true,
|
||||
};
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
// 是否已经生成过动态路由
|
||||
if (accessStore.isAccessChecked) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 生成路由表
|
||||
// 当前登录用户拥有的角色标识列表
|
||||
const userInfo = userStore.userInfo || (await authStore.fetchUserInfo());
|
||||
const userRoles = userInfo.roles ?? [];
|
||||
|
||||
// 生成菜单和路由
|
||||
const { accessibleMenus, accessibleRoutes } = await generateAccess({
|
||||
roles: userRoles,
|
||||
router,
|
||||
// 则会在菜单中显示,但是访问会被重定向到403
|
||||
routes: accessRoutes,
|
||||
});
|
||||
|
||||
// 保存菜单信息和路由信息
|
||||
accessStore.setAccessMenus(accessibleMenus);
|
||||
accessStore.setAccessRoutes(accessibleRoutes);
|
||||
accessStore.setIsAccessChecked(true);
|
||||
let redirectPath: string;
|
||||
if (from.query.redirect) {
|
||||
redirectPath = from.query.redirect as string;
|
||||
} else if (to.path === preferences.app.defaultHomePath) {
|
||||
redirectPath = preferences.app.defaultHomePath;
|
||||
} else if (userInfo.homePath && to.path === userInfo.homePath) {
|
||||
redirectPath = userInfo.homePath;
|
||||
} else {
|
||||
redirectPath = to.fullPath;
|
||||
}
|
||||
return {
|
||||
...router.resolve(decodeURIComponent(redirectPath)),
|
||||
replace: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function createRouterGuard(router: Router) {
|
||||
/** 通用 */
|
||||
setupCommonGuard(router);
|
||||
/** 权限访问 */
|
||||
setupAccessGuard(router);
|
||||
}
|
||||
|
||||
export { createRouterGuard };
|
||||
37
web/playground/src/router/index.ts
Normal file
37
web/playground/src/router/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
createRouter,
|
||||
createWebHashHistory,
|
||||
createWebHistory,
|
||||
} from 'vue-router';
|
||||
|
||||
import { resetStaticRoutes } from '@vben/utils';
|
||||
|
||||
import { createRouterGuard } from './guard';
|
||||
import { routes } from './routes';
|
||||
|
||||
/**
|
||||
* @zh_CN 创建vue-router实例
|
||||
*/
|
||||
const router = createRouter({
|
||||
history:
|
||||
import.meta.env.VITE_ROUTER_HISTORY === 'hash'
|
||||
? createWebHashHistory(import.meta.env.VITE_BASE)
|
||||
: createWebHistory(import.meta.env.VITE_BASE),
|
||||
// 应该添加到路由的初始路由列表。
|
||||
routes,
|
||||
scrollBehavior: (to, _from, savedPosition) => {
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
}
|
||||
return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 };
|
||||
},
|
||||
// 是否应该禁止尾部斜杠。
|
||||
// strict: true,
|
||||
});
|
||||
|
||||
const resetRoutes = () => resetStaticRoutes(router, routes);
|
||||
|
||||
// 创建路由守卫
|
||||
createRouterGuard(router);
|
||||
|
||||
export { resetRoutes, router };
|
||||
97
web/playground/src/router/routes/core.ts
Normal file
97
web/playground/src/router/routes/core.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { LOGIN_PATH } from '@vben/constants';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const BasicLayout = () => import('#/layouts/basic.vue');
|
||||
const AuthPageLayout = () => import('#/layouts/auth.vue');
|
||||
/** 全局404页面 */
|
||||
const fallbackNotFoundRoute: RouteRecordRaw = {
|
||||
component: () => import('#/views/_core/fallback/not-found.vue'),
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
title: '404',
|
||||
},
|
||||
name: 'FallbackNotFound',
|
||||
path: '/:path(.*)*',
|
||||
};
|
||||
|
||||
/** 基本路由,这些路由是必须存在的 */
|
||||
const coreRoutes: RouteRecordRaw[] = [
|
||||
/**
|
||||
* 根路由
|
||||
* 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。
|
||||
* 此路由必须存在,且不应修改
|
||||
*/
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
title: 'Root',
|
||||
},
|
||||
name: 'Root',
|
||||
path: '/',
|
||||
redirect: preferences.app.defaultHomePath,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
component: AuthPageLayout,
|
||||
meta: {
|
||||
hideInTab: true,
|
||||
title: 'Authentication',
|
||||
},
|
||||
name: 'Authentication',
|
||||
path: '/auth',
|
||||
redirect: LOGIN_PATH,
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
path: 'login',
|
||||
component: () => import('#/views/_core/authentication/login.vue'),
|
||||
meta: {
|
||||
title: $t('page.auth.login'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'CodeLogin',
|
||||
path: 'code-login',
|
||||
component: () => import('#/views/_core/authentication/code-login.vue'),
|
||||
meta: {
|
||||
title: $t('page.auth.codeLogin'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'QrCodeLogin',
|
||||
path: 'qrcode-login',
|
||||
component: () =>
|
||||
import('#/views/_core/authentication/qrcode-login.vue'),
|
||||
meta: {
|
||||
title: $t('page.auth.qrcodeLogin'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ForgetPassword',
|
||||
path: 'forget-password',
|
||||
component: () =>
|
||||
import('#/views/_core/authentication/forget-password.vue'),
|
||||
meta: {
|
||||
title: $t('page.auth.forgetPassword'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Register',
|
||||
path: 'register',
|
||||
component: () => import('#/views/_core/authentication/register.vue'),
|
||||
meta: {
|
||||
title: $t('page.auth.register'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export { coreRoutes, fallbackNotFoundRoute };
|
||||
47
web/playground/src/router/routes/index.ts
Normal file
47
web/playground/src/router/routes/index.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { mergeRouteModules, traverseTreeValues } from '@vben/utils';
|
||||
|
||||
import { coreRoutes, fallbackNotFoundRoute } from './core';
|
||||
|
||||
const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
// 有需要可以自行打开注释,并创建文件夹
|
||||
// const externalRouteFiles = import.meta.glob('./external/**/*.ts', { eager: true });
|
||||
// const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true });
|
||||
|
||||
/** 动态路由 */
|
||||
const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
|
||||
|
||||
/** 外部路由列表,访问这些页面可以不需要Layout,可能用于内嵌在别的系统(不会显示在菜单中) */
|
||||
// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles);
|
||||
// const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles);
|
||||
const staticRoutes: RouteRecordRaw[] = [];
|
||||
const externalRoutes: RouteRecordRaw[] = [];
|
||||
|
||||
/** 路由列表,由基本路由、外部路由和404兜底路由组成
|
||||
* 无需走权限验证(会一直显示在菜单中) */
|
||||
const routes: RouteRecordRaw[] = [
|
||||
...coreRoutes,
|
||||
...externalRoutes,
|
||||
fallbackNotFoundRoute,
|
||||
];
|
||||
|
||||
/** 基本路由列表,这些路由不需要进入权限拦截 */
|
||||
const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
||||
|
||||
/** 有权限校验的路由列表,包含动态路由和静态路由 */
|
||||
const accessRoutes = [...dynamicRoutes, ...staticRoutes];
|
||||
|
||||
const componentKeys: string[] = Object.keys(
|
||||
import.meta.glob('../../views/**/*.vue'),
|
||||
)
|
||||
.filter((item) => !item.includes('/modules/'))
|
||||
.map((v) => {
|
||||
const path = v.replace('../../views/', '/');
|
||||
return path.endsWith('.vue') ? path.slice(0, -4) : path;
|
||||
});
|
||||
|
||||
export { accessRoutes, componentKeys, coreRouteNames, routes };
|
||||
38
web/playground/src/router/routes/modules/dashboard.ts
Normal file
38
web/playground/src/router/routes/modules/dashboard.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
meta: {
|
||||
icon: 'lucide:layout-dashboard',
|
||||
order: -1,
|
||||
title: $t('page.dashboard.title'),
|
||||
},
|
||||
name: 'Dashboard',
|
||||
path: '/dashboard',
|
||||
children: [
|
||||
{
|
||||
name: 'Analytics',
|
||||
path: '/analytics',
|
||||
component: () => import('#/views/dashboard/analytics/index.vue'),
|
||||
meta: {
|
||||
affixTab: true,
|
||||
icon: 'lucide:area-chart',
|
||||
title: $t('page.dashboard.analytics'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Workspace',
|
||||
path: '/workspace',
|
||||
component: () => import('#/views/dashboard/workspace/index.vue'),
|
||||
meta: {
|
||||
icon: 'carbon:workspace',
|
||||
title: $t('page.dashboard.workspace'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
594
web/playground/src/router/routes/modules/demos.ts
Normal file
594
web/playground/src/router/routes/modules/demos.ts
Normal file
@@ -0,0 +1,594 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { IFrameView } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
meta: {
|
||||
icon: 'ic:baseline-view-in-ar',
|
||||
keepAlive: true,
|
||||
order: 1000,
|
||||
title: $t('demos.title'),
|
||||
},
|
||||
name: 'Demos',
|
||||
path: '/demos',
|
||||
children: [
|
||||
// 权限控制
|
||||
{
|
||||
meta: {
|
||||
icon: 'mdi:shield-key-outline',
|
||||
title: $t('demos.access.frontendPermissions'),
|
||||
},
|
||||
name: 'AccessDemos',
|
||||
path: '/demos/access',
|
||||
children: [
|
||||
{
|
||||
name: 'AccessPageControlDemo',
|
||||
path: '/demos/access/page-control',
|
||||
component: () => import('#/views/demos/access/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:page-previous-outline',
|
||||
title: $t('demos.access.pageAccess'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'AccessButtonControlDemo',
|
||||
path: '/demos/access/button-control',
|
||||
component: () => import('#/views/demos/access/button-control.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:button-cursor',
|
||||
title: $t('demos.access.buttonControl'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'AccessMenuVisible403Demo',
|
||||
path: '/demos/access/menu-visible-403',
|
||||
component: () =>
|
||||
import('#/views/demos/access/menu-visible-403.vue'),
|
||||
meta: {
|
||||
authority: ['no-body'],
|
||||
icon: 'mdi:button-cursor',
|
||||
menuVisibleWithForbidden: true,
|
||||
title: $t('demos.access.menuVisible403'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'AccessSuperVisibleDemo',
|
||||
path: '/demos/access/super-visible',
|
||||
component: () => import('#/views/demos/access/super-visible.vue'),
|
||||
meta: {
|
||||
authority: ['super'],
|
||||
icon: 'mdi:button-cursor',
|
||||
title: $t('demos.access.superVisible'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'AccessAdminVisibleDemo',
|
||||
path: '/demos/access/admin-visible',
|
||||
component: () => import('#/views/demos/access/admin-visible.vue'),
|
||||
meta: {
|
||||
authority: ['admin'],
|
||||
icon: 'mdi:button-cursor',
|
||||
title: $t('demos.access.adminVisible'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'AccessUserVisibleDemo',
|
||||
path: '/demos/access/user-visible',
|
||||
component: () => import('#/views/demos/access/user-visible.vue'),
|
||||
meta: {
|
||||
authority: ['user'],
|
||||
icon: 'mdi:button-cursor',
|
||||
title: $t('demos.access.userVisible'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 功能
|
||||
{
|
||||
meta: {
|
||||
icon: 'mdi:feature-highlight',
|
||||
title: $t('demos.features.title'),
|
||||
},
|
||||
name: 'FeaturesDemos',
|
||||
path: '/demos/features',
|
||||
children: [
|
||||
{
|
||||
name: 'LoginExpiredDemo',
|
||||
path: '/demos/features/login-expired',
|
||||
component: () =>
|
||||
import('#/views/demos/features/login-expired/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:encryption-expiration',
|
||||
title: $t('demos.features.loginExpired'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'IconsDemo',
|
||||
path: '/demos/features/icons',
|
||||
component: () => import('#/views/demos/features/icons/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:annoyed',
|
||||
title: $t('demos.features.icons'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'WatermarkDemo',
|
||||
path: '/demos/features/watermark',
|
||||
component: () =>
|
||||
import('#/views/demos/features/watermark/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:tags',
|
||||
title: $t('demos.features.watermark'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FeatureTabsDemo',
|
||||
path: '/demos/features/tabs',
|
||||
component: () => import('#/views/demos/features/tabs/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:app-window',
|
||||
title: $t('demos.features.tabs'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FeatureTabDetailDemo',
|
||||
path: '/demos/features/tabs/detail/:id',
|
||||
component: () =>
|
||||
import('#/views/demos/features/tabs/tab-detail.vue'),
|
||||
meta: {
|
||||
activePath: '/demos/features/tabs',
|
||||
hideInMenu: true,
|
||||
maxNumOfOpenTab: 3,
|
||||
title: $t('demos.features.tabDetail'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'HideChildrenInMenuParentDemo',
|
||||
path: '/demos/features/hide-menu-children',
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
icon: 'ic:round-menu',
|
||||
title: $t('demos.features.hideChildrenInMenu'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'HideChildrenInMenuDemo',
|
||||
path: '',
|
||||
component: () =>
|
||||
import(
|
||||
'#/views/demos/features/hide-menu-children/parent.vue'
|
||||
),
|
||||
meta: {
|
||||
// hideInMenu: true,
|
||||
title: $t('demos.features.hideChildrenInMenu'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'HideChildrenInMenuChildrenDemo',
|
||||
path: '/demos/features/hide-menu-children/children',
|
||||
component: () =>
|
||||
import(
|
||||
'#/views/demos/features/hide-menu-children/children.vue'
|
||||
),
|
||||
meta: {
|
||||
activePath: '/demos/features/hide-menu-children',
|
||||
title: $t('demos.features.hideChildrenInMenu'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'FullScreenDemo',
|
||||
path: '/demos/features/full-screen',
|
||||
component: () =>
|
||||
import('#/views/demos/features/full-screen/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:fullscreen',
|
||||
title: $t('demos.features.fullScreen'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FileDownloadDemo',
|
||||
path: '/demos/features/file-download',
|
||||
component: () =>
|
||||
import('#/views/demos/features/file-download/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:hard-drive-download',
|
||||
title: $t('demos.features.fileDownload'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ClipboardDemo',
|
||||
path: '/demos/features/clipboard',
|
||||
component: () =>
|
||||
import('#/views/demos/features/clipboard/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:copy',
|
||||
title: $t('demos.features.clipboard'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'MenuQueryDemo',
|
||||
path: '/demos/menu-query',
|
||||
component: () =>
|
||||
import('#/views/demos/features/menu-query/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:curly-braces',
|
||||
query: {
|
||||
id: 1,
|
||||
},
|
||||
title: $t('demos.features.menuWithQuery'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'NewWindowDemo',
|
||||
path: '/demos/new-window',
|
||||
component: () =>
|
||||
import('#/views/demos/features/new-window/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:app-window',
|
||||
openInNewWindow: true,
|
||||
title: $t('demos.features.openInNewWindow'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VueQueryDemo',
|
||||
path: '/demos/features/vue-query',
|
||||
component: () =>
|
||||
import('#/views/demos/features/vue-query/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:git-pull-request-arrow',
|
||||
title: 'Tanstack Query',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'RequestParamsSerializerDemo',
|
||||
path: '/demos/features/request-params-serializer',
|
||||
component: () =>
|
||||
import(
|
||||
'#/views/demos/features/request-params-serializer/index.vue'
|
||||
),
|
||||
meta: {
|
||||
icon: 'lucide:git-pull-request-arrow',
|
||||
title: $t('demos.features.requestParamsSerializer'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'BigIntDemo',
|
||||
path: '/demos/features/json-bigint',
|
||||
component: () =>
|
||||
import('#/views/demos/features/json-bigint/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:grape',
|
||||
title: 'JSON BigInt',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 面包屑导航
|
||||
{
|
||||
name: 'BreadcrumbDemos',
|
||||
path: '/demos/breadcrumb',
|
||||
meta: {
|
||||
icon: 'lucide:navigation',
|
||||
title: $t('demos.breadcrumb.navigation'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'BreadcrumbLateralDemo',
|
||||
path: '/demos/breadcrumb/lateral',
|
||||
component: () => import('#/views/demos/breadcrumb/lateral.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:navigation',
|
||||
title: $t('demos.breadcrumb.lateral'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'BreadcrumbLateralDetailDemo',
|
||||
path: '/demos/breadcrumb/lateral-detail',
|
||||
component: () =>
|
||||
import('#/views/demos/breadcrumb/lateral-detail.vue'),
|
||||
meta: {
|
||||
activePath: '/demos/breadcrumb/lateral',
|
||||
hideInMenu: true,
|
||||
title: $t('demos.breadcrumb.lateralDetail'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'BreadcrumbLevelDemo',
|
||||
path: '/demos/breadcrumb/level',
|
||||
meta: {
|
||||
icon: 'lucide:navigation',
|
||||
title: $t('demos.breadcrumb.level'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'BreadcrumbLevelDetailDemo',
|
||||
path: '/demos/breadcrumb/level/detail',
|
||||
component: () =>
|
||||
import('#/views/demos/breadcrumb/level-detail.vue'),
|
||||
meta: {
|
||||
title: $t('demos.breadcrumb.levelDetail'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// 缺省页
|
||||
{
|
||||
meta: {
|
||||
icon: 'mdi:lightbulb-error-outline',
|
||||
title: $t('demos.fallback.title'),
|
||||
},
|
||||
name: 'FallbackDemos',
|
||||
path: '/demos/fallback',
|
||||
children: [
|
||||
{
|
||||
name: 'Fallback403Demo',
|
||||
path: '/demos/fallback/403',
|
||||
component: () => import('#/views/_core/fallback/forbidden.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:do-not-disturb-alt',
|
||||
title: '403',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Fallback404Demo',
|
||||
path: '/demos/fallback/404',
|
||||
component: () => import('#/views/_core/fallback/not-found.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:table-off',
|
||||
title: '404',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Fallback500Demo',
|
||||
path: '/demos/fallback/500',
|
||||
component: () =>
|
||||
import('#/views/_core/fallback/internal-error.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:server-network-off',
|
||||
title: '500',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FallbackOfflineDemo',
|
||||
path: '/demos/fallback/offline',
|
||||
component: () => import('#/views/_core/fallback/offline.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:offline',
|
||||
title: $t('ui.fallback.offline'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 菜单徽标
|
||||
{
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
badgeVariants: 'destructive',
|
||||
icon: 'lucide:circle-dot',
|
||||
title: $t('demos.badge.title'),
|
||||
},
|
||||
name: 'BadgeDemos',
|
||||
path: '/demos/badge',
|
||||
children: [
|
||||
{
|
||||
name: 'BadgeDotDemo',
|
||||
component: () => import('#/views/demos/badge/index.vue'),
|
||||
path: '/demos/badge/dot',
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
icon: 'lucide:square-dot',
|
||||
title: $t('demos.badge.dot'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'BadgeTextDemo',
|
||||
component: () => import('#/views/demos/badge/index.vue'),
|
||||
path: '/demos/badge/text',
|
||||
meta: {
|
||||
badge: '10',
|
||||
icon: 'lucide:square-dot',
|
||||
title: $t('demos.badge.text'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'BadgeColorDemo',
|
||||
component: () => import('#/views/demos/badge/index.vue'),
|
||||
path: '/demos/badge/color',
|
||||
meta: {
|
||||
badge: 'Hot',
|
||||
badgeVariants: 'destructive',
|
||||
icon: 'lucide:square-dot',
|
||||
title: $t('demos.badge.color'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 菜单激活图标
|
||||
{
|
||||
meta: {
|
||||
activeIcon: 'fluent-emoji:radioactive',
|
||||
icon: 'bi:radioactive',
|
||||
title: $t('demos.activeIcon.title'),
|
||||
},
|
||||
name: 'ActiveIconDemos',
|
||||
path: '/demos/active-icon',
|
||||
children: [
|
||||
{
|
||||
name: 'ActiveIconDemo',
|
||||
component: () => import('#/views/demos/active-icon/index.vue'),
|
||||
path: '/demos/active-icon/children',
|
||||
meta: {
|
||||
activeIcon: 'fluent-emoji:radioactive',
|
||||
icon: 'bi:radioactive',
|
||||
title: $t('demos.activeIcon.children'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 外部链接
|
||||
{
|
||||
meta: {
|
||||
icon: 'ic:round-settings-input-composite',
|
||||
title: $t('demos.outside.title'),
|
||||
},
|
||||
name: 'OutsideDemos',
|
||||
path: '/demos/outside',
|
||||
children: [
|
||||
{
|
||||
name: 'IframeDemos',
|
||||
path: '/demos/outside/iframe',
|
||||
meta: {
|
||||
icon: 'mdi:newspaper-variant-outline',
|
||||
title: $t('demos.outside.embedded'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'VueDocumentDemo',
|
||||
path: '/demos/outside/iframe/vue-document',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
icon: 'logos:vue',
|
||||
iframeSrc: 'https://cn.vuejs.org/',
|
||||
keepAlive: true,
|
||||
title: 'Vue',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'TailwindcssDemo',
|
||||
path: '/demos/outside/iframe/tailwindcss',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
icon: 'devicon:tailwindcss',
|
||||
iframeSrc: 'https://tailwindcss.com/',
|
||||
// keepAlive: true,
|
||||
title: 'Tailwindcss',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'ExternalLinkDemos',
|
||||
path: '/demos/outside/external-link',
|
||||
meta: {
|
||||
icon: 'mdi:newspaper-variant-multiple-outline',
|
||||
title: $t('demos.outside.externalLink'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'ViteDemo',
|
||||
path: '/demos/outside/external-link/vite',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
icon: 'logos:vitejs',
|
||||
link: 'https://vitejs.dev/',
|
||||
title: 'Vite',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VueUseDemo',
|
||||
path: '/demos/outside/external-link/vue-use',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
icon: 'logos:vueuse',
|
||||
link: 'https://vueuse.org',
|
||||
title: 'VueUse',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// 嵌套菜单
|
||||
{
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
title: $t('demos.nested.title'),
|
||||
},
|
||||
name: 'NestedDemos',
|
||||
path: '/demos/nested',
|
||||
children: [
|
||||
{
|
||||
name: 'Menu1Demo',
|
||||
path: '/demos/nested/menu1',
|
||||
component: () => import('#/views/demos/nested/menu-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('demos.nested.menu1'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Menu2Demo',
|
||||
path: '/demos/nested/menu2',
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('demos.nested.menu2'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Menu21Demo',
|
||||
path: '/demos/nested/menu2/menu2-1',
|
||||
component: () => import('#/views/demos/nested/menu-2-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('demos.nested.menu2_1'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Menu3Demo',
|
||||
path: '/demos/nested/menu3',
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
title: $t('demos.nested.menu3'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Menu31Demo',
|
||||
path: '/demos/nested/menu3/menu3-1',
|
||||
component: () => import('#/views/demos/nested/menu-3-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('demos.nested.menu3_1'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Menu32Demo',
|
||||
path: '/demos/nested/menu3/menu3-2',
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
title: $t('demos.nested.menu3_2'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Menu321Demo',
|
||||
path: '/demos/nested/menu3/menu3-2/menu3-2-1',
|
||||
component: () =>
|
||||
import('#/views/demos/nested/menu-3-2-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('demos.nested.menu3_2_1'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
317
web/playground/src/router/routes/modules/examples.ts
Normal file
317
web/playground/src/router/routes/modules/examples.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
meta: {
|
||||
icon: 'ion:layers-outline',
|
||||
keepAlive: true,
|
||||
order: 1000,
|
||||
title: $t('examples.title'),
|
||||
},
|
||||
name: 'Examples',
|
||||
path: '/examples',
|
||||
children: [
|
||||
{
|
||||
name: 'FormExample',
|
||||
path: '/examples/form',
|
||||
meta: {
|
||||
icon: 'mdi:form-select',
|
||||
title: $t('examples.form.title'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'FormBasicExample',
|
||||
path: '/examples/form/basic',
|
||||
component: () => import('#/views/examples/form/basic.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.basic'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormQueryExample',
|
||||
path: '/examples/form/query',
|
||||
component: () => import('#/views/examples/form/query.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.query'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormRulesExample',
|
||||
path: '/examples/form/rules',
|
||||
component: () => import('#/views/examples/form/rules.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.rules'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormDynamicExample',
|
||||
path: '/examples/form/dynamic',
|
||||
component: () => import('#/views/examples/form/dynamic.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.dynamic'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormLayoutExample',
|
||||
path: '/examples/form/custom-layout',
|
||||
component: () => import('#/views/examples/form/custom-layout.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.layout'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormCustomExample',
|
||||
path: '/examples/form/custom',
|
||||
component: () => import('#/views/examples/form/custom.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.custom'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormApiExample',
|
||||
path: '/examples/form/api',
|
||||
component: () => import('#/views/examples/form/api.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.api'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormMergeExample',
|
||||
path: '/examples/form/merge',
|
||||
component: () => import('#/views/examples/form/merge.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.merge'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'VxeTableExample',
|
||||
path: '/examples/vxe-table',
|
||||
meta: {
|
||||
icon: 'lucide:table',
|
||||
title: $t('examples.vxeTable.title'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'VxeTableBasicExample',
|
||||
path: '/examples/vxe-table/basic',
|
||||
component: () => import('#/views/examples/vxe-table/basic.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.basic'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableRemoteExample',
|
||||
path: '/examples/vxe-table/remote',
|
||||
component: () => import('#/views/examples/vxe-table/remote.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.remote'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableTreeExample',
|
||||
path: '/examples/vxe-table/tree',
|
||||
component: () => import('#/views/examples/vxe-table/tree.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.tree'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableFixedExample',
|
||||
path: '/examples/vxe-table/fixed',
|
||||
component: () => import('#/views/examples/vxe-table/fixed.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.fixed'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableCustomCellExample',
|
||||
path: '/examples/vxe-table/custom-cell',
|
||||
component: () =>
|
||||
import('#/views/examples/vxe-table/custom-cell.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.custom-cell'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableFormExample',
|
||||
path: '/examples/vxe-table/form',
|
||||
component: () => import('#/views/examples/vxe-table/form.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.form'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableEditCellExample',
|
||||
path: '/examples/vxe-table/edit-cell',
|
||||
component: () => import('#/views/examples/vxe-table/edit-cell.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.editCell'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableEditRowExample',
|
||||
path: '/examples/vxe-table/edit-row',
|
||||
component: () => import('#/views/examples/vxe-table/edit-row.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.editRow'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VxeTableVirtualExample',
|
||||
path: '/examples/vxe-table/virtual',
|
||||
component: () => import('#/views/examples/vxe-table/virtual.vue'),
|
||||
meta: {
|
||||
title: $t('examples.vxeTable.virtual'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'CaptchaExample',
|
||||
path: '/examples/captcha',
|
||||
meta: {
|
||||
icon: 'logos:recaptcha',
|
||||
title: $t('examples.captcha.title'),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'DragVerifyExample',
|
||||
path: '/examples/captcha/slider',
|
||||
component: () =>
|
||||
import('#/views/examples/captcha/slider-captcha.vue'),
|
||||
meta: {
|
||||
title: $t('examples.captcha.sliderCaptcha'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'RotateVerifyExample',
|
||||
path: '/examples/captcha/slider-rotate',
|
||||
component: () =>
|
||||
import('#/views/examples/captcha/slider-rotate-captcha.vue'),
|
||||
meta: {
|
||||
title: $t('examples.captcha.sliderRotateCaptcha'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'CaptchaPointSelectionExample',
|
||||
path: '/examples/captcha/point-selection',
|
||||
component: () =>
|
||||
import('#/views/examples/captcha/point-selection-captcha.vue'),
|
||||
meta: {
|
||||
title: $t('examples.captcha.pointSelection'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'ModalExample',
|
||||
path: '/examples/modal',
|
||||
component: () => import('#/views/examples/modal/index.vue'),
|
||||
meta: {
|
||||
icon: 'system-uicons:window-content',
|
||||
keepAlive: true,
|
||||
title: $t('examples.modal.title'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'DrawerExample',
|
||||
path: '/examples/drawer',
|
||||
component: () => import('#/views/examples/drawer/index.vue'),
|
||||
meta: {
|
||||
icon: 'iconoir:drawer',
|
||||
keepAlive: true,
|
||||
title: $t('examples.drawer.title'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'EllipsisExample',
|
||||
path: '/examples/ellipsis',
|
||||
component: () => import('#/views/examples/ellipsis/index.vue'),
|
||||
meta: {
|
||||
icon: 'ion:ellipsis-horizontal',
|
||||
title: $t('examples.ellipsis.title'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VueResizeDemo',
|
||||
path: '/demos/resize/basic',
|
||||
component: () => import('#/views/examples/resize/basic.vue'),
|
||||
meta: {
|
||||
icon: 'material-symbols:resize',
|
||||
title: $t('examples.resize.title'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ColPageDemo',
|
||||
path: '/examples/layout/col-page',
|
||||
component: () => import('#/views/examples/layout/col-page.vue'),
|
||||
meta: {
|
||||
badge: 'Alpha',
|
||||
badgeVariants: 'destructive',
|
||||
icon: 'material-symbols:horizontal-distribute',
|
||||
title: $t('examples.layout.col-page'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'TippyDemo',
|
||||
path: '/examples/tippy',
|
||||
component: () => import('#/views/examples/tippy/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:message-settings-outline',
|
||||
title: 'Tippy',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'JsonViewer',
|
||||
path: '/examples/json-viewer',
|
||||
component: () => import('#/views/examples/json-viewer/index.vue'),
|
||||
meta: {
|
||||
icon: 'tabler:json',
|
||||
title: 'JsonViewer',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Motion',
|
||||
path: '/examples/motion',
|
||||
component: () => import('#/views/examples/motion/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:animation-play',
|
||||
title: 'Motion',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'CountTo',
|
||||
path: '/examples/count-to',
|
||||
component: () => import('#/views/examples/count-to/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:animation-play',
|
||||
title: 'CountTo',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Loading',
|
||||
path: '/examples/loading',
|
||||
component: () => import('#/views/examples/loading/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:circle-double',
|
||||
title: 'Loading',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ButtonGroup',
|
||||
path: '/examples/button-group',
|
||||
component: () => import('#/views/examples/button-group/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:check-circle',
|
||||
title: $t('examples.button-group.title'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
46
web/playground/src/router/routes/modules/system.ts
Normal file
46
web/playground/src/router/routes/modules/system.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
meta: {
|
||||
icon: 'ion:settings-outline',
|
||||
order: 9997,
|
||||
title: $t('system.title'),
|
||||
},
|
||||
name: 'System',
|
||||
path: '/system',
|
||||
children: [
|
||||
{
|
||||
path: '/system/role',
|
||||
name: 'SystemRole',
|
||||
meta: {
|
||||
icon: 'mdi:account-group',
|
||||
title: $t('system.role.title'),
|
||||
},
|
||||
component: () => import('#/views/system/role/list.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/menu',
|
||||
name: 'SystemMenu',
|
||||
meta: {
|
||||
icon: 'mdi:menu',
|
||||
title: $t('system.menu.title'),
|
||||
},
|
||||
component: () => import('#/views/system/menu/list.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/dept',
|
||||
name: 'SystemDept',
|
||||
meta: {
|
||||
icon: 'charm:organisation',
|
||||
title: $t('system.dept.title'),
|
||||
},
|
||||
component: () => import('#/views/system/dept/list.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
94
web/playground/src/router/routes/modules/vben.ts
Normal file
94
web/playground/src/router/routes/modules/vben.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import {
|
||||
VBEN_ANT_PREVIEW_URL,
|
||||
VBEN_DOC_URL,
|
||||
VBEN_ELE_PREVIEW_URL,
|
||||
VBEN_GITHUB_URL,
|
||||
VBEN_LOGO_URL,
|
||||
VBEN_NAIVE_PREVIEW_URL,
|
||||
} from '@vben/constants';
|
||||
import { SvgAntdvLogoIcon } from '@vben/icons';
|
||||
|
||||
import { IFrameView } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
icon: VBEN_LOGO_URL,
|
||||
order: 9998,
|
||||
title: $t('demos.vben.title'),
|
||||
},
|
||||
name: 'VbenProject',
|
||||
path: '/vben-admin',
|
||||
children: [
|
||||
{
|
||||
name: 'VbenDocument',
|
||||
path: '/vben-admin/document',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
icon: 'lucide:book-open-text',
|
||||
link: VBEN_DOC_URL,
|
||||
title: $t('demos.vben.document'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VbenGithub',
|
||||
path: '/vben-admin/github',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
icon: 'mdi:github',
|
||||
link: VBEN_GITHUB_URL,
|
||||
title: 'Github',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VbenAntdv',
|
||||
path: '/vben-admin/antdv',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
icon: SvgAntdvLogoIcon,
|
||||
link: VBEN_ANT_PREVIEW_URL,
|
||||
title: $t('demos.vben.antdv'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VbenNaive',
|
||||
path: '/vben-admin/naive',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
icon: 'logos:naiveui',
|
||||
link: VBEN_NAIVE_PREVIEW_URL,
|
||||
title: $t('demos.vben.naive-ui'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'VbenElementPlus',
|
||||
path: '/vben-admin/ele',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
icon: 'logos:element',
|
||||
link: VBEN_ELE_PREVIEW_URL,
|
||||
title: $t('demos.vben.element-plus'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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;
|
||||
Reference in New Issue
Block a user