初始化 ui.

This commit is contained in:
lijiahang
2023-07-27 18:48:15 +08:00
parent ee53ec6486
commit 5be2c7fda2
185 changed files with 1107 additions and 17815 deletions

View File

@@ -1,16 +0,0 @@
import { appRoutes, appExternalRoutes } from '../routes';
const mixinRoutes = [...appRoutes, ...appExternalRoutes];
const appClientMenus = mixinRoutes.map((el) => {
const { name, path, meta, redirect, children } = el;
return {
name,
path,
meta,
redirect,
children,
};
});
export default appClientMenus;

View File

@@ -1,18 +1,30 @@
export const WHITE_LIST = [
{ name: 'notFound', children: [] },
{ name: 'login', children: [] },
];
export const REDIRECT_ROUTE_NAME = 'redirect';
export const NOT_FOUND = {
name: 'notFound',
};
export const LOGIN_ROUTE_NAME = 'login';
export const REDIRECT_ROUTE_NAME = 'Redirect';
export const FORBIDDEN_ROUTER_NAME = 'forbidden';
export const DEFAULT_ROUTE_NAME = 'Workplace';
export const NOT_FOUND_ROUTER_NAME = 'notFound';
export const DEFAULT_ROUTE = {
export const DEFAULT_ROUTE_NAME = 'workplace';
export const DEFAULT_ROUTE_FULL_PATH = '/dashboard/workplace';
/**
* 默认 tab 页面
*/
export const DEFAULT_TAB = {
title: 'menu.dashboard.workplace',
name: DEFAULT_ROUTE_NAME,
fullPath: '/dashboard/workplace',
fullPath: DEFAULT_ROUTE_FULL_PATH,
};
/**
* 路由白名单
*/
export const WHITE_ROUTER_LIST = [
{ name: LOGIN_ROUTE_NAME, children: [] },
{ name: NOT_FOUND_ROUTER_NAME, children: [] },
{ name: FORBIDDEN_ROUTER_NAME, children: [] },
{ name: REDIRECT_ROUTE_NAME, children: [] },
];

View File

@@ -1,17 +1,13 @@
import type { Router } from 'vue-router';
import { setRouteEmitter } from '@/utils/route-listener';
import setupUserLoginInfoGuard from './userLoginInfo';
import setupPermissionGuard from './permission';
function setupPageGuard(router: Router) {
router.beforeEach(async (to) => {
// emit route change
setRouteEmitter(to);
});
}
import setupUserLoginInfoGuard from './user-login-info';
import setupPermissionGuard from './router-permission';
import setupRouteEmitterGuard from './router-listener-emitter';
/**
* 创建路由守卫
*/
export default function createRouteGuard(router: Router) {
setupPageGuard(router);
setupRouteEmitterGuard(router);
setupUserLoginInfoGuard(router);
setupPermissionGuard(router);
}

View File

@@ -1,55 +0,0 @@
import type { Router, RouteRecordNormalized } from 'vue-router';
import NProgress from 'nprogress'; // progress bar
import usePermission from '@/hooks/permission';
import { useUserStore, useAppStore } from '@/store';
import { appRoutes } from '../routes';
import { WHITE_LIST, NOT_FOUND } from '../constants';
export default function setupPermissionGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
const appStore = useAppStore();
const userStore = useUserStore();
const Permission = usePermission();
const permissionsAllow = Permission.accessRouter(to);
if (appStore.menuFromServer) {
// 针对来自服务端的菜单配置进行处理
// Handle routing configuration from the server
// 根据需要自行完善来源于服务端的菜单配置的permission逻辑
// Refine the permission logic from the server's menu configuration as needed
if (
!appStore.appAsyncMenus.length &&
!WHITE_LIST.find((el) => el.name === to.name)
) {
await appStore.fetchServerMenuConfig();
}
const serverMenuConfig = [...appStore.appAsyncMenus, ...WHITE_LIST];
let exist = false;
while (serverMenuConfig.length && !exist) {
const element = serverMenuConfig.shift();
if (element?.name === to.name) exist = true;
if (element?.children) {
serverMenuConfig.push(
...(element.children as unknown as RouteRecordNormalized[])
);
}
}
if (exist && permissionsAllow) {
next();
} else next(NOT_FOUND);
} else {
// eslint-disable-next-line no-lonely-if
if (permissionsAllow) next();
else {
const destination =
Permission.findFirstPermissionRoute(appRoutes, userStore.role) ||
NOT_FOUND;
next(destination);
}
}
NProgress.done();
});
}

View File

