微信小程序 tree树形组件的实现

前言

最近公司的小程序来了一个需求,需要实现一个树形结构的展示。pc端的tree组件可谓百家争鸣,拿来即用的组件很多,奈何在网上检索一番,并未发现小程序端有适合的tree组件,于是便开始了尝试,自己写一个简单的tree组件。(末尾附完整代码)
先看效果
在这里插入图片描述

开始

首先项目结构如图
在这里插入图片描述
模拟一下树形结构数据
index/index.js

  data: {
    dataTree: [
      {
        id: 1,
        name: '一级A',
        children: [
          {
            id: 23,
            name: '二级A-a',
            children: [
              {
                id: 98,
                name: '三级A-a-1'
              }
            ]
          },
          {
            id: 20,
            name: '二级A-b',
          }
        ]
      },
      {
        id: 2,
        name: '一级B',
        children: [
          {
            id: 21,
            name: '二级B-a',
          }
        ]
      }
    ],

由于数据的层级是未知的,所以需要使用递归组件,即tree组件调用自身
tree/index.js

{
  "component": true,
  "usingComponents": {
    "c-tree": "/component/tree/index"
  }
}

只有当父节点被展开且存在子节点时才会展示递归组件

wx:if="{{item.children && item.children.length > 0 && item.open }}

tree/index.wxml

    <c-tree
      wx:if="{{item.children && item.children.length > 0 && item.open }}"
      dataTree='{{ item.children }}'
      selectKey="{{selectKey}}"
      isSelectLastNode="{{isSelectLastNode}}"
      isOpenAll="{{isOpenAll}}"
    >
    </c-tree>

接下来是组件向页面传递一个自定义事件select

item : 被点击的数据
isSelectLastNode: 配置参数,是否必须选择最后一个节点

tree/index.js

select(e) {
      const item = e.currentTarget.dataset.item
      if(this.properties.isSelectLastNode) {
        console.log(item)
        if (!item.children || item.children.length == 0) {
          this.triggerEvent('select', { item: item }, { bubbles: true, composed: true })
        } else {
          this.triggerEvent('select', { tips: '必须选择最后一个节点' }, { bubbles: true, composed: true })
        }
      } else {
        this.triggerEvent('select', { item: item }, { bubbles: true, composed: true })
      }
   }

注意:
为了给选中的节点加上样式,在页面引入tree组件时需要传入一个参数selectKey,其值为节点数据的id,所以在页面拿到组件回传的数据时都要更新selectKey

完整代码

tree组件
index.js

// pages/common/comTree/index.js
/**
 dataTree = [
   {
     id: 1,
     name: '一级名称',
     children: []
   }
 ]

  */
 Component({
  /**
   * 组件的属性列表
   */
  properties: {
    dataTree: {
      type: Array,
      value: []
    },
    selectKey: { // 选中的节点id
      type: String,
      value: ''
    },
    isSelectLastNode: { //是否必须选中最后一节点
      type: Boolean,
      value: false
    },
    isOpenAll: { //是否展开全部节点
      type: Boolean,
      value: false
    }
  },
  observers: {
    'dataTree': function(params) {
      params.forEach(v => {
        v.open = this.properties.isOpenAll // 是否展开
      })
      this.setData({
        tree: params
      })
    }
  },
  /**
   * 组件的初始数据
   */
  data: {
    tree: []
  },

  /**
   * 组件的方法列表
   */
  methods: {
    isOpen(e) {
      const open = 'tree[' + e.currentTarget.dataset.index + '].open'
      this.setData({
        [open]: !this.data.tree[e.currentTarget.dataset.index].open
      })
    },
    select(e) {
      const item = e.currentTarget.dataset.item
      if(this.properties.isSelectLastNode) {
        console.log(item)
        if (!item.children || item.children.length == 0) {
          this.triggerEvent('select', { item: item }, { bubbles: true, composed: true })
        } else {
          this.triggerEvent('select', { tips: '必须选择最后一个节点' }, { bubbles: true, composed: true })
        }
      } else {
        this.triggerEvent('select', { item: item }, { bubbles: true, composed: true })
      }
    }
  }
})

index.wxml

<!--pages/common/comTree/index.wxml-->
  <view wx:for="{{tree}}" wx:key="index" class="tree">
    <view class="tree-item tree-item-select">
      <view class="tree-item-onOff" wx:if="{{item.children && item.children.length > 0}}"  bindtap="isOpen" data-index="{{index}}"> 
        <image src="/assets/u1490.svg" class="{{item.open ? 'tree-item-onOff-open' : 'tree-item-onOff-closed'}}"></image>
      </view>
      <view class="tree-item-onOff" wx:else>
      </view>
      <view class="tree-item-name {{selectKey == item.id ? 'tree-item-name-select' : '' }}" bindtap="select" data-item="{{item}}" data-index="{{index}}">
        <view class="name">{{item.name}}</view>
        <view class="img">
          <!-- <image wx:if="{{selectKey == item.id }}" src="/assets/icon/u435.svg"></image> -->
        </view>
      </view>
    </view>
    <c-tree
      wx:if="{{item.children && item.children.length > 0 && item.open }}"
      dataTree='{{ item.children }}'
      selectKey="{{selectKey}}"
      isSelectLastNode="{{isSelectLastNode}}"
      isOpenAll="{{isOpenAll}}"
    >
    </c-tree>
  </view>

index.wxss

.tree {
  text-align: left;
  /* height: 45px;
  line-height: 45px; */
  padding-left: 15px;
}
.tree-item {
  height: 45px;
  line-height: 45px;
  display: flex;
  /* padding-left: 15px; */
}
.tree-item-onOff {
  width: 30px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.tree-item-onOff  image {
  width: 9px;
  height: 5px;
  display: block;
  transition: 0.4s;
}
.tree-item-onOff-closed {
  transform: rotate(-90deg);
}
.tree-item-onOff-open {
  transform: rotate(0deg);
}
.tree-item-name {
  width: calc(100% - 40px);
  display: flex;
  padding-left: 10px;
}
.name {
  width: calc(100% - 50px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.img {
  width: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.img  image {
  width: 15px;
  height: 10px;
  display: block;
}
.tree-item-name-select {
  background: #ECF7FA;
  color: #0079FE;
}
.tree-item-name-select2 {
  color: #0079FE;
}

index.json

{
  "component": true,
  "usingComponents": {
    "c-tree": "/component/tree/index"
  }
}

index页面
index.json

{
  "usingComponents": {
    "tree": "/component/tree/index"
  }
}

index.wxml

<!--index.wxml-->
<view class="container">
  <tree
    dataTree="{{dataTree}}"
    selectKey="{{selectKey}}"
    bind:select="handleSelect"
    isSelectLastNode="true"
    isOpenAll="true"
  ></tree>
</view>

index.js


Page({
  data: {
    dataTree: [
      {
        id: 1,
        name: '一级A',
        children: [
          {
            id: 23,
            name: '二级A-a',
            children: [
              {
                id: 98,
                name: '三级A-a-1'
              }
            ]
          },
          {
            id: 20,
            name: '二级A-b',
          }
        ]
      },
      {
        id: 2,
        name: '一级B',
        children: [
          {
            id: 21,
            name: '二级B-a',
          }
        ]
      }
    ],
    selectKey: '', //选中的节点id
  },
  handleSelect(e) {
    if (e.detail.tips) {
      console.log('必须选择到最后一个节点')
    } else {
      this.setData({
        selectKey: e.detail.item.id
      })
    }
  },
  onLoad: function () {
  }
})

完整项目的gitee地址

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值