Home

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

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

功能包含

全选按钮功能、反选按钮功能

父级列表前的开关icon

子级列表的选中的禁止或启用

父级列表显示子级列表可选数量

父级下所有可选子级选中则父选中(禁用状态不算)

使用了van-collapse组件带有折叠关闭动画效果

效果展示

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

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

<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部分

/*
 * @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() {},
});