父子不关联的级联选择器

2019-10-05  本文已影响0人  小小的开发人员

背景

项目公告栏可见范围选择可见部门以及可见职位时需要设计以下选择器:
1、选择父级时所有子级默认置灰、为不可选状态。
2、选中值为当前勾选框的值,不涉及子级。

如下效果图:


问题

采用的element-ui框架提供的级联选择器不能同时满足上述两种要求,需要做改造。

解决思路

element-ui v2.9.2之后,级联选择器提供了多选模式,checkStrictly属性可以严格遵守父子节点不互相关联,这可以解决问题2,element-ui支持对子级数据设置disabled属性,控制子级为不可选状态,置灰样式可以针对disabled的class定制化设计,这可以解决问题1。

解决方案

<el-cascader
    v-model="value"
    :options="options"
    :props="{ multiple: true, checkStrictly: true, expandTrigger: 'click' }"
    @change="changeParent"
    :collapse-tags="true"
    size="large"
    :show-all-levels="false"
    clearable></el>

1、需要将后台返回的部门数据格式转化成element能识别的。

 transDepartmentsToElmentData(data) {
      function findChildren(children) {
        if (!children || children.length === 0) return
        for (let i = 0; i < children.length; i += 1) {
          const item = children[i]
          item.value = item.departmentId
          item.label = item.name
          if (_.isArray(item.children) && item.children.length > 0) {
            findChildren(item.children)
          }
        }
      }
      findChildren(data)
      return data
    },

2、进行选中值的所有后代进行置灰

(1)当选中的值发生变化时触发setGray(arg)方法,传递选中的值

changeParent(arg) {
      this.options = this.dataList
      this.setGray(arg)
    },

(2)进行置灰操作

setGray(arg) {
      const data = _.cloneDeep(this.options) // 源数据
      const value = this.value // 选中值
      let clearedValue = null // 经过clearSelectedChildren过滤后的选中值
      let grayChildren = [] // 置灰数组,是一个一维数组,记录所有置灰的后代

      function findChildren(value1, dataArr) { // 找到value1的children
        const filterData = _.filter(dataArr, (item) => item.value === value1)
        return filterData[0].children
      }

      function setChildrenGray(children) { // 向所有子代添加disabled属性
        if (!children) return
        grayChildren = _.concat(grayChildren, children)
        for (let i = 0; i < children.length; i += 1) {
          const item = children[i]
          item.disabled = true
          if (!item.value) {
            return
          }
          if (_.isArray(item.children)) {
            setChildrenGray(item.children)
          }
        }
      }

      function clearSelectedChildren(children, value2) { // children是置灰数组,value2是选中的值
        if (!children) return
        const newValue = _.map(value2, (arr) => {
          const newArr = _.filter(arr, (val) => { // 过滤掉在置灰数组中的选中值
            let isFind = false
            for (let i = 0; i < children.length; i += 1) {
              if (val === children[i].value) {
                isFind = true
              }
            }
            return !isFind
          })
          return newArr
        })
        clearedValue = _.uniqWith(newValue, _.isEqual) // 去重
      }

      _.forEach(arg, (arr) => { // 找到选中值的所有的后代,传入的arg的数据结构是[[val1], [val2, val22, val222]]
        let children
        if (arr.length === 1) {
          children = findChildren(arr[0], data)
        } else if (arr.length > 1) {
          let head = 0
          let findedChildren
          while (head < arr.length) {
            if (head === 0) {
              findedChildren = findChildren(arr[0], data)
            } else {
              findedChildren = findChildren(arr[head], findedChildren)
            }
            head += 1
          }
          children = findedChildren
        }
        setChildrenGray(children)
      })

      clearSelectedChildren(grayChildren, value)
      this.options = data
      this.value = clearedValue
    },

(3)选中值是完整链路[['value1'], ['value2', 'value22']'],传给后台的值应该是选中框对应的唯一值 ['value1', 'value22'],取最后一级

getDepartmentsValue(value) {
this.announcementForm.departmentIds = value.map((item) => item[item.length - 1]) || []
},

(4)对返回的 'value22'进行链路查找,得到完整链路['value2', 'value22']',能被element-ui组件识别

getElementPath(departmentsList, departmentId) { // departmentsList是源数据, departmentId被查找的值
      const recordPath = [] // 记录链路,
      let finalChild = null // 回溯的最终值

      function getFinalChild(data, value, parent) {
        if (!data) return
        for (let i = 0; i < data.length; i += 1) {
          const item = data[i]
          item.parent = parent // 挂载parent属性
          if (item.value === value) {
            finalChild = item // 确定最终值
            return
          }
          if (Array.isArray(item.children)) {
            getFinalChild(item.children, value, item)
          }
        }
      }

      function getPath(finalValue, value) {
        if (!finalValue) {
          recordPath.unshift(value)
        } else {
          while (finalValue) { // 对parent进行回溯
            recordPath.unshift(finalValue.value)
            finalValue = finalValue.parent
          }
        }
        return recordPath
      }
      getFinalChild(departmentsList, departmentId, null)
      return getPath(finalChild, departmentId)
    },

(5)对置灰的选项进行样式定制

.department-cascader .el-checkbox__input.is-disabled .el-checkbox__inner {
    background-color: #909399;
    border-color: #909399;
    }
    .department-cascader .el-checkbox__input.is-disabled .el-checkbox__inner::before {
    content: '';
    position: absolute;
    left: 4px;
    top: 1px;
    width: 3px;
    height: 7px;
    border-color: #fff;
    border-style: solid;
    border-width: 0 1px 1px 0;
    transform: rotate(45deg);
  }

效果图


不足

elment-ui v2.9.0对级联选择器进行了重构,重构后对之前版本的级联选择器的特性支持很差,而目前项目用的element是v2.4.11,直接升级版本是有影响的,升级工作需要花更多时间排查、测试,这是调研不充分导致的,在编写代码之处没有考虑到的,也是下次需要注意的地方。

上一篇下一篇

猜你喜欢

热点阅读