element-plus cascader级联组件 父子半关联(父取消子不取消 子全选自动勾选父)

element-plus cascader级联组件 父子半关联(父取消子不取消 子全选自动勾选父)

需求

1、分类项,标签项,不可以勾选,标签值可以勾选;
2、当标签值下还有标签值时下一级全勾选上父级标签值自动打勾,父级取消勾选,子标签值不取消勾选;
3、每一个标签值都是独立的标签。

思路

使用组件自带的父子关联肯定是做不了这个需求,所以需要设置checkStrictly属性设置为true,然后通过判断现有选中的子节点数据长度是否去勾选父
使用ref调用组件内部暴露的calculateCheckedValue更新视图
不可以勾选的选项通过在对应的数据位置设置disabled:true,再通过样式把前置复选框给隐藏就可以

效果图

请添加图片描述

代码

<!-- html -->
<el-cascader
  :props="cascaderProps"
  :options="cascaderOptions"
  :show-all-levels="false"
  placeholder="请选择"
  collapse-tags
  collapse-tags-tooltip
  :max-collapse-tags="5"
  ref="cascaderRef"
  clearable
  v-model="opearteFrom.labelIds"
/>
// 内容标签下拉值
const cascaderRef = ref()
const cascaderOptions = ref([])
const cascaderProps = { multiple: true, value: 'id', label: 'name', checkStrictly: true, emitPath: false }
const checkedNode = ref([])

watch(() => opearteFrom.value.labelIds, (newArr, oldArr) => {
  if (newArr && newArr.length) {
    const current = findCurrentCascaderChecked(newArr, oldArr || [])
    if (!current) return
    nextTick(() => {
      // 从级联选择器中取出内部的节点数组,找到当前节点
      let targetNode:any = ''
      checkedNode.value = cascaderRef.value.getCheckedNodes()
      targetNode = checkedNode.value.find((item:CascaderNode) => {
        return item.value === current.value
      })
      if (current.type === 'checked') {
        if (targetNode) {
          const peerIds = targetNode.parent.childrenData.map((obj:CascaderOption) => obj.id)
          const repeatCount = countMatchingIds(peerIds, opearteFrom.value.labelIds || [])
          if (repeatCount >= peerIds.length && !targetNode.parent.isDisabled) {
            targetNode.parent.checked = true
            cascaderRef.value.cascaderPanelRef.calculateCheckedValue()
          }
        }
      } else if (current.type === 'cancel') {
        if (targetNode) {
          targetNode.parent.checked = false
          cascaderRef.value.cascaderPanelRef.calculateCheckedValue()
        }
      }
    })
  }
}, { deep: true })


// 最后获取选中的值
const checkedNodeArr = cascaderRef.value.getCheckedNodes()
const labelIds = checkedNodeArr.map((obj:any) => parseInt(obj.value))
console.log(labelIds)
// utils.ts 
// 判断当前操作是选中还是取消
export const findCurrentCascaderChecked = (newArr: number[], oldArr: number[]) => {
  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'
        }
      }
    }
  }
}

// 两数字数组 重复值计数
export function countMatchingIds(arr1: number[], arr2: number[]) {
  // 创建一个Set集合,用于存储数组1中的所有ID
  const idSet = new Set(arr1)
  let count = 0

  // 遍历数组2,检查是否存在于Set集合中,并增加计数
  for (let i = 0; i < arr2.length; i++) {
    if (idSet.has(arr2[i])) {
      count++
    }
  }
  return count
}

拓展

到这产品描述的需求就做完,思考了一下,感觉很别扭,不太合理。正常逻辑应该是除开不可勾选的,标签值父子关联,也就是父勾选子自动勾选,这里提供两个方法进行参考

// 处理树节点 选中父 子全部选上
export function checkedChildren(val) {
  for (const item of val) {
    item.checked = true
    if (item.children && item.children.length !== 0) {
      checkedChildren(item.children)
    }
  }
  return val
}

// 处理树节点 取消父 子全部取消
export function cancelChildren(val) {
  for (const item of val) {
    item.checked = false
    if (item.children && item.children.length !== 0) {
      cancelChildren(item.children)
    }
  }
  return val
}

// 传入的val 就是上面 findCurrentCascaderChecked方法返回的value

小声bb

产品a:我就要这么做,我感觉挺简单的。
我:市面上有这种吗,可以找到给我参考下吗?
产品b: emmm…别管,就是要这种,你就说会不会做吧。
我:…
擦,真的是产品一句话说完就下班,开发裤裤加班改。。。。

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
对于element-plus中的Cascader择器,使用Tooltip组件预览内容可以如下实现: 首先,在需要展示Tooltip的地方,使用`el-tooltip`组件包裹需要预览的内容,设置`content`属性为预览的内容,`disabled`属性为false(默认为true,需要手动设置为false才能生效),`effect`属性为`dark`或者`light`(根据UI风格择),其余属性可根据需求自行设置。 然后,在Cascader组件上,使用`slot-scope`自定义模板,在需要展示的地方,使用`el-tooltip`组件包裹需要预览的内容,设置`content`属性为预览的内容,`disabled`属性为false(默认为true,需要手动设置为false才能生效),`effect`属性为`dark`或者`light`(根据UI风格择),其余属性可根据需求自行设置。 示例代码如下: ```html <template> <el-cascader v-model="selectedOptions" :options="options" :props="props" @change="handleChange" :filterable="true" :clearable="true" :show-all-levels="false" :expand-trigger="expandTrigger" :change-on-select="changeOnSelect" > <template #default="{ labels, selectedOptions }"> <el-tooltip class="item" effect="dark" :disabled="!labels || labels.length === 0" :content="labels.join(' / ')" > <span class="el-cascader__label">{{ labels[labels.length - 1] }}</span> </el-tooltip> </template> <template #expand="{ index, data }"> <el-tooltip v-for="item in data" :key="item.value" class="item" effect="light" :content="item.label" :disabled="!item.label" > <span class="el-cascader-menu__item" @click="handleItemClick(index, item)"> {{ item.label }} </span> </el-tooltip> </template> </el-cascader> </template> ``` 注意事项: 1. `el-tooltip`组件必须设置`disabled`属性为`false`,否则会导致搜索框无法输入。 2. `el-tooltip`组件的`content`属性可以为任何类型的值,可以是字符串、数字、HTML标签等。 3. `el-tooltip`组件的`effect`属性可值有`dark`和`light`,根据UI风格择。 4. `el-cascader`组件的`expand-trigger`属性可值有`click`和`hover`,根据需求择。 5. `el-cascader`组件的`change-on-select`属性可值有`true`和`false`,根据需求择。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值