对于我们的最终效果来说,是不是很简单呢?
当然,如果我这里写三个h-wrapper的话,我们自然而然就变成了三级联动
最外层的slector组件用来暴露在外部显示的文字,以及控制级联选择器的显示隐藏,在每一次开始一个新的功能的时候,我们应该先完成重点的功能,重点功能完成之后,再去修改一些样式包括交互效果
对于这个模板来说,需要解释的地方很少,我都写在注释里边
首先需要重点提一下的是在mounted方法中,我们使用了this.$on方法,订阅了一个在当前组件内并没有发布的事件,这个事件,我定义在了h-wrapper这个组件中,稍后可以看到
其余的地方没有太复杂的功能和逻辑,就不一一解释
在模板中,需要重点关注的其实就是两个style,以及四个事件,当然了,还有插槽的位置,我这里使用了一个小技巧,在原始位置直接写好了四个option,并且其中一个还显示请选择,用来保证可选项的位置永远都可以处在最中间的位置
在逻辑中,我通过操作activeIndex这个索引值来动态的修改数据中ul的位移,使得当前永远显示的是对应索引与option高度计算出的位置
通过touchstart、touchmove、touchend三个事件来操作元素的位置与滑动
通过watch侦听对应属性,并实时触发事件,使得级联选择器发生改变,达到内外同步
通过mounted与updated钩子函数来保证当前的级联选择器属性会随着刷新而刷新
通过slot插槽来显示外部传入的option选项
对于option组件来说,并没有多少内容,它只需要负责显示数据,以及让级联选择器可以正确的获取到值即可
首先当前这个级联选择器的样式,我没有做太多的处理,但是已经很好看了有没有!
最终,我们就已经成功的打造了一款移动端的级联选择器,可以实现一级二级三级联动,当然了,多级也没有任何问题,但是在移动端,我建议最多到三级联动,否则的话影响用户体验感
对于我们的最终效果来说,是不是很简单呢?
当然,如果我这里写三个h-wrapper的话,我们自然而然就变成了三级联动
最外层的slector组件用来暴露在外部显示的文字,以及控制级联选择器的显示隐藏,在每一次开始一个新的功能的时候,我们应该先完成重点的功能,重点功能完成之后,再去修改一些样式包括交互效果
对于这个模板来说,需要解释的地方很少,我都写在注释里边
首先需要重点提一下的是在mounted方法中,我们使用了this.$on方法,订阅了一个在当前组件内并没有发布的事件,这个事件,我定义在了h-wrapper这个组件中,稍后可以看到
其余的地方没有太复杂的功能和逻辑,就不一一解释
在模板中,需要重点关注的其实就是两个style,以及四个事件,当然了,还有插槽的位置,我这里使用了一个小技巧,在原始位置直接写好了四个option,并且其中一个还显示请选择,用来保证可选项的位置永远都可以处在最中间的位置
在逻辑中,我通过操作activeIndex这个索引值来动态的修改数据中ul的位移,使得当前永远显示的是对应索引与option高度计算出的位置
通过touchstart、touchmove、touchend三个事件来操作元素的位置与滑动
通过watch侦听对应属性,并实时触发事件,使得级联选择器发生改变,达到内外同步
通过mounted与updated钩子函数来保证当前的级联选择器属性会随着刷新而刷新
通过slot插槽来显示外部传入的option选项
对于option组件来说,并没有多少内容,它只需要负责显示数据,以及让级联选择器可以正确的获取到值即可
首先当前这个级联选择器的样式,我没有做太多的处理,但是已经很好看了有没有!
最终,我们就已经成功的打造了一款移动端的级联选择器,可以实现一级二级三级联动,当然了,多级也没有任何问题,但是在移动端,我建议最多到三级联动,否则的话影响用户体验感
最近做项目经常会用到一些UI库,比如 element 、 iview 等,这些能够快速构建应用的库真的十分方便。比如 iview 的级联菜单很是好用,但是在使用的同时不免起了一些疑惑,它是怎么做到多级联动,依我所知,如果一个数据是多层的,我们就要多次使用 v-for ,并且需要知道要循环几次,但是这个组件貌似不需要多次复用。好啦,下面进入正题!
首先我们手里是一份这样的 JSON 数据:
这是最重要的环节,组件的递归实现了级联菜单的多级联动,这个过程就如同实现对象的深复制一样。那么接下来我们按照这份数据写一个递归组件:
这里要强调以下,组件的 name 属性必须要有,否则无法实现递归,当然递归也需要有终止的条件,上边的 isFloder 便是了。
这里也有需要注意的, v-for 循环需要绑定 key ,否则会报警告。
最后来看下这个简单级联菜单的效果:
<Col span="24">
<FormItem label="所属区域" prop="areaValue">
<Cascader :data="areatree" v-model="formData.areaValue" @on-change="getAreaData" placeholder="请选择所属区域" change-on-select></Cascader>
</FormItem>
</Col>
<script>
import { interFaceImpl } from "@/api/index" // 封装的方法,用于调用指定接口
import { api } from '@/views/sluiceConfig/lockRoomInfo/index' //请求接口
export default {
data() {
return {
defaultTree: [], //默认区域数据
areatree: [],//所属区域树形列表
formData: { // 表单字段对象
areaCode: "", // 所属闸室
areaValue: [],
},
watch: {
// 监听详细信息赋值
detailData(newVal) {
if (JSON.stringify(newVal) !== "{}") {
this.formData = newVal
this.formData.areaValue = []
if (this.formData.areaCode) {
this.getAreaValue(this.formData.areaCode)
}
} else {
this.formData = {
areaCode: "", // areaCode
}
this.formData.areaValue = []
this.$refs["formDataValidate"].resetFields()
}
}
},
created() {
this.getAreaTreeData()
},
methods: {
getAreaTreeData() { // 获取区域树信息
var that = this
return new Promise((resolve,reject) => {
this.$interFaceImpl(api['area_findTree']).then(res => {
// debugger
if (res && res.data) {
res.data.data.forEach(ele => {
if (ele.areaCode != '-1') {
that.defaultTree.push({
value: ele.areaCode,
label: ele.areaName,
parentValue: ele.parAreaCode
})
}
})
that.areatree = transform(that.defaultTree, 'value', 'parentValue', 'children')
}
resolve()
}).catch(err => {
reject(err)
})
})
},
//级联选择赋值
getAreaData: function(val){
val.forEach(ele => {
this.formData.areaValue.unshift(ele)
})
this.formData.areaCode = val[val.length-1]
},
//初始化默认值
getAreaValue: function(id){
var that = this
that.defaultTree.forEach(ele => {
if(ele.value == id){
that.formData.areaValue.unshift(ele.value)
that.formData.areaCode = that.formData.areaValue[that.formData.areaValue.length-1]
that.getAreaValue(ele.parentValue)
}
})
},
}
}
</script>