uniapp小程序实现无限层级的树形数据结构

在uniapp中由于没有封装好的树形组件,只能自己纯手写实现。树形结构是无限层级的,所以想到了递归,使用组件循环技术实现。

子组件: tree-item.vue

<view>
  <view style="display: flex;line-height: 30px;" @click="clickNode(data)">
   <view v-if="data.children && data.children.length">
    <uni-icons v-if="data.isOpen" type="bottom" size="16" color="#2A5EFF"></uni-icons>
    <uni-icons v-else type="forward" size="16"></uni-icons>
   </view>
   <view style="padding-left: 10px;">{{ data.label }}</view>
  </view>
  <view v-if="data.children && data.children.length && data.isOpen" style="margin-left:   16px;">
   <tree-item v-for="(child,index) in data.children" :key="child.id" :node="child"
    @clickNode="clickNode"></tree-item>
  </view>
</view>
<script>
  export default {
   props: ['node'],
   name: "TreeItem",
   data() {
    return {
     data: null
    };
   },
   computed: {},
   methods: {
    clickNode(item) {
        console.log(item)
     if (item.type) {
      this.$emit('clickNode', item)
     } else {
      item.isOpen = !item.isOpen
     }
    }
   },
   created() {
    this.data = this.node
   }
  }
</script>
父组件popupTree.vue
 //uni-popup,uni-icons,uni-easyinput记得引入 在uniapp中的扩展组件中。
   也可使用uview中封装好的popup
<uni-popup ref="popup" background-color="#fff" :mask-click="false" :is-mask-click="false">
  <view style="width: 75vw;">
   <view class="header">
    <text style="font-size: 16px;">人员选择</text>
    <uni-icons type='closeempty' @click="close()" size="18"></uni-icons>
   </view>
   <view style="padding: 10px 10px 0 10px;">
    <uni-easyinput suffixIcon="search" v-model="value" placeholder="请输入人名搜索" @iconClick="iconClick"
     @blur="iconClick" trim></uni-easyinput>
   </view>
   <view style="padding: 10px;" v-if="loading">
    <scroll-view style="height: 400px;" scroll-y="true">
     <tree-item v-for="(item,index) in treeData" :key="item.id" :node="item" @clickNode="clickNode"></tree-item>
    </scroll-view>
   </view>
  </view>
</uni-popup>


<script>
  export default {
   props: ['show'],
   name: "popupTree",
   data() {
    return {
     loading: true,
     value: '',
     data: [],
     treeData: []
    };
   },
   watch: {
    show(val) {
     if (val) {
      this.$refs.popup.open()
     } else {
      this.$refs.popup.close()
     }
    }
   },
   methods: {
    close() {
     this.$emit('closePop')
    },
    clickNode(item) {
     this.$emit('clickNode',  item)
     this.$emit('closePop')
    },
//扁平数据转换成树
    convertToTreeData(data, pid) {
     const result = []
     let temp = []
     for (let i = 0; i < data.length; i++) {
      if (data[i].pid === pid) {
       const obj = {
        label: data[i].name,
        id: data[i].id,
        isOpen: data[i].type !== 'user'&&data[i].pid===null,
        type: data[i].type === 'user'
       }
       temp = this.convertToTreeData(data, data[i].id)
       if (temp.length > 0) {
        obj.children = temp
       }
       result.push(obj)
      }
     }
     return result
    },
//从后端获取人员列表(拿到的是扁平的数据结构)
    getUserTree() {
     uni.$u.http.post('/dept/listAll').then(res => {
      this.data = this.convertToTreeData(res, null)
      const data = JSON.parse(JSON.stringify(this.data))
          console.log(data)
      this.treeData = this.filterData(this.data, this.value || '')
          console.log(this.treeData)
     })
    },
//筛选出符合条件的数据
    filterData(data, keyword) {
     return data.filter(node => {
      // 判断当前节点是否匹配筛选条件
      if (node.label.includes(keyword)) {
            if(!node.type){
              node.isOpen=true
            }
       return true;
      }
      // 递归过滤子节点
      if (node.children && node.children.length) {
       node.children = this.filterData(node.children, keyword);
            node.isOpen=true
       return node.children.length > 0;
      }
      return false;
     });
    },
//点击搜索
    iconClick() {
     this.loading = false
     const data = JSON.parse(JSON.stringify(this.data))
     this.treeData = this.filterData(data, this.value || '')
     setTimeout(() => {
      this.loading = true
     }, 10)
    }
   },
   mounted() {
    this.getUserTree()
   }
  }
</script>

<style lang="scss" scoped>
  .header {
   display: flex;
   justify-content: space-between;
   border-bottom: 1px solid rgb(214, 215, 217);
   padding: 10px;
  }
</style>

实现效果:

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值