vue多級復雜列表展開/折疊,全選/分組全選實現


首先,來看下效果圖

效果圖.gif
在線體驗地址:https://hxkj.vip/demo/multipleList/。溫馨提示,打開之后按F12,使用手機模式食用,口味更佳!

可以看出,這個列表有三種展現形式:

1.第一層級中包含直屬子項和第二層級,其中第二層級里包含子項
2.第一層級中只包含第二層級,第二層級里包含子項
3.第一層級中只包含直屬子項

接下來,再分析列表所實現的功能:

1.點擊父級可以展開與折疊該直屬子級;
2.點擊父級級的勾選圖標可以全選或取消該層級下列的所有子項;
3.點擊子項達到該父級的全選狀態時時,父級的勾選圖標自動勾選;反之,沒達到全選時,父級的勾選圖標自動取消勾選;
4.所有相同層級之間勾選狀態的改變互不影響;

分析完了,緊接着就到了擼碼時刻了。

1.首先構建我們所需要的數據結構。
data() {
	return {
		headColor: ['#1c71fb', '#f7aa47', '#00c182', '#ff6769', '#917ee6', '#2cb2eb'],//待選顏色
		jobList: [{
			"id": "2511",
			"name": "嫩江第一中學",
			"member": [{
				"pid": "12058",
				"userName": "冷風",
				"job": "安全主任",
				"name": "冷風"
			}, {
				"pid": "12005",
				"userName": "周大龍",
				"job": "安全主任",
				"name": "大龍"
			}],
			"son": [{
				"id": "2513",
				"name": "校領導",
				"member": [{
					"pid": "12056",
					"userName": "凌凌",
					"job": "安全主任",
					"name": "凌凌"
				}, {
					"pid": "12039",
					"userName": "唐老師",
					"job": "安全主任",
					"name": "老師"
				}]
			}]
		}, {
			"id": "2510",
			"name": "黑龍江黑河市嫩江縣教育局",
			"son": [{
				"id": "2525",
				"name": "辦公室 ",
				"member": [{
					"pid": "12006",
					"userName": "張喵喵",
					"job": "安全主任",
					"name": "喵喵"
				}, {
					"pid": "12024",
					"userName": "張徳龍",
					"job": "老師",
					"name": "徳龍"
				}]
			}, {
				"id": "2524",
				"name": "局長部",
				"member": [{
					"pid": "12015",
					"userName": "小組長",
					"job": "安全主任",
					"name": "組長"
				}, {
					"pid": "12025",
					"userName": "TSY",
					"job": "11",
					"name": "SY"
				}]
			}]
		}, {
			"id": "2545",
			"name": "城市之星2總部",
			"member": [{
				"pid": "12076",
				"userName": "文明",
				"job": "前端開發工程師",
				"name": "文明"
			}, {
				"pid": "12077",
				"userName": "不文明",
				"job": "高級架構師",
				"name": "文明"
			}]
		}], //從后台獲取的人員列表信息
		selectPeople: [],//存儲被選擇的人員
		isOpenItem: [],//控制每級面板的打開與折疊
		isSelectAll: [],//控制每級面板的選中狀態
	}
}
2.初始化數據

初始化數據的主要目的。

1.根據數據來給頭像設置隨機顏色
2.根據數據初始化一層級全選按鈕狀態
3.根據數據初始化折疊面板折疊狀態
4.根據數據設置第二層級的選中狀態

initData() {//數據初始化
	//設置頭像背景顏色
	let len = this.jobList.length;
	for (let i = 0; i < len; i++) {
		this.setHeadColor(this.jobList[i].member, this.headColor);
		//根據數據初始化全選按鈕狀態
		this.isSelectAll.push([]);
		this.$set(this.isSelectAll[i], 'group', false);
		this.$set(this.isSelectAll[i], 'child', []);
		//根據數據初始化折疊面板折疊狀態
		this.isOpenItem.push([]);
		this.$set(this.isOpenItem[i], 'group', false);
		this.$set(this.isOpenItem[i], 'child', []);
		//設置第二層級的狀態
		for (let j in this.jobList[i].son) {
			this.isSelectAll[i].child.push(false)
			this.isOpenItem[i].child.push(false)
			this.setHeadColor(this.jobList[i].son[j].member, this.headColor);
		}
	}
}
3.為父級綁定事件

