vue合家福實例(3):根據路由生成菜單

本章的最終效果

最終效果圖

這章的開發在admin子項目中進行。這里我們在project/admin中創建文件App.vue。內容如vue合家福實例(2):使用element-ui el-scrollbar中的,建立一個項目的頁面框架。統一的菜單和頂部狀態欄。
在開發中,要增加一個頁面。我是先寫一個簡單的組件(.vue)文件,然后配置路由。接下去是修改菜單(這樣的情況下,一般菜單會建立一個組件)。那么,如果路由配置好后就可以增加到菜單中,即菜單是根據路由生成的,這樣修改路由就不用再去改菜單的代碼。想想挺酷的。
目錄結構:
目錄結構

我們在紅框中的文件目錄進行工作。
BhLayout>src>menu-item.vue (一個菜單項組件,對element-ui的菜單組件進行再封裝)
菜單的代碼在App.vue中,routers.js是路由配置文件。

App.vue:

<script>
import _ from 'lodash'
import {BhMenuItem} from '@/components/BhLayout' // 加入菜單項組件
export default {
  name: 'app',
  components: {
    BhMenuItem
  },
  data () {
    return {
      isCollapse: false,
      asideWidth: '230px',
      vmenus: [],
      defActive: this.activePath()
    }
  },
  created () {},
  watch: {
    $route () {
      this.defActive = this.activePath()
      this.setPageTitle()
    }
  },
  methods: {
    // 改變菜單欄的寬度
    changeCollapse () {
      this.isCollapse = !this.isCollapse
      this.$emit('collapse-change', this.isCollapse)
      if (this.isCollapse) {
        this.asideWidth = '65px'
      } else {
        this.asideWidth = '230px'
      }
    },
    // 設置頁面標題
    setPageTitle () {
      let path = this.$route.path
      let pathArr = _.split(path, '/')
      let l = pathArr.length
      let menus = this.$router.options.routes
      if (_.isEmpty(menus) || l < 2) {
        return
      }
      let ts = ['vue全家福']
      let i = 1
      let children = menus
      while (i < l) {
        path = _.join(_.slice(pathArr, 0, i + 1), '/')
        let index = _.findIndex(children, menu => {
          let menuPath = menu.path
          let i = menuPath.indexOf('/:')
          if (i > 0) {
            menuPath = menuPath.substring(0, i)
          }
          return menuPath === path
        })
        if (index < 0) {
          break
        }
        if (!children[index]) {
          break
        }
        if (children[index]['text']) {
          ts.push(children[index]['text'])
        }
        if (!children[index]['children'] || _.isEmpty(children[index]['children'])) {
          break
        }
        children = children[index]['children']
        i++
      }
      this.title = _.clone(ts)
    },
    // 計算菜單當前選中的路徑
    activePath () {
      let path = this.$route.path
      let pathArr = _.split(path, '/')
      let l = pathArr.length
      if (l <= 2) {
        return path
      }
      let menus = this.$router.options.routes
      if (_.isEmpty(menus)) {
        return path
      }
      let i = 1
      let children = menus
      while (i < l) {
        path = _.join(_.slice(pathArr, 0, i + 1), '/')
        let index = _.findIndex(children, {'path': path})
        if (index < 0) {
          break
        }
        if (!children[index]) {
          break
        }
        if (children[index]['hasChildren'] === false || !children[index]['children'] || _.isEmpty(children[index]['children'])) {
          break
        }
        children = children[index]['children']
        i++
      }
      return path
    }
  }
}
</script>

<template>
  <el-container>
    <!-- 菜單欄 -->
    <el-aside :width="asideWidth">
      <el-scrollbar class="default-scrollbar" wrap-class="default-scrollbar__wrap" view-class="default-scrollbar__view">
        <div :class="isCollapse ? 'menu-collapsed' : 'menu-expanded'">
          <el-menu
            :default-active="defActive"
            class="el-menu-vertical-demo"
            unique-opened
            router
            :collapse="isCollapse">
            <bh-menu-item :menu="item" :key="item.name" v-for="item in $router.options.routes" v-if="item.menu"></bh-menu-item>
          </el-menu>
        </div>
      </el-scrollbar>
    </el-aside>
    <el-container>
      <!-- 右邊上面的欄目 -->
      <el-header class="clear">
        <div class="collapse-btn" @click.prevent="changeCollapse">
          <i class="fas fa-bars" :class="{ rotate90: isCollapse }"></i>
        </div>
      </el-header>
      <!-- 路由容器 -->
      <router-view></router-view>
    </el-container>
  </el-container>
