基于mintui的picker和popup方法,自定义了一个多级联动,我觉得还有可以优化的点,但是应该可以勉强食用了,当前我用于省市区三级联动切换。
引入,注册,渲染三连。
<el-form-item label="名称" prop="field" >
<el-input v-model="form.field" @focus="togglePickerStatus()" placeholder="请选择" ref="onPicker" readonly>
<i slot="suffix" class="el-input__icon el-icon-arrow-right"></i>
</el-input>
</el-form-item>
<picker-bottom v-if="showPicker" :pickerParams="pickerParams" @handleData="togglePickerStatus"></picker-bottom>
import pickerBottom from '../PickerBottom.vue'
components: { pickerBottom }
提供一下我使用的方法,视情况自定义,仅供参考。
togglePickerStatus (data) {
this.pickerParams = {
slots: this.yourList || [],
title: '标题',
level: 3,
valueKey: 'showName'
}
this.showPicker = !this.showPicker
if (!this.showPicker) {
console.log('data', data)
if (data) {
// 这里获取数据并赋值
}
this.$refs.onPicker.blur()
}
}
以下为注册的公共组件,css或者less不需要的自行删除。
<!-- 功能描述:多级联动组件v1.21.0122
传过来的参数支持:
slots(必填):数组,子数组数据在childrenKey下
title(必填):字符串,弹窗中间的文本
level(非必填,默认为1): 大于0的数字,需要显示几列
needAll(非必填,默认false): 布尔值,是否返回全部数据
valueKey(必填,展示的字段):字符串,
childrenKey(非必填,默认children): 字符串, 存放数组的子数据的地方
closeOnClickModal(非必填,默认false): 布尔值,点击弹窗上层遮罩时,是否允许关闭弹窗
showCount(非必填,默认为5): 大于0的数字,打开窗口时最多同时展示几行,mintui组件固定参数
修订记录:1, 2021.11.11 增加customKeyword处理三级联动动时,某一级childrenKey不存在的问题
2,后期发现在部分环境不可用,经研究和mint-ui的版本有关系,当前mint-ui支持版本为2.2.9
-->
<template>
<div class="data-picker" v-if="status">
<mt-popup
v-model="status"
:modal="true"
:closeOnClickModal="closeOnClickModal"
position="bottom">
<div class="dialog-control">
<div slot="left">
<a href="javascript:void(0)" @click="cancelForm">取消</a>
</div>
<h3 v-text="title"></h3>
<div slot="right" class="text-right">
<a href="javascript:void(0)" @click="submitForm">确定</a>
</div>
</div>
<mt-picker :slots="slots" ref="picker" :visible-item-count="showCount" :itemHeight="50" :valueKey="valueKey" @change="changeSlots">
</mt-picker>
</mt-popup>
</div>
</template>
<script>
export default {
props: {
pickerParams: {
type: Object,
default: function () {
return {}
}
}
},
data () {
return {
status: false,
params: this.pickerParams,
oldPickers: [],
currentPickers: [],
currentChoose: [], // 记录当前选择位置,默认为level长度的全部为0的数组
slots: [],
title: '默认标题',
level: 1,
valueKey: this.pickerParams.valueKey,
childrenKey: 'children',
closeOnClickModal: false,
clickSubmit: false,
needAll: false,
showCount: 5
}
},
mounted () {
// 初始化数据
this.title = this.params.title || this.title
this.closeOnClickModal = this.params.closeOnClickModal || this.closeOnClickModal
this.childrenKey = this.params.childrenKey || this.childrenKey
this.level = this.params.level || this.level
this.showCount = this.params.showCount || this.showCount
this.needAll = this.params.needAll || this.needAll
// 判断传参基本符合要求
if (!this.params.slots.length) {
this.$toast({
message: this.title + '列表信息加载失败',
className: 'false',
position: 'top',
duration: 3000
})
this.$emit('handleData')
return false
}
if (!this.valueKey) {
this.$toast({
message: 'valueKey值不能为空',
className: 'false',
position: 'top',
duration: 3000
})
this.$emit('handleData')
return false
}
// 打开窗口,赋值
this.status = true
if (this.level === 1) { // 只有一级的就直接安排,省了下面的操作
this.pushData(this.params.slots, 0)
} else {
for (let index = 0; index < this.level; index++) {
let values = this.params.slots
for (let n = 0; n < index; n++) {
values = values[0][this.childrenKey]
}
this.pushData(values, index)
}
}
let _this = this
this.oldPickers = this.slots.map(function (item) {
return item.values[0][_this.valueKey]
})
},
methods: {
cancelForm () {
this.status = false
},
changeSlots (picker, value) {
// console.log('picker, value', picker, value)
if (!value.includes(undefined)) { // 打开窗口时,会默认执行该方法,由于数据是动态进入的,一共会执行level*2次,最后一次没有undefined
console.log('change', value)
let _this = this
this.currentPickers = value.map(function (item) {
return item[_this.valueKey]
})
let pickerCompare = this.currentPickers.map(function (item) {
if (_this.oldPickers.includes(item)) {
return true
}
return item
})
let currentKey = pickerCompare.filter(item => item !== true)
if (currentKey.length) {
let falseIndex = pickerCompare.indexOf(currentKey[0]) // 如果滚动的是最后一级,不需要更新slots数据
// console.log('falseIndex', falseIndex)
if (this.level !== falseIndex + 1) {
this.initSolts(falseIndex, currentKey[0])
this.oldPickers = this.currentPickers
}
}
}
},
pushData (values, index) {
let params = {
flex: 1,
values,
defaultIndex: 0,
className: `slot${index + 1}`
}
this.slots.push(params)
this.currentChoose.push(0)
this.$nextTick(function () {
this.$refs.picker.setSlotValue(index, this.slots[index].values[0])
})
},
initSolts (falseIndex, currentKeyValue) {
let _this = this
let childrenKey = this.childrenKey
let valueKey = this.valueKey
let values = this.params.slots
for (let n = 0; n < falseIndex; n++) {
let num = this.currentChoose[n]
values = values[num][childrenKey]
}
let chooseData = values.map(item => item[valueKey])
let chooseIndex = chooseData.indexOf(currentKeyValue)
if (chooseIndex > -1) {
// 滚动之后,重新记录位置
this.currentChoose[falseIndex] = chooseIndex
this.currentChoose = this.currentChoose.map(function (item, index) {
if (index === falseIndex) {
return chooseIndex
} else if (index > falseIndex) {
return 0
} else {
return item
}
})
// console.log('this.currentChoose', this.currentChoose)
let chooseArray = this.params.slots
this.currentChoose.forEach((element, index) => {
if (index > 0) {
if (chooseArray.length) {
_this.slots[index].values = chooseArray
} else { // 有的列表最后一级无返回值,给了一个默认的暂位值(目前解决了中国省市区三级联动 河南省济源市 的问题,不排除存在其他问题,比如四级联动时从第二级别就开始没有子级)
let defaultArr = []
let params = {
customKeyword: 'custom'
}
params[valueKey] = '-'
defaultArr.push(params)
_this.slots[index].values = defaultArr
}
}
if (chooseArray.length) {
chooseArray = chooseArray[element][childrenKey] || []
}
})
}
},
submitForm () {
// console.log('this.$refs', this.$refs.picker.getValues())
let params = []
let values = this.$refs.picker.getValues()
values = values.filter(item => item.customKeyword !== 'custom')
if (this.needAll) {
params = values
} else {
params = values[values.length - 1]
}
this.$emit('handleData', params)
this.clickSubmit = true
this.status = false
}
},
watch: {
status () {
if (!this.status && !this.clickSubmit) { // 写在这里而不是写在cancel按钮下的目的是,为了兼容点击modal关闭弹窗的响应。
this.$emit('handleData')
}
}
}
}
</script>
<style lang="less" scoped>
.data-picker {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
margin: 0;
// background-color: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
z-index: 10;
.dialog-control {
background: #EBECED;
border-radius: 0;
-webkit-border-radius: 0;
-moz-border-radius: 0;
padding: 0 0.2rem;
h3 {
font-size: 0.16rem;
}
a {
font-size: 0.16rem;
}
}
.mint-popup {
width: 100%;
}
.picker {
padding: 0.15rem 0;
background: #FAFAFA ;
}
/deep/ .picker-item {
color: #999;
font-size: 0.16rem;
&.picker-selected {
color: #333;
}
}
}
</style>