问题:vue2项目,实现四层checkbox嵌套联动;
<template>
<div id="app">
<div class="topCheckbox">
<div
v-for="(timeSheetItem, index) in timeSheetInfoData"
:key="index"
class="oneFloor"
>
<div class="oneTitle">
<van-checkbox
v-model="timeSheetItem.checked"
:name="`${timeSheetItem.evaIds}${index}`"
class="titleLift"
icon-size="18px"
@click="oneCheckboxClick(index, timeSheetItem)"
>
{{ timeSheetItem.userName }} {{ timeSheetItem.period }}
{{ timeSheetItem.totalHour }}h
van-checkbox>
<div class="titleRight" @click="oneFloorClick(timeSheetItem, index)">
<span>{{ timeSheetItem.unflod % 2 === 0 ? "折叠" : "展开" }}span>
<div>
<van-icon
v-if="timeSheetItem.unflod % 2 === 0"
name="arrow-up"
size="16px"
>van-icon>
<van-icon v-else name="arrow-down" size="16px">van-icon>
div>
div>
div>
<template v-if="timeSheetItem.unflod % 2 === 0">
<div
v-for="(itemSecond, indexSecond) in restList[index]"
:key="indexSecond"
class="twoFloor"
>
<div class="twoTitle">
<van-checkbox
v-model="itemSecond.checked"
:name="`${itemSecond.evaIds}${index}`"
class="titleLift"
icon-size="15px"
@click="
twoCheckboxClick(
index,
itemSecond,
timeSheetItem,
indexSecond
)
"
>
{{ itemSecond.reportDate }} {{ itemSecond.week }}
{{ itemSecond.hour }}h
van-checkbox>
<div
class="titleRight"
@click="twoFloorClick(itemSecond, index, indexSecond)"
>
<span>{{ itemSecond.unflod % 2 === 0 ? "折叠" : "展开" }}span>
<div>
<van-icon
v-if="itemSecond.unflod % 2 === 0"
name="arrow-up"
>van-icon>
<van-icon v-else name="arrow-down">van-icon>
div>
div>
div>
<template v-if="itemSecond.unflod % 2 === 0">
<div
v-for="(itemThird, indexThird) in itemSecond.list"
:key="indexThird"
class="threeFloor"
>
<van-checkbox
ref="checkboxThird"
v-model="itemThird.ckecked"
class="left"
:name="`${itemThird.evaIds}`"
icon-size="15px"
@click="checkboxClickThree(index, itemSecond)"
>van-checkbox>
<div class="right">
<div class="date">
{{ itemThird.taskName }}
div>
div>
div>
template>
div>
template>
div>
div>
<van-checkbox v-model="isCheckAll" @click="checkAll">全选van-checkbox>
div>
template>
<script>
// import Vue from 'vue'
import mockData from '../src/assets/util/js/mock'
export default {
name: 'FourFloorCheckbox',
data () {
return {
timeSheetInfoData: [],
restList: [],
isCheckAll: false
}
},
created () {
this.loadFirstList()
},
methods: {
// 获取第一层数据
loadFirstList () {
setTimeout(() => {
this.timeSheetInfoData = mockData.firstFloorData
}, 200)
},
// 全选
checkAll () {
if (this.isCheckAll) {
this.timeSheetInfoData.map((item) => {
console.log(11)
item.checked = true
return item
})
} else {
this.timeSheetInfoData.map((item) => {
item.checked = false
return item
})
}
},
// 点击第一层复选框
oneCheckboxClick (index, item) {
console.log('change1', item.checked, item.unflod)
// this.$forceUpdate()
// 与全选按钮的关系
const allChecked = this.timeSheetInfoData.every(item => item.checked === true)
const someChecked = this.timeSheetInfoData.some(item => item.checked === false)
if (allChecked) {
this.isCheckAll = true
}
if (someChecked) {
this.isCheckAll = false
}
if (item.unflod !== 1) {
this.$forceUpdate()
if (item.checked) {
this.restList[index].map((item) => {
item.checked = true
return item
})
} else {
this.restList[index].map(item => {
item.checked = false
return item
})
}
}
},
// 点击第一层展开/折叠 展开-发送接口拿剩余数据
oneFloorClick (timeSheetItem, index) {
// 第一次单击 展开 调接口
if (timeSheetItem.unflod === 1) {
this.loadRestList(timeSheetItem, index)
} else {
timeSheetItem.unflod++
}
this.$forceUpdate()
},
// 获取第二层及第三层的数据
loadRestList (timeSheetItem, index) {
setTimeout((item) => {
console.log('调接口拿到数据')
this.$set(this.restList, index, mockData.restData)
if (timeSheetItem.checked) {
this.restList[index].map(item => {
item.checked = true
return item
})
} else {
this.restList[index].map(item => {
item.checked = false
return item
})
}
timeSheetItem.unflod++ // 展开折叠板
}, 200)
},
// 点击第二层复选框
twoCheckboxClick (index, itemSecond, timeSheetItem, indexSecond) {
console.log(2222, itemSecond.checked)
// 与第一层的关系
const allChecked = this.restList[index].every(item => item.checked === true)
const someChecked = this.restList[index].some(item => item.checked === false)
if (allChecked) {
timeSheetItem.checked = true
}
if (someChecked) {
timeSheetItem.checked = false
}
},
// 点击第二层展开/折叠
twoFloorClick (itemSecond, index, indexSecond) {
itemSecond.unflod++
this.$forceUpdate()
if (itemSecond.checked) {
console.log(2223)
itemSecond.list.map((item) => {
item.checked = true
return item
})
// itemSecond.list.map((item) => { this.$set(item, 'checked', true) })
// this.restList[index][indexSecond].list.forEach((val, indx) => {
// this.$set(this.restList[index][indexSecond].list[indx], 'checked', true)
// })
} else {
console.log(2224)
itemSecond.list.map((item) => {
item.checked = false
return item
})
// itemSecond.list.map((item) => { this.$set(item, 'checked', false) })
// this.restList[index][indexSecond].list.forEach((val, indx) => {
// this.$set(this.restList[index][indexSecond].list[indx], 'checked', false)
// })
}
},
// 点击第三层复选框
checkboxClickThree (index, itemSecond) {
this.$forceUpdate()
// 与第二层的关系
const allChecked = itemSecond.list.every(item => item.checked === true)
const someChecked = itemSecond.list.some(item => item.checked === false)
if (allChecked) {
itemSecond.checked = true
}
if (someChecked) {
itemSecond.checked = false
}
},
// 测试按钮点击
testClick () {
console.log('timeSheetData', this.timeSheetInfoData)
console.log('restList', this.restList)
}
}
}
script>
<style lang="less">
.oneTitle {
display: flex;
justify-content: space-between;
padding: 20px 15px;
border-bottom: 1px solid #2c3e50;
.titleRight {
display: flex;
justify-content: flex-end;
}
}
.twoFloor {
padding: 15px 10px;
.twoTitle {
display: flex;
justify-content: space-between;
.titleLift {
padding-left: 20px;
}
}
.titleRight {
display: flex;
// justify-content: flex-end;
}
}
.threeFloor {
display: flex;
justify-content: space-between;
padding: 10px;
.left {
padding-left: 30px;
}
}
.topCheckbox {
padding-bottom: 50px;
}
style>
下面是模拟的后台数据:
const mockData = {}
mockData.firstFloorData = [
{
checked: false,
evaIds: '450162,450163,450164,450165,450172,450173,450174,450175,450176,450177,450179,450180,450181,450274,450275',
month: '0',
period: '2023年01月',
totalHour: '112',
unflod: 1,
userId: 1383,
userName: '范令令',
week: null,
year: '2023'
},
{
checked: false,
evaIds: '450162,450163,450164,450165,450172,450173,450174',
month: '0',
period: '2023年08月',
totalHour: '224',
unflod: 1,
userId: 1383,
userName: '张三',
week: null,
year: '2023'
},
{
checked: false,
evaIds: '450162',
month: '0',
period: '2025年09月',
totalHour: '189',
unflod: 1,
userId: 1383,
userName: '李斯',
week: null,
year: '2023'
}
]
mockData.restData = [
{
checked: false,
evaIds: '450274',
hour: '8.0',
reportDate: '2023-01-30',
unflod: 1,
week: '周一',
list: [
{
checked: false,
comment: '',
taskName: '学习歌单听得有点难过',
taskSource: '部门日常任务',
unflod: 1,
week: '周一'
},
{
checked: false,
comment: '',
taskName: '人要往前走,就要换歌单,以前的歌单太重了',
taskSource: '部门日常任务',
unflod: 1,
week: '周一'
}
]
},
{
checked: false,
evaIds: '450274',
hour: '8.0',
reportDate: '2023-01-30',
unflod: 1,
week: '周一',
list: [
{
checked: false,
comment: '',
taskName: '学习歌单听得有点难过',
taskSource: '部门日常任务',
unflod: 1,
week: '周一'
},
{
checked: false,
comment: '',
taskName: '人要往前走,就要换歌单,以前的歌单太重了',
taskSource: '部门日常任务',
unflod: 1,
week: '周一'
}
]
}
]
export default mockData
由于数据起初不是响应式,加上我思维混乱,搞晕了。
两个思路:
1.设置一个变量表示是否全选, isSelectedAll, 在全选按钮的响应中修改其值,在每个checkbox中取值为isSelectedAll与原本值的与值
2.直接在全选按钮中的响应中遍历修改每个checkbox的值
可远程帮助实现
你这不就是树吗,为啥不用tree
引用chatGPT作答,这是一个Vue 2项目中实现四层嵌套的复选框联动的代码。该代码通过使用v-for和v-if指令来实现复杂的嵌套结构,并使用v-model指令绑定数据与复选框状态,从而实现复选框的联动。
代码中的方法包括loadFirstList(加载第一层数据),checkAll(全选),oneCheckboxClick(第一层复选框点击事件处理函数),twoCheckboxClick(第二层复选框点击事件处理函数),twoFloorClick(第二层标题点击事件处理函数),checkboxClickThree(第三层复选框点击事件处理函数)等。
其中,点击第一层复选框时,会检查该复选框的选中状态,并将其与全选按钮的状态进行关联。如果选中状态改变,则会更新数据并操作第二层和第三层复选框的状态。点击第二层复选框时,会更新第一层复选框的状态并操作第三层复选框的状态。
该代码还使用了setTimeout函数来模拟异步加载数据的过程,并使用mockData来模拟数据。
1.表格加一列
<el-table-column type="selection" align="center" width="100" />
2.隐藏掉全选按钮
/deep/ thead .el-table-column--selection .cell{
display: none;
}
3.给表格加一个 ref
,例如:ref="Table"
(加在el-table的属性里)
给表格加一个事件 @selection="getCurrentRow"
// 获取当前行数据
getCurrentRow(val,row){
if (val.length > 1) {
this.$refs.SenderTable.clearSelection()
this.$refs.SenderTable.toggleRowSelection(val.pop())
}
this.checkedUserId = row.userId // row里包含了选中当前行的数据
},
4.如果要实现点击行就选中,给el-table加一个事件row-click
对应的方法是
// 点击表行也可实现选中当前行
showRowClick(row) {
this.$refs.SenderTable.clearSelection()
this.$refs.SenderTable.toggleRowSelection(row)
this.checkedUserId = row.userId
},
很抱歉,由于本人专业领域不是前端,也没有相关的vue2项目经验,无法给出确切的解答。
参考以下代码:
<template>
<div>
<div>
<input type="checkbox" v-model="selectedAll" @change="selectAll" />
全选
</div>
<div v-for="(level1, index1) in data" :key="index1">
<div>
<input type="checkbox"
:checked="isSelected(level1)"
@change="selectOne(level1)" />
{{ level1.name }}
</div>
<div v-for="(level2, index2) in level1.children" :key="index2">
<div style="margin-left: 20px;">
<input type="checkbox"
:checked="isSelected(level2)"
@change="selectOne(level2)" />
{{ level2.name }}
</div>
<div v-for="(level3, index3) in level2.children" :key="index3">
<div style="margin-left: 40px;">
<input type="checkbox"
:checked="isSelected(level3)"
@change="selectOne(level3)" />
{{ level3.name }}
</div>
<div v-for="(level4, index4) in level3.children" :key="index4">
<div style="margin-left: 60px;">
<input type="checkbox"
:checked="isSelected(level4)"
@change="selectOne(level4)" />
{{ level4.name }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
data: [
{
name: "第一层-1",
selected: false,
children: [
{
name: "第二层-1",
selected: false,
children: [
{
name: "第三层-1",
selected: false,
children: [
{
name: "第四层-1",
selected: false
},
{
name: "第四层-2",
selected: false
}
]
},
{
name: "第三层-2",
selected: false,
children: [
{
name: "第四层-3",
selected: false
},
{
name: "第四层-4",
selected: false
}
]
}
]
},
{
name: "第二层-2",
selected: false,
children: [
{
name: "第三层-3",
selected: false,
children: [
{
name: "第四层-5",
selected: false
},
{
name: "第四层-6",
selected: false
}
]
},
{
name: "第三层-4",
selected: false,
children: [
{
name: "第四层-7",
selected: false
},
{
name: "第四层-8",
selected: false
}
]
}
]
}
]
},
{
name: "第一层-2",
selected: false,
children: [
{
name: "第二层-3",
selected: false,
children: [
{
name: "第三层-5",
selected: false,
children: [
{
name: "第四层-9",
selected: false
},
{
name: "第四层-10",
selected: false
}
]
},
{
name: "第三层-6",
selected: false,
children: [
{
name: "第四层-11",
selected: false
},
{
name: "第四层-12",
selected: false
}
]
}
]
},
{
name: "第二层-4",
selected: false,
children: [
{
name: "第三层-7",
selected: false,
children: [
{
name: "第四层-13",
selected: false
},
{
name: "第四层-14",
selected: false
}
]
},
{
name: "第三层-8",
selected: false,
children: [
{
name: "第四层-15",
selected: false
},
{
name: "第四层-16",
selected: false
}
]
}
]
}
]
}
],
selectedAll: false
};
},
methods: {
selectOne(item) {
item.selected = !item.selected;
if (this.isSelected(item)) {
this.selectParents(item);
this.selectChildren(item);
} else {
this.unselectParents(item);
this.unselectChildren(item);
}
},
isSelected(item) {
if (item.children && item.children.length > 0) {
return (
item.children.filter(child => child.selected).length ===
item.children.length
);
}
return item.selected;
},
selectParents(item) {
let parent = this.findParent(item);
while (parent) {
parent.selected = true;
parent = this.findParent(parent);
}
},
unselectParents(item) {
let parent = this.findParent(item);
while (parent) {
if (!this.isSelected(parent)) {
parent.selected = false;
}
parent = this.findParent(parent);
}
},
selectChildren(item) {
if (item.children && item.children.length > 0) {
item.children.forEach(child => {
child.selected = true;
this.selectChildren(child);
});
}
},
unselectChildren(item) {
if (item.children && item.children.length > 0) {
item.children.forEach(child => {
child.selected = false;
this.unselectChildren(child);
});
}
},
findParent(item) {
for (let i = 0; i < this.data.length; i++) {
const parent = this.findParentRecursively(item, this.data[i]);
if (parent) {
return parent;
}
}
},
findParentRecursively(item, parent) {
if (parent.children && parent.children.length > 0) {
for (let i = 0; i < parent.children.length; i++) {
const child = parent.children[i];
if (child === item) {
return parent;
} else {
const foundInChild = this.findParentRecursively(item, child);
if (foundInChild) {
return foundInChild;
}
}
}
}
},
selectAll() {
if (this.selectedAll) {
this.selectChildren({ children: this.data });
} else {
this.unselectChildren({ children: this.data });
}
}
}
};