首页 前端知识 vue封装el-cascader级联选择器组件单选、多选、搜索、数据回显问题

vue封装el-cascader级联选择器组件单选、多选、搜索、数据回显问题

2025-02-24 13:02:37 前端知识 前端哥 986 383 我要收藏

不知道怎么描述,反正是折腾了大半天,前端也不是很精通,看一下文档,如果有人遇到和我一样的问题,希望能给到一点启发吧

刚开始的需求描述

最近项目上需要使用级联选择器,绑定组织机构,组件需要满足以下要求:

  1. 在编辑详情时只能进行单选,在单选情况下,可以选择任何一级,如下图所示

在这里插入图片描述

  1. 在列表查询时支持多选,在 多选情况下,选择父节点可以全选子节点,父子节点一起作为参数过滤查询列表,如下图所示
    在这里插入图片描述
    以上功能看el-cascader的官方说明文档,是能直接实现的,上一部分代码:
    1)封装组件部分
<el-cascader
      :options="information"
      ref="cascaderAddr"
      v-model="dwCodes"
      :show-all-levels="false"
      :props="propsOpyions"
      @change="handleChange"
      collapse-tags
      collapse-tags-tooltip
      clearable
      filterable
      :disabled="disabled"
    >
</el-cascader>

export default {
	props: {
		value: {
            type: null
        },
        // 是否禁用
        disabled: {
          type: Boolean,
          default: false,
          required: false
        },
        // 是否多选
        multiple: {
          type: Boolean,
          default: false,
          required: false
        }
    },
    data() {
      return {
        dwCodes: [], //回显绑定的值
        information: [], // 选择器数据源
        propsOpyions: {
          multiple: true, // 是否允许多选
          emitPath: false, // 是否展示完整路径
          checkStrictly: true, // 是否可选择任意一级
          expandTrigger: 'click' // 展开事件
        }
      };
    },
    created() {
      this.init()
      this.propsOpyions.multiple = this.multiple
      // 多选时,若checkStrictly为true,则选择父节点时,只能选到父节点
      // 不能全选它下面的所有子节点,所以需要设置为false
      if(this.multiple) {
         this.$set(this.propsOpyions, 'checkStrictly', false)
      }
    },
    watch: {
      value: {
        // 数据回显,很重要
        handler(newVal, oldVal) {
          this.dwCodes = newVal
          if(this.multiple && this.dwCodes) {
            this.dwCodes = this.dwCodes.split(',')
          }
        },
        immediate: false,
        deep: false
      }
    },
    methods: {
     async init() {
    	// 初始化数据源
     },
     // 值改变
     handleChange(checked) {
        if(this.multiple && checked) {
          let checknodes = this.$refs['cascaderAddr'].getCheckedNodes()
          let checkNodeids = checknodes.map((obj,index) => {
              return obj.value
          });
          this.$emit("input", checkNodeids.join());
        } else {
          this.$emit("input", checked);
        }
      }
    }
}

2)引入组件部分

// 单选时
<dw-Cascader :multiple="false" v-model="gldw" :disabled="disabledFlag">
</dw-Cascader>

// 多选时
<dw-Cascader :multiple="true" v-model="gldw" ></dw-Cascader>

问题来了

  1. 在允许多选时,子节点全部取消的情况下,父节点也会取消选中状态,但是目前的需求是,有些数据是直接归属到父节点单位下面的,如果只想查父节点下的数据,是实现不了的。(checkStrictly为true,虽然可以选到父节点,但是这个时候父节点勾选,子节点全选就不起作用了)
    在这里插入图片描述
  2. 在搜索时,只能检索到最后一级,同样也是受checkStrictly为true的限制,在用户在搜索父级节点时是不显示的,只能搜索最后一级
    在这里插入图片描述

搜索【长沙市】结果,只显示了子节点
在这里插入图片描述

解决

所以在保持原有需求不变的情况下,在面向百度查询了一番,结合互联网好人提供的思路(已经找不到出处了,搜一搜应该也能找到),解决上面说的两个问题。
第一步:在多选情况下,也设置checkStrictly为true,先保证父节点能单独勾选;

created() {
      this.init()
      this.propsOpyions.multiple = this.multiple
      // if(this.multiple) {
      //   this.$set(this.propsOpyions, 'checkStrictly', false)
      // }
},

第二步,监听级联选择框绑定值的变化,这个示例中v-model绑定的是dwcodes,就监听这个值,实现全选和取消全选,取消子节点勾选,父节点保留勾选状态

