一、实现的功能
1.支持用户点击父复选框进行勾选并勾选底下多有子复选框,取消勾选时取消所有子复选框。
2.当没有全选子复选框时,父复选框将会取消勾选状态。
3.用户完成选择后,可以保存所有被勾选的最后一层复选框。比如有勾选两层复选框,就保存第二级内容,有勾选三层复选框,就只保存第三层复选框。
4.在保存之前,对选中的复选框进行判重处理。
二、数据结构定义
有一个form数组,每个元素代表一个复选框项,其结构如下:
//数据需要的字段
{
id: Number,
ffunName: String,
isChecked: Boolean,
isExpanded: Boolean,
children: Array // 子复选框数组
}
selectList: [], //要保存的数据
//数据格式
form: [{
ffunName: "商品1",
isExpanded: false, // 判断是否需要展开
isChecked: false, //判断是否全被勾选
children: [{
ffunName: "商品1.1",
isExpanded: false,
isChecked: false,
children: [{
ffunName: "商品1.1.1",
isExpanded: false,
isChecked: false,
children: [{}],
}],
}, {
ffunName: "商品1.2",
isExpanded: false,
isChecked: false,
children: [{
ffunName: "商品1.2.1",
isExpanded: false,
isChecked: false,
children: [{}],
}, {
ffunName: "商品1.2.2",
isExpanded: false,
isChecked: false,
children: [{}],
}],
}],
}, {
ffunName: "商品2",
isExpanded: false, // 判断是否需要展开
isChecked: false, //判断是否全被勾选
children: [{
ffunName: "商品2.1",
isExpanded: false,
isChecked: false,
children: [{
ffunName: "商品2.1.1",
isExpanded: false,
isChecked: false,
children: [{}],
}],
}],
}],
三、核心逻辑实现
1. 展开/收起父复选框
javascript
methods: {
getClick(index) {
this.form[index].isExpanded = !this.form[index].isExpanded;
},
// ... 其他方法
}
getClick方法用于切换复选框的展开/收起状态。
2. 父复选框勾选状态变化,并自动勾选全部子复选框
handleParentChange(parentIndex) {
const parent = this.form[parentIndex];
const isChecked = !parent.isChecked;
const isExpanded = isChecked;
this.$set(this.form, parentIndex, {
...parent,
isChecked: isChecked,
isExpanded: true,// 勾选父复选框时自动展开
});
// 更新所有子复选框的勾选状态,当调用此方法时,所有底下的子复选框全部被勾选
this.updateChildrenCheckedStatus(parent.children, isChecked, isExpanded);
},
updateChildrenCheckedStatus(children, isChecked, isExpanded) {
if (children && children.length > 0) {
children.forEach(child => {
this.$set(child, 'isChecked', isChecked);
this.$set(child, 'isExpanded', isExpanded);
if (child.children) {
//递归,展开并勾选所有复选框
this.updateChildrenCheckedStatus(child.children, isChecked, isExpanded);
}
});
}
},
handleParentChange方法用于处理父复选框的勾选状态变化,并递归更新其子复选框的勾选状态和展开状态。
3. 子复选框勾选状态变化处理
handleChildChange(parentIndex, childIndex) {
const parent = this.form[parentIndex];
let child = parent.children[childIndex];
// 更新子复选框的勾选状态和展开状态
this.$set(parent.children, childIndex, {
...child,
isChecked: !child.isChecked,
isExpanded: true,
});
// 重新获取更新后的 child 对象
const cc = parent.children[childIndex];
// 如果当前子复选框有子复选框,则更新它们的状态
if (child.children && child.children.length > 0) {
cc.children.forEach((subChild, subIndex) => {
this.$set(cc.children, subIndex, {
...subChild,
isChecked: cc.isChecked,
});
});
}
this.sent(parentIndex, childIndex);
},
sent(index1, index2) {
const parent = this.form[index1];
const child = parent.children[index2];
// 更新上一级选框
// every方法:每一个 item 的 isChecked 属性是否都为 false。如果所有直接子项的 isChecked 属性都为 false,
// 则 noDirectChildrenChecked 的值为 true;否则,为 false
const allChildrenChecked = parent.children.every(item => item.isChecked);
// some方法:是否至少有一个 item 的 isChecked 属性为 true。则 anyChildChecked 的值为 true;
// 所有子项的复选框都未被选中,则 anyChildChecked 的值为 false。
const anyChildChecked = parent.children.some(item => item.isChecked);
// 如果所有子项都勾选,则勾选父级复选框;如果都没有勾选,则取消勾选父级复选框
// 如果部分子项勾选,就取消勾选父级复选框
if (allChildrenChecked) {
this.$set(this.form, index1, {
...parent,
isChecked: true
});
} else if (!anyChildChecked) {
this.$set(this.form, index1, {
...parent,
isChecked: false
});
} else {
this.$set(this.form, index1, {
...parent,
isChecked: false
});
}
},
handleChildChange方法用于处理子复选框的勾选状态变化,并更新其父复选框的勾选状态。sent方法用于根据子复选框的勾选状态更新父复选框的勾选状态。
4. 子子复选框勾选状态变化处理
handleChildChanges(parentIndex, childIndex, index3) {
const parent = this.form[parentIndex];
const child = parent.children[childIndex];
const children = child.children[index3];
// 更新子子复选框的勾选状态
this.$set(child.children, index3, {
...children,
isChecked: !children.isChecked,
isExpanded: !children.isExpanded,
});
this.sentCh(parentIndex, childIndex);
},
// 更新父级复选框
sentCh(index1, index2) {
const parent = this.form[index1];
const child = parent.children[index2];
// 更新上一级选框
const allChildrenChecked = child.children.every(item => item.isChecked);
const anyChildChecked = child.children.some(item => item.isChecked);
// 如果所有子项都勾选,则勾选父级复选框;如果都没有勾选,则取消勾选父级复选框
// 如果部分子项勾选,就取消勾选父级复选框
if (allChildrenChecked) {
this.$set(parent.children, index2, {
...child,
isChecked: true,
});
const allDirectChildrenChecked = parent.children.every(item => item.isChecked);
if (allDirectChildrenChecked) {
this.$set(this.form, index1, {
...parent,
isChecked: true
});
}
} else if (!anyChildChecked) {
this.$set(parent.children, index2, {
...child,
isChecked: false,
});
const noDirectChildrenChecked = parent.children.every(item => !item.isChecked);
if (noDirectChildrenChecked) {
this.$set(this.form, index1, {
...parent,
isChecked: false
});
}
} else {
this.$set(parent.children, index2, {
...child,
isChecked: false,
});
this.$set(this.form, index1, {
...parent,
isChecked: false
});
}
},
handleChildChanges方法用于处理子子复选框的勾选状态变化,并更新其父级复选框的勾选状态。sentCh方法用于根据子子复选框的勾选状态更新其父级复选框的勾选状态。
5. 保存功能实现
save() {
this.selectList = [];
let that = this;
uni.showModal({
title: '确定保存?',
success: function(resu) {
if (resu.confirm) {
//这里因为我只是用了三级,所有就直接写固定了,这里实际可以改成递归的形式来完成
// 获取所有被勾选的最后一级复选框
that.form.forEach((item) => {
item.children.forEach((item1) => {
if (item1.children && item1.children.length > 0) {
// 如果有子子复选框,则递归检查子子复选框
item1.children.forEach((item2) => {
if (item2.isChecked) {
// 将对象添加到 selectList 数组中
that.selectList.push(item2);
}
});
} else {
// 如果没有子子复选框,则检查子复选框是否被勾选
if (item1.isChecked) {
that.selectList.push(item1);
}
}
});
});
// 判重,添加
that.heavySentencing();
}
}
});
},
heavySentencing() {
let valueList = [];
this.selectList.forEach((item) => {
const selectedItem = {
fFunName: item.ffunName
};
valueList.push(selectedItem);
});
console.log("保存", valueList);
},
save方法用于弹出确认框,并在用户确认后遍历所有复选框,将被勾选的复选框对象添加到selectList数组中,然后调用heavySentencing方法进行判重处理。
四、页面样式
<template>
<view class="">
<view class="page-wraper">
<view class="page-main">
<u-checkbox-group placement="column">
<view v-for="(item, index) in form" :key="index" class="boxs1" ref="form">
<view class="boxs0" @click="getClick(index)">{{item.ffunName}}
</view>
<!-- 父多选框 -->
<u-checkbox v-show="item.isExpanded" :label="item.ffunName" :size="25" :labelSize="20"
:name="item.ffunName" :checked="item.isChecked" @change="handleParentChange(index)">
</u-checkbox>
<!-- 子多选框 -->
<view v-show="item.isExpanded" v-for="(item1, index1) in item.children" :key="index1"
class="boxs2">
<u-checkbox :checked="item1.isChecked" :label="item1.ffunName" :name="item1.ffunName"
:size="25" :labelSize="20" @change="handleChildChange(index, index1)">
</u-checkbox>
<!-- 子多选框的子多选框 -->
<view v-show="item1.isExpanded" v-for="(item2, index2) in item1.children" :key="index2"
class="boxs3">
<u-checkbox :checked="item2.isChecked" :label="item2.ffunName" :name="item2.ffunName"
:size="25" :labelSize="20" @change="handleChildChanges(index, index1,index2)">
</u-checkbox>
</view>
</view>
</view>
</u-checkbox-group>
</view>
<view class="page-footer">
<view style="display:flex">
<u-button type="primary" @click="save()" text="保存">
</u-button>
</view>
</view>
</view>
</view>
</template>