vue 移动端 级联选择器支持多选父子关联 、父关联子不关联、还有父子 都不关联。外加一个单选

组件

<template>

  <div>

    <van-action-sheet @click-overlay="sureSubmit" :value="showInfo" :closeable="false" :title="titleInfo">

      <transition name="fade">

        <div class="container-searchPanel background-color-white">

          <div class="wrapper1">

            <!-- <form action="/">

              <van-search

                v-model="value"

                show-action

                placeholder="请输入搜索关键词"

                @clear="clearInfo"

                @search="onSearch"

                @cancel="close"

              >

                <template #label>

                  <van-dropdown-menu>

                    <van-dropdown-item

                      v-model="searchListValue"

                      :options="[

                        { text: '查询全部', value: '查询全部' },

                        { text: '当页查询', value: '当页查询' }

                      ]"

                    />

                  </van-dropdown-menu>

                </template>

                <template #left-icon>

                  <van-icon name="bm-systemSearch" class="icon-size-16 color-text-secondary" @click="onSearch" />

                </template>

              </van-search>

            </form> -->

            <!-- 面包屑 -->

            <ul class="category-wrapper">

              <!-- <span v-if="!isre" class="none">全部</span> -->

              <li

                v-for="(item, index) in navList"

                :key="index"

                class="category-item"

                @click="navListClick(item, index, navList)"

              >

                <van-icon v-if="index > 0" name="arrow" />

                <span v-if="index == navList.length - 1">

                  {{ item.name }}

                </span>

                <span class="active" v-else>

                  {{ item.name }}

                </span>

              </li>

              <!-- <text v-else class="active">全部</text> -->

            </ul>

            <!-- 数据展示 -->

            <div class="infoShow" v-if="data.length > 0">

              <div class="item-p" v-for="(item, index) in data" :key="index">

                <van-cell

                  :title="item.name"

                  :is-link="item.childs && item.childs.length > 0"

                  @click="checkedMethods(item, index)"

                >

                  <template #title>

                    <span style="" class="select_group">

                      <van-icon v-if="item.checked" name="arrow" class="van-icon-bm-zhengque" />

                    </span>

                    {{ item.name }}

                  </template>

                  <template #right-icon>

                    <van-icon

                      @click.stop="clickChild(item, index, data)"

                      v-if="item.childs && item.childs.length > 0"

                      name="arrow"

                      class="van-icon-bm-xiaji_line iconSize"

                    />

                  </template>

                </van-cell>

              </div>

              <div>

                <van-button round class="vanbtn" type="primary" @click="sureSubmit" block>确认</van-button>

              </div>

              <!-- <div style="height:100px"></div> -->

            </div>

            <div class="Nomore" v-else>

              <van-empty description="暂无更多" />

            </div>

          </div>

        </div>

      </transition>

    </van-action-sheet>

  </div>

</template>

<script>

export default {

  name: 'ProdTreeNew',

  data() {

    return {

      searchListValue: '查询全部',

      navList: [],

      data: [],

      value: '',

      selectInfo: [],

      fatherCheckout: false,

      dataEcho: [],

      // 承载数据

      newData: []

    }

  },

  props: {

    titleInfo: {

      type: String,

      default: '产品树'

    },

    // 接口

    methodsFunc: {

      type: Function,

      required: true

    },

    showInfo: {

      type: Boolean,

      default: false

    },

    showEnable: {

      // 是否显示第三层禁用项

      type: Boolean,

      default: false

    },

    isAdd: {

      // 是否是新增页面 新增页面不展示禁用项

      type: Boolean,

      default: false

    },

    // 隐藏最后一层

    hidePart: {

      type: Boolean,

      default: false

    },

    // 只显示三层

    isThree: {

      type: Boolean,

      default: false

    },

    infoValue: {

      type: Array,

      default: () => []

    },

    /**

    //

    * 多选

     父子关联 0

     只关联父(勾选父级显示), 子不关联 1 但如果子集 有值 ,父级取消选中 那么对应的父级下面子集 全部取消选中

     父子完全不关联 2

    * **/

    isNoMultiplechoice: {

      type: String,

      default: '0'

    },

    //  只关联父(勾选父级显示), 子不关联 3 但如果子集 有值 ,父级取消选中 那么对应的父级下面子集 不会取消选中

    SubsetnotSelected: {

      type: Boolean,

      default: false

    },

    // 开启单选

    openRadioselection: {

      type: Boolean,

      default: false

    }

  },

  created() {

    this.productTree()

  },

  watch: {

    // 由于 值都是一样的

    infoValue: {

      handler(newValue, oldValue) {

        this.$nextTick(() => {

          if (newValue && newValue.length > 0) {

            if (!this.openRadioselection) {

              if (this.isNoMultiplechoice === '0' || this.isNoMultiplechoice === '1') {

                this.dataEcho = [

                  ...new Set([

                    ...this.flatten(

                      newValue.map(item => {

                        return [item.oneId, item.secondId, item.threeId, item.fourId]

                      })

                    )

                  ])

                ].map(ite => {

                  this.breadthQuery(this.navList[0].data, ite)

                })

              } else if (this.isNoMultiplechoice === '2') {

                newValue.map(ite => {

                  console.log(ite, 'ite')

                  this.breadthQuery(this.navList[0].data, ite.id || ite.oneId)

                })

              }

              // 打印输出

            } else {

              // 多选

              newValue.map(ite => {

                console.log(ite, 'ite')

                this.breadthQuery(this.navList[0].data, ite.id || ite.oneId)

              })

            }

          }

          console.log(newValue)

        })

      },

      deep: true,

      immediate: true

    }

  },

  methods: {

    // 通过id 遍历 查询匹配的值 并且返回对象

    breadthQuery(tree, id) {

      var stark = []

      stark = stark.concat(tree)

      while (stark.length) {

        var temp = stark.shift()

        if (temp.childs) {

          stark = stark.concat(temp.childs)

        }

        if (temp.id === id) {

          temp.checked = true

          return temp

        }

      }

    },

    // 二维转换一维数组

    flatten(arr) {

      return [].concat(...arr.map(x => (Array.isArray(x) ? this.flatten(x) : x)))

    },

    // 接口 ----------------------处理格式

    productTree() {

      this.methodsFunc().then(res => {

        this.fnItemTreeArea(res.data)

      })

    },

    fnItemTreeArea(data) {

      let list = this.fn(data)

      if (this.isAdd) {

        list = this.fn1(list)

      }

      if (this.showEnable) {

        list = this.filter1(list)

      } else {

        list = this.filter(list)

      }

        // list 是最终的数据源 上面写了那么多 是因为 我们业务要过滤各种情况下的数据(如果你们业务没有可以删除)

//list 的数据结构 就是 tree 大家可以直接套

      this.data = list

      this.navList = [{ name: '全部', value: '1', data: this.data }]

    },

    fn(arr = []) {

      return arr.map(item => {

        item.disabled = item.node.isDelete === '1'

        item.checked = false

        if (item.childs && item.childs.length) {

          item.childs = this.fn(item.childs)

        }

        return {

          ...item

        }

      })

    },

    fn1(arr = []) {

      // 判断改归属地是否禁用,禁用了不展示

      return arr.filter(item => item.node.enable === '1')

    },

    filter1(arr = []) {

      return arr.filter(item => {

        // 用于判断 最底层是否被禁用了 如果禁用了 就不显示

        if (item.node.type !== '4') {

          return item.childs && item.childs.length

        }

        return true

      })

    },

    filter(arr = []) {

      return arr.filter(item => {

        if (!this.hidePart && !this.isThree) {

          if (!item.disabled && item.childs && item.childs.length) {

            item.childs = this.filter(item.childs)

          }

        }

        // 用于判断 最底层是否被禁用了 如果禁用了 就不显示

        if (item.node.type !== '4') {

          return !item.disabled && item.childs && item.childs.length

        } else {

          return !item.disabled

        }

      })

    },

    // 接口 ----------------------处理格式

    // --------------提交 方法

    sureSubmit() {

      if (!this.openRadioselection) {

        // 过滤树形结构 checkout 为true的

        if (this.isNoMultiplechoice === '0') {

          const a = this.filterMenuList(this.navList[0].data)

          const b = this.traverse(a).map((item, index) => {

            const obj = {

              oneId: item[0].id,

              oneContent: item[0].name,

              secondId: item[1].id,

              secondName: item[1].name,

              threeId: item[2].id,

              threeName: item[2].name,

              fourId: item[3].id,

              fourName: item[3].name

            }

            console.log(obj)

            return {

              ...obj

            }

          })

          this.navList.splice(1)

          this.data = this.navList[0].data

          this.$emit('sureSubmit', b) // 提交数据

          // this.fn(this.navList[0].data) // 将数据变成false

          // this.close()

        } else if (this.isNoMultiplechoice === '1') {

          const copyData = JSON.parse(JSON.stringify(this.navList[0].data))

          this.newData = []

          this.filterCheckoutTrue(copyData)

          const listSigle = this.newData

            .map(item => {

              const aa = this.findIdList(this.navList[0].data, item.id, item)

              console.log(aa, 'aa')

              return aa

            })

            .map(ite => {

              const obj = {

                oneId: ite[0].infoItem.id,

                oneContent: ite[0].infoItem.name,

                secondId: ite[1] ? ite[1].infoItem.id : '',

                secondName: ite[1] ? ite[1].infoItem.name : '',

                threeId: ite[2] ? ite[2].infoItem.id : '',

                threeName: ite[2] ? ite[2].infoItem.name : '',

                fourId: ite[3] ? ite[3].infoItem.id : '',

                fourName: ite[3] ? ite[3].infoItem.name : ''

              }

              this.preProcessData(obj)

              return {

                ...obj

              }

            })

          this.navList.splice(1)

          this.data = this.navList[0].data

          this.$emit('sureSubmit', listSigle) // 提交数据

        } else if (this.isNoMultiplechoice === '2') {

          const copyData = JSON.parse(JSON.stringify(this.navList[0].data))

          this.newData = []

          this.filterCheckoutTrue(copyData)

          const listSigle = this.newData

            .map(item => {

              const aa = this.findIdList(this.navList[0].data, item.id, item)

              console.log(aa, 'aa')

              return aa

            })

            .map(ite => {

              const obj = {

                oneId: ite[0].infoItem.id,

                oneContent: ite[0].infoItem.name,

                secondId: ite[1] ? ite[1].infoItem.id : '',

                secondName: ite[1] ? ite[1].infoItem.name : '',

                threeId: ite[2] ? ite[2].infoItem.id : '',

                threeName: ite[2] ? ite[2].infoItem.name : '',

                fourId: ite[3] ? ite[3].infoItem.id : '',

                fourName: ite[3] ? ite[3].infoItem.name : ''

              }

              this.preProcessData(obj)

              return {

                ...obj

              }

            })

          this.navList.splice(1)

          this.data = this.navList[0].data

          this.$emit(

            'sureSubmit',

            this.newData.map(ite => {

              return {

                ...ite,

                oneContent: ite.name,

                oneId: ite.id

              }

            }), // 当前自己勾选的数据

            listSigle // 含有勾选的父级数据

          ) // 提交数据

        }

        // this.fn(this.navList[0].data) // 将数据变成false

        // this.close()

      } else {

        // 单选

        const copyData = JSON.parse(JSON.stringify(this.navList[0].data))

        this.newData = []

        this.filterCheckoutTrue(copyData)

        const listSigle = this.newData

          .map(item => {

            const aa = this.findIdList(this.navList[0].data, item.id, item)

            console.log(aa, 'aa')

            return aa

          })

          .map(ite => {

            const obj = {

              oneId: ite[0].infoItem.id,

              oneContent: ite[0].infoItem.name,

              secondId: ite[1] ? ite[1].infoItem.id : '',

              secondName: ite[1] ? ite[1].infoItem.name : '',

              threeId: ite[2] ? ite[2].infoItem.id : '',

              threeName: ite[2] ? ite[2].infoItem.name : '',

              fourId: ite[3] ? ite[3].infoItem.id : '',

              fourName: ite[3] ? ite[3].infoItem.name : ''

            }

            this.preProcessData(obj)

            return {

              ...obj

            }

          })

        this.navList.splice(1)

        this.data = this.navList[0].data

        this.$emit(

          'sureSubmit',

          this.newData.map(ite => {

            return {

              ...ite,

              oneContent: ite.name,

              oneId: ite.id

            }

          }), // 当前自己勾选的数据

          listSigle // 含有勾选的父级数据

        ) // 提交数据

      }

      this.fn(this.navList[0].data) // 将数据变成false

      this.close()

    },

    // 判断对象里面有 'undefined' null  ''

    isEmpty(obj) {

      if (typeof obj === 'undefined' || obj === null || obj === '') return true

      return false

    },

    preProcessData(formData) {

      Object.keys(formData).forEach(item => {

        if (this.isEmpty(formData[item])) {

          delete formData[item]

        }

      })

      return formData

    },

    // 递归查询为true 的

    filterCheckoutTrue(data) {

      data.forEach(item => {

        if (item.childs) {

          this.filterCheckoutTrue(item.childs)

          delete item.childs

          if (item.checked === true) {

            this.newData.push(item)

          }

        } else {

          if (item.checked === true) {

            this.newData.push(item)

          }

        }

      })

    },

    // ------------------

    // 将tree 转换二维数组

    traverse(tree, path = [], result = []) {

      if (!tree) return []

      for (const data of tree) {

        path.push(data)

        const isLeaf = !data.childs || !data.childs.length

        isLeaf ? result.push([...path]) : this.traverse(data.childs, path, result)

        path.pop()

      }

      return result

    },

    // -----------------

    // 处理格式

    filterMenuList(list) {

      const copyList = JSON.parse(JSON.stringify(list))

      const filterList = list => {

        if (list instanceof Array && list.length > 0) {

          const temp = list.filter(item => item.checked === true)

          temp.forEach(e => {

            if (e.childs) {

              e.childs = filterList(e.childs)

            }

          })

          return temp

        }

      }

      return filterList(copyList)

    },

    clearInfo() {},

    onSearch() {},

    close() {

      this.$emit('closepop') // 通知父组件改变。

    },

    navListClick(item, index) {

      if (index === this.navList.length - 1) {

        return

      }

      if (item.name === '全部') {

        this.navList.splice(1)

        this.data = this.navList[0].data

      } else {

        // 点击某一层级树

        if (this.navList.length - 1 > index) {

          this.navList.splice(index + 1)

        }

        this.data = this.navList[index].data

      }

    },

    checkedMethods(item) {

      if (!this.openRadioselection) {

        if (this.isNoMultiplechoice === '0') {

          if (item.node.type === '1') {

            // 如果选择第一级

            item.checked = !item.checked

            this.fatherCheckout = item.checked

            this.sonInfo(item.childs)

          } else {

            this.findIdList(this.navList[0].data, item.id, item)

            this.fatherCheckout = item.checked

            if (item.childs) {

              this.sonInfo(item.childs)

            }

          }

        } else if (this.isNoMultiplechoice === '1') {

          this.findIdList(this.navList[0].data, item.id, item)

          if (item.childs && !this.SubsetnotSelected) {

            this.sonInfo(item.childs)

          }

          // this.fatherCheckout = item.checked

        } else if (this.isNoMultiplechoice === '2') {

          item.checked = !item.checked

          // this.fatherCheckout = item.checked

        }

      } else {

        this.breadthQuerySelect(this.navList[0].data, item.id)

        this.sureSubmit()

      }

    },

    // 通过id 遍历 查询匹配的值 并且返回对象

    breadthQuerySelect(tree, id) {

      var stark = []

      stark = stark.concat(tree)

      while (stark.length) {

        var temp = stark.shift()

        if (temp.childs) {

          stark = stark.concat(temp.childs)

        }

        if (temp.id === id) {

          temp.checked = true

        } else {

          temp.checked = false

        }

      }

    },

    sonInfo(arr) {

      return arr.map(item => {

        if (item.node) {

          item.disabled = item.node.isDelete === '1'

          if (this.fatherCheckout === false) {

            item.checked = false

          } else {

            item.checked = !item.checked

          }

        }

        if (item.childs && item.childs.length > 0) {

          item.childs = this.sonInfo(item.childs)

        } else {

          item.childs = []

        }

        return {

          ...item

        }

      })

    },

    clickChild(item) {

      if (!item.childs || item.childs.length === 0) {

        // Toast('暂无更多')

        return

      }

      this.data = item.childs

      this.navList.push({ name: item.name, value: item.id, data: item.childs })

    },

    // 获取 父级

    findIdList(data2, id, itemInfo, children = 'childs', level = 0) {

      var arrRes = []

      const obj = {

        id: 0,

        [children]: data2

      }

      const rev = (data, id, level) => {

        if (!data || !data[children] || !data[children].length) {

          return

        }

        for (var i = 0; i < data[children].length; i++) {

          const item = data[children][i]

          if (item.id === id) {

            arrRes.unshift({ level, activeId: item.id, infoItem: item })

            rev(obj, data.id, 0)

            break

          } else if (item[children] && item[children].length > 0) {

            // 如果有子集,则把子集作为参数重新执行本方法

            rev(item, id, level + 1)

          }

        }

      }

      rev(obj, id, level)

      itemInfo.checked = !itemInfo.checked

      const a = this.navList[this.navList.length - 1].data.some(it => it.checked)

      arrRes.slice(0, arrRes.length - 1).forEach(ite => {

        if (a) {

          ite.infoItem.checked = true

        } else {

          ite.infoItem.checked = false

        }

      })

      return arrRes

    }

  }

}