全選框HTML。使用@click="checkAll(index)"綁定全選事件,用於改變全選框的全選狀態

<div class="checkGroup" @click="checkAll(index)" @click.stop>
        <i class="iconfont"
             :class="{'icon-gouxuan':isSelectAll[index] && isSelectAll[index].group,'icon-checkboxround0':isSelectAll[index] && !isSelectAll[index].group}"></i>
</div>

全選框JS。通過改變this.isSelectAll[index]中的group屬性,來動態修改父級的選中狀態。因為子級選項的數據this.selectPeople是由v-model綁定的,所有只需要對其進行增加和刪除的操作,就可以改變子級每一項的選中狀態

checkAll(index) {//父級的全選操作
	this.$set(this.isSelectAll[index], 'group', !this.isSelectAll[index].group);//改變當前按鈕的選中狀態
	if (!this.jobList[index].member && !this.jobList[index].son) {
		return
	}
	if (!this.isSelectAll[index].group) {// 從全選 =》 全不選
		for (let key in this.isSelectAll[index].child) { // 移除所有第二層級子項的選中狀態
			this.$set(this.isSelectAll[index].child, key, false);
		}
		for (let i = 0, len = this.selectPeople.length; i < len; i++) {
			if (!this.selectPeople[i]) { //刪除干凈了
				return
			}
			for (let k in this.jobList[index].son) {//循環刪除有部門的人員(對應列表第三層級)
				for (let j in this.jobList[index].son[k].member) {
					if (this.selectPeople[i] && this.selectPeople[i].pid == this.jobList[index].son[k].member[j].pid) {
						this.selectPeople.splice(i, 1);
						i--;
						break;
					}
				}
			}
			for (let j in this.jobList[index].member) {//循環刪除沒有部門的人員(對應列表第二層級)
				if (this.selectPeople[i] && this.selectPeople[i].pid == this.jobList[index].member[j].pid) {
					this.selectPeople.splice(i, 1);
					i--;
					break;
				}
			}
		}
	} else {// 從全不選 =》 全選
		for (let key in this.isSelectAll[index].child) { // 給所有第二層級子項添加選中狀態
			this.$set(this.isSelectAll[index].child, key, true);
		}
		for (let i in this.jobList[index].member) {//循環添加沒有部門的人員(對應列表第二層級)
			if (this.selectPeople.includes(this.jobList[index].member[i])) { //如果已經存在,就不用再進行添加
				continue;
			}
			this.selectPeople.push(this.jobList[index].member[i]);
		}
		for (let i in this.jobList[index].son) {//循環添加有部門的人員(對應列表第三層級)
			for (let j in this.jobList[index].son[i].member) {
				if (this.selectPeople.includes(this.jobList[index].son[i].member[j])) { //如果已經存在,就不用再進行添加
					continue;
				}
				this.selectPeople.push(this.jobList[index].son[i].member[j]);
			}
		}
	}
}
4.通過子級改變父級的選中狀態

子級HTML。需要注意的是,這里面綁定了兩次stateChanged事件,只是參數不同。@click="stateChanged(index, j, k)"代表第二層級的子級。@click="stateChanged(index, i)"代表第一層級的子級。

