微信小程序原生写法实现树型选择组件,完整代码。

本文详细描述了如何在微信小程序中使用原生写法实现树形选择器组件,包括组件结构、样式编写、数据处理和使用示例。开发者分享了手写递归组件的方法以及关键代码片段。
摘要由CSDN通过智能技术生成

最近在做小程序项目,用的微信原生写法,业务用到了树型选择器,在网上找了一些weui,vantui都没有类似的组件,就自己手写了一个。

  1. 首先想到的肯定递归组件,我们现先创建一个组件.设置好图标,随便找一些图片都可以,这里我用了vantui中的图标.组件建议写在专门的组件文件夹下。
<!-- 树形组件 tree.wxml -->
<view>
	<view style="padding-left:25rpx;">
		<view wx:for="{{ treeList }}" wx:for-item="item" wx:key="id">
			<view class="tree-item">
				<view bind:tap="showChildren" data-id="{{ item.id }}" data-parentid="{{ item.parentId }}" class="tree-label">
					<van-icon custom-style="{{ item.collapse?'transform:rotate(90deg)':'transform:rotate(0deg)' }}" name="play" wx:if="{{ item.children }}" />
					<view class="{{ item.selected? 'current':'general' }}">{{item.name}}</view>
				</view>
				<van-icon
					bind:tap="handleClick"
					data-currentitem="{{ item }}"
					wx:if="{{ !item.children && item.selected }}"
					style="border-color: #1989fa;"
					size="20px"
					color="#1989fa"
					name="checked"
				/>
				<van-icon
					bind:tap="handleClick"
					data-currentitem="{{ item }}"
					wx:if="{{ !item.children && !item.selected }}"
					size="20px"
					name="circle"
					color="#c8c9cc"
				/>
			</view>
			<!-- 递归组件 -->
			<block wx:if="{{ item.children && item.collapse }}">
				<tree treeList="{{ item.children }}" treeListTemp="{{ treeListTemp }}" currentItem="{{ currentItem }}"></tree>
			</block>
		</view>
	</view>
</view>

在组件的json文件中声明组件,“component”: true,调用自己。

{
  "component": true,
  "usingComponents": {
    "van-icon": "@vant/weapp/icon/index",
    "tree": "../tree/tree"
  }
}

这是组件里的样式,主要区分选中状态的样式,这里大家可以再优化下。

/*tree.wxss*/
view{
  font-size: 16px;
  font-family:  PingFangSC-Regular;
}

