vue-element-admin实现顶部菜单栏,并可互相切换

目录

零、先上传效果图

一、配置顶部菜单

二、配置右侧切换菜单位置按钮

三、配置index.vue和Navbar.vue


零、先上传效果图

 一、配置顶部菜单

1.复制一份src/layout/componets/Sidebar所有文件至同级目录,改名为Headbar,并在src/layout/components/index.js中声明Headbar

声明Headbar

2.修改Headbar/index.vue文件内容,主要删除了logo和更改了el-menu的mode属性为horizontal,并将name更改为Headbar;

<template>
  <el-scrollbar wrap-class="scrollbar-wrapper">
    <el-menu
      :default-active="activeMenu"
      :background-color="variables.menuBg"
      :text-color="variables.menuText"
      :active-text-color="variables.menuActiveText"
      mode="horizontal">

      <sidebar-item
        v-for="route in permission_routes"
        :key="route.path"
        :item="route"
        :base-path="route.path"/>
    </el-menu>
  </el-scrollbar>

</template>

<script>
  import {mapGetters} from 'vuex'
  import SidebarItem from './SidebarItem'
  import variables from '@/styles/variables.scss'

  export default {
    name: 'Headbar',
    components: {SidebarItem},
    computed: {
      ...mapGetters([
        'permission_routes',
        'sidebar'
      ]),
      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
        }
        return path
      },
      showLogo() {
        return this.$store.state.settings.sidebarLogo
      },
      variables() {
        return variables
      },
      isCollapse() {
        return !this.sidebar.opened
      }
    }
  }
</script>

 3.修改Headbar/SidebarItem.vue文件内容,主要配置stype、删除icon、增加固定宽度;

<template>
  <!-- style设置为inline-block,避免垂直布局的标题-->
  <div v-if="!item.hidden" style="display:inline-block;">
    <template
      v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
      <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
        <el-menu-item :index="resolvePath(onlyOneChild.path)">
          <item :title="generateTitle(onlyOneChild.meta.title)"/>
        </el-menu-item>
      </app-link>
    </template>

      <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" >
        <template slot="title">
          <item v-if="item.meta" :title="generateTitle(item.meta.title)"/>
          <!-- 增加固定宽度防止箭头被遮挡-->
          <div style="display: inline-block; width:20px;"></div>
        </template>

        <sidebar-item
          v-for="child in item.children"
          :key="child.path"
          :is-nest="true"
          :item="child"
          :base-path="resolvePath(child.path)"
          class="nest-menu"
        />
      </el-submenu>


  </div>
</template>

<script>
  import path from 'path'
  import {generateTitle} from '@/utils/i18n'
  import {isExternal} from '@/utils/validate'
  import Item from './Item'
  import AppLink from './Link'
  import FixiOSBug from './FixiOSBug'
  import SidebarItem from "./SidebarItem";

  export default {
    name: 'SidebarItem',
    components: {Item, AppLink, SidebarItem},
    mixins: [FixiOSBug],
    props: {
      // route object
      item: {
        type: Object,
        required: true
      },
      isNest: {
        type: Boolean,
        default: false
      },
      basePath: {
        type: String,
        default: ''
      }
    },
    data() {
      // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
      // TODO: refactor with render function
      this.onlyOneChild = null
      return {}
    },
    methods: {
      hasOneShowingChild(children = [], parent) {
        const showingChildren = children.filter(item => {
          if (item.hidden) {
            return false
          } else {
            // Temp set(will be used if only has one showing child)
            this.onlyOneChild = item
            return true
          }
        })

        // When there is only one child router, the child router is displayed by default
        if (showingChildren.length === 1) {
          return true
        }

        // Show parent if there are no child router to display
        if (showingChildren.length === 0) {
          this.onlyOneChild = {...parent, path: '', noShowingChildren: true}
          return true
        }

        return false
      },
      resolvePath(routePath) {
        if (isExternal(routePath)) {
          return routePath
        }
        if (isExternal(this.basePath)) {
          return this.basePath
        }
        return path.resolve(this.basePath, routePath)
      },

      generateTitle
    }
  }
</script>

二、配置右侧切换菜单位置按钮

1.在src/settings.js中增加menuInLeft 

 2.在右侧边栏中增加切换按钮,src/layout/components/Settings/index.vue

<template>
  <div class="drawer-container">
    <div>
      <!--原有标签保持不变,此处省略-->

      <!--增加切换按钮-->
      <div class="drawer-item">
        <span>切换菜单位置</span>
        <el-switch v-model="menuInLeft" class="drawer-switch" />
      </div>

    </div>
  </div>
</template>

<script>
import ThemePicker from '@/components/ThemePicker'

export default {
  components: { ThemePicker },
  data() {
    return {}
  },
  computed: {
    //  ===原有参数保持不变,此处省略====
    
    // 增加绑定的menuInLeft值
    menuInLeft: {
      get() {
        return this.$store.state.settings.menuInLeft
      },
      set(val) {
        this.$store.dispatch('settings/changeSetting', {
          key: 'menuInLeft',
          value: val
        })
      }
    },
  },
  methods: {
    themeChange(val) {
      this.$store.dispatch('settings/changeSetting', {
        key: 'theme',
        value: val
      })
    }
  }
}
</script>

<style lang="scss" scoped>
/*原有样式保持不变,此处省略*/
</style>

3.引入menuInLeft,src/store/modules/settings.js

三、配置index.vue和Navbar.vue

1.配置index.vue(src/layout/inex.vue)

<template>
  <div :class="classObj" class="app-wrapper">

    <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>

    <!--当 menuInLeft===true,左菜单栏显示,顶部隐藏-->
    <sidebar class="sidebar-container" v-if="menuInLeft"/>

    <!--当左菜单栏显示时,需配置main-container 样式,改样式位置在src/styles/sidebar.scss文件中-->
    <div :class="{hasTagsView:needTagsView,'main-container':menuInLeft}">
      <div :class="{'fixed-header':fixedHeader}">

        <navbar/>

        <!--当 menuInLeft===false,左菜单栏隐藏,顶部显示-->
        <headbar v-if="!menuInLeft"/>

        <tags-view v-if="needTagsView"/>
      </div>

      <app-main/>

      <right-panel v-if="showSettings">
        <settings/>
      </right-panel>
    </div>
  </div>

</template>

<script>
  import RightPanel from '@/components/RightPanel'
  //引入Sidebar
  import {AppMain, Navbar, Settings, Headbar, Sidebar, TagsView} from './components'
  import ResizeMixin from './mixin/ResizeHandler'
  import {mapState} from 'vuex'

  export default {
    name: 'Layout',
    components: {
      AppMain,
      Navbar,
      RightPanel,
      Settings,
      Sidebar,//引入Sidebar
      Headbar,
      TagsView
    },
    mixins: [ResizeMixin],
    computed: {
      ...mapState({
        sidebar: state => state.app.sidebar,
        device: state => state.app.device,
        showSettings: state => state.settings.showSettings,
        needTagsView: state => state.settings.tagsView,
        fixedHeader: state => state.settings.fixedHeader,
        //获取menuInLeft值
        menuInLeft: state => state.settings.menuInLeft
      }),
      classObj() {
        return {
          hideSidebar: !this.sidebar.opened,
          openSidebar: this.sidebar.opened,
          withoutAnimation: this.sidebar.withoutAnimation,
          mobile: this.device === 'mobile'
        }
      }
    },
    methods: {
      handleClickOutside() {
        this.$store.dispatch('app/closeSideBar', {withoutAnimation: false})
      }
    }
  }
</script>

 2、配置Navbar.vue(src/layout/components/Navbar.vue)

