本章的最终效果
这章的开发在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;
}
最终效果
点击图标后收缩菜单。