2024-12-22 小汇总
· 阅读需 3 分钟
级联选择
复选框树形选择器比较常见,比如antd里的TreeSelect和Cascader组件。当选择某一项时,有以下几种可能:
- 如果是选中,则它的下级全都会被选中,并且还需要判断他有没有父级,如果有父级,还需要判断父级是否也是全选中或者半选中状态;
- 如果是取消选中,它的下级也会全部被取消选中,并且也要判断父级(或者说是上级)的选中状态。
要实现一个updateSelectedStatus函数,它的前面如下:
enum SelectedStatus {
/** 未选中 */
Unselected,
/** 选中 */
Selected,
/** 半选中 */
Indeterminate,
}
interface Cascade {
id: string;
selectedStatus: SelectedStatus;
children?: Cascade[];
}
function updateSelectedStatus(
checked: boolean,
current: Cascade,
tree: Cascade[]
): Cascade[];
updateSelectedStatus的参数如下:
checked当前项是不是选中状态;current当前项;tree整颗级联树;
返回值是路径数组,比如一个属性结构是:
1
|----1_1
|--------1_1_1
|--------1_1_2
|----1_2
|--------1_2_1
|--------1_2_2
2
|----2_1
|--------2_1_1
|--------2_1_2
|----2_2
如果一开始都是未选中状态,这时候我去选了1_2这一项。则:1_2和他的下级1_2_1、1_2_2都会被选中,并且1这一项将变成半选状态。返回值将会是:
[
{
id: '1_2',
selectedStatus: SelectedStatus.Selected,
children: [...]
},
{
id: '1',
selectedStatus: SelectedStatus.Indeterminate,
children: [...]
},
]
实现:
/** 更新父选择状态 */
function updateParentSelectedType (parent: Cascade) {
const children = parent.children;
/* 是不是半选 */
let hasIndeterminate = false;
if (children?.length) {
const checkedList = children.filter((item) => {
if (item.selectedType === SelectedType.Indeterminate) {
hasIndeterminate = true;
return false;
}
return item.selectedType === SelectedType.Selected;
}
);
if (hasIndeterminate) {
parent.selectedType = SelectedType.Indeterminate;
} else if (checkedList.length === children.length) {
parent.selectedType = SelectedType.Selected;
} else if (!checkedList.length) {
parent.selectedType = SelectedType.Unselected;
} else {
parent.selectedType = SelectedType.Indeterminate;
}
}
return parent;
};
function updateSelectedStatus(
checked: boolean,
current: Cascade,
tree: Cascade[],
) {
const _f = (
checked: boolean,
current: Cascade,
tree: Cascade[],
parent?: Cascade
): Cascade[] => {
// 存放更新的路径
const records: Cascade[] = [];
for (const child of tree) {
if (child.id === current.id) {
child.selectedType = checked
? SelectedType.Selected
: SelectedType.Unselected;
records.push(child);
/** 更新子项 */
if (child.children?.length) {
child.children.forEach((item) => {
_f(checked, item, child.children, child);
});
}
/** 更新父项 */
if (parent) {
records.push(updateParentSelectedType(parent));
}
// 找到后就可以返回了,不用再循环去找了
return records;
} else {
if (child.children?.length) {
const list = _f(checked, current, child.children, child);
// list 中有值说明在下级找到了 current,否则就是没找到
if (parent && list.length) {
return [...list, updateParentSelectedType(parent)];
}
return list;
}
}
}
return records;
};
return _f(checked, current, tree);
}