本文以 Vue使用ivew组件实现响应式布局.为基础继续进行
封装菜单组件
1.在components文件夹中新建side-menu文件夹
在该文件夹中新建side-menu.vue、re-submenu.vue、re-dropdown.vue、index.js文件
2.配置index.js
// 另外两个组件我们在side-menu中用,所以不用导出
import SideMenu from './side-menu.vue'
export default SideMenu
在layout.vue中引入定义SideMenu组件
import SideMenu from '_c/side-menu'
components: {
SideMenu
},
<Sider collapsible hide-trigger breakpoint='sm' v-model="collapsed">
<side-menu :collapsed="collapsed"></side-menu>
</Sider>
3.在配置递归组件
3.1在side-menu.vue配置基本框架
<template>
<div class="side-menu-wrapper">
<!-- 插槽为了放置左上角的logo -->
<slot></slot>
<!-- 展开时候显示Menu -->
<Menu width="auto" theme="dark">
</Menu>
<!-- 收起时显示dropdown -->
<div>
</div>
</div>
</template>
<script>
export default {
name: "SideMenu",
}
</script>
<style lang="less">
.side-menu-wrapper{
width: 100%;
}
</style>
3.2配置props定义要从父组件接受的值
props: {
collapsed: {
type: Boolean,
default: false
},
list: {
type: Array,
// 如果默认值为空数组,需要使用函数return一个值,该值为空数组
default: () => []
}
}
3.3继续配置递归组件
<template>
<div class="side-menu-wrapper">
<!-- 插槽为了放置左上角的logo -->
<slot></slot>
<!-- 展开时候显示Menu -->
<Menu width="auto" theme="dark">
<template v-for="item in list">
<!--此时要判断list有无子集-->
<!-- 有子集 -->
<Submenu v-if="item.children" :key="`menu_${item.name}`" :name="item.name">
<!-- 在此考虑是否有未知层级 -->
</Submenu>
<menu-item v-else :key="`menu_${item.name}`" :name="item.name">
<icon :type="item.icon"/>
{{ item.title }}
</menu-item>
</template>
</Menu>
<!-- 收起时显示dropdown -->
<div>
</div>
</div>
</template>
3.3.1在re-submenu.vue文件中进行多级组件封装
注意submenu和menu-item为同级关系,我们先以Submenu作为递归组件名,封装好后更换
<template>
</template>
<script>
export default {
name: 'ReSubmenu',
props: {
// 定义接受数据,接受的应该是含有children的数据对象
parent: {
type: Object,
default: () => ({})
}
}
}
</script>
<style lang="less">
</style>
到此我们需要从side-menu中获取数据
3.4在side-menu.vue引入并定义ReSubmenu组件,并且把子组件需要的值传入
import ReSubmenu from './re-submenu.vue'
components: {
ReSubmenu
},
<Menu width="auto" theme="dark">
<template v-for="item in list">
<!--此时要判断list有无子集-->
<!-- 有子集 -->
<re-submenu
v-if="item.children"
:key="`menu_${item.name}`"
:name="item.name"
:parent="item"
>
<!-- 考虑是否有未知层级 -->
</re-submenu>
<menu-item v-else :key="`menu_${item.name}`" :name="item.name">{{ item.title }}</menu-item>
</template>
</Menu>
3.41继续在re-submenu.vue页面中封装递归组件,此时我们已经接受到父组件传来的数据
<template :name="parent.name">
<Submenu>
<template slot="title">
<icon :type="parent.icon"/>
{{ parent.title }}
</template>
</Submenu>
</template>
3.42在re-submenu.vue子组件中使用父组件的逻辑,也就是调用他自身
所以在之前要给递归组件命名
这里的list换成parent.children
<template>
<Submenu :name="parent.name">
<template slot="title">
<icon :type="parent.icon"/>
{{ parent.title }}
</template>
<template v-for="item in parent.children">
<!--此时要判断list有无子集-->
<!-- 有子集 -->
<re-submenu
v-if="item.children"
:key="`menu_${item.name}`"
:name="item.name"
:parent="item"
>
</re-submenu>
<menu-item v-else :key="`menu_${item.name}`" :name="item.name">
<icon :type="item.icon"/>
{{ item.title }}
</menu-item>
</template>
</Submenu>
</template>
4.收缩情况下的side-menu组件
收缩后菜单有children则显示可以拓展箭头,没有children则显示小图标
4.1先来写没有children时的状态
<template>
<div class="side-menu-wrapper">
<!-- 插槽为了放置左上角的logo -->
<slot></slot>
<!-- 收起时显示dropdown -->
<div>
<template v-for="item in list">
<!-- 有children的情况 -->
<!-- 没有children的情况 -->
<Tooltip transfer :content="item.title" placement="right" :key="`drop_${item.name}`">
<span class="drop-menu-span">
<Icon :type="item.icon" :size="20"></Icon>
</span>
</Tooltip>
</template>
</div>
</div>
</template>
4.1.2给span标签添加样式
<style lang="less">
.side-menu-wrapper{
width: 100%;
.drop-menu-span, .ivu-tooltip{
display: inline-block;
width: 100%;
color: #fff;
text-align: center;
padding: 5px 0;
}
}
</style>
4.2当有需要渲染的数据children的时候
1.在re-dropdown.vue中封装递归组件
<template>
<Dropdown>
<!-- 这里的a标签和我们一级的标签是同级的,所以直接复制side-menu.vue中的a标签 -->
<span class="drop-menu-span">
<Icon :type="item.icon" :size="20"></Icon>
<span>{{ parent.title }}</span>
</span>
<DropdownMenu slot="list" >
<template v-for="item in parent.children">
<re-dropdown v-if="item.children" :key="`drop_${item.name}`" :parent="item"></re-dropdown>
<DropdownItem v-else :key="`drop_${item.name}`" :name="item.name">
<Icon :type="parent.icon" :color='iconColor' :size="20"></Icon>
{{ item.title }}
</DropdownItem>
</template>
</DropdownMenu>
</Dropdown>
</template>
<script>
export default {
name: 'ReDropdown',
props: {
parent: {
type: Object,
default: () => ({})
}
}
}
</script>
<style lang="less">
</style>
2.在side-menu.vue中注册并定义re-dropdown.vue组件,同时引入
<!-- 有children的情况 -->
<re-dropdown v-if="item.children" :key="`drop_${item.name}`" :parent="item"></re-dropdown>
import ReDropdown from './re-dropdown.vue'
components: {
ReSubmenu,
ReDropdown
},
3.在side-menu.vue中继续优化
取消样式中的color,在icon中定义
<Icon :type="item.icon" color="#fff" :size="20"></Icon>
4.在side-menu.vue中传递颜色值icon-color给子组件
<re-dropdown v-if="item.children" icon-color="#fff" :key="`drop_${item.name}`" :parent="item"></re-dropdown>
5.在re-dropdown.vue中定义需要从父组件的到的值并使用
props: {
parent: {
type: Object,
default: () => ({})
},
iconColor: {
type: String,
default: '#515a6e'
}
}
<Icon :type="parent.icon" :color='iconColor' :size="20"></Icon>
6.第一级不显示title,里面的内容显示title
在re-dropdown.vue组件中定义我们需要的值
showTitle: {
type: Boolean,
default: true
}
在side-menu.vue中定义该属性并设置值传入子组件
<re-dropdown v-if="item.children" :show-title="false" icon-color="#fff" :key="`drop_${item.name}`" :parent="item"></re-dropdown>
回到re-dropdown.vue中使用
<span v-if="showTitle">{{ parent.title }}</span>
7.在re-dropdown.vue中将DropdownItem中的颜色设为定值
<Icon :type="item.icon" color='#515a6e' :size="20"></Icon>
8.在re-submenu.vue给子集菜单设置居中样式
1.在re-submenu.vue中的子集菜单的一级标签绑定一个动态style
<span class="drop-menu-span" :style="titleStyle">
2.配置计算属性
computed: {
titleStyle () {
return {
textAlign: this.showTitle ? 'left' : 'center',
paddingLeft: this.showTitle ? '16px' : ''
}
}
}
4.3给主菜单logo设置居中
1在side-menu.vue中给包裹示dropdown组件的div设置class类名
<div class="drop-wrapper">
2.通过直接子集选择器找到主菜单的logo并配置样式
<style lang="less">
.side-menu-wrapper{
width: 100%;
.drop-menu-span, .ivu-tooltip{
display: inline-block;
width: 100%;
text-align: center;
padding: 5px 0;
}
.drop-wrapper > .ivu-dropdown{
display: block;
margin: 0 auto;
padding: 5px;
}
}
</style>
4.收缩展开的处理
由于收缩和展开为较为频繁的点击操作所以我们在side-menu.vue添加v-show
<!-- 展开时候显示Menu -->
<Menu v-show="!collapsed" width="auto" theme="dark">
<!-- 收起时显示dropdown -->
<div v-show="!collapsed" class="drop-wrapper">
5.通过绑定事件找到当前的菜单项
1.展开状态下
side-menu.vue中
<Menu v-if="!collapsed" width="auto" theme="dark" @on-select="handleSelect">
methods: {
handleSelect (name) {
console.log(name)
}
}
2.收缩状态下
side-menu.vue中
<span @click="handleClick(item.name)" class="drop-menu-span">
3.在re-submenu.vue中添加事件并定义
<Dropdown @on-click="handleClick" placement="right-start">
在事件中我们抛出一个事件
methods: {
handleClick (name) {
if (!this.showTitle) this.$emit('on-select', name)
}
}
在side-menu.vue 中
<!-- 有children的情况 -->
<re-dropdown @on-select="handleSelect" v-if="item.children" :show-title="false" icon-color="#fff" :key="`drop_${item.name}`" :parent="item"></re-dropdown>
================================================