<ul class="item_second" v-show="isOpenItem[index] && isOpenItem[index].group">
	<div class="item_third" v-for="(second,j) in item.son" :key="second.id">
		<div @click="checkSecondItem(index, j)" class="item">
			<div class="checkGroup" @click="checkSecondAll(index, j)" @click.stop>
				<i class="iconfont"
				   :class="{'icon-gouxuan':isSelectAll[index] && isSelectAll[index].child[j],'icon-checkboxround0':isSelectAll[index] && !isSelectAll[index].child[j]}"></i>
			</div>
			{{second.name}}
		</div>
		<ul class="item_fourth" v-show="isOpenItem[index] && isOpenItem[index].child[j]">
			<li v-for="(third,k) in second.member" :key="third.pid">
				<input @click="stateChanged(index, j, k)" type="checkbox" name="school"
					   :id="'check'+third.pid"
					   v-model="selectPeople"
					   :value="third">
				<label class="content-wrap" :for="'check'+third.pid">
					<div class="item_img" :style="{ background: third.headColor }">{{ third.name }}</div>
					<div class="content">
						<p class="content_name">
							{{third.userName}}
						</p>
						<p class="vice">{{ third.job }}</p>
					</div>
				</label>
			</li>
		</ul>
	</div>
	<li v-for="(people,i) in item.member" :key="people.pid">
		<input @click="stateChanged(index,i)" type="checkbox" name="school" :id="'check'+people.pid"
			   v-model="selectPeople"
			   :value="people">
		<label class="content-wrap" :for="'check'+people.pid">
			<div class="item_img" :style="{ background: people.headColor }">{{ people.name }}</div>
			<div class="content">
				<p class="content_name">
					{{people.userName}}
				</p>
				<p class="vice">{{ people.job }}</p>
			</div>
		</label>
	</li>
</ul>

子級JS。其中,stateChanged函數,通過傳入的參數不同來判斷當前屬於哪一層級的數據。setFirstLevelChecked函數,通過判斷所有子級選項的選中狀態來給第一層級添加選中狀態。

stateChanged(index, i, j) {
	if (j !== undefined) { //如果有j值,代表第三層級數據
		if (this.selectPeople.includes(this.jobList[index].son[i].member[j])) {//點擊之前為選中狀態
			this.$set(this.isSelectAll[index].child, i, false);//改變父級按鈕的選中狀態為非選中狀態
			this.$set(this.isSelectAll[index], 'group', false);//改變頂級按鈕的選中狀態為非選中狀態
		} else {//點擊之前為非選中狀態
			//給父級添加選中狀態
			for (let k = 0; k < this.jobList[index].son[i].member.length; k++) {
				if (!this.selectPeople.includes(this.jobList[index].son[i].member[k]) && this.jobList[index].son[i].member[k] != this.jobList[index].son[i].member[j]) {//只要有其中一個未選中,就跳出循環,不給父級添加選中狀態
					return false
				}
			}
			this.$set(this.isSelectAll[index].child, i, true);//改變父級按鈕的選中狀態為選中狀態
			this.setFirstLevelChecked(index, this.jobList[index].son[i].member[j]);//給第一級添加選中狀態
		}
	} else {//沒有j值,第二層級數據
		if (this.selectPeople.includes(this.jobList[index].member[i])) {//點擊之前為選中狀態
			this.$set(this.isSelectAll[index], 'group', false);//改變父級按鈕的選中狀態為非選中狀態
		} else {//點擊之前為非選中狀態
			this.setFirstLevelChecked(index, this.jobList[index].member[i]);//給第一級添加選中狀態
		}
	}
},
setFirstLevelChecked(index, data) {//給第一級添加選中狀態
	for (let k in this.jobList[index].member) {
		if (!this.selectPeople.includes(this.jobList[index].member[k]) && data != this.jobList[index].member[k]) {//只要有其中一個未選中,就跳出循環,不給父級添加選中狀態
			return false
		}
	}
	for (let i in this.jobList[index].son) {//循環添加有部門的人員(對應列表第三層級)
		for (let j in this.jobList[index].son[i].member) {
			if (!this.selectPeople.includes(this.jobList[index].son[i].member[j]) && data != this.jobList[index].son[i].member[j]) { //如果已經存在,就不用再進行添加
				return false
			}
		}
	}
	this.$set(this.isSelectAll[index], 'group', true);//改變第一級按鈕的選中狀態為選中狀態
}
以上就是全部的實現過程,如果有不懂的,可以留言反饋。

項目GitHub地址:https://github.com/TangSY/multiple-list-demo

轉載請注明出處:https://www.jianshu.com/p/154ffc0abed4
作者:TSY
個人空間:https://www.hxkj.vip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM