(element-plus cascader级联组件 父子半关联(父取消子不取消 子全选自动勾选父))
需求
1、分类项,标签项,不可以勾选,标签值可以勾选; 2、当标签值下还有标签值时下一级全勾选上父级标签值自动打勾,父级取消勾选,子标签值不取消勾选; 3、每一个标签值都是独立的标签。
思路
使用组件自带的父子关联肯定是做不了这个需求,所以需要设置checkStrictly属性设置为true,然后通过判断现有选中的子节点数据长度是否去勾选父 使用ref调用组件内部暴露的calculateCheckedValue更新视图 不可以勾选的选项通过在对应的数据位置设置disabled:true,再通过样式把前置复选框给隐藏就可以
效果图
代码
html
<!-- 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"
/>
typescript
// 内容标签下拉值
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)
typescript
// 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
}
拓展
到这产品描述的需求就做完,思考了一下,感觉很别扭,不太合理。正常逻辑应该是除开不可勾选的,标签值父子关联,也就是父勾选子自动勾选,这里提供两个方法进行参考
typescript
// 处理树节点 选中父 子全部选上
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....别管,就是要这种,你就说会不会做吧。 我:...... 擦,真的是产品一句话说完就下班,开发裤裤加班改。。。。