先看一下最后的效果

image.png
折叠后

image.png
可以看页面可以分为三部分,左边的sidebar、上面的topbar、和内容appMain
来看一下我代码的结构

image.png
上代码
// sidebar.vue
<template>
<div class="sidebar-container" :class="{'collapse-width': isCollapse}">
<div class="logo" :class="{'collapse-logo': isCollapse}">
LOGO
</div>
<el-menu
:default-active="onRoutes"
class="el-menu-vertical"
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#10B9D3"
mode="vertical"
:collapse="isCollapse"
unique-opened
router
>
<el-menu-item index="dashboard">
<i class="el-icon-menu"></i>
<span slot="title">控制面板</span>
</el-menu-item>
<el-menu-item index="link1">
<i class="el-icon-menu"></i>
<span slot="title">导航一</span>
</el-menu-item>
<el-menu-item index="link2">
<i class="el-icon-document"></i>
<span slot="title">导航二</span>
</el-menu-item>
</el-menu>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
onRoutes() {
return this.$route.path.replace("/", "");
},
...mapState({
isCollapse: (state) => state.common.isCollapse
})
},
methods: {}
}
</script>
<style lang="scss" scoped>
.collapse-width {
width: 64px !important;
}
.collapse-logo {
font-size: 18px !important;
}
.sidebar-container {
width: 180px;
height: 100%;
position: fixed;
top: 0;
bottom: 0;
left: 0;
transition: width 0.28s;
z-index: 1001;
overflow: hidden;
.el-menu-vertical:not(.el-menu--collapse) {
width: 180px;
}
.el-menu {
border: none;
height: 100%;
width: 100% !important;
overflow-y: auto;
}
.logo {
width: 100%;
height: 50px;
line-height: 50px;
font-size: 30px;
text-align: center;
background-color: rgb(48, 65, 86);
color: #fff;
}
}
</style>
// topbar.vue
<template>
<div class="topbar-container">
<div class="toggle-btn" @click="toggleSidebar">
<i class="fa fa-navicon"></i>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import BreadCrumd from './breadCrumd'
export default {
data() {
return {
}
},
components: {
BreadCrumd
},
computed: {
...mapState({
isCollapse: (state) => state.common.isCollapse
})
},
methods: {
...mapActions(['toggleSidebar'])
}
}
</script>
<style lang="scss" scoped>
.topbar-container {
width: 100%;
height: 50px;
line-height: 50px;
border-bottom: 1px solid #ccc;
background-color: #fff;
.toggle-btn {
display: inline-block;
padding: 0 20px;
}
}
</style>
// appMain.vue
<template>
<div class="app-main-container">
<transition name="fade-transform" mode="out-in">
<router-view></router-view>
</transition>
</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
.app-main-container {
position: absolute;
width: 100%;
height: calc(100% - 70px);
margin: 10px;
padding: 20px;
background-color: #fff;
}
</style>
// Breadcrumb
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp' // npm包,运行 npm install path-to-regexp --save 下载
export default {
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
// only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (!this.isDashboard(first)) {
matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched)
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
isDashboard(route) {
const name = route && route.name
if (!name) {
return false
}
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>
// 组装三个组件的index.vue
<template>
<div>
<SideBar></SideBar>
<div class="right-container" :class="{'collapse-ml': isCollapse}">
<TopBar></TopBar>
<AppMain></AppMain>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import SideBar from './sideBar'
import TopBar from './TopBar/index'
import AppMain from './appMain'
export default {
computed: {
...mapState({
isCollapse: (state) => state.common.isCollapse
})
},
components: {
SideBar,
TopBar,
AppMain
}
}
</script>
<style lang="scss" scoped>
.right-container{
width: 100%;
min-height: 100%;
margin-left: 180px;
transition: margin-left 0.28s;
}
.collapse-ml{
margin-left: 64px
}
</style>
// vuex common.js
const common = {
state: {
isCollapse: false
},
mutations: {
// 折叠侧边栏
TOGGLE_SIDEBAR(state) {
state.isCollapse = !state.isCollapse
}
},
actions: {
toggleSidebar({ commit }) {
commit('TOGGLE_SIDEBAR')
}
}
}
export default common