@@ -0,0 +1,11 @@
import type { Router } from 'vue-router';
import { setRouteEmitter } from '@/utils/route-listener';
/**
* 初始化路由监听订阅
*/
export default function setupRouteEmitterGuard(router: Router) {
router.beforeEach(async (to) => {
setRouteEmitter(to);
});
}

View File

@@ -0,0 +1,47 @@
import type { Router, RouteRecordNormalized } from 'vue-router';
import NProgress from 'nprogress';
import usePermission from '@/hooks/permission';
import { useAppStore } from '@/store';
import { WHITE_ROUTER_LIST, NOT_FOUND_ROUTER_NAME, FORBIDDEN_ROUTER_NAME } from '../constants';
export default function setupPermissionGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
const appStore = useAppStore();
const permission = usePermission();
// 未加载菜单 并且 未从白名单中找到 to.name
if (
!appStore.appAsyncMenus.length &&
!WHITE_ROUTER_LIST.find((el) => el.name === to.name)
) {
// 加载菜单
await appStore.fetchMenuConfig();
}
// 检查路由是否存在
const menuConfig = [...appStore.appAsyncMenus, ...WHITE_ROUTER_LIST];
let exist = false;
while (menuConfig.length && !exist) {
const element = menuConfig.shift();
if (element?.name === to.name) exist = true;
if (element?.children) {
menuConfig.push(
...(element.children as unknown as RouteRecordNormalized[])
);
}
}
// 检查是否有权限
const permissionsAllow = permission.accessRouter(to);
if (!exist) {
// 页面不存在
next({ name: NOT_FOUND_ROUTER_NAME });
} else if (!permissionsAllow) {
// 无权限
next({ name: FORBIDDEN_ROUTER_NAME });
} else {
// 正常跳转
next();
}
NProgress.done();
});
}

View File

