[每天进步一点点~] uni-app、 JavaScript实现无限级递归树、商品分类功能,子级无数据也不会返回空数组...

接口数据

转换后

转换后数据

前端使用到uView插件,里面的【垂直分类】模板
前端页面渲染数据:

类别-电脑
类别-手机

uni-app 前端代码:

<template>
    <view class="u-wrap">
        <view class="u-search-box">
            <view class="u-search-inner">
                <u-icon name="search" color="#909399" :size="28"></u-icon>
                <text class="u-search-text">搜索uView</text>
            </view>
        </view>
        <view class="u-menu-wrap">
            <scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop">
                <view v-for="(item,index) in newCatList" :key="index" class="u-tab-item" :class="[current==index ? 'u-tab-item-active' : '']"
                 :data-current="index" @tap.stop="swichMenu(index)">
                    <text class="u-line-1">{{item.label}}</text>
                </view>
            </scroll-view>
            <block v-for="(item,index) in newCatList" :key="index">
                <scroll-view scroll-y class="right-box" v-if="current==index">
                    <view class="page-view">
                        <view class="class-item">
                            <view class="item-title">
                                <text>{{item.label}}</text>
                            </view>
                            <view class="item-container">
                                <view class="thumb-box" v-for="(item1, index1) in item.children" :key="index1">
                                    <!-- <image class="item-menu-image" :src="item1.icon" mode=""></image> -->
                                    <u-icon name="grid"></u-icon>
                                    <view class="item-menu-name">{{item1.label}}</view>
                                    <view class="item-container">
                                        <view class="thumb-box" v-for="(item2, index2) in item1.children" :key="index2">
                                            <!-- <image class="item-menu-image" :src="item1.icon" mode=""></image> -->
                                            <u-icon name="grid"></u-icon>
                                            <view class="item-menu-name">{{item2.label}}</view>
                                        </view>
                                    </view>
                                </view>
                            </view>
                        </view>
                    </view>
                </scroll-view>
            </block>
        </view>
    </view>
</template>


<script>
    // import classifyData from '@/common/classify.data.js';
    export default {
        data() {
            return {
                scrollTop: 0, //tab标题的滚动条位置
                oldScrollTop: 0,
                current: 0, // 预设当前项的值
                menuHeight: 0, // 左边菜单的高度
                menuItemHeight: 0, // 左边菜单item的高度
                itemId: '', // 栏目右边scroll-view用于滚动的id
                // tabbar: classifyData,
                menuItemPos: [],
                arr: [],
                scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
                timer: null, // 定时器
                // category
                catList:[],
                newCatList:[],
            }
        },
        onLoad() {
            this.getCategory();
        },
        methods: {
            // category
            getCategory: async function () {
                let that = this;
                let rest = await that.$api.getCategorys();
                if(rest.code == 1) {
                    that.catList = rest.data;
                    that.newCatList = that.$common.toTreeData(that.catList,0); // 使用封装的js方法
                    // 使用方法一输出
                    console.log(that.$common.toTreeData(that.catList,0));
                    // 使用方法二输出,要先用 that.newCatList 保存数据
                    console.log(that.newCatList);
                }
            },
            getImg() {
                return Math.floor(Math.random() * 35);
            },
            // 点击左边的栏目切换
            async swichMenu(index) {
                if(index == this.current) return ;
                this.current = index;
                // 如果为0,意味着尚未初始化
                if(this.menuHeight == 0 || this.menuItemHeight == 0) {
                    await this.getElRect('menu-scroll-view', 'menuHeight');
                    await this.getElRect('u-tab-item', 'menuItemHeight');
                }
                // 将菜单菜单活动item垂直居中
                this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
            },
            // 获取一个目标元素的高度
            getElRect(elClass, dataVal) {
                new Promise((resolve, reject) => {
                    const query = uni.createSelectorQuery().in(this);
                    query.select('.' + elClass).fields({size: true},res => {
                        // 如果节点尚未生成,res值为null,循环调用执行
                        if(!res) {
                            setTimeout(() => {
                                this.getElRect(elClass);
                            }, 10);
                            return ;
                        }
                        this[dataVal] = res.height;
                    }).exec();
                })
            }
        }
    }
</script>