</script>

<style lang="scss" scoped>

.container-searchPanel {

  width: 100%;

  height: 100%;

  // margin-top: 200px;

  -webkit-overflow-scrolling: touch;

  word-break: break-all;

  .wrapper {

    height: 100%;

    width: 100%;

  }

  .category-wrapper {

    height: 50px;

    width: 100%;

    padding-left: 10px;

    background-color: rgb(237, 239, 247);

    white-space: nowrap;

    overflow-y: hidden;

    overflow-x: auto;

    &::-webkit-scrollbar {

      display: none;

    }

    .category-item {

      display: inline-block;

      font-size: 14px;

      height: 30px;

      padding: 0 14px;

      background-color: #fff;

      border-radius: 30px;

      line-height: 30px;

      margin: 10px 8px 0 0;

      min-width: 66px;

      text-align: center;

      color: #666666;

      &.active {

        color: #4297ed;

      }

    }

  }

}

.background-nav {

  width: 100%;

  background: rgb(247, 247, 247);

  // height: 40px;

  // white-space: nowrap;

  // background: yellow;

  // overflow: scroll;

  // white-space: nowrap;

  .inline-item {

    // display: block;

    line-height: 40px;

    white-space: nowrap;

    // background: yellow;

    overflow: scroll;

    // -webkit-overflow-scrolling: touch; //解决ios滑动慢的问题

    padding-left: 12px;

    // background: red;

    // display: inline-block;

    // 导航栏项-无状态

    .none {

      color: #666666;

      display: inline-block;

    }

  }

}

