一.路由配置
- 路由懒加载:可用这种方法引入
{ name:'home', path: '/home', // meta:{title:'首页'}, component: () => import('../views/Main/home/index.vue') },
- 路由出口
- 假如路由view文件夹一共有两个路由login和main,那么这两个路由的出口就是APP.vue,因此只需在APP.vue使用router-view即可在页面展示main和login
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
- 在Main文件夹下又有四个字路由文件夹(home、mall、page1、page2和user),为了是这几个子路由展示出来,路由出口可以写在main下面的index.vue
在index.vue中便是整个页面的布局,如下图
<el-container> <el-header> 项目标题 面包屑(单独用一个组件来写并把各组件引入进来) 退出按钮 </el-header> <el-container> <el-aside> 侧边栏 </el-aside> <el-mian> 导航的tabs按钮(专门用一个组件来实现,并引入进来) main部分用 <router-view></router-view>设置home、mall、page1、page2和user路由组件的出口 </el-mian> </el-container> </el-container>
<template>
<div id="module">
<el-container>
<el-header id="header">
<div class="header-title">
<h3>朝夕电量智能监控系统</h3>
</div>
<!-- 面包屑 -->
<NavBar></NavBar>
退出按钮
</el-dropdown>
</el-header>
<el-container>
<el-aside>
<NavMenu></NavMenu>
</el-aside>
<tabMenu></tabMenu>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import NavMenu from './NavMenu.vue'
import NavBar from './NavBar.vue'
import tabMenu from './tabMenu.vue'
注释:一个项目router下面的index.js(路由配置)并不是由侧边栏是否有子菜单或者你建的路由文件夹是否有子文件夹决定的,而是一个页面展示功能可能需要子页面配合展示功能,这时就可以设置一个子路由,当然是否设置子路由也与项目的复杂程度有关,可根据实际情况而定。
二.navMenu(侧边栏)的实现
- 首先拿到所有的菜单数据如下menu
- 用计算属性判断是否有子菜单,即判断是否有children,并拿到这两个值hasChildren和noChildren
computed: {
hasChildren() {
return this.menu.filter((item) => item.children)
},
noChildren() {
return this.menu.filter((item) => !item.children)
},
},
3.在template模版里面先遍历没有子菜单的,再通过hasChildren拿到子菜单,然后进行遍历
<template>
<div id="aside">
<el-menu
:default-active="activeMenu"
class="el-menu-vertical-demo"
:collapse="collapse"
:collapse-transition="false"
text-color="black"
active-text-color="#ffd04b"
>
<el-menu-item
:index="v.label"
v-for="v in noChildren"
:key="v.name"
@click="clickMenu(v)"
>
<i :class="`el-icon-${v.icon}`"></i>
<span slot="title">{{ v.label }}</span>
</el-menu-item>
<el-submenu :index="v.label" v-for="v in hasChildren" :key="v.name">
<template slot="title">
<i :class="`el-icon-${v.icon}`"></i>
<span slot="title">{{ v.label }}</span>
</template>
<el-menu-item-group
v-for="value in v.children"
:key="value.name"
>
<el-menu-item :index="value.label" @click="clickMenu(value)"
>{{ value.label }}
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</div>
</template>
4.给每一个菜单绑定点击事件 @click="clickMenu(value)",并实现点击时候进行路由跳转(为了防止路由跳转同样的地址而报错,需要在跳转前进行判断)
methods: {
clickMenu(item) {
// console.log("item", item)
// 路由跳转
// this.$route 此处的$route是指当前页面的路由,而$router是指router实例
if (
this.$route.path !== item.path &&
!(this.$route.path === '/home' && item.path === '/')
) {
// 同页面的路由进行跳转
this.$router.push(item.path)
}
},
},
5.侧边栏的整体实现
<template>
<div id="aside">
<el-menu
:default-active="activeMenu"
class="el-menu-vertical-demo"
:collapse="collapse"
:collapse-transition="false"
text-color="black"
active-text-color="#ffd04b"
>
<el-menu-item
:index="v.label"
v-for="v in noChildren"
:key="v.name"
@click="clickMenu(v)"
>
<i :class="`el-icon-${v.icon}`"></i>
<span slot="title">{{ v.label }}</span>
</el-menu-item>
<el-submenu :index="v.label" v-for="v in hasChildren" :key="v.name">
<template slot="title">
<i :class="`el-icon-${v.icon}`"></i>
<span slot="title">{{ v.label }}</span>
</template>
<el-menu-item-group
v-for="value in v.children"
:key="value.name"
>
<el-menu-item :index="value.label" @click="clickMenu(value)"
>{{ value.label }}
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</div>
</template>
<script>
export default {
name: 'App',
props: {
collapse: {
type: Boolean,
default: true,
},
},
data() {
return {
menu: [
{
path: '/home',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home.vue',
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'Mall.vue',
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'User.vue',
},
{
label: '其他',
icon: 'location',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting',
url: 'Other/PageOne',
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting',
url: 'Other/pageTwo',
},
],
},
],
activeMenu: '',
}
},
created() {
this.$bus.$on('menu-select', (tab) => {
// this.clickMenu(tab.$route)
// console.log(tab._props.name,'name')
this.activeMenu = tab.label
})
},
methods: {
clickMenu(item) {
// console.log("item", item)
// 路由跳转
// this.$route 此处的$route是指当前页面的路由,而$router是指router实例
if (
this.$route.path !== item.path &&
!(this.$route.path === '/home' && item.path === '/')
) {
// 同页面的路由进行跳转
this.$router.push(item.path)
}
// this.$store.commit('selectMenu', item)
this.$bus.$emit('addTab', item)
},
},
mounted() {},
computed: {
hasChildren() {
return this.menu.filter((item) => item.children)
},
noChildren() {
return this.menu.filter((item) => !item.children)
},
},
watch: {},
}
</script>
<style lang="less">
.el-menu-vertical-demo > li:first-child {
border-right: 3px solid red;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min-height: 100vh;
}
</style>
二.header部分面包屑的实现
- 在data里面定义一个breadcrumb存储路由跳转信息
- 定义一个方法generateBreadcrumb,通过$route里面的matched属性获取路由信息,并存储在breadcrumb
methods: { generateBreadcrumb(route) { console.log("route", route) const matched = route.matched this.breadcrumb = matched // console.log("this.breadcrumb", this.breadcrumb) }, },
- 在created中触发generateBreadcrumb方法,并将当前的路由信息传过去
created() {
this.generateBreadcrumb(this.$route)
},
4.点击侧边栏之后通过watch属性检测路由变化,并继续触发generateBreadcrumb方法
watch: {
$route(to) {
this.generateBreadcrumb(to)
},
},
5.在template中遍历路由名称(这个与route下面的index.js的路由配置有关,因为在路由配置时页面二的父路由是首页,首页遍历出:首页/页面二)
效果图:
<template>
<!-- 面包屑 -->
<el-breadcrumb separator="/" class="breadcrumb">
<el-breadcrumb-item
v-for="(item, index) in breadcrumb"
:key="index"
>
{{ item.meta.title }}
</el-breadcrumb-item>
</el-breadcrumb>
</template>
6.面包屑实现总代码
<template>
<!-- 面包屑 -->
<el-breadcrumb separator="/" class="breadcrumb">
<el-breadcrumb-item
v-for="(item, index) in breadcrumb"
:key="index"
>
{{ item.meta.title }}
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script>
export default {
data() {
return {
breadcrumb: [],
}
},
created() {
this.generateBreadcrumb(this.$route)
},
watch: {
$route(to) {
this.generateBreadcrumb(to)
},
},
methods: {
generateBreadcrumb(route) {
// console.log("route", route)
const matched = route.matched
this.breadcrumb = matched
// console.log("this.breadcrumb", this.breadcrumb)
},
},
}
</script>
<style lang="less">
.breadcrumb {
font-size: 16px;
display: flex;
line-height: 63px;
margin-left: -14px;
.el-breadcrumb__item:last-child .el-breadcrumb__inner {
color: white;
}
}
</style>
三.mian部分tabs按钮的实现
- 效果图:
- 实现思路:点击侧边栏菜单出现对应的按钮--->可以手动删除不需要的tab按钮-->点击tab按钮侧边栏对应的菜单也会出现高亮效果--->点击tab按钮侧边栏对应的菜单实现路由跳转
- 要想实现在navMenu组件点击菜单在tabs组件实现tab按钮添加,可以用自定义事件实现兄弟组件间的传值
- navMenu在clickMenu方法最后添加一句代码,将点击信息传给tabs组件
- tabs组件定义变量tabs来存储tab按钮信息
- tabs组件接受navMenu组件传过来的信息,并触发this.handleMenuClick(menuItem)这个方法,判断是否已经存在点击侧边栏菜单传过来对应的按钮,如果已经存在,则切换到已经存在的tab按钮,如果不存在,则添加对应的按钮
created() {
this.$bus.$on('addTab', (menuItem) => {
// 根据menuItem的信息动态添加tab按钮
this.handleMenuClick(menuItem)
})
},
methods: {
handleMenuClick(menuItem) {
let existingTab = this.tabs.find(
(tab) => tab.name === menuItem.name
)
if (existingTab) {
this.activeTab = existingTab.label // 切换到已存在的tab
} else {
this.tabs.push({
label: menuItem.label,
name: menuItem.name,
path: menuItem.path,
})
this.activeTab = menuItem.label // 设置新添加的tab为当前激活的tab
}
},
}
4.在template模版里面绑定 @tab-remove="handleTabClose"这个方法来移除tab按钮, @tab-click="handleTabClick"来实现路由跳转
<template>
<div class="tabMenu_div">
<el-tabs
v-model="activeTab"
type="card"
closable
@tab-click="handleTabClick"
@tab-remove="handleTabClose"
>
<el-tab-pane
:key="item.name"
v-for="(item, index) in tabs"
:label="item.label"
:name="item.name"
>
{{ item.label }}
</el-tab-pane>
</el-tabs>
</div>
</template>
methods: {
handleTabClose(tabName) {
this.tabs = this.tabs.filter((tab) => tab.name !== tabName)
},
handleTabClick(tab) {
if (this.$route.name !== tab.name &&!(this.$route.path === '/home' && tab.name === 'home')) {
this.$router.push({ name: tab._props.name })
}
},
},
5.点击tab按钮侧边栏对应的菜单高亮,可在tab组件用自定义事件,将点击信息传给navMenu组件
6.navMenu组件接受tab点击的信息,并赋值给:default-active="activeMenu"
created() {
this.$bus.$on('menu-select', (tab) => {
this.activeMenu = tab.label
})
},
7.tabs按钮总体代码实现
<template>
<div class="tabMenu_div">
<el-tabs
v-model="activeTab"
type="card"
closable
@tab-click="handleTabClick"
@tab-remove="handleTabClose"
>
<el-tab-pane
:key="item.name"
v-for="(item, index) in tabs"
:label="item.label"
:name="item.name"
>
{{ item.label }}
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
data() {
return {
activeTab: '',
routes: [],
selectKey: '',
tabs: [],
tabIndex: 2,
}
},
created() {
this.$bus.$on('addTab', (menuItem) => {
// 根据menuItem的信息动态添加tab按钮
// console.log('menuItem', menuItem)
this.handleMenuClick(menuItem)
// this.tabs.push({
// label: menuItem.label,
// name: menuItem.name,
// })
})
},
methods: {
handleMenuClick(menuItem) {
// debugger
// console.log(this.tabs,'this.tabs')
let existingTab = this.tabs.find(
(tab) => tab.name === menuItem.name
)
if (existingTab) {
// console.log(this.activeTab,'this.activeTab')
this.activeTab = existingTab.label // 切换到已存在的tab
} else {
this.tabs.push({
label: menuItem.label,
name: menuItem.name,
path: menuItem.path,
// content: menuItem.name,
})
this.activeTab = menuItem.label // 设置新添加的tab为当前激活的tab
}
},
handleTabClose(tabName) {
// console.log(tabName,'this.tabName')
this.tabs = this.tabs.filter((tab) => tab.name !== tabName)
},
handleTabClick(tab) {
if (this.$route.name !== tab.name &&!(this.$route.path === '/home' && tab.name === 'home')) {
this.$router.push({ name: tab._props.name })
}
this.$bus.$emit('menu-select', tab) // 通知兄弟组件更新navMenu的选中状态
},
},
watch: {
// $route: {
// handler: function (val, oldVal) {
// this.addRoute(val)
// },
// // 深度观察监听
// deep: true,
// },
},
mounted() {},
}
</script>
<style scoped lang="less"></style>