上篇介绍了前端项目部署以及基本依赖的应用,这次主要对于路由以及布局进行模块化处理
一、 创建layout模块
1、新建src/layout/index.vue
javascript
<template>
<el-container class="common-layout">
<!-- <el-aside class="aside">
<Aside/>
</el-aside> -->
<el-container>
<el-header class="header">
<Header/>
</el-header>
<el-main class="main">
<router-view></router-view>
</el-main>
<el-footer class="footer">Copyright © 2020 云风网, All rights reserved. 京ICP备20002789号-1</el-footer>
</el-container>
</el-container>
</template>
<script setup lang="ts">
// import Aside from './components/aside.vue'
import Header from './components/header.vue'
</script>
<style lang="scss" scoped>
@import '@/assets/styles/mixins.scss';
.common-layout{
width: 100%;
height: 100vh;
.aside{
height: 100vh;
width: 200px;
}
.header{
height: 60px;
padding: 0;
}
.main{
@include scrollBar;
}
.footer{
height: 60px;
background-color: #343a40;
color: #fff;
line-height: 60px;
}
}
</style>
2、新建头部组件src/layout/components/header.vue
javascript
<template>
<el-menu
:default-active="activeIndex"
class="header-container"
mode="horizontal"
:ellipsis="false"
@select="handleSelect"
>
<el-menu-item index="0">
<img
class="logo"
src="@/assets/logo.svg"
alt="logo"
/>
</el-menu-item>
<navbar-item
v-for="(route, index) in routerList"
:key="route.path + index"
:item="route"
:base-path="route.path"
:is-nest="false"
/>
</el-menu>
</template>
<script setup lang="ts">
import NavbarItem from './navbarItem.vue';
import { useRouter} from 'vue-router';
const router = useRouter();
const activeIndex = ref('1')
const routerList = router.getRoutes();
console.log('routerList',routerList);
const handleSelect = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
</script>
<style lang="scss" scoped>
.el-menu--horizontal > .el-menu-item:nth-child(1) {
margin-right: auto;
}
.header-container{
height: 100%;
padding: 0 50px;
background-color: #fff;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.logo{
width: 40px;
}
}
</style>
3、新建菜单组件src/layout/components/navbarItem.vue
javascript
<template>
<div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren)">
<app-link v-if="onlyOneChild.meta.title" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path,'')" @click="handleClick(item.path)">
<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
<template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>
</el-menu-item>
</app-link>
</template>
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path,'')" teleported>
<template v-if="item.meta" #title>
<svg-icon :icon-class="item.meta && item.meta.icon" />
<span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
</template>
<app-link v-for="child in item.children" :key="child.path" :to="resolvePath(child.path, '')">
<el-menu-item
:item="child"
class="nest-menu"
@click="handleClick(child.path)"
>
<template #title><span class="menu-title">{{ child.meta.title }}</span></template>
</el-menu-item>
</app-link>
</el-sub-menu>
</div>
</template>
<script setup lang="ts">
import { isExternal } from '@/utils/validate.ts'
import AppLink from './Link.vue'
import { getNormalPath } from '@/utils/mis'
interface MenuItem {
path: string;
meta?: {
icon?: string;
title?: string;
[key: string]: any;
};
query?: string;
children?: MenuItem[];
hidden?: boolean;
noShowingChildren?: boolean;
}
const props = defineProps<{
item: MenuItem;
isNest: boolean;
basePath: string;
}>();
const onlyOneChild = ref<MenuItem>({ path: '', meta: {} });
function hasOneShowingChild(children: MenuItem[] = [], parent: MenuItem): boolean {
const showingChildren = children.filter(item => {
if (item.hidden) {
return false;
} else {
onlyOneChild.value = item;
return true;
}
});
if (showingChildren.length === 1) {
return true;
}
if (showingChildren.length === 0) {
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true, meta: {} };
return true;
}
return false;
}
function handleClick(routePath: string) {
localStorage.setItem('routePath', routePath);
}
function resolvePath(routePath: string, routeQuery?: string) {
if (isExternal(routePath)) {
return routePath;
}
if (isExternal(props.basePath)) {
return props.basePath;
}
if (routeQuery) {
let query = JSON.parse(routeQuery);
return { path: getNormalPath(`${props.basePath}/${routePath}`), query };
}
return getNormalPath(`${props.basePath}/${routePath}`);
}
function hasTitle(title: string | undefined): string {
return title && title.length > 5 ? title : '';
}
</script>
4、新建菜单组件src/layout/components/Link.vue
javascript
<template>
<component :is="type" v-bind="linkProps()">
<slot />
</component>
</template>
<script setup lang="ts">
import { isExternal } from '@/utils/validate.ts'
const props = defineProps({
to: {
type: [String, Object],
required: true
}
})
const isExt = computed(() => {
return isExternal(props.to)
})
const type = computed(() => {
if (isExt.value) {
return 'a'
}
return 'router-link'
})
function linkProps() {
if (isExt.value) {
return {
href: props.to,
target: '_blank',
rel: 'noopener'
}
}
return {
to: props.to
}
}
</script>
5、新建方法封装文件src/utils/validate.ts
javascript
/**
* 判断url是否是http或https
* @param {string} path
* @returns {Boolean}
*/
export function isHttp(url) {
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
}
/**
* 判断path是否为外链
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
/**
* @param {string} url
* @returns {Boolean}
*/
export function validURL(url) {
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validLowerCase(str) {
const reg = /^[a-z]+$/
return reg.test(str)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUpperCase(str) {
const reg = /^[A-Z]+$/
return reg.test(str)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validAlphabets(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
/**
* @param {string} email
* @returns {Boolean}
*/
export function validEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return reg.test(email)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function isString(str) {
if (typeof str === 'string' || str instanceof String) {
return true
}
return false
}
/**
* @param {Array} arg
* @returns {Boolean}
*/
export function isArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'
}
return Array.isArray(arg)
}
二、修改router/index.ts
javascript
import { createRouter, createWebHistory } from 'vue-router'
import Layout from '@/layout/index.vue'
//公共路由
export const constantRoutes = [
{
path: '/',
component: Layout,
redirect: '/home',
hidden: false,
children: [
{
path: 'home',
name: 'Home',
meta:{ title: '系统知识',routerNum: 0},
component: () => import('@/views/Home.vue')
}
]
},
{
path: '/',
redirect: "/article",
component: Layout,
hidden: false,
children: [
{
path: 'article',
name: 'Article',
meta:{ title: '插件管理',routerNum: 1},
component: () => import('@/views/Article.vue')
}
]
},
{
path: '/',
redirect: "/mix",
component: Layout,
hidden: false,
meta:{ title: '多级菜单',routerNum: 0},
children: [
{
path: 'home',
name: 'Home',
meta:{ title: '系统知识',routerNum: 0},
component: () => import('@/views/Home.vue')
},
{
path: 'article',
name: 'Article',
meta:{ title: '插件管理',routerNum: 1},
component: () => import('@/views/Article.vue')
}
]
},
// {
// path: '/article/:id',
// name: 'Article',
// meta:{ title: '插件管理',routerNum: 1},
// component: () => import('@/views/Article.vue')
// }
]
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes
})
export default router