.infoShow {

  height: calc(100vh - 150px);

  overflow-x: hidden;

  overflow-y: scroll;

  .item-p {

    border-bottom: 1px solid #ccc;

    .van-cell {

      padding-left: 5px;

    }

  }

  .vanbtn {

    position: fixed;

    bottom: 4px;

  }

}

// .infoShow::-webkit-scrollbar {

//   display: none;

// }

// 导航栏项-启用状态

.active {

  color: #4297ed !important;

}

// / 导航栏项样式

// .inline-item {

// }

.iconSize {

  font-size: 24px;

}

.Nomore {

  text-align: center;

  margin-top: 20px;

}

.fade-enter-active,

.fade-leave-active {

  transition: opacity 0.5s;

}

.fade-enter,

.fade-leave-to {

  opacity: 0;

}

.select_group {

  display: inline-block;

  width: 10px;

  margin-right: 10px;

  color: #ed3458;

  // margin: 12px 0px;

}

::v-deep .van-dropdown-menu__bar {

  height: 33px;

  background-color: #edeff7;

  box-shadow: none;

}

::v-deep .van-dropdown-menu__title {

  padding-left: 0px;

  // margin-right: 10px;

}

::v-deep .van-search .van-cell {

  padding-left: 13px;

}

.van-cell__title {

  overflow: hidden;

  white-space: nowrap;

  text-overflow: ellipsis;

}

