项目地址:地址
演示地址,可以直观看到效果:地址
效果图:设置第二层是互斥层级(节点具有单选功能)
首先,需要了解cascader组件的底层逻辑,以父子关联、互斥层级为第二层为前提。
当用户随便点击了一个节点,那么节点组件cascader-node便会执行以下三个步骤:
- 通知其子代更新选中状态
- 根据其子代的选中情况带更新自身的情况
- 通知其父代更新
// 用户点击节点
doCheck(checked) {
if (this.checked !== checked) {
if (this.config.checkStrictly) {
this.checked = checked;
} else {
// 通知其子代更新选中状态
this.broadcast('check', checked);
// 根据其子代的选中情况带更新自身的情况
this.setCheckState(checked);
// 通知其父代更新
this.emit('check');
}
}
}
目标:同一层级的节点互斥。
我们要分三种情况:
一、点击的节点处于互斥层级,我们直接在第二步的
// 根据其子代的选中情况带更新自身的情况
this.setCheckState(checked);
里面将同层级的其他节点给取消。
// 当前选择的节点属于互斥层级,则将其兄弟节点,全取消checked
mutualExclusion() {
// 互斥逻辑
const {checked, indeterminate, level, value, parent, config} = this;
if (!checked && !indeterminate) return;
if (config.mutualExcluseLabel && config.mutualExcluseLabel === level) {
// 当互斥层级设置为1时,会很特殊,此处不讲
if (level === 1) {
this.firstLevelNodes.forEach(el => {
el.value !== value && (el.checked || el.indeterminate) && !el.isDisabled && (el.doCheck(false, true, false));
});
} else {
// 直接看这里,互斥层级非1时,直接通过节点的parent节点拿到节点的兄弟节点,然后设置互斥即可。
parent.children.forEach(el => {
el.value !== value && (el.checked || el.indeterminate) && !el.isDisabled && (el.doCheck(false, true, false));
});
}
}
}
二、点击的节点所处的层级是大于互斥层级时,处理逻辑和情况一
一样
三、点击的点击所处的层级是互斥层级的父层级时,得分情况
- 如果互斥层级已经有选中的节点时,得取消当前点击的节点
- 如果互斥层级没有选中的节点时,只通知子代的第一个节点更新状态。
broadcast(event, ...args) {
let {config, level, children} = this;
const handlerName = `onParent${capitalize(event)}`;
// 此处要加互斥逻辑,因为父节点的点击,理论上要全选其子代,但使用了互斥逻辑的层级不行
// 如果是互斥节点的父节点被点击了,且是选中的操作,那么只通知第一个子代更新即可
if (config.mutualExcluseLabel && level + 1 === config.mutualExcluseLabel && args[0]) {
if (children[0]) {
let child = children[0];
child.broadcast(event, ...args);
child[handlerName] && child[handlerName](...args);
}
} else {
this.children.forEach(child => {
if (child) {
// bottom up
child.broadcast(event, ...args);
child[handlerName] && child[handlerName](...args);
}
});
}
}
异常情况: 其实原生的input组件type为checkbox时,我们这样的修改会产生很多很多的问题,列出来太麻烦,但都被我解决了,建议看官直接用文章顶部的项目地址即可。