前段時間公司突然要寫小程式,專案中有一個樹形控制元件,我找了很久的插件和框架,沒有發現小程式能用的,只能硬著頭皮自己寫,
老規矩,先貼圖

- 為什么要特意強調此圖示呢?
- 因為該圖示為中間狀態,此處是我后期要優化的地方,在下面的代碼中還未實作,我有了初步的思考,
我的代碼中設定(以網路部為例)- 0 該部門成員全部未選中
- 1 該部門成員全部選中
- -1 該部門成員至少選中一人切少于部門總人員數
好了,下面我們來實作樹形結構的全選和零選中狀態
- 首先是樹形組件的代碼
components檔案夾下創建tree-select檔案夾,選擇新建component,命名為index
index.wxml檔案
<view id="treeItem">
<block wx:for="{{treeList}}" wx:key="deptId">
<view class="tree-item" style="margin-left: {{depth*40}}rpx" data-id="{{item.deptId}}" data-is-check="{{item.isCheck}}" catchtap="handleClick" >
<!-- 全選 -->
<view class="iconfont iconxuanzhong" wx:if="{{item.isCheck == 1}}" style="color: #007AFF" ></view>
<!-- 未選中 -->
<view class="iconfont iconweixuanzhong1" wx:if="{{item.isCheck == 0}}" style="color:#000" ></view>
</view>
<!-- children -->
<view wx:if="{{item.children}}">
<tree-select treeList="{{item.children}}" treeArray="{{treeArray}}" depth="{{depth+1}}" catchhandleClick="treeClick" id="treeItem"></tree-select>
</view>
</block>
</view>
樹形組件主要用的就是遞回,所以上面組件中我在組件中又呼叫組件自身,通過depth來判斷層級關系,來實作資料的縮進,treeList為頁面中傳進來的人員資料,treeArrary為我清洗資料之后傳過來的初始的人員選中狀態的陣列
index.json*
{
"component": true,
"usingComponenuts": {
"tree-select": "/components/tree-select/index"
}
}
此處要在usingComponenuts中引入組件自身
index.js
/**
* 組件的屬性串列
*/
properties: {
treeList: {
type: Array,
value: []
},
depth: {
type: Number,
value: 0
},
treeArray: {
type: Array,
value: []
}
},
/**
* 組件的初始資料
*/
data: {
realTreeMap: new Map(),
},
因為小程式頁面中不能直接傳遞map物件給組件,所以我在組件中定義了map物件,他的主要作用是用來修正人員的真實選中狀態,
ready(){
const {
treeList,
treeArray,
} = this.properties;
this.setData({
treeList,
treeArray,
})
},

此處,獲取頁面中傳遞過來的資料
// 獲取當前真實狀態鍵值Map結構
initRealTreeMap() {
// 獲取真實狀態鍵值串列
const {treeArray} = this.data
// 生成真實狀態鍵值Map結構
var mRealTreeMap = new Map()
treeArray.forEach(item => {
mRealTreeMap.set(item.key, item.value);
})
// 給組件內部變數賦值真實狀態Map結構
this.setData({
realTreeMap: mRealTreeMap
})
},

這里有一個要注意的就是,你點擊某一項資料時,只能獲取到與他同級的所有資料,比如:點擊網路部,你拿到的資料就是 楊xx,馬xx,營銷部,網路部這四項資料
handleClick(e){
// 獲取當前真實狀態鍵值Map結構
this.initRealTreeMap()
// 獲取當前的樹形結構串列,真實狀態鍵值串列,真實狀態鍵值Map結構
const { treeList, treeArray, realTreeMap } = this.data
// 獲取點擊項id及其前一個狀態
const t = this.selectAllComponents('#treeItem');
const { id, isCheck } = e.currentTarget.dataset
// 根據點擊項id獲取點擊項及子節點id集合
const idSet = new Set()
this.getData(treeList, id, idSet)
// 修改TreeList點擊項及其子節點選中狀態,但是,平級別的其他項及子項還是原始資料
// this.setStatus(treeList, idSet, !isCheck)
this.setStatus(treeList, idSet, !isCheck)
// 修正TreeList除點擊項及其子節點之外的其他平級及子節點項狀態為真實狀態
this.repairTreeList(id, treeList)
treeArray.forEach(item => {
item.value = realTreeMap.get(item.key)
})
// 賦值給組件內部三大核心變數,樹形結構串列,真實狀態鍵值串列,真實狀態鍵值Map結構
this.setData({
treeList,
treeArray,
realTreeMap
})
// 觸發向上通信傳遞事件,向父組件傳遞變數,主要為TreeList和TreeArray
this.triggerEvent('handleClick', { id, isCheck, treeList, treeArray});
},
因為treeList 所傳遞的資料為所選中的部門的平級資料,所以每次點擊后之前操作的資料就會被重置為初始狀態,所以用 repairTreeList來修正資料的真正狀態
// 【遞回函式】由于TreeList遞回傳遞渲染,導致平級非點擊項資料為原始狀態,此方法用于修正TreeList資料狀態為真實狀態,真實狀態保存在treeArray及RealTreeMap中
repairTreeList(id, treeList) {
treeList.forEach(item => {
item.isCheck = this.data.realTreeMap.get(item.deptId)
if(this.hasValidChildren(item)) {
this.repairTreeList(id, item.children)
}
})
},
// 父組件向子組件傳值
treeClick(e){
// 接收子組件向上傳遞的變數
const { id, isCheck, treeList, treeArray } = e.detail
// 設定真實狀態鍵值串列
this.setData({
treeArray
})
// 子組件內嵌套組件,回圈向上觸發通信傳遞事件,向父組件傳遞變數,主要為TreeArray
this.triggerEvent('handleClick', { id, isCheck, treeList, treeArray });
},
setStatus方法 將getData()方法回傳的ids集合的id設為選中狀態
// 【遞回函式】切換選中狀態
setStatus(dataTree, idSet, isCheck) {
for (let i = 0; i < dataTree.length; i += 1) {
// 遍歷節點串列,若切換集合包含該節點,則切換選中狀態
if(idSet.has(dataTree[i].deptId)) {
// 修正選中項狀態
this.resetCheckStatus(dataTree[i], isCheck)
// 若當前節點的子節點串列不為空,則遍歷子節點
if (this.hasValidChildren(dataTree[i])) {
let children = dataTree[i].children
children.forEach( item => {
// 若切換集合包含該節點,則切換子節點選中狀態
if(idSet.has(item.deptId)) {
// 修正子節點選中狀態
this.resetCheckStatus(item, isCheck)
}
// 若子節點的子節點串列不為空,則遞回呼叫切換選中狀態
if(this.hasValidChildren(item)) {
this.setStatus(children, idSet, isCheck)
}
})
}
}
}
},
// 修正選中項狀態
resetCheckStatus(item, isCheck) {
// 修改TreeList里的資料,用于渲染
item.isCheck = isCheck
// 修改RealTreeMap里的資料,保存真實狀態
this.data.realTreeMap.set(item.deptId, isCheck)
// 優化后放到修正RepairTreeList完成之后,防止重復遍歷造成性能損耗
修改treeArray里的資料,保存真實狀態,用于組件間傳值
// treeArray.forEach(i => {
// i.value = this.data.realTreeMap.get(i.key)
// })
},
getData方法實作的邏輯是當部門被選擇時,他下面的部員都被選中,部門被取消選中時,部員也取消選中,并回傳選中的人員的id集合
// 【遞回函式】獲取點擊項及其子節點項id集合,即切換集合
getData(dataTree, id, idSet) {
// 由于dataTree為id項的同級別串列,因此遍歷同級別串列,匹配其中id項及其子節點
for (let i = 0; i < dataTree.length; i += 1) {
if (dataTree[i].deptId === id) {
// id匹配,加入切換集合
idSet.add(id)
if (this.hasValidChildren(dataTree[i])) {
// 存在子節點串列
let children = dataTree[i].children
// 遍歷其子節點串列,將其子節點加入切換集合
children.forEach(item=> {
idSet.add(item.deptId)
// 子節點存在子節點串列,則遞回呼叫函式,獲取其子節點的子節點串列及其包含項
if(this.hasValidChildren(item)) {
// 注意,此處傳遞item及其同級別節點串列,而不是傳遞其子節點串列
this.getData(children, item.deptId, idSet);
}
})
}
}
}
return idSet
},
此方法用來判斷資料的children不為空
// 判斷物件是否存在有效的子節點串列
hasValidChildren(item) {
return item.children != undefined && item.children.length > 0
},
- 下面為引入組件的頁面中的代碼
- wxml頁面
<tree-select treeList='{{treeData}}' treeArray='{{treeArray}}' catchhandleClick="treeClick" depth="{{0}}" id="treeItem"></tree-select>
- js頁面
// 將樹形串列結構轉化為一級串列結構,傳遞進入組件中,便于遍歷、比較和賦值
getTreeKeyValueArray(data, mTreeArray) {
data.forEach(item => {
mTreeArray.unshift({
'key': item.deptId,
'value': item.isCheck
})
// 若存在子節點串列,則遞回處理資料
if(this.hasValidChildren(item)) {
this.getTreeKeyValueArray(item.children, mTreeArray)
}
})
},
treeClick(e){
// 接收子組件向上傳遞的變數
const { id, isCheck, treeList, treeArray } = e.detail
// 更新設定樹形結構串列及真實狀態鍵值串列
this.setData({
treeList,
treeArray
})
this.getCheckedIds(treeArray)
},
到此,這個組件就全部完成了,
后續中間狀態完成以后,我還會繼續更新,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/240564.html
標籤:其他
上一篇:iic協議通訊驅動