</style>

父组件使用

<!-- Created by Tanking. -->

<!-- 事故事件 -->

<template>

  <div class="container container-supports">

   

      <div v-for="(item, index) in data" :key="index">{{ item }}</div>

      <span class="title" @click="aaShow">事故调查</span>

      <prodTreeNew

        isAdd

        :showInfo="show"

        isNoMultiplechoice="0"

        @closepop="show = false"

        :methodsFunc="productTree"

        :infoValue="dataCopy"

        @sureSubmit="sureSubmit"

      ></prodTreeNew>

    </div>

  </div>

</template>

<script>

// import { productTree } from '@/api/plan'

import { chooseAreaInfo } from '@/api/safetyCheck'

export default {

  name: 'EventManagement',

  data() {

    return {

      show: false,

      data: [], //页面回显

      dataCopy: [], //数据回显

      dataCopy1: [] //数据回显

    }

  },

  computed: {

    productTree() {

      return chooseAreaInfo

    }

  },

  methods: {

    aaShow(info) {

      this.show = true

    },

    sureSubmit(info, infoList) {

      console.log(info)

      console.log(infoList)

      this.dataCopy = info

      this.data = info.map(ite => {

        let a = [ite.oneContent, ite.secondName, ite.threeName, ite.fourName].filter(item => item)

        return a.join('-')

      })

    }

  }

}

</script>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值