Kaynağa Gözat

refactor:router&&sidemenu

Pan 7 yıl önce
ebeveyn
işleme
86096e4eab

+ 17 - 17
src/components/Breadcrumb/index.vue

@@ -1,11 +1,11 @@
1 1
 <template>
2 2
   <el-breadcrumb class="app-breadcrumb" separator="/">
3
-  <transition-group name="breadcrumb">
4
-    <el-breadcrumb-item v-for="(item,index)  in levelList" :key="item.path">
5
-      <router-link v-if='item.redirect==="noredirect"||index==levelList.length-1' to="" class="no-redirect">{{item.name}}</router-link>
6
-      <router-link v-else :to="item.redirect||item.path">{{item.name}}</router-link>
7
-    </el-breadcrumb-item>
8
-     </transition-group>
3
+    <transition-group name="breadcrumb">
4
+      <el-breadcrumb-item v-for="(item,index)  in levelList" :key="item.path" v-if='item.meta.title'>
5
+        <span v-if='item.redirect==="noredirect"||index==levelList.length-1' class="no-redirect">{{item.meta.title}}</span>
6
+        <router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link>
7
+      </el-breadcrumb-item>
8
+    </transition-group>
9 9
   </el-breadcrumb>
10 10
 </template>
11 11
 
@@ -23,8 +23,8 @@ export default {
23 23
     getBreadcrumb() {
24 24
       let matched = this.$route.matched.filter(item => item.name)
25 25
       const first = matched[0]
26
-      if (first && (first.name !== 'Home' || first.path !== '')) {
27
-        matched = [{ name: 'Home', path: '/' }].concat(matched)
26
+      if (first && first.name !== 'dashboard') {
27
+        matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
28 28
       }
29 29
       this.levelList = matched
30 30
     }
@@ -38,14 +38,14 @@ export default {
38 38
 </script>
39 39
 
40 40
 <style rel="stylesheet/scss" lang="scss" scoped>
41
-.app-breadcrumb.el-breadcrumb {
42
-  display: inline-block;
43
-  font-size: 14px;
44
-  line-height: 50px;
45
-  margin-left: 10px;
46
-  .no-redirect {
47
-    color: #97a8be;
48
-    cursor: text;
41
+  .app-breadcrumb.el-breadcrumb {
42
+    display: inline-block;
43
+    font-size: 14px;
44
+    line-height: 50px;
45
+    margin-left: 10px;
46
+    .no-redirect {
47
+      color: #97a8be;
48
+      cursor: text;
49
+    }
49 50
   }
50
-}
51 51
 </style>

+ 56 - 0
src/components/ScrollBar/index.vue

@@ -0,0 +1,56 @@
1
+<template>
2
+  <div class='scroll-container' ref='scrollContainer' @mousewheel="handleScroll">
3
+    <div class='scroll-wrapper' ref='scrollWrapper' :style="{top: top + 'px'}">
4
+      <slot></slot>
5
+    </div>
6
+  </div>
7
+</template>
8
+
9
+<script>
10
+const delta = 15
11
+export default {
12
+  name: 'scrollBar',
13
+  data() {
14
+    return {
15
+      top: 0
16
+    }
17
+  },
18
+  methods: {
19
+    handleScroll(e) {
20
+      e.preventDefault()
21
+      const $container = this.$refs.scrollContainer
22
+      const $containerHeight = $container.offsetHeight
23
+      const $wrapper = this.$refs.scrollWrapper
24
+      const $wrapperHeight = $wrapper.offsetHeight
25
+      if (e.wheelDelta > 0) {
26
+        this.top = Math.min(0, this.top + e.wheelDelta)
27
+      } else {
28
+        if ($containerHeight - delta < $wrapperHeight) {
29
+          if (this.top < -($wrapperHeight - $containerHeight + delta)) {
30
+            this.top = this.top
31
+          } else {
32
+            this.top = Math.max(this.top + e.wheelDelta, $containerHeight - $wrapperHeight - delta)
33
+          }
34
+        } else {
35
+          this.top = 0
36
+        }
37
+      }
38
+    }
39
+  }
40
+}
41
+</script>
42
+
43
+<style rel="stylesheet/scss" lang="scss" scoped>
44
+@import '../../styles/variables.scss';
45
+
46
+.scroll-container {
47
+  position: relative;
48
+  width: 100%;
49
+  height: 100%;
50
+  background-color: $menuBg;
51
+  .scroll-wrapper {
52
+    position: absolute;
53
+     width: 100%!important;
54
+  }
55
+}
56
+</style>

+ 31 - 15
src/router/index.js

@@ -1,19 +1,23 @@
1 1
 import Vue from 'vue'
2 2
 import Router from 'vue-router'
3 3
 const _import = require('./_import_' + process.env.NODE_ENV)
4
-// 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
5
-
6
-/* layout */
7
-import Layout from '../views/layout/Layout'
4
+// 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;
5
+// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
8 6
 
9 7
 Vue.use(Router)
10 8
 
9
+/* Layout */
10
+import Layout from '../views/layout/Layout'
11
+
11 12
 /**
12
-* icon : the icon show in the sidebar
13
-* hidden : if `hidden:true` will not show in the sidebar
14
-* redirect : if `redirect:noredirect` will not redirct in the levelbar
15
-* noDropdown : if `noDropdown:true` will not has submenu in the sidebar
16
-* meta : `{ role: ['admin'] }`  will control the page role
13
+* hidden: true                   if `hidden:true` will not show in the sidebar(default is false)
14
+* redirect: noredirect           if `redirect:noredirect` will no redirct in the breadcrumb
15
+* name:'router-name'             the name is used by <keep-alive> (must set!!!)
16
+* meta : {
17
+    role: ['admin','editor']     will control the page role (you can set multiple roles)
18
+    title: 'title'               the name show in submenu and breadcrumb (recommend set)
19
+    icon: 'svg-name'             the icon show in the sidebar,
20
+  }
17 21
 **/
18 22
 export const constantRouterMap = [
19 23
   { path: '/login', component: _import('login/index'), hidden: true },
@@ -24,7 +28,11 @@ export const constantRouterMap = [
24 28
     redirect: '/dashboard',
25 29
     name: 'Dashboard',
26 30
     hidden: true,
27
-    children: [{ path: 'dashboard', component: _import('dashboard/index') }]
31
+    children: [{
32
+      path: 'dashboard',
33
+      component: _import('dashboard/index'),
34
+      meta: { title: 'dashboard', icon: 'dashboard' }
35
+    }]
28 36
   },
29 37
 
30 38
   {
@@ -32,9 +40,14 @@ export const constantRouterMap = [
32 40
     component: Layout,
33 41
     redirect: 'noredirect',
34 42
     name: 'Example',
35
-    icon: 'example',
43
+    meta: { title: 'Example', icon: 'example' },
36 44
     children: [
37
-      { path: 'index', name: 'Form', icon: 'form', component: _import('page/form') }
45
+      {
46
+        path: 'index',
47
+        name: 'Form',
48
+        component: _import('page/form'),
49
+        meta: { title: 'Form', icon: 'form' }
50
+      }
38 51
     ]
39 52
   },
40 53
 
@@ -42,9 +55,12 @@ export const constantRouterMap = [
42 55
     path: '/table',
43 56
     component: Layout,
44 57
     redirect: '/table/index',
45
-    icon: 'table',
46
-    noDropdown: true,
47
-    children: [{ path: 'index', name: 'Table', component: _import('table/index'), meta: { role: ['admin'] }}]
58
+    children: [{
59
+      path: 'index',
60
+      name: 'Table',
61
+      component: _import('table/index'),
62
+      meta: { title: 'Table', icon: 'table', role: ['admin'] }}
63
+    ]
48 64
   },
49 65
 
50 66
   { path: '*', redirect: '/404', hidden: true }

+ 3 - 1
src/styles/index.scss

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

+ 100 - 0
src/styles/sidebar.scss

@@ -0,0 +1,100 @@
1
+#app {
2
+  // 主体区域
3
+  .main-container {
4
+    min-height: 100%;
5
+    transition: margin-left 0.28s;
6
+    margin-left: 180px;
7
+  } // 侧边栏
8
+  .sidebar-container {
9
+    transition: width 0.28s;
10
+    width: 180px!important;
11
+    height: 100%;
12
+    position: fixed;
13
+    top: 0;
14
+    bottom: 0;
15
+    left: 0;
16
+    z-index: 1001;
17
+    a {
18
+      display: inline-block;
19
+      width: 100%;
20
+    }
21
+    .svg-icon {
22
+      margin-right: 16px;
23
+    }
24
+    .el-menu {
25
+      border: none;
26
+      width: 100%;
27
+    }
28
+  }
29
+  .hideSidebar {
30
+    .sidebar-container,.sidebar-container .el-menu {
31
+      width: 36px!important;
32
+      // overflow: inherit;
33
+    }
34
+    .main-container {
35
+      margin-left: 36px;
36
+    }
37
+  }
38
+  .hideSidebar {
39
+    .submenu-title-noDropdown {
40
+      padding-left: 10px!important;
41
+      position: relative;
42
+      span {
43
+        height: 0;
44
+        width: 0;
45
+        overflow: hidden;
46
+        visibility: hidden;
47
+        transition: opacity .3s cubic-bezier(.55, 0, .1, 1);
48
+        opacity: 0;
49
+        display: inline-block;
50
+      }
51
+      &:hover {
52
+        span {
53
+          display: block;
54
+          border-radius: 3px;
55
+          z-index: 1002;
56
+          width: 140px;
57
+          height: 56px;
58
+          visibility: visible;
59
+          position: absolute;
60
+          right: -145px;
61
+          text-align: left;
62
+          text-indent: 20px;
63
+          top: 0px;
64
+          background-color: $subMenuBg!important;
65
+          opacity: 1;
66
+        }
67
+      }
68
+    }
69
+    .el-submenu {
70
+      &>.el-submenu__title {
71
+        padding-left: 10px!important;
72
+        &>span {
73
+          display: none;
74
+        }
75
+        .el-submenu__icon-arrow {
76
+          display: none;
77
+        }
78
+      }
79
+      .nest-menu {
80
+        .el-submenu__icon-arrow {
81
+          display: block!important;
82
+        }
83
+        span {
84
+          display: inline-block!important;
85
+        }
86
+      }
87
+    }
88
+  }
89
+  .nest-menu .el-submenu>.el-submenu__title,
90
+  .el-submenu .el-menu-item {
91
+    min-width: 180px!important;
92
+    background-color: $subMenuBg!important;
93
+    &:hover {
94
+      background-color: $menuHover!important;
95
+    }
96
+  }
97
+  .el-menu--collapse .el-menu .el-submenu{
98
+    min-width: 180px!important;
99
+  }
100
+}

+ 4 - 0
src/styles/variables.scss

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

+ 8 - 52
src/views/layout/Layout.vue

@@ -1,8 +1,6 @@
1 1
 <template>
2 2
   <div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
3
-    <div class="sidebar-wrapper">
4
-      <sidebar class="sidebar-container"></sidebar>
5
-    </div>
3
+    <sidebar class="sidebar-container"></sidebar>
6 4
     <div class="main-container">
7 5
       <navbar></navbar>
8 6
       <app-main></app-main>
@@ -10,7 +8,6 @@
10 8
   </div>
11 9
 </template>
12 10
 
13
-
14 11
 <script>
15 12
 import { Navbar, Sidebar, AppMain } from '@/views/layout/components'
16 13
 
@@ -30,52 +27,11 @@ export default {
30 27
 </script>
31 28
 
32 29
 <style rel="stylesheet/scss" lang="scss" scoped>
33
-    @import "src/styles/mixin.scss";
34
-    .app-wrapper {
35
-        @include clearfix;
36
-        position: relative;
37
-        height: 100%;
38
-        width: 100%;
39
-        &.hideSidebar {
40
-            .sidebar-wrapper {
41
-                transform: translate(-140px, 0);
42
-                .sidebar-container {
43
-                    transform: translate(132px, 0);
44
-                }
45
-                &:hover {
46
-                    transform: translate(0, 0);
47
-                    .sidebar-container {
48
-                        transform: translate(0, 0);
49
-                    }
50
-                }
51
-            }
52
-            .main-container {
53
-                margin-left: 40px;
54
-            }
55
-        }
56
-        .sidebar-wrapper {
57
-            width: 180px;
58
-            position: fixed;
59
-            top: 0;
60
-            bottom: 0;
61
-            left: 0;
62
-            z-index: 1001;
63
-            overflow: hidden;
64
-            transition: all .28s ease-out;
65
-        }
66
-        .sidebar-container {
67
-            transition: all .28s ease-out;
68
-            position: absolute;
69
-            top: 0;
70
-            bottom: 0;
71
-            left: 0;
72
-            right: -17px;
73
-            overflow-y: scroll;
74
-        }
75
-        .main-container {
76
-            min-height: 100%;
77
-            transition: all .28s ease-out;
78
-            margin-left: 180px;
79
-        }
80
-    }
30
+@import "src/styles/mixin.scss";
31
+.app-wrapper {
32
+  @include clearfix;
33
+  position: relative;
34
+  height: 100%;
35
+  width: 100%;
36
+}
81 37
 </style>

+ 38 - 43
src/views/layout/components/Navbar.vue

@@ -51,49 +51,44 @@ export default {
51 51
 </script>
52 52
 
53 53
 <style rel="stylesheet/scss" lang="scss" scoped>
54
-    .navbar {
55
-        height: 50px;
56
-        line-height: 50px;
57
-        border-radius: 0px !important;
58
-        .hamburger-container {
59
-            line-height: 58px;
60
-            height: 50px;
61
-            float: left;
62
-            padding: 0 10px;
63
-        }
64
-        .errLog-container {
65
-            display: inline-block;
66
-            position: absolute;
67
-            right: 150px;
68
-        }
69
-        .screenfull {
70
-            position: absolute;
71
-            right: 90px;
72
-            top: 16px;
73
-            color: red;
74
-        }
75
-        .avatar-container {
76
-            height: 50px;
77
-            display: inline-block;
78
-            position: absolute;
79
-            right: 35px;
80
-            .avatar-wrapper {
81
-                cursor: pointer;
82
-                margin-top: 5px;
83
-                position: relative;
84
-                .user-avatar {
85
-                    width: 40px;
86
-                    height: 40px;
87
-                    border-radius: 10px;
88
-                }
89
-                .el-icon-caret-bottom {
90
-                    position: absolute;
91
-                    right: -20px;
92
-                    top: 25px;
93
-                    font-size: 12px;
94
-                }
95
-            }
96
-        }
54
+.navbar {
55
+  height: 50px;
56
+  line-height: 50px;
57
+  border-radius: 0px !important;
58
+  .hamburger-container {
59
+    line-height: 58px;
60
+    height: 50px;
61
+    float: left;
62
+    padding: 0 10px;
63
+  }
64
+  .screenfull {
65
+    position: absolute;
66
+    right: 90px;
67
+    top: 16px;
68
+    color: red;
69
+  }
70
+  .avatar-container {
71
+    height: 50px;
72
+    display: inline-block;
73
+    position: absolute;
74
+    right: 35px;
75
+    .avatar-wrapper {
76
+      cursor: pointer;
77
+      margin-top: 5px;
78
+      position: relative;
79
+      .user-avatar {
80
+        width: 40px;
81
+        height: 40px;
82
+        border-radius: 10px;
83
+      }
84
+      .el-icon-caret-bottom {
85
+        position: absolute;
86
+        right: -20px;
87
+        top: 25px;
88
+        font-size: 12px;
89
+      }
97 90
     }
91
+  }
92
+}
98 93
 </style>
99 94
 

+ 18 - 21
src/views/layout/components/Sidebar/SidebarItem.vue

@@ -1,24 +1,32 @@
1 1
 <template>
2
-  <div>
2
+  <div class='menu-wrapper'>
3 3
     <template v-for="item in routes">
4
-      <router-link v-if="!item.hidden&&item.noDropdown&&item.children.length>0" :to="item.path+'/'+item.children[0].path">
5
-        <el-menu-item :index="item.path+'/'+item.children[0].path">
6
-          <svg-icon v-if='item.icon' :icon-class="item.icon" /> {{item.children[0].name}}
4
+
5
+      <router-link v-if="!item.hidden&&item.children&&item.children.length===1" :to="item.path+'/'+item.children[0].path" :key='item.children[0].name'>
6
+        <el-menu-item :index="item.path+'/'+item.children[0].path" class='submenu-title-noDropdown'>
7
+          <svg-icon v-if='item.children[0].meta&&item.children[0].meta.icon' :icon-class="item.children[0].meta.icon"></svg-icon>
8
+          <span v-if='item.children[0].meta&&item.children[0].meta.title'>{{item.children[0].meta.title}}</span>
7 9
         </el-menu-item>
8 10
       </router-link>
9
-      <el-submenu :index="item.name" v-if="!item.noDropdown&&!item.hidden">
11
+
12
+      <el-submenu v-if="!item.hidden&&item.children&&item.children.length>1" :index="item.name||item.path" :key='item.name'>
10 13
         <template slot="title">
11
-          <svg-icon v-if='item.icon' :icon-class="item.icon" /> {{item.name}}
14
+          <svg-icon v-if='item.meta&&item.meta.icon' :icon-class="item.meta.icon"></svg-icon>
15
+          <span v-if='item.meta&&item.meta.title'>{{item.meta.title}}</span>
12 16
         </template>
13
-        <template v-for="child in item.children" v-if='!child.hidden'>
14
-          <sidebar-item class='menu-indent' v-if='child.children&&child.children.length>0' :routes='[child]'> </sidebar-item>
15
-          <router-link v-else class="menu-indent" :to="item.path+'/'+child.path">
17
+
18
+        <template v-if='!child.hidden' v-for="child in item.children">
19
+          <sidebar-item class='nest-menu' v-if='child.children&&child.children.length>0' :routes='[child]' :key='child.path'></sidebar-item>
20
+
21
+          <router-link v-else :to="item.path+'/'+child.path" :key='child.name'>
16 22
             <el-menu-item :index="item.path+'/'+child.path">
17
-              {{child.name}}
23
+              <svg-icon v-if='child.meta&&child.meta.icon' :icon-class="child.meta.icon"></svg-icon>
24
+              <span v-if='child.meta&&child.meta.title'>{{child.meta.title}}</span>
18 25
             </el-menu-item>
19 26
           </router-link>
20 27
         </template>
21 28
       </el-submenu>
29
+
22 30
     </template>
23 31
   </div>
24 32
 </template>
@@ -33,14 +41,3 @@ export default {
33 41
   }
34 42
 }
35 43
 </script>
36
-
37
-<style rel="stylesheet/scss" lang="scss" scoped>
38
-.svg-icon {
39
-  margin-right: 10px;
40
-}
41
-.hideSidebar .menu-indent{
42
-  display: block;
43
-  text-indent: 10px;
44
-}
45
-</style>
46
-

+ 15 - 10
src/views/layout/components/Sidebar/index.vue

@@ -1,23 +1,28 @@
1 1
 <template>
2
-  <el-menu mode="vertical" theme="dark" :default-active="$route.path">
3
-    <sidebar-item :routes="routes"></sidebar-item>
4
-  </el-menu>
2
+  <scroll-bar>
3
+    <el-menu mode="vertical" unique-opened :default-active="$route.path" :collapse="isCollapse" background-color="#304156" text-color="#fff" active-text-color="#409EFF">
4
+      <sidebar-item :routes='routes'></sidebar-item>
5
+    </el-menu>
6
+  </scroll-bar>
5 7
 </template>
6 8
 
7 9
 <script>
10
+import { mapGetters } from 'vuex'
8 11
 import SidebarItem from './SidebarItem'
12
+import ScrollBar from '@/components/ScrollBar'
13
+
9 14
 export default {
10
-  components: { SidebarItem },
15
+  components: { SidebarItem, ScrollBar },
11 16
   computed: {
17
+    ...mapGetters([
18
+      'sidebar'
19
+    ]),
12 20
     routes() {
13 21
       return this.$router.options.routes
22
+    },
23
+    isCollapse() {
24
+      return !this.sidebar.opened
14 25
     }
15 26
   }
16 27
 }
17 28
 </script>
18
-
19
-<style rel="stylesheet/scss" lang="scss" scoped>
20
-.el-menu {
21
-  min-height: 100%;
22
-}
23
-</style>