@@ -1,21 +1,26 @@
import type { Router, LocationQueryRaw } from 'vue-router';
import NProgress from 'nprogress'; // progress bar
import NProgress from 'nprogress';
import { useUserStore } from '@/store';
import { isLogin } from '@/utils/auth';
/**
*
*/
export default function setupUserLoginInfoGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
NProgress.start();
const userStore = useUserStore();
if (isLogin()) {
if (userStore.role) {
// 获取用户信息
if (userStore.id) {
next();
} else {
try {
// 获取用户信息
await userStore.info();
next();
} catch (error) {
// 获取失败退出登录
await userStore.logout();
next({
name: 'login',
@@ -27,10 +32,12 @@ export default function setupUserLoginInfoGuard(router: Router) {
}
}
} else {
// 未登录跳转到登录页
if (to.name === 'login') {
next();
return;
}
// 跳转到登录页
next({
name: 'login',
query: {

View File

@@ -1,37 +1,25 @@
import { createRouter, createWebHistory } from 'vue-router';
import NProgress from 'nprogress'; // progress bar
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import { appRoutes } from './routes';
import { REDIRECT_MAIN, NOT_FOUND_ROUTE } from './routes/base';
import BASE_ROUTERS from './routes/base';
import createRouteGuard from './guard';
NProgress.configure({ showSpinner: false }); // NProgress Configuration
NProgress.configure({ showSpinner: false });
// 创建路由
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: 'login',
},
{
path: '/login',
name: 'login',
component: () => import('@/views/login/index.vue'),
meta: {
requiresAuth: false,
},
},
...BASE_ROUTERS,
...appRoutes,
REDIRECT_MAIN,
NOT_FOUND_ROUTE,
],
scrollBehavior() {
return { top: 0 };
},
});
// 创建路由守卫
createRouteGuard(router);
export default router;

View File

@@ -1,9 +1,38 @@
import type { RouteRecordRaw } from 'vue-router';
import { REDIRECT_ROUTE_NAME } from '@/router/constants';
import {
DEFAULT_ROUTE_FULL_PATH,
REDIRECT_ROUTE_NAME,
LOGIN_ROUTE_NAME,
FORBIDDEN_ROUTER_NAME,
NOT_FOUND_ROUTER_NAME,
} from '@/router/constants';
export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue');
export const REDIRECT_MAIN: RouteRecordRaw = {
/**
* 根页面
*/
export const ROOT_ROUTER: RouteRecordRaw = {
path: '/',
redirect: DEFAULT_ROUTE_FULL_PATH,
};
/**
* 登录页面
*/
export const LOGIN_ROUTER: RouteRecordRaw = {
path: '/login',
name: LOGIN_ROUTE_NAME,
component: () => import('@/views/login/index.vue'),
meta: {
requiresAuth: false,
},
};
/**
* 重定向页面
*/
export const REDIRECT_ROUTER: RouteRecordRaw = {
path: '/redirect',
name: 'redirectWrapper',
component: DEFAULT_LAYOUT,
@@ -24,8 +53,28 @@ export const REDIRECT_MAIN: RouteRecordRaw = {
],
};
/**
* 403 页面
*/
export const FORBIDDEN_ROUTE: RouteRecordRaw = {
path: '/:pathMatch(.*)*',
name: FORBIDDEN_ROUTER_NAME,
component: () => import('@/views/base/forbidden/index.vue'),
};
/**
* 404 页面
*/
export const NOT_FOUND_ROUTE: RouteRecordRaw = {
path: '/:pathMatch(.*)*',
name: 'notFound',
component: () => import('@/views/not-found/index.vue'),
name: NOT_FOUND_ROUTER_NAME,
component: () => import('@/views/base/not-found/index.vue'),
};
export default [
ROOT_ROUTER,
LOGIN_ROUTER,
REDIRECT_ROUTER,
NOT_FOUND_ROUTE,
FORBIDDEN_ROUTE
];

View File

@@ -1,10 +0,0 @@
export default {
path: 'https://arco.design',
name: 'arcoWebsite',
meta: {
locale: 'menu.arcoWebsite',
icon: 'icon-link',
requiresAuth: true,
order: 8,
},
};

View File

@@ -1,10 +0,0 @@
export default {
path: 'https://arco.design/vue/docs/pro/faq',
name: 'faq',
meta: {
locale: 'menu.faq',
icon: 'icon-question-circle',
requiresAuth: true,
order: 9,
},
};

View File

@@ -1,9 +1,7 @@
import type { RouteRecordNormalized } from 'vue-router';
// 应用模块
const modules = import.meta.glob('./modules/*.ts', { eager: true });
const externalModules = import.meta.glob('./externalModules/*.ts', {
eager: true,
});
function formatModules(_modules: any, result: RouteRecordNormalized[]) {
Object.keys(_modules).forEach((key) => {
@@ -17,9 +15,7 @@ function formatModules(_modules: any, result: RouteRecordNormalized[]) {
return result;
}
/**
* 应用路由
*/
export const appRoutes: RouteRecordNormalized[] = formatModules(modules, []);
export const appExternalRoutes: RouteRecordNormalized[] = formatModules(
externalModules,
[]
);

View File

@@ -14,23 +14,11 @@ const DASHBOARD: AppRouteRecordRaw = {
children: [
{
path: 'workplace',
name: 'Workplace',
name: 'workplace',
component: () => import('@/views/dashboard/workplace/index.vue'),
meta: {
locale: 'menu.dashboard.workplace',
requiresAuth: true,
roles: ['*'],
},
},
{
path: 'monitor',
name: 'Monitor',
component: () => import('@/views/dashboard/monitor/index.vue'),
meta: {
locale: 'menu.dashboard.monitor',
requiresAuth: true,
roles: ['admin'],
},
},
],

View File

@@ -1,48 +0,0 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const EXCEPTION: AppRouteRecordRaw = {
path: '/exception',
name: 'exception',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.exception',
requiresAuth: true,
icon: 'icon-exclamation-circle',
order: 6,
},
children: [
{
path: '403',
name: '403',
component: () => import('@/views/exception/403/index.vue'),
meta: {
locale: 'menu.exception.403',
requiresAuth: true,
roles: ['admin'],
},
},
{
path: '404',
name: '404',
component: () => import('@/views/exception/404/index.vue'),
meta: {
locale: 'menu.exception.404',
requiresAuth: true,
roles: ['*'],
},
},
{
path: '500',
name: '500',
component: () => import('@/views/exception/500/index.vue'),
meta: {
locale: 'menu.exception.500',
requiresAuth: true,
roles: ['*'],
},
},
],
};
export default EXCEPTION;

View File

@@ -1,38 +0,0 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const FORM: AppRouteRecordRaw = {
path: '/form',
name: 'form',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.form',
icon: 'icon-settings',
requiresAuth: true,
order: 3,
},
children: [
{
path: 'step',
name: 'Step',
component: () => import('@/views/form/step/index.vue'),
meta: {
locale: 'menu.form.step',
requiresAuth: true,
roles: ['admin'],
},
},
{
path: 'group',
name: 'Group',
component: () => import('@/views/form/group/index.vue'),
meta: {
locale: 'menu.form.group',
requiresAuth: true,
roles: ['admin'],
},
},
],
};
export default FORM;

View File

@@ -1,38 +0,0 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const LIST: AppRouteRecordRaw = {
path: '/list',
name: 'list',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.list',
requiresAuth: true,
icon: 'icon-list',
order: 2,
},
children: [
{
path: 'search-table', // The midline path complies with SEO specifications
name: 'SearchTable',
component: () => import('@/views/list/search-table/index.vue'),
meta: {
locale: 'menu.list.searchTable',
requiresAuth: true,
roles: ['*'],
},
},
{
path: 'card',
name: 'Card',
component: () => import('@/views/list/card/index.vue'),
meta: {
locale: 'menu.list.cardList',
requiresAuth: true,
roles: ['*'],
},
},
],
};
export default LIST;

View File

@@ -1,28 +0,0 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const PROFILE: AppRouteRecordRaw = {
path: '/profile',
name: 'profile',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.profile',
requiresAuth: true,
icon: 'icon-file',
order: 4,
},
children: [
{
path: 'basic',
name: 'Basic',
component: () => import('@/views/profile/basic/index.vue'),
meta: {
locale: 'menu.profile.basic',
requiresAuth: true,
roles: ['admin'],
},
},
],
};
export default PROFILE;

View File

@@ -1,38 +0,0 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const RESULT: AppRouteRecordRaw = {
path: '/result',
name: 'result',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.result',
icon: 'icon-check-circle',
requiresAuth: true,
order: 5,
},
children: [
{
path: 'success',
name: 'Success',
component: () => import('@/views/result/success/index.vue'),
meta: {
locale: 'menu.result.success',
requiresAuth: true,
roles: ['admin'],
},
},
{
path: 'error',
name: 'Error',
component: () => import('@/views/result/error/index.vue'),
meta: {
locale: 'menu.result.error',
requiresAuth: true,
roles: ['admin'],
},
},
],
};
export default RESULT;

View File

@@ -6,30 +6,19 @@ const USER: AppRouteRecordRaw = {
name: 'user',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.user',
icon: 'icon-user',
locale: 'USER',
requiresAuth: true,
order: 7,
icon: 'icon-dashboard',
order: 0,
},
children: [
{
path: 'info',
name: 'Info',
component: () => import('@/views/user/info/index.vue'),
path: 'userChild',
name: 'userChild',
component: () => import('@/views/user/child/index.vue'),
meta: {
locale: 'menu.user.info',
locale: '用户子页面',
requiresAuth: true,
roles: ['*'],
},
},
{
path: 'setting',
name: 'Setting',
component: () => import('@/views/user/setting/index.vue'),
meta: {
locale: 'menu.user.setting',
requiresAuth: true,
roles: ['*'],
},
},
],

View File

@@ -1,39 +0,0 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const VISUALIZATION: AppRouteRecordRaw = {
path: '/visualization',
name: 'visualization',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.visualization',
requiresAuth: true,
icon: 'icon-apps',
order: 1,
},
children: [
{
path: 'data-analysis',
name: 'DataAnalysis',
component: () => import('@/views/visualization/data-analysis/index.vue'),
meta: {
locale: 'menu.visualization.dataAnalysis',
requiresAuth: true,
roles: ['admin'],
},
},
{
path: 'multi-dimension-data-analysis',
name: 'MultiDimensionDataAnalysis',
component: () =>
import('@/views/visualization/multi-dimension-data-analysis/index.vue'),
meta: {
locale: 'menu.visualization.multiDimensionDataAnalysis',
requiresAuth: true,
roles: ['admin'],
},
},
],
};
export default VISUALIZATION;

View File

@@ -2,15 +2,15 @@ import 'vue-router';
declare module 'vue-router' {
interface RouteMeta {
roles?: string[]; // Controls roles that have access to the page
requiresAuth: boolean; // Whether login is required to access the current page (every route must declare)
icon?: string; // The icon show in the side menu
locale?: string; // The locale name show in side menu and breadcrumb
hideInMenu?: boolean; // If true, it is not displayed in the side menu
hideChildrenInMenu?: boolean; // if set true, the children are not displayed in the side menu
activeMenu?: string; // if set name, the menu will be highlighted according to the name you set
order?: number; // Sort routing menu items. If set key, the higher the value, the more forward it is
noAffix?: boolean; // if set true, the tag will not affix in the tab-bar
ignoreCache?: boolean; // if set true, the page will not be cached
permission?: string;
requiresAuth: boolean;
icon?: string;
locale?: string;
hideInMenu?: boolean;
hideChildrenInMenu?: boolean;
activeMenu?: string;
order?: number;
noAffix?: boolean;
ignoreCache?: boolean;
}
}