refactor(projects): 完善路由配置

This commit is contained in:
Soybean
2021-09-14 01:31:29 +08:00
parent 1dcdcdc485
commit ab77d58e46
24 changed files with 441 additions and 301 deletions

View File

@ -1,4 +1,5 @@
export { ContentType, EnumDataType, EnumLoginModule } from './common';
export { EnumAnimate } from './animate';
export { EnumNavMode, EnumNavTheme } from './theme';
export { EnumRoutePaths } from './route';
export { EnumRoutePath, EnumRouteTitle } from './route';
export { EnumStorageKey } from './storage';

View File

@ -1,12 +1,32 @@
export enum EnumRoutePaths {
export enum EnumRoutePath {
'root' = '/',
'system' = '/system',
'login' = '/login',
'not-found' = '/exception/404',
'no-permission' = '/exception/403',
'service-error' = '/exception/500',
'not-found' = '/404',
'no-permission' = '/403',
'service-error' = '/500',
// 自定义路由
'dashboard' = '/dashboard',
'dashboard-analysis' = '/dashboard/analysis',
'dashboard-workbench' = '/dashboard/workbench',
'exception-403' = '/exception/e403',
'exception-404' = '/exception/e404',
'exception-500' = '/exception/e500'
'exception' = '/exception',
'exception-403' = '/exception/403',
'exception-404' = '/exception/404',
'exception-500' = '/exception/500'
}
export enum EnumRouteTitle {
'root' = 'root',
'system' = '系统',
'login' = '登录',
'not-found' = '未找到',
'no-permission' = '无权限',
'service-error' = '服务器错误',
'dashboard' = '仪表盘',
'dashboard-analysis' = '分析页',
'dashboard-workbench' = '工作台',
'exception' = '异常页',
'exception-403' = '异常页-403',
'exception-404' = '异常页-404',
'exception-500' = '异常页-500'
}

6
src/enum/storage.ts Normal file
View File

@ -0,0 +1,6 @@
export enum EnumStorageKey {
/** 用户token */
'token' = '__TOKEN__',
/** 用户信息 */
'user-info' = '__USER_INFO__'
}

View File

@ -1,5 +1,6 @@
import useAppTitle from './useAppTitle';
import useCreateContext from './useCreateContext';
import useRouterChange from './useRouterChange';
import useRouteParam from './useRouteParam';
export { useAppTitle, useCreateContext, useRouterChange };
export { useAppTitle, useCreateContext, useRouterChange, useRouteParam };

View File

@ -0,0 +1,20 @@
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { RouteNameMap } from '@/router';
export default function useRouteParam() {
const route = useRoute();
/** 登录跳转链接 */
const loginRedirectUrl = computed(() => {
let url = '';
if (route.name === RouteNameMap.get('login')) {
url = (route.params?.redirectUrl as string) ?? '';
}
return url;
});
return {
loginRedirectUrl
};
}

View File

@ -1,11 +1,16 @@
import { useRouter } from 'vue-router';
import { EnumRoutePaths } from '@/enum';
import { EnumRoutePath } from '@/enum';
import { RouteNameMap } from '@/router';
import type { LoginModuleType } from '@/interface';
export default function useRouterChange() {
const router = useRouter();
/** 跳转首页 */
function toHome() {
router.push('/');
}
/**
* 跳转登录页面(通过vue路由)
* @param module - 展示的登录模块
@ -28,7 +33,7 @@ export default function useRouterChange() {
* @param redirectUrl - 登录后重定向的页面路径
*/
function toLoginByLocation(module: LoginModuleType = 'pwd-login', redirectUrl?: string) {
let href = `${window.location.origin + EnumRoutePaths.login}/${module}`;
let href = `${window.location.origin + EnumRoutePath.login}/${module}`;
if (redirectUrl) {
href += redirectUrl;
}
@ -36,6 +41,7 @@ export default function useRouterChange() {
}
return {
toHome,
toLogin,
toLoginByLocation
};

View File

@ -1,2 +1,2 @@
export { useAppTitle, useCreateContext, useRouterChange } from './common';
export { useAppTitle, useCreateContext, useRouterChange, useRouteParam } from './common';
export { useCountDown, useSmsCode } from './business';

View File

@ -1,10 +1,16 @@
import { EnumRoutePaths, EnumLoginModule } from '@/enum';
import { EnumRoutePath, EnumLoginModule } from '@/enum';
/** 路由描述 */
export interface RouteMeta {
/** 路由名称 */
title?: string;
/** 页面100%视高 */
fullPage?: boolean;
/** 作为菜单 */
asMenu?: boolean;
/** 菜单和面包屑对应的图标 */
icon?: string;
}
export type RoutePathKey = keyof typeof EnumRoutePaths;
export type RoutePathKey = keyof typeof EnumRoutePath;
export type LoginModuleType = keyof typeof EnumLoginModule;

View File

@ -1,4 +1,7 @@
import type { Router } from 'vue-router';
import { useTitle } from '@vueuse/core';
import { getToken, getLoginRedirectUrl } from '@/utils';
import { RouteNameMap, whitelistRoutes } from './routes';
/**
* 路由守卫函数
@ -7,9 +10,26 @@ import type { Router } from 'vue-router';
export default function createRouterGuide(router: Router) {
router.beforeEach((to, from, next) => {
window.$loadingBar?.start();
next();
const token = getToken();
if (whitelistRoutes.includes(to.name as string)) {
if (to.name === RouteNameMap.get('login') && token) {
next('/');
return;
}
next();
return;
}
if (token) {
next();
} else {
const redirectUrl = getLoginRedirectUrl();
next({ name: RouteNameMap.get('login'), query: { redirectUrl } });
}
});
router.afterEach(() => {
router.afterEach(to => {
// 设置document title
useTitle(to.meta.title as string);
// 结束 loadingBar
window.$loadingBar?.finish();
});
}

View File

@ -1,12 +1,12 @@
import type { RouteRecordRaw } from 'vue-router';
import { BasicLayout, BlankLayout } from '@/layouts';
import { EnumRoutePaths } from '@/enum';
import { EnumRoutePath, EnumRouteTitle } from '@/enum';
import type { RoutePathKey, LoginModuleType } from '@/interface';
import { getLoginModuleRegExp } from '@/utils';
/** 路由名称 */
export const RouteNameMap = new Map<RoutePathKey, RoutePathKey>(
(Object.keys(EnumRoutePaths) as RoutePathKey[]).map(v => [v, v])
(Object.keys(EnumRoutePath) as RoutePathKey[]).map(v => [v, v])
);
const loginModuleRegExp = getLoginModuleRegExp();
@ -15,17 +15,20 @@ const loginModuleRegExp = getLoginModuleRegExp();
* 固定不变的路由
* @description !最后一项重定向未找到的路由须放置路由的最后一项
*/
export const constantRoutes: Array<RouteRecordRaw> = [
export const constantRoutes: RouteRecordRaw[] = [
{
name: 'system',
path: '/system',
name: RouteNameMap.get('system'),
path: EnumRoutePath.system,
component: BlankLayout,
redirect: { name: 'not-found' },
redirect: { name: RouteNameMap.get('not-found') },
meta: {
title: EnumRouteTitle.system
},
children: [
// 登录
{
name: RouteNameMap.get('login'),
path: `${EnumRoutePaths.login}/:module(/${loginModuleRegExp}/)?`,
path: `${EnumRoutePath.login}/:module(/${loginModuleRegExp}/)?`,
component: () => import('@/views/system/login/index.vue'),
props: route => {
const moduleType: LoginModuleType = (route.params.module as LoginModuleType) || 'pwd-login';
@ -34,33 +37,37 @@ export const constantRoutes: Array<RouteRecordRaw> = [
};
},
meta: {
title: EnumRouteTitle.login,
fullPage: true
}
},
// 404
{
name: RouteNameMap.get('not-found'),
path: EnumRoutePaths['not-found'],
path: EnumRoutePath['not-found'],
component: () => import('@/views/system/exception/404.vue'),
meta: {
title: EnumRouteTitle['not-found'],
fullPage: true
}
},
// 403
{
name: RouteNameMap.get('no-permission'),
path: EnumRoutePaths['no-permission'],
path: EnumRoutePath['no-permission'],
component: () => import('@/views/system/exception/403.vue'),
meta: {
title: EnumRouteTitle['no-permission'],
fullPage: true
}
},
// 500
{
name: RouteNameMap.get('service-error'),
path: EnumRoutePaths['service-error'],
path: EnumRoutePath['service-error'],
component: () => import('@/views/system/exception/500.vue'),
meta: {
title: EnumRouteTitle['service-error'],
fullPage: true
}
}
@ -78,48 +85,89 @@ export const constantRoutes: Array<RouteRecordRaw> = [
*/
export const customRoutes: Array<RouteRecordRaw> = [
{
name: 'root',
path: '/',
redirect: { name: RouteNameMap.get('dashboard-analysis') }
name: RouteNameMap.get('root'),
path: EnumRoutePath.root,
redirect: { name: RouteNameMap.get('dashboard-analysis') },
meta: {
asMenu: false
}
},
{
name: 'dashboard',
path: '/dashboard',
name: RouteNameMap.get('dashboard'),
path: EnumRoutePath.dashboard,
component: BasicLayout,
redirect: { name: RouteNameMap.get('dashboard-analysis') },
meta: {
title: EnumRouteTitle.dashboard,
asMenu: true,
icon: 'mdi:view-dashboard'
},
children: [
{
name: RouteNameMap.get('dashboard-analysis'),
path: EnumRoutePaths['dashboard-analysis'],
component: () => import('@/views/dashboard/analysis/index.vue')
path: EnumRoutePath['dashboard-analysis'],
component: () => import('@/views/dashboard/analysis/index.vue'),
meta: {
title: EnumRouteTitle['dashboard-analysis'],
asMenu: true
}
},
{
name: RouteNameMap.get('dashboard-workbench'),
path: EnumRoutePaths['dashboard-workbench'],
component: () => import('@/views/dashboard/workbench/index.vue')
path: EnumRoutePath['dashboard-workbench'],
component: () => import('@/views/dashboard/workbench/index.vue'),
meta: {
title: EnumRouteTitle['dashboard-workbench'],
asMenu: true
}
}
]
},
{
name: 'exception',
path: '/exception',
name: RouteNameMap.get('exception'),
path: EnumRoutePath.exception,
component: BasicLayout,
meta: {
title: EnumRouteTitle.exception,
asMenu: true,
icon: 'ant-design:exception-outlined'
},
children: [
{
name: RouteNameMap.get('exception-403'),
path: EnumRoutePaths['exception-403'],
component: () => import('@/views/system/exception/403.vue')
path: EnumRoutePath['exception-403'],
component: () => import('@/views/system/exception/403.vue'),
meta: {
title: EnumRouteTitle['exception-403'],
asMenu: true
}
},
{
name: RouteNameMap.get('exception-404'),
path: EnumRoutePaths['exception-404'],
component: () => import('@/views/system/exception/404.vue')
path: EnumRoutePath['exception-404'],
component: () => import('@/views/system/exception/404.vue'),
meta: {
title: EnumRouteTitle['exception-404'],
asMenu: true
}
},
{
name: RouteNameMap.get('exception-500'),
path: EnumRoutePaths['exception-500'],
component: () => import('@/views/system/exception/500.vue')
path: EnumRoutePath['exception-500'],
component: () => import('@/views/system/exception/500.vue'),
meta: {
title: EnumRouteTitle['exception-500'],
asMenu: true
}
}
]
}
];
/** 路由白名单(不需要登录) */
export const whitelistRoutes: string[] = [
RouteNameMap.get('login')!,
RouteNameMap.get('exception-403')!,
RouteNameMap.get('exception-404')!,
RouteNameMap.get('exception-500')!
];

View File

@ -1,13 +1,2 @@
import type { LoginModuleType } from '@/interface';
export function getToken() {
return '';
}
export function getUserInfo() {}
/** 获取登录模块的正则字符串 */
export function getLoginModuleRegExp() {
const arr: LoginModuleType[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat'];
return arr.join('|');
}
export { getToken, setToken, getUserInfo, getLoginModuleRegExp } from './user';
export { getLoginRedirectUrl, toLoginRedirectUrl, toHomeByLocation } from './location';

View File

@ -0,0 +1,14 @@
/** 获取登录重定向的地址 */
export function getLoginRedirectUrl() {
return window.location.href;
}
/** 登录后跳转重定向的地址 */
export function toLoginRedirectUrl(redirectUrl: string) {
window.location.href = redirectUrl;
}
/** 回到首页 */
export function toHomeByLocation() {
window.location.href = '/';
}

21
src/utils/auth/user.ts Normal file
View File

@ -0,0 +1,21 @@
import { EnumStorageKey } from '@/enum';
import type { LoginModuleType } from '@/interface';
import { setLocal, getLocal } from '../storage';
/** 设置token */
export function getToken() {
return getLocal<string>(EnumStorageKey.token) || '';
}
/** 获取token */
export function setToken(token: string) {
setLocal(EnumStorageKey.token, token);
}
export function getUserInfo() {}
/** 获取登录模块的正则字符串 */
export function getLoginModuleRegExp() {
const arr: LoginModuleType[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat'];
return arr.join('|');
}

View File

@ -1,4 +1,13 @@
export { getToken, getUserInfo, getLoginModuleRegExp } from './auth';
export {
setToken,
getToken,
getUserInfo,
getLoginModuleRegExp,
getLoginRedirectUrl,
toLoginRedirectUrl,
toHomeByLocation
} from './auth';
export {
isNumber,
isString,
@ -14,3 +23,5 @@ export {
brightenColor,
darkenColor
} from './common';
export { setLocal, getLocal, setSession, getSession } from './storage';

View File

@ -0,0 +1,2 @@
export { setLocal, getLocal } from './local';
export { setSession, getSession } from './session';

View File

@ -0,0 +1,12 @@
export function setLocal(key: string, value: unknown) {
const json = JSON.stringify(value);
localStorage.setItem(key, json);
}
export function getLocal<T>(key: string) {
const json = localStorage.getItem(key);
if (json) {
return JSON.parse(json) as T;
}
return json;
}

View File

@ -0,0 +1,12 @@
export function setSession(key: string, value: unknown) {
const json = JSON.stringify(value);
sessionStorage.setItem(key, json);
}
export function getSession<T>(key: string) {
const json = sessionStorage.getItem(key);
if (json) {
return JSON.parse(json) as T;
}
return json;
}

View File

@ -2,14 +2,14 @@
<div class="p-10px">
<data-card :loading="loading" />
<nav-card :loading="loading" />
<router-link :to="EnumRoutePaths['dashboard-workbench']">workbench</router-link>
<router-link :to="EnumRoutePath['dashboard-workbench']">workbench</router-link>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { DataCard, NavCard } from './components';
import { EnumRoutePaths } from '@/enum';
import { EnumRoutePath } from '@/enum';
const loading = ref(true);

View File

@ -1,7 +1,7 @@
<template>
<div>
<h2>工作台</h2>
<router-link :to="EnumRoutePaths['dashboard-analysis']">analysis</router-link>
<router-link :to="EnumRoutePath['dashboard-analysis']">analysis</router-link>
<n-button @click="removeCurrent">去除</n-button>
</div>
</template>
@ -9,7 +9,7 @@
<script lang="ts" setup>
import { NButton } from 'naive-ui';
import { useRouter } from 'vue-router';
import { EnumRoutePaths } from '@/enum';
import { EnumRoutePath } from '@/enum';
import { RouteNameMap } from '@/router';
const router = useRouter();

View File

@ -1,25 +0,0 @@
<template>
<div>
<h3>System</h3>
<n-button>
<router-link to="/home">home</router-link>
</n-button>
<n-switch :value="true" />
<n-button @click="showModal = true">来吧</n-button>
<n-modal v-model:show="showModal">
<n-card style="width: 600px" title="模态框" :bordered="false" size="huge">
<template #header-extra></template>
内容
<template #footer>尾部</template>
</n-card>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { NButton, NSwitch, NModal, NCard } from 'naive-ui';
const showModal = ref(false);
</script>
<style scoped></style>

View File

@ -28,19 +28,21 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { NForm, NFormItem, NInput, NSpace, NCheckbox, NButton, useMessage } from 'naive-ui';
import { NForm, NFormItem, NInput, NSpace, NCheckbox, NButton, useNotification } from 'naive-ui';
import type { FormInst } from 'naive-ui';
import { EnumLoginModule } from '@/enum';
import { useRouterChange } from '@/hooks';
import { useRouterChange, useRouteParam } from '@/hooks';
import { setToken, toLoginRedirectUrl } from '@/utils';
import { OtherLogin } from '../common';
const { toLogin } = useRouterChange();
const message = useMessage();
const { toLogin, toHome } = useRouterChange();
const { loginRedirectUrl } = useRouteParam();
const notification = useNotification();
const formRef = ref<(HTMLElement & FormInst) | null>(null);
const model = reactive({
phone: '',
pwd: ''
phone: '15170283876',
pwd: '123456'
});
const rules = {
phone: {
@ -62,9 +64,17 @@ function handleSubmit(e: MouseEvent) {
formRef.value.validate(errors => {
if (!errors) {
message.success('验证成功');
} else {
message.error('验证失败');
setToken('temp-token');
if (loginRedirectUrl.value) {
toLoginRedirectUrl(loginRedirectUrl.value);
} else {
toHome();
}
notification.success({
title: '登录成功!',
content: '欢迎回来Soybean!'
});
}
});
}

View File

@ -1,17 +0,0 @@
import type { Ref } from 'vue';
import { useCreateContext } from '@/hooks';
import { LoginModuleType } from '@/interface';
interface ShareState {
activeModule: Ref<LoginModuleType>;
handleLoginModule(module: LoginModuleType): void;
}
const { useContext, useProvider } = useCreateContext<ShareState>();
export function useLoginContext() {
return {
useContext,
useProvider
};
}