本章的最終效果
最終效果圖
這章的開發在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;
}
最終效果
效果一
點擊圖標后收縮菜單。
效果二