watch: {
      value: {
        handler(newVal, oldVal) {
          this.dwCodes = newVal
          if(this.multiple && this.dwCodes) {
            this.dwCodes = this.dwCodes.split(',')
          }
        },
        immediate: false,
        deep: false
      },
      dwCodes: {
        handler(newVal, oldVal) {
          this.dwCodes = newVal
          if(this.multiple && this.dwCodes) {
            // 判断当前是添加勾选还是取消勾选,并获取当前操作的节点值
            const current = this.findCurrentDepartment(newVal, oldVal)
            if (!current) return
            this.$nextTick(() => {
              if (current.type === 'checked') {
                  // 级联选择器当前勾选的节点数组
                  this.currentCheckedNodes = this.$refs.cascaderAddr.checkedNodes
                  // 从级联选择器中取出内部的节点数组,找到当前节点
                  const targetNode = this.$refs.cascaderAddr.checkedNodes.find((item) => {
                      return item.value === current.value
                  })
                  if (targetNode) {
                      // 递归找出所有子孙节点,并手动勾选
                      this.checkedChildrenDepartment([targetNode])
                      // 更新视图
                      this.$refs.cascaderAddr.$refs.panel.calculateMultiCheckedValue()
                  }
              } else if (current.type === "cancel") {
                  // 从当前勾选的节点数组中递归找出所有子孙节点,并取消勾选
                  this.cancelChildrenDepartment(current.value, this.currentCheckedNodes)
                  // 更新视图
                  this.$refs.cascaderAddr.$refs.panel.calculateMultiCheckedValue()
              }
            })
          }
        },
        immediate: false,
        deep: true
      }
    },
    methods: {
     async init() {
    	// 初始化数据源
     },
     // 值改变
     handleChange(checked) {
        if(this.multiple && checked) {
          let checknodes = this.$refs['cascaderAddr'].getCheckedNodes()
          let checkNodeids = checknodes.map((obj,index) => {
              return obj.value
          });
          // 回传给父组件的值
          this.$emit("input", checkNodeids.join());
        } else {
          this.$emit("input", checked);
        }
      },
      checkedChildrenDepartment(list = []) {
        list.forEach((item) => {
          item.doCheck(true)
          // 勾选选中当前值,若有子节点,子节点遍历全选
          if (item.children.length > 0) {
            this.checkedChildrenDepartment(item.children)
          }
        })
      },
      cancelChildrenDepartment(cancelNodeValue, list = []) {
        list.forEach((item) => {
          // 取消勾选时,取消当前值,若当前值有子节点时,实现取消父节点勾选,子节点取消全选的功能,同时实现子节点取消勾选,不会影响父节点勾选状态
          if(cancelNodeValue == item.value || cancelNodeValue == item.parent?.value) {
            item.doCheck(false)
            if (item.children.length > 0) {
              // 遍历
              this.cancelChildrenDepartment(item.value, item.children)
            }
          }
        })
      },
      findCurrentDepartment(newArr, oldArr) {
        const catchNewArr = [...newArr]
        const catchOldArr = [...oldArr]
        if (catchNewArr.length > catchOldArr.length) {
            for (let i = 0; i < catchNewArr.length; i++) {
                const targetIndex = catchOldArr.indexOf(catchNewArr[i])
                if (targetIndex === -1) {
                    return {
                        value: catchNewArr[i],
                        type: 'checked',
                    }
                }
            }
        } else {
            for (let i = 0; i < catchOldArr.length; i++) {
                const targetIndex = catchNewArr.indexOf(catchOldArr[i])
                if (targetIndex === -1) {
                    return {
                        value: catchOldArr[i],
                        type: 'cancel',
                    }
                }
            }
        }
      }
    }

实现效果

1.有全选功能
在这里插入图片描述
取消子节点选择,父节点保持选择状态
在这里插入图片描述
2.搜索能检索到父节点
在这里插入图片描述
3.绑定数据回显
在这里插入图片描述

其他

不知道还有没有其他遗漏的地方,反正是实现了,前端开发忙的马不停蹄,一个后端开发又又又在前端挣扎了一下,再次感谢互联网好人们!

我翻了一下历史记录: 参考代码

转载请注明出处或者链接地址:https://www.qianduange.cn//article/21269.html
标签
评论
发布的文章

C/C | 每日一练 (2)

2025-02-24 13:02:49

Linux性能监控工具汇总

2025-02-24 13:02:48

Python常见面试题的详解16

2025-02-24 13:02:48

QQ登录测试用例报告

2025-02-24 13:02:47

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!