使用有赞的vant组件库开发微信小程序过程中,使用到了tree树形控件,但vant组件库无此组件,所以手撸了一个简陋二级tree树形控件

功能包含

全选按钮功能、反选按钮功能
父级列表前的开关icon
子级列表的选中的禁止或启用
父级列表显示子级列表可选数量
父级下所有可选子级选中则父选中(禁用状态不算)
使用了van-collapse组件带有折叠关闭动画效果

效果展示

微信小程序自用Tree树形控件

微信小程序自Tree树形控件 WXML代码部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<van-collapse value="{{ activeNames }}" bind:change="onChange">
<block wx:for="{{wrongList}}" wx:for-item="itm" wx:key="index">
<van-collapse-item name="{{itm.code}}" data-hans="itm.code">
<view slot="title">
<image src="https://xxxx/{{KG[itm.code]!=true ?'open':'close'}}.png" style="width:20rpx" mode="widthFix" />
{{itm.name}}
<view catchtap="catchtap">
<van-checkbox class="fuCheck" disabled="{{tempCodeArr[itm.code].length==0}}" value="{{ checkedAll[itm.code] }}" data-hans="{{itm.code}}" bind:change="checkcheck" />
<view class="counts" style="right:106rpx">({{itm.fallibleCount}})</view>
</view>
</view>
<view class="items">
<van-checkbox-group value="{{ choisObj[itm.code] }}" data-hanscode="{{itm.code}}" bind:change="checkChange">
<van-cell-group>
<block wx:if="{{!_itm.hansFu}}" wx:for="{{ itm.children }}" wx:for-index="_index" wx:for-item="_itm" wx:key="code">
<van-cell title="{{ _itm.name }}" value-class="value-class" clickable>
<van-checkbox name="{{ _itm.code }}" disabled="{{_itm.fallibleCount=='0'?true:false}}" />
<view class="counts">({{_itm.fallibleCount}})</view>
</van-cell>
</block>
</van-cell-group>
</van-checkbox-group>
</view>
</van-collapse-item>
</block>
</van-collapse>

微信小程序自Tree树形控件 JS部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
* @Author: Han
* @Date: 2021-01-04 14:18:09
* @LastEditors: Han
* @LastEditTime: 2021-01-04 15:07:09
* @FilePath: \wechat-app\tree.js
*/
import { get } from "api";
Page({
data: {
// 父级按钮合集
checkedAll: [],
// 面板状态合集
activeNames: [],
// 章节List
wrongList: [],
// 选中的子节点合集
choisObj: {},
// 临时 父 子 数组
tempCodeArr: {},
// 全选按钮状态
selectAllStatus: true,
},
// 全选事件
selectAll() {
const _this = this;
const status = this.data.selectAllStatus;
const okTempCodeArr = JSON.parse(JSON.stringify(_this.data.tempCodeArr));
Object.keys(okTempCodeArr).forEach((itm) => {
okTempCodeArr[itm].length == 0 && delete okTempCodeArr[itm];
});
// 模拟点击
Object.keys(okTempCodeArr).forEach((itm) => {
_this.checkChange({
currentTarget: {
dataset: {
hanscode: itm,
},
},
detail: status ? okTempCodeArr[itm] : [],
});
});
this.setData({
selectAllStatus: !status,
});
},
// 父级按钮
checkcheck(e) {
const codes = e.currentTarget.dataset.hans;
// 判断父级按钮状态
this.setData({
checkedAll: {
...this.data.checkedAll,
[codes]: e.detail,
},
choisObj: {
...this.data.choisObj,
[codes]: e.detail ? this.data.tempCodeArr[codes] : [],
},
});
// 去除空对象,并设置按钮状态
const tempObj = this.data.choisObj;
Object.keys(tempObj).forEach((itm) => {
tempObj[itm].length == 0 && delete tempObj[itm];
});
this.setData({
choisObj: tempObj,
btnStatus: Object.keys(tempObj).length > 0,
});
},
// 子级按钮点击选中或非事件
checkChange(e) {
const codes = e.currentTarget.dataset.hanscode;
// 可选中的是否全选
const status = e.detail.length == this.data.tempCodeArr[codes].length;
this.setData({
choisObj: {
...this.data.choisObj,
[codes]: e.detail,
},
checkedAll: {
...this.data.checkedAll,
[codes]: status,
},
});
const tempObj = this.data.choisObj;
Object.keys(tempObj).forEach((itm) => {
tempObj[itm].length == 0 && delete tempObj[itm];
});
this.setData({
choisObj: tempObj,
btnStatus: Object.keys(tempObj).length > 0,
});
},
// 折叠面板切换事件
onChange(event) {
let tempArr = [];
// 当前面板折叠状态 临时变量
let key = false;

// 由于面板可以多个同时展开,所以 ?
// 控制面板标题前 图片的 + 或 -
if (this.data.activeNames.length > event.detail.length) {
// 深拷贝
tempArr = JSON.parse(JSON.stringify(this.data.activeNames));
event.detail.forEach((itm) => {
const n = tempArr.indexOf(itm);
n != -1 && tempArr.splice(n, 1);
});
key = false;
} else {
tempArr = JSON.parse(JSON.stringify(event.detail));
this.data.activeNames.forEach((itm) => {
const n = tempArr.indexOf(itm);
n != -1 && tempArr.splice(n, 1);
});
key = true;
}
this.setData({
activeNames: event.detail,
KG: {
...this.data.KG,
[tempArr[0]]: key,
},
});
},
// 切换事件
onSwitchChange(e) {
// 切换时,清空除额外所有数据
this.setData({
xx: [],
xx: {},
...xx,
});
// 之后重新获取列表
this.XXX();
},

// 获取书本信息
async getBookArr() {
wx.showLoading({
title: "获取数据中",
});
let tempArr = [];
const _this = this;
const {
ret: { bookList: _res },
} = await get("api");
wx.hideLoading();
// 书本信息赋给下拉框
Array.isArray(_res) &&
_res.forEach((itm) => {
tempArr.push({
text: itm.name,
value: itm.id,
});
});
tempArr.length > 0 &&
_this.setData({
bookDataArr: tempArr,
});
},
// 获取章节列表
async getWrongList() {
const _this = this;
const _res = await get(`api`);
let _tempArr = _res.ret.bookCatalogs;
// 章节数据处理
Array.isArray(_tempArr) &&
_tempArr.length > 0 &&
_tempArr.forEach((itm, idx) => {
let tempArrs = [];
itm.fallibleCount = Number(itm.fallibleCount);
itm.children.length > 0
? itm.children.forEach((_itm) => {
itm.fallibleCount += Number(_itm.fallibleCount);
_itm.fallibleCount != "0" && tempArrs.push(_itm.code);
})
: _tempArr[idx].children.push({
name: itm.name,
code: itm.code,
fallibleCount: itm.fallibleCount,
hansFu: true,
});
_this.setData({
tempCodeArr: {
..._this.data.tempCodeArr,
[itm.code]: tempArrs,
},
});
});
_res.ret &&
this.setData({
wrongList: _tempArr,
});
},
// 防止父级点击事件冒泡空事件
catchtap() {},
});