<template>
  <div class="navbar">
    <!--注释 侧边栏展开关闭按钮-->
    <hamburger v-if="menuInLeft"
               id="hamburger-container"
               :is-active="sidebar.opened"
               class="hamburger-container"
               @toggleClick="toggleSideBar"/>

    <breadcrumb id="breadcrumb-container" class="breadcrumb-container"/>

    <div class="right-menu">

      <template v-if="device!=='mobile'">
        <search id="header-search" class="right-menu-item"/>

        <error-log class="errLog-container right-menu-item hover-effect"/>

        <screenfull id="screenfull" class="right-menu-item hover-effect"/>

        <el-tooltip :content="$t('navbar.size')" effect="dark" placement="bottom">
          <size-select id="size-select" class="right-menu-item hover-effect"/>
        </el-tooltip>

        <lang-select class="right-menu-item hover-effect"/>

      </template>

      <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
        <div class="avatar-wrapper">
          <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
          <i class="el-icon-caret-bottom"/>
        </div>
        <el-dropdown-menu slot="dropdown">
          <router-link to="/profile/index">
            <el-dropdown-item>
              {{ $t('navbar.profile') }}
            </el-dropdown-item>
          </router-link>
          <router-link to="/">
            <el-dropdown-item>
              {{ $t('navbar.dashboard') }}
            </el-dropdown-item>
          </router-link>
          <a target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/">
            <el-dropdown-item>
              {{ $t('navbar.github') }}
            </el-dropdown-item>
          </a>
          <a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/">
            <el-dropdown-item>Docs</el-dropdown-item>
          </a>
          <el-dropdown-item divided @click.native="logout">
            <span style="display:block;">{{ $t('navbar.logOut') }}</span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>

  </div>
</template>

<script>
  import {mapGetters, mapState} from 'vuex'
  import Breadcrumb from '@/components/Breadcrumb'
  import Hamburger from '@/components/Hamburger'
  import ErrorLog from '@/components/ErrorLog'
  import Screenfull from '@/components/Screenfull'
  import SizeSelect from '@/components/SizeSelect'
  import LangSelect from '@/components/LangSelect'
  import Search from '@/components/HeaderSearch'

  export default {
    components: {
      Breadcrumb,
      Hamburger,
      ErrorLog,
      Screenfull,
      SizeSelect,
      LangSelect,
      Search,
    },
    computed: {
      ...mapGetters([
        'sidebar',
        'avatar',
        'device'
      ]),
      ...mapState({
        menuInLeft: state => state.settings.menuInLeft
      }),
    },
    methods: {
      toggleSideBar() {
        this.$store.dispatch('app/toggleSideBar')
      },
      async logout() {
        await this.$store.dispatch('user/logout')
        this.$router.push(`/login?redirect=${this.$route.fullPath}`)
      }
    }
  }
</script>

<style lang="scss" scoped>
  .navbar {
    height: 50px;
    overflow: hidden;
    position: relative;
    background: #fff;
    box-shadow: 0 1px 4px rgba(0, 21, 41, .08);

    .hamburger-container {
      line-height: 46px;
      height: 100%;
      float: left;
      cursor: pointer;
      transition: background .3s;
      -webkit-tap-highlight-color: transparent;

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

    .breadcrumb-container {
      float: left;
    }

    .errLog-container {
      display: inline-block;
      vertical-align: top;
    }

    .right-menu {
      float: right;
      height: 100%;
      line-height: 50px;

      &: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;
          }
        }
      }
    }
  }
</style>

-----------------------------------------------------------分割线-------------------------------------------------------------

说明:

上面文章中用的是vue-element-amdin的,i18n版本(增加了多语言功能),generateTitle方法是原项目默认的,在代码的import位置进行了引用。

评论中的百度云源代码使用的是项目的master版本,里面没有generateTitle。

-----------------------------------------------------------分割线-------------------------------------------------------------

四、将顶部菜单放入到navbar中

        

 直接在layout/components/Navbar.vue中引入 headbar,并加入headbar标签即可。效果如下:

 由于顶部位置有限,在菜单过多时,会存在放不下的情况。代码中为了将位置空出来,我把breadrumb标签进行了隐藏,不过还是没有放下…………

最好的解决方式是让顶部菜单能够左右滑动,目前还没有去调试加上,先进行记录一下, 后面调试出来了再加。

  • 25
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值