</template>

<style scoped>
.collapse-btn {
  float: left;
  font-size: 24px;
  cursor: pointer;
}
.rotate90 {
  filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
  -moz-transform: rotate(90deg);
  -o-transform: rotate(90deg);
  -webkit-transform: rotate(90deg);
  transform: rotate(90deg);
}
</style>

補充說明:$router.options.routes(如果在js中是this.$router.options.routes)。取出當前路由內容,即src>project>admin>router>routers.js文件中的內容
菜單是通過$router.options.routes(項目路由)自動生成的。下面是路由(routers.js)的內容:

import Wrap from '@/components/Wrap.vue'

const routers = [{
  path: '/',
  name: 'home',
  text: '首頁',
  menu: true,
  icon: 'fas fa-home',
  component: resolve => require(['../views/Home'], resolve)
}, {
  path: '/dashboard',
  name: 'dashboard',
  text: '儀表盤',
  menu: true,
  icon: 'fas fa-tachometer-alt',
  component: resolve => require(['../views/Dashboard'], resolve)
}, {
  path: '/user',
  name: 'user',
  text: '用戶管理',
  menu: true,
  hasChildren: true,
  icon: 'fas fa-user-cog',
  component: Wrap,
  children: [{
    path: '/user/permission',
    name: 'user_permission',
    text: '權限管理',
    menu: true,
    component: resolve => require(['../views/user/Permission'], resolve)
  }, {
    path: '/user/role',
    name: 'user_role',
    text: '角色管理',
    menu: true,
    component: resolve => require(['../views/user/Role'], resolve)
  }, {
    path: '/user/user',
    name: 'user_user',
    text: '用戶管理',
    menu: true,
    component: resolve => require(['../views/user/User'], resolve)
  }]
}]

export default routers

補充說明:Wrap.vue文件只是一個簡單的路由容器,內容如下。因為vue-cli3項目生成的,如果寫component: { template: '<router-view></router-view>' } 會報異常。我的方案是建立一個組件,引入。

<script>
export default {
  name: 'Wrap'
}
</script>

<template>
  <router-view></router-view>
</template>

生成菜單的組件BhMenuItem,關鍵內容如下:

<template>
  <component v-bind:is="currentItemComponent" :index="menu.path" :key="menu.path">
    <i :class="menu.icon" v-if="menu.icon && !hc"></i><span v-if="!hc" slot="title">{{menu.text}}</span>
    <template slot="title" v-if="hc">
      <i :class="menu.icon" v-if="menu.icon"></i><span slot="title">{{menu.text}}</span>
    </template>
    <!-- 這里用了遞歸生成菜單項 -->
    <bh-menu-item :menu="child" :key="child.name" v-for="child in menu.children" v-if="hc && child.menu"></bh-menu-item>
  </component>
</template>

<script>
export default {
  name: 'BhMenuItem',
  props: {
    menu: Object
  },
  data () {
    return {
      hc: false
    }
  },
  computed: {
    currentItemComponent: function () {
      return this.hasChildren() ? 'el-submenu' : 'el-menu-item'
    }
  },
  methods: {
    hasChildren () {
      this.hc = this.menu.hasChildren !== false && this.menu.children && this.menu.children.length > 0
      return this.hc
    }
  }
}
</script>

補充說明:菜單的結構是一棵樹,可以一層層深入。在這里判斷如果菜單項沒有子菜單的話就用element-ui的el-menu-item組件,如果還有子菜單,則用el-menu-item。最后如果有子菜單,遞歸使用組件。
在App.vue用BhMenuItem組件生成菜單樹。完成后,菜單如圖:


生成的菜單

發現菜單的圖標樣式不合理,這是font awesome圖標。下章補充font awesome圖標使用方法,在這里與element-ui 圖標的padding不一樣。在全局樣式表中加入樣式:

.el-menu .fa,
.el-menu .fas,
.el-menu .far {
  margin-right: 10px;
}

最終效果


效果一

點擊圖標后收縮菜單。


效果二
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容