全网最全攻略之基于vue-admin-template模版改造左上布局侧边栏

先看示例图

image.png

我们逐一来分析:

1,既然是在顶部改造topbar,那么我们首先来找到对应的位置来写dom元素。(在src > layout > index.vue)

我是topBar我是topBar我是topBar我是topBar我是topBar我是topBar

2,写完可以发现,顶部出现了 我是topBar我是topBar我是topBar我是topBar我是topBar我是topBar 的字样,但是左侧没有被顶下来,是因为左侧使用了定位,我们来找到定位的地方,把top注释掉(src > styles > sidebar.scss 大概在第10行可以找到 .sidebar-container 的样式

image.png

.sidebar-container { 
        transition: width 0.28s;
        width: $sideBarWidth !important;
        background-color: $menuBg;
        height: 100%;
        position: fixed;
        font-size: 0px;
        //改动的地方start
        // top: 0;
        //改动的地方end
        bottom: 0;
        left: 0;
        z-index: 1001;
        overflow: hidden;
}

3,改完发现没效果,别急,我们来改一下height:90%试一下

.sidebar-container { 
        transition: width 0.28s;
        width: $sideBarWidth !important;
        background-color: $menuBg;
       //改动的地方start
        height: 90%;
      //改动的地方end
        position: fixed;
        font-size: 0px;
        //改动的地方start
        // top: 0;
        //改动的地方end
        bottom: 0;
        left: 0;
        z-index: 1001;
        overflow: hidden;
}

4,改完再看下,发现下来了,说明我们改的没毛病,就是这


image.png

5,这时候 你会怎么想怎么做?我想到的是左侧区域怎么也能下来呢?怎么和左侧的顶部持平呢?带着疑问,我们继续往下走。

image.png

通过上图可以看出,右侧主体区域的最外层的class类名是main-container,我们尝试着改下min-height试试看呢
ps:改过以后,发现没有变化,无用,回退
这时候,就要想,可不可以 加一个margin-top,让他下来呢,那就加上试试。

.main-container {
        min-height: 100%;
        transition: margin-left .28s;
        margin-left: $sideBarWidth;
        position: relative;
        // 改动的地方start
        margin-top: 50px;
        // 改动的地方end
}

下图可以看到,确实生效了。

image.png

但是 margin-top 的值给多少才合适呢?通过分析得知, margin-top 的值 应该是顶部你想要设置的高度的像素,比如我们顶部 想要设置高度56px,那么我们的 margin-top 的值就是56px,这时候最好设置一个变量方便我们去管理。在哪设置变量呢,路径(src > styles > variables.scss

$subMenuBg:#1f2d3d;
$subMenuHover:#001528;

$sideBarWidth: 210px;
// 改的的地方start
$topBarHeight: 56px;
// 改的的地方end

然后把 margin-top 的值 改成$topBarHeight:,就是这样了

image.png

6,问题随着而来,左侧slidbar 的高度肯定不能是固定的90%,那应该是多少呢?应该是100vh - 顶部的高度 ,所以我们还要设置一个变量来管理这个slidbar的高度,同样在(src > styles > variables.scss

$menuBg:#304156;
$menuHover:#263445;

$subMenuBg:#1f2d3d;
$subMenuHover:#001528;

$sideBarWidth: 210px;
// 改的的地方start
$topBarHeight: 56px;
$contentHeight: calc(100vh - 56px);
// 改的的地方end
.sidebar-container {
        transition: width 0.28s;
        width: $sideBarWidth !important;
        background-color: $menuBg;
         // 改的的地方start
        height: $contentHeight;
        // 改的的地方end
        position: fixed;
        font-size: 0px;
        //改动的地方start
        // top: 0;
        //改动的地方end
        bottom: 0;
        left: 0;
        z-index: 1001;
        overflow: hidden;
}

然后把顶部的文字注释掉 完事是这样的!


image.png

image.png

左边和右边都持平了。达到了我们想要的效果

下面开始改造顶部。

1,在《src > layout > components》下新建 Topbar.vue,效仿 Navbar的引入方式

// 改的地方start
import { Navbar, Sidebar, AppMain, Topbar } from './components'
// 改的地方end
 components: {
    Navbar,
    Sidebar,
    AppMain,
    // 改的地方start
    Topbar
    // 改的地方end
  },

/*src > layout > components > index.js */
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
export { default as AppMain } from './AppMain'
/** 改动的地方 start */
export { default as Topbar } from './Topbar'
/** 改动的地方 end */

完事在如图的地方引入

image.png

然后就是这样


image.png

2,在topbar页面里写入这些代码






页面肯定会报错,我们跟着报错一步一步来解决

image.png

首先来解决 "method "variables" is not defined的问题, variables 是一个计算属性,return出来 variables 文件 先把 variables scss文件 引过来

import variables from '@/styles/variables.scss'

然后在计算属性里 这样写

computed:{
  variables() {
      return variables
    },
}

在来解决 method "activeMenu" is not defined的问题
activeMenu也是一个计算属性

activeMenu() {
      const route = this.$route
      const { meta, path } = route
      // if set path, the sidebar will highlight the path you set
      if (meta.activeMenu) {
        return meta.activeMenu
      }
      // 如果是首页,首页高亮
      if (path === '/dashboard') {
        return '/'
      }
      // 如果不是首页,高亮一级菜单
      const activeMenu = '/' + path.split('/')[1]
      return activeMenu
},

在来解决 method "handleSelect" is not defined的问题,在methods里定义这个方法

 methods: {
    handleSelect() {}
  },

在解决 method "routes" is not defined,routes是我们要循环遍历的路由,在data里定义一个变量,把路由引过来赋值给这个变量

import { constantRoutes } from '@/router'

 data() {
    return {
      routes: constantRoutes
    }
  },

刷新页面在来解决 method "resolvePath" is not defined的问题,在methods里定义

 resolvePath(item) {
      // 如果是个完成的url直接返回
      if (isExternal(item.path)) {
        return item.path
      }
      // 如果是首页,就返回重定向路由
      if (item.path === '/') {
        const path = item.redirect
        return path
      }

      // 如果有子项,默认跳转第一个子项路由
      let path = ''
      /**
       * item 路由子项
       * parent 路由父项
       */
      const getDefaultPath = (item, parent) => {
        // 如果path是个外部链接(不建议),直接返回链接,存在个问题:如果是外部链接点击跳转后当前页内容还是上一个路由内容
        if (isExternal(item.path)) {
          path = item.path
          return
        }
        // 第一次需要父项路由拼接,所以只是第一个传parent
        if (parent) {
          path += (parent.path + '/' + item.path)
        } else {
          path += ('/' + item.path)
        }
        // 如果还有子项,继续递归
        if (item.children) {
          getDefaultPath(item.children[0])
        }
      }

      if (item.children) {
        getDefaultPath(item.children[0], item)
        return path
      }

      return item.path
    },

在引入 import { isExternal } from '@/utils/validate'
在把app-link组件引入过来

import AppLink from './Sidebar/Link'
components: {
    AppLink
  },

完事是这样


image.png

3,在来把css样式写一写,在styles文件夹下新建topbar.scss

.top-nav {
    // margin-left: $sideBarWidth;
    width: 100%;
    background-color: #304156;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1001;
    overflow: hidden;

    .log {
        padding: 0 20px;
        line-height: 56px;
        font-size: 24px;
        font-weight: bold;
        color: rgb(191, 203, 217);
        float: left;
        margin-right: 50px;
    }

    .el-menu {
        float: left;
        border: none !important;
        background-color: #304156;

        .nav-item {
            display: inline-block;

            .el-menu-item {
                color: rgb(191, 203, 217);

                &:hover {
                    background-color: $subMenuHover !important;
                }

                &:focus {
                    background-color: $subMenuHover !important;
                    // color: $subMenuActiveText !important;
                }
            }
        }
    }

    .right-menu {
        float: right;
        height: 100%;

        &:focus {
            outline: none;
        }

        .right-menu-item {
            display: inline-block;
            padding: 0 8px;
            height: 100%;
            font-size: 18px;
            color: #5a5e66;
            vertical-align: text-bottom;

            &.hover-effect {
                cursor: pointer;
                transition: background .3s;

                &:hover {
                    background: rgba(0, 0, 0, .025)
                }
            }
        }

        .avatar-container {
            margin-right: 30px;

            .avatar-wrapper {
                margin-top: 5px;
                position: relative;

                .user-avatar {
                    cursor: pointer;
                    width: 40px;
                    height: 40px;
                    border-radius: 10px;
                }

                .el-icon-caret-bottom {
                    cursor: pointer;
                    position: absolute;
                    right: -20px;
                    top: 25px;
                    font-size: 12px;
                }
            }
        }
    }
}

4,在index.js里引入topbar.scss

@import './variables.scss';
@import './mixin.scss';
@import './transition.scss';
@import './element-ui.scss';
@import './sidebar.scss';
// 修改的地方start
@import './topbar.scss';
// 修改的地方end

这样我们就把一级菜单渲染到了顶部


image.png

然后把头像的东西渲染到上面


image.png

把代码放这里
image.png

下面解决报错,把头像从vuex拿进来


image.png
import { mapGetters } from 'vuex'
...mapGetters(['avatar'])

最后把下面的右侧区域删掉就ok了。

image.png

5,上方的布局算是实现了,但是左侧我们想要渲染子级菜单,并不想渲染所有的,下面我们就来改造,(思路是这样 :当点击顶部的一级菜单时,拿到点击菜单的子级路由,保存起来,左侧去渲染这个保存起来的子级路由
6,找到我们topbar.vue 找到select事件定义的方法handleSelect
image.png

我们可以看到该事件会回调选中菜单的index 和path,开始写逻辑

 handleSelect(key, keyPath) {
      //得到子级路由
      const route = this.routes.find(item => item.path === key)
      console.log(route);
      // 把选中路由的子路由保存store
    },

7,下一步就是 把选中路由的子路由保存store,那么我们就要在store里定义方法供这里调用。

现在 《src > store > modules 》下新建一个 permission.js

const state = {
    currentRoutes: {}
}

const mutations = {
    SET_CURRENT_ROUTES: (state, routes) => {
        state.currentRoutes = routes
    }
}

export default {
    namespaced: true,
    state,
    mutations
}

在 《src > store > index.js 》下引入 permission.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
/** 改动的地方*/
import permission from './modules/permission'

Vue.use(Vuex)

const store = new Vuex.Store({
    modules: {
        app,
        settings,
        user,
        /** 改动的地方*/
        permission
    },
    getters
})

export default store

好,我们现在调用store的方法,把子级路由存起来,

handleSelect(key, keyPath) {
      // 把选中路由的子路由保存store
      const route = this.routes.find(item => item.path === key)
      this.$store.commit('permission/SET_CURRENT_ROUTES', route)
    },

通过调试工具可以看到,确实把子级路由存进去了。

下面把 src > layout > components > Sidebar > index.vue 里的routes计算属性改成下面这样

 routes() {
      // return this.$router.options.routes
      // 改动的地方
      return this.$store.state.permission.currentRoutes.children
    },

点击一级菜单看页面,已经正确渲染了。但是存在几个问题,现在一个一个来解决。

(1)刷新页面左侧子级路导航没了!
众所周知,vuex里面的数据刷新页面就会丢失,这就是 左侧子级路导航没了 的原因,下面开始解决
在topbar.vue里新建一个方法 initCurrentRoutes ,在页面加载的时候就调用,代码如下

// 通过当前路径找到二级菜单对应项,存到store,用来渲染左侧菜单
    initCurrentRoutes() {
      const { path } = this.$route
      let route = this.routes.find(
        item => item.path === '/' + path.split('/')[1]
      )
      // 如果找不到这个路由,说明是首页
      if (!route) {
        route = this.routes.find(item => item.path === '/')
      }
      this.$store.commit('permission/SET_CURRENT_ROUTES', route)
    },
 mounted() {
    this.initCurrentRoutes()
  },

在刷新页面就不会出现上面那种情况了。

(2)点击顶部一级菜单左侧没有高亮显示第一个子级路由
src > layout > components > Sidebar > SidebarItem.vue里 修改这里

image.png

/** 改动的地方 */
      const currentRoutes = this.$store.state.permission.currentRoutes
      if (currentRoutes && currentRoutes.path) {
        return path.resolve(currentRoutes.path, this.basePath, routePath)
  }

ok!

(3)点击左侧跳转404
完事你会惊讶的发现,这一个问题竟然也被修复了 哈哈...

至此,我们已经完成了 顶部一级导航,左侧子级导航的布局修改!如果你想精益求精,在左侧没有一个子级的时候,把侧边栏隐藏掉。
topbar.vue新建一个方法

// 设置侧边栏的显示和隐藏
    setSidebarHide(route) {
      if (!route.children || route.children.length === 1) {
        this.$store.dispatch('app/toggleSideBarHide', true)
      } else {
        this.$store.dispatch('app/toggleSideBarHide', false)
      }
    },

handleSelect 、 initCurrentRoutes方法内调用

 this.setSidebarHide(route)

store的 app.js里的actions里新建toggleSideBarHide方法,在mutations里新建SET_SIDEBAR_HIDE ,state 里 加一个hide

SET_SIDEBAR_HIDE: (state, status) => {
        state.sidebar.hide = status
    }
 /** 改动的地方*/
    toggleSideBarHide({ commit }, status) {
        commit('SET_SIDEBAR_HIDE', status)
    },
 /** 改动的地方*/
const state = {
    sidebar: {
        opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
        withoutAnimation: false,
        hide: false
    },
    device: 'desktop'
}

然后在这里加上 v-if 判断


image.png
 
 

完事可以看到,侧边栏消失了,但是左侧还占位,但是可以看到class类名加上了,我们把margin-left 设置成0 就可以了


image.png

ok 最后的效果


image.png

但是,主体区域好像高出了顶部的高度的距离,
看图理解


image.png

AppMain.vue里修改

.app-main {
  /** 改动的地方 */
  /*107 = navbar 50 + topbar 57  */
  min-height: calc(100vh - 107px);
  width: 100%;
  position: relative;
  overflow: hidden;
}
image.png

image.png

image.png

image.png

image.png

image.png

好了 根据截图的代码全局搜一下 就可以找到位置啦
大工告成。累死我了

你可能感兴趣的:(全网最全攻略之基于vue-admin-template模版改造左上布局侧边栏)