refactor:router&&sidemenu

This commit is contained in:
Pan 2017-11-24 16:30:40 +08:00 committed by 花裤衩
parent 29ec7f8a18
commit 86096e4eab
10 changed files with 290 additions and 159 deletions

View File

@ -1,11 +1,11 @@
<template> <template>
<el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path"> <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if='item.meta.title'>
<router-link v-if='item.redirect==="noredirect"||index==levelList.length-1' to="" class="no-redirect">{{item.name}}</router-link> <span v-if='item.redirect==="noredirect"||index==levelList.length-1' class="no-redirect">{{item.meta.title}}</span>
<router-link v-else :to="item.redirect||item.path">{{item.name}}</router-link> <router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
</el-breadcrumb> </el-breadcrumb>
</template> </template>
@ -23,8 +23,8 @@ export default {
getBreadcrumb() { getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name) let matched = this.$route.matched.filter(item => item.name)
const first = matched[0] const first = matched[0]
if (first && (first.name !== 'Home' || first.path !== '')) { if (first && first.name !== 'dashboard') {
matched = [{ name: 'Home', path: '/' }].concat(matched) matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
} }
this.levelList = matched this.levelList = matched
} }
@ -38,14 +38,14 @@ export default {
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb { .app-breadcrumb.el-breadcrumb {
display: inline-block; display: inline-block;
font-size: 14px; font-size: 14px;
line-height: 50px; line-height: 50px;
margin-left: 10px; margin-left: 10px;
.no-redirect { .no-redirect {
color: #97a8be; color: #97a8be;
cursor: text; cursor: text;
}
} }
}
</style> </style>

View File

@ -0,0 +1,56 @@
<template>
<div class='scroll-container' ref='scrollContainer' @mousewheel="handleScroll">
<div class='scroll-wrapper' ref='scrollWrapper' :style="{top: top + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const delta = 15
export default {
name: 'scrollBar',
data() {
return {
top: 0
}
},
methods: {
handleScroll(e) {
e.preventDefault()
const $container = this.$refs.scrollContainer
const $containerHeight = $container.offsetHeight
const $wrapper = this.$refs.scrollWrapper
const $wrapperHeight = $wrapper.offsetHeight
if (e.wheelDelta > 0) {
this.top = Math.min(0, this.top + e.wheelDelta)
} else {
if ($containerHeight - delta < $wrapperHeight) {
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
this.top = this.top
} else {
this.top = Math.max(this.top + e.wheelDelta, $containerHeight - $wrapperHeight - delta)
}
} else {
this.top = 0
}
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import '../../styles/variables.scss';
.scroll-container {
position: relative;
width: 100%;
height: 100%;
background-color: $menuBg;
.scroll-wrapper {
position: absolute;
width: 100%!important;
}
}
</style>

View File

@ -1,19 +1,23 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
const _import = require('./_import_' + process.env.NODE_ENV) const _import = require('./_import_' + process.env.NODE_ENV)
// in development env not use Lazy Loading,because Lazy Loading too many pages will cause webpack hot update too slow.so only in production use Lazy Loading // in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
/* layout */
import Layout from '../views/layout/Layout'
Vue.use(Router) Vue.use(Router)
/* Layout */
import Layout from '../views/layout/Layout'
/** /**
* icon : the icon show in the sidebar * hidden: true if `hidden:true` will not show in the sidebar(default is false)
* hidden : if `hidden:true` will not show in the sidebar * redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb
* redirect : if `redirect:noredirect` will not redirct in the levelbar * name:'router-name' the name is used by <keep-alive> (must set!!!)
* noDropdown : if `noDropdown:true` will not has submenu in the sidebar * meta : {
* meta : `{ role: ['admin'] }` will control the page role role: ['admin','editor'] will control the page role (you can set multiple roles)
title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar,
}
**/ **/
export const constantRouterMap = [ export const constantRouterMap = [
{ path: '/login', component: _import('login/index'), hidden: true }, { path: '/login', component: _import('login/index'), hidden: true },
@ -24,7 +28,11 @@ export const constantRouterMap = [
redirect: '/dashboard', redirect: '/dashboard',
name: 'Dashboard', name: 'Dashboard',
hidden: true, hidden: true,
children: [{ path: 'dashboard', component: _import('dashboard/index') }] children: [{
path: 'dashboard',
component: _import('dashboard/index'),
meta: { title: 'dashboard', icon: 'dashboard' }
}]
}, },
{ {
@ -32,9 +40,14 @@ export const constantRouterMap = [
component: Layout, component: Layout,
redirect: 'noredirect', redirect: 'noredirect',
name: 'Example', name: 'Example',
icon: 'example', meta: { title: 'Example', icon: 'example' },
children: [ children: [
{ path: 'index', name: 'Form', icon: 'form', component: _import('page/form') } {
path: 'index',
name: 'Form',
component: _import('page/form'),
meta: { title: 'Form', icon: 'form' }
}
] ]
}, },
@ -42,9 +55,12 @@ export const constantRouterMap = [
path: '/table', path: '/table',
component: Layout, component: Layout,
redirect: '/table/index', redirect: '/table/index',
icon: 'table', children: [{
noDropdown: true, path: 'index',
children: [{ path: 'index', name: 'Table', component: _import('table/index'), meta: { role: ['admin'] }}] name: 'Table',
component: _import('table/index'),
meta: { title: 'Table', icon: 'table', role: ['admin'] }}
]
}, },
{ path: '*', redirect: '/404', hidden: true } { path: '*', redirect: '/404', hidden: true }

View File

@ -1,6 +1,8 @@
@import './element-ui.scss'; @import './variables.scss';
@import './mixin.scss'; @import './mixin.scss';
@import './transition.scss'; @import './transition.scss';
@import './element-ui.scss';
@import './sidebar.scss';
body { body {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;

100
src/styles/sidebar.scss Normal file
View File

@ -0,0 +1,100 @@
#app {
// 主体区域
.main-container {
min-height: 100%;
transition: margin-left 0.28s;
margin-left: 180px;
} // 侧边栏
.sidebar-container {
transition: width 0.28s;
width: 180px!important;
height: 100%;
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
a {
display: inline-block;
width: 100%;
}
.svg-icon {
margin-right: 16px;
}
.el-menu {
border: none;
width: 100%;
}
}
.hideSidebar {
.sidebar-container,.sidebar-container .el-menu {
width: 36px!important;
// overflow: inherit;
}
.main-container {
margin-left: 36px;
}
}
.hideSidebar {
.submenu-title-noDropdown {
padding-left: 10px!important;
position: relative;
span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
transition: opacity .3s cubic-bezier(.55, 0, .1, 1);
opacity: 0;
display: inline-block;
}
&:hover {
span {
display: block;
border-radius: 3px;
z-index: 1002;
width: 140px;
height: 56px;
visibility: visible;
position: absolute;
right: -145px;
text-align: left;
text-indent: 20px;
top: 0px;
background-color: $subMenuBg!important;
opacity: 1;
}
}
}
.el-submenu {
&>.el-submenu__title {
padding-left: 10px!important;
&>span {
display: none;
}
.el-submenu__icon-arrow {
display: none;
}
}
.nest-menu {
.el-submenu__icon-arrow {
display: block!important;
}
span {
display: inline-block!important;
}
}
}
}
.nest-menu .el-submenu>.el-submenu__title,
.el-submenu .el-menu-item {
min-width: 180px!important;
background-color: $subMenuBg!important;
&:hover {
background-color: $menuHover!important;
}
}
.el-menu--collapse .el-menu .el-submenu{
min-width: 180px!important;
}
}

View File

@ -0,0 +1,4 @@
//sidebar
$menuBg:#304156;
$subMenuBg:#1f2d3d;
$menuHover:#001528;

View File

@ -1,8 +1,6 @@
<template> <template>
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}"> <div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
<div class="sidebar-wrapper"> <sidebar class="sidebar-container"></sidebar>
<sidebar class="sidebar-container"></sidebar>
</div>
<div class="main-container"> <div class="main-container">
<navbar></navbar> <navbar></navbar>
<app-main></app-main> <app-main></app-main>
@ -10,7 +8,6 @@
</div> </div>
</template> </template>
<script> <script>
import { Navbar, Sidebar, AppMain } from '@/views/layout/components' import { Navbar, Sidebar, AppMain } from '@/views/layout/components'
@ -30,52 +27,11 @@ export default {
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss"; @import "src/styles/mixin.scss";
.app-wrapper { .app-wrapper {
@include clearfix; @include clearfix;
position: relative; position: relative;
height: 100%; height: 100%;
width: 100%; width: 100%;
&.hideSidebar { }
.sidebar-wrapper {
transform: translate(-140px, 0);
.sidebar-container {
transform: translate(132px, 0);
}
&:hover {
transform: translate(0, 0);
.sidebar-container {
transform: translate(0, 0);
}
}
}
.main-container {
margin-left: 40px;
}
}
.sidebar-wrapper {
width: 180px;
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
transition: all .28s ease-out;
}
.sidebar-container {
transition: all .28s ease-out;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: -17px;
overflow-y: scroll;
}
.main-container {
min-height: 100%;
transition: all .28s ease-out;
margin-left: 180px;
}
}
</style> </style>

View File

@ -51,49 +51,44 @@ export default {
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.navbar { .navbar {
height: 50px; height: 50px;
line-height: 50px; line-height: 50px;
border-radius: 0px !important; border-radius: 0px !important;
.hamburger-container { .hamburger-container {
line-height: 58px; line-height: 58px;
height: 50px; height: 50px;
float: left; float: left;
padding: 0 10px; padding: 0 10px;
} }
.errLog-container { .screenfull {
display: inline-block; position: absolute;
position: absolute; right: 90px;
right: 150px; top: 16px;
} color: red;
.screenfull { }
position: absolute; .avatar-container {
right: 90px; height: 50px;
top: 16px; display: inline-block;
color: red; position: absolute;
} right: 35px;
.avatar-container { .avatar-wrapper {
height: 50px; cursor: pointer;
display: inline-block; margin-top: 5px;
position: absolute; position: relative;
right: 35px; .user-avatar {
.avatar-wrapper { width: 40px;
cursor: pointer; height: 40px;
margin-top: 5px; border-radius: 10px;
position: relative; }
.user-avatar { .el-icon-caret-bottom {
width: 40px; position: absolute;
height: 40px; right: -20px;
border-radius: 10px; top: 25px;
} font-size: 12px;
.el-icon-caret-bottom { }
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
} }
}
}
</style> </style>

View File

@ -1,24 +1,32 @@
<template> <template>
<div> <div class='menu-wrapper'>
<template v-for="item in routes"> <template v-for="item in routes">
<router-link v-if="!item.hidden&&item.noDropdown&&item.children.length>0" :to="item.path+'/'+item.children[0].path">
<el-menu-item :index="item.path+'/'+item.children[0].path"> <router-link v-if="!item.hidden&&item.children&&item.children.length===1" :to="item.path+'/'+item.children[0].path" :key='item.children[0].name'>
<svg-icon v-if='item.icon' :icon-class="item.icon" /> {{item.children[0].name}} <el-menu-item :index="item.path+'/'+item.children[0].path" class='submenu-title-noDropdown'>
<svg-icon v-if='item.children[0].meta&&item.children[0].meta.icon' :icon-class="item.children[0].meta.icon"></svg-icon>
<span v-if='item.children[0].meta&&item.children[0].meta.title'>{{item.children[0].meta.title}}</span>
</el-menu-item> </el-menu-item>
</router-link> </router-link>
<el-submenu :index="item.name" v-if="!item.noDropdown&&!item.hidden">
<el-submenu v-if="!item.hidden&&item.children&&item.children.length>1" :index="item.name||item.path" :key='item.name'>
<template slot="title"> <template slot="title">
<svg-icon v-if='item.icon' :icon-class="item.icon" /> {{item.name}} <svg-icon v-if='item.meta&&item.meta.icon' :icon-class="item.meta.icon"></svg-icon>
<span v-if='item.meta&&item.meta.title'>{{item.meta.title}}</span>
</template> </template>
<template v-for="child in item.children" v-if='!child.hidden'>
<sidebar-item class='menu-indent' v-if='child.children&&child.children.length>0' :routes='[child]'> </sidebar-item> <template v-if='!child.hidden' v-for="child in item.children">
<router-link v-else class="menu-indent" :to="item.path+'/'+child.path"> <sidebar-item class='nest-menu' v-if='child.children&&child.children.length>0' :routes='[child]' :key='child.path'></sidebar-item>
<router-link v-else :to="item.path+'/'+child.path" :key='child.name'>
<el-menu-item :index="item.path+'/'+child.path"> <el-menu-item :index="item.path+'/'+child.path">
{{child.name}} <svg-icon v-if='child.meta&&child.meta.icon' :icon-class="child.meta.icon"></svg-icon>
<span v-if='child.meta&&child.meta.title'>{{child.meta.title}}</span>
</el-menu-item> </el-menu-item>
</router-link> </router-link>
</template> </template>
</el-submenu> </el-submenu>
</template> </template>
</div> </div>
</template> </template>
@ -33,14 +41,3 @@ export default {
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped>
.svg-icon {
margin-right: 10px;
}
.hideSidebar .menu-indent{
display: block;
text-indent: 10px;
}
</style>

View File

@ -1,23 +1,28 @@
<template> <template>
<el-menu mode="vertical" theme="dark" :default-active="$route.path"> <scroll-bar>
<sidebar-item :routes="routes"></sidebar-item> <el-menu mode="vertical" unique-opened :default-active="$route.path" :collapse="isCollapse" background-color="#304156" text-color="#fff" active-text-color="#409EFF">
</el-menu> <sidebar-item :routes='routes'></sidebar-item>
</el-menu>
</scroll-bar>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem' import SidebarItem from './SidebarItem'
import ScrollBar from '@/components/ScrollBar'
export default { export default {
components: { SidebarItem }, components: { SidebarItem, ScrollBar },
computed: { computed: {
...mapGetters([
'sidebar'
]),
routes() { routes() {
return this.$router.options.routes return this.$router.options.routes
},
isCollapse() {
return !this.sidebar.opened
} }
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped>
.el-menu {
min-height: 100%;
}
</style>