.tree-item{
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.tree-label{
  padding-right: 15rpx;
  flex: 1;
  display: flex;
  align-items: center;
  position: relative;
}

.current{
  color: #1989fa;
}

.general{
  color: #1c2438;
}

然后是数据处理,设置选中状态及折叠状态,这里可以将选中的值冒泡给调用处去处理。下面的组件中的方法,我对每个方法都作了详细的说明。

// tree.ts
//树形单选组件
Component({
  properties: {
    treeList: Array, // 树形数组
    treeListTemp: Array,
    currentItem: Object
  },
  data: {},
  methods: {

    /**
     * 点击标签展示子节点
     * @param e 
     */
    showChildren: function (e: any) {
      //点击项的id
      let id = e.currentTarget.dataset.id;
      // 父级id
      let parentid = e.currentTarget.dataset.parentid;
      this.findCurrentItem(id, this.data.treeList);
      if (parentid == null) {
        this.setData({
          treeListTemp: this.data.treeList
        })
      } else {
        this.hanleStatusChange(this.data.treeListTemp, parentid);
        this.setData({
          treeListTemp: this.data.treeListTemp
        })
      }
      this.triggerEvent("resetTree", { changeList: this.data.treeListTemp }, { bubbles: true, composed: true });
    },

    /**
     * 设置折叠状态
     * @param list 处理集合
     * @param parentid 父级id
     */
    hanleStatusChange: function (list: AnyArray, parentid: Number) {
      for (let i = 0; i < list.length; i++) {
        if (list[i].id == parentid) {
          list[i].children = this.data.treeList;
          break;
        } else {
          if (list[i].children) {
            this.hanleStatusChange(list[i].children, parentid);
          }
        }
      }
    },

    /**
     * 功能类型 collapse:点击标签展开收起
     * */
    findCurrentItem: function (id: Number, list: AnyArray) {
      list.forEach(it => {
        if (it.id == id) {
          it.collapse = !it.collapse;
        } else {
          if (it.children) {
            this.findCurrentItem(id, it.children);
          }
        }
      })
    },

    /**
     * 点击选择框
     * @param e 节点
     */
    handleClick: function (e: any) {
      let checkedItem = e.currentTarget.dataset.currentitem;
      console.log('选中项', e.currentTarget.dataset.currentitem);
      this.setChooseStatus(checkedItem.id, this.data.treeListTemp);
      checkedItem.selected = !checkedItem.selected;
      this.setData({
        treeListTemp: this.data.treeListTemp
      })
      this.triggerEvent("resetTree", { changeList: this.data.treeListTemp, checkedItem: checkedItem }, { bubbles: true, composed: true });
    },

    /**
     * 设置选中状态
     * @param id 选中项的id
     */
    setChooseStatus(id: Number, list: AnyArray) {
      list.forEach(el => {
        if (el.id == id) {
          el.selected = !el.selected;
        } else {
          el.selected = false;
          if (el.children) {
            this.setChooseStatus(id, el.children);
          }
        }
      });
    },
  },
})
  1. 下面开始使用组件,在你页面引入这个树型组件,你可以使用一个容器控制显示与隐藏,这里我用的vantui中的popup弹出层组件。将你的树型数组传入组件,写一个监听事件,监听自组件的选中事件。
<!--index.wxml-->
<view class="container">
	<view class="content">{{context}}</view>
	<view class="btn">
		<van-button bind:click="openPicker" type="primary" block>选择地区</van-button>
	</view>
</view>
<van-popup show="{{ picker }}" close-on-click-overlay position="bottom" lock-scroll="{{ true }}" bind:click-overlay="closePicker">
	<van-cell class="toolbar">
		<view class="title-group">
			<view bindtap="closePicker" class="cancel">取消</view>
			<view class="picker-title">选择地区</view>
			<view bindtap="confirm" class="confirm">确认</view>
		</view>
	</van-cell>
	<view class="tree-style">
		<custom-tree treeList="{{ list }}" treeListTemp="{{ list }}" bind:resetTree="resetTree" />
	</view>
</van-popup>

页面的js文件

// index.ts
//@ts-nocheck
Page({
  data: {
    picker: false,
    context: '',
    list: [
      {
        id: '9',
        parentId: null,
        name: '上海市',
        collapse: false,
        selected: false,
        children: [
          {
            id: '91',
            parentId: '9',
            name: '静安区',
            collapse: false,
            selected: false,
          },
          {
            id: '92',
            parentId: '9',
            name: '静安区',
            collapse: false,
            selected: false,
          }
        ]
      },
      {
        id: '1',
        parentId: null,
        name: '江苏省',
        collapse: false,
        selected: false,
        children: [
          {
            id: '11',
            parentId: '1',
            name: '南京市',
            collapse: false,
            selected: false,
            children: [
              {
                id: '111',
                parentId: '11',
                name: '玄武区',
                collapse: false,
                selected: false,
              }
            ]
          }
        ]
      },
      {
        id: '2',
        parentId: null,
        name: '浙江省',
        collapse: false,
        selected: false,
        children: [
          {
            id: '21',
            parentId: '2',
            name: '金华市',
            collapse: false,
            selected: false,
            children: [
              {
                id: '211',
                parentId: '21',
                name: '婺城区',
                collapse: false,
                selected: false,
              }
            ]
          }
        ]
      }
    ],
    currentCheck: {},         //当前选中项
  },

  /**
   * 打开选择窗
   */
  openPicker() {
    this.setData({
      picker: true
    })
  },

  closePicker() {
    this.setData({
      picker: false
    })
  },

  confirm() {
    let { name } = this.data.currentCheck;
    this.setData({
      context: name,
      picker: false
    })
  },

  /**
   * 监听事件,自组件选中时会触发,重置整棵树
   */
  resetTree: function (e: any) {
    this.setData({
      currentCheck: e.detail.checkedItem,
      list: e.detail.changeList
    })
  },

  onLoad() { },

})

至此就完成了,如果有什么问题,欢迎在评论区留言或者私信我,完整代码已经传到了gitee上,也欢迎大家提Issue。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值