<style lang="scss" scoped>
    .u-wrap {
        height: calc(100vh);
        /* #ifdef H5 */
        height: calc(100vh - var(--window-top));
        /* #endif */
        display: flex;
        flex-direction: column;
    }

    .u-search-box {
        padding: 18rpx 30rpx;
    }

    .u-menu-wrap {
        flex: 1;
        display: flex;
        overflow: hidden;
    }

    .u-search-inner {
        background-color: rgb(234, 234, 234);
        border-radius: 100rpx;
        display: flex;
        align-items: center;
        padding: 10rpx 16rpx;
    }

    .u-search-text {
        font-size: 26rpx;
        color: $u-tips-color;
        margin-left: 10rpx;
    }

    .u-tab-view {
        width: 200rpx;
        height: 100%;
    }

    .u-tab-item {
        height: 110rpx;
        background: #f6f6f6;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 26rpx;
        color: #444;
        font-weight: 400;
        line-height: 1;
    }
    
    .u-tab-item-active {
        position: relative;
        color: #000;
        font-size: 30rpx;
        font-weight: 600;
        background: #fff;
    }
    
    .u-tab-item-active::before {
        content: "";
        position: absolute;
        border-left: 4px solid $u-type-primary;
        height: 32rpx;
        left: 0;
        top: 39rpx;
    }

    .u-tab-view {
        height: 100%;
    }
    
    .right-box {
        background-color: rgb(250, 250, 250);
    }
    
    .page-view {
        padding: 16rpx;
    }
    
    .class-item {
        margin-bottom: 30rpx;
        background-color: #fff;
        padding: 16rpx;
        border-radius: 8rpx;
    }
    
    .item-title {
        font-size: 26rpx;
        color: $u-main-color;
        font-weight: bold;
    }
    
    .item-menu-name {
        width: 100rpx;
        text-align: center;
        font-weight: normal;
        font-size: 24rpx;
        color: $u-main-color;
    }
    
    .item-container {
        display: flex;
        align-items: flex-start;
        flex-wrap: wrap;
    }
    
    .thumb-box {
        width: 45%;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        margin-top: 20rpx;
    }
    
    .item-menu-image {
        width: 120rpx;
        height: 120rpx;
    }
</style>

在项目根目录下创建文件夹,放一些封装的接口和方法

封装的方法文件夹

common.js中,封装toTreeData()来转换接口方法

// 递归
function toTreeData(data,pid){
 // 方法一:该方法有个缺点,如果子级没有数据children返回[]。
/*
  function tree(id) {
    let arr = []
    data.filter(item => {
      return item.pid === id;
    }).forEach(item => {
            arr.push(item) */
/*arr.push({
                area_id: item.id,
                label: item.name,
                children: tree(item.id)
            })*/
           // 补充:加上 下面3行代码,子级没有数据不会加上children字段,不会返回[],解决了原来方法一的缺点。
            /*temp = tree(item.id);
            if(temp.length > 0) {
                item.children = temp;
            }
    })
    return arr
  }
// 第一级节点的父id,是null或者0,视情况传入
  return tree(pid) 
*/
    // 方法二:子级无数据,children不会返回空数组 [],弥补了方法一
     let result = [] ;
     let temp = [];
     for(var i = 0; i < data.length; i++){
        if(data[i].pid === pid) {
            result.push(data[i]);
            temp = toTreeData(data, data[i].id);
            if(temp.length > 0) {
                data[i].children = temp;
            }
        }
     }
     return result;

    // 和方法二相同
/*
    let arr = [];
    let temp = [];
    data.filter(item => {
        // 创建新数组,将 符合【 item.pid === pid 】条件的元素 放入新数组中
        return item.pid === pid;
    }).forEach(item => {    
        arr.push(item);
        temp = toTreeData(data, item.id); // 递归
        if(temp.length > 0) {
            item.children = temp;
        }
    })  
    return arr;
*/
}
// 记得导出定义的方法
export {
    toTreeData
}

api.js封装接口方法

//GET方法
const get = async (url, data = {}) => {
    return new Promise((resole, reject) => {
        uni.showLoading({
            title: '加载中'
        });
        let url = '/url地址';
        let header = {
            'Content-Type': 'application/json', //自定义请求头信息
        }
        uni.request({
            url: url, // 自己的接口url
            data: data,
            header: header,
            method: 'GET',
            success: (response) => {
                uni.hideLoading();
                const result = response.data
                resole(result);
            },
            fail: (error) => {
                resole({});
                if (error && error.response) {
                    showError(error.response);
                }
            },
            complete: () => {
                setTimeout(function() {
                    uni.hideLoading();
                }, 250);
            }
        });
    })
}
// 导出接口方法
export const getCategorys = async (data) => await get('url',data);

项目根目录下的main.js中,加入以下代码就能使用封装好的方法

import * as Api from './config/api.js'
import * as Common from './config/common.js'
import * as Db from './config/db.js'

Vue.prototype.$api = Api;
Vue.prototype.$common = Common;
Vue.prototype.$db = Db;

分析过程仅供参考:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值