当前位置: 首页>前端>正文

vue mintui 自定义多级联动

基于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>

https://www.xamrdz.com/web/2i71995252.html

相关文章: