立聪堂德州十三局店 发表于 2025-1-14 10:54:14

vue3 uniapp封装一个瀑布流组件


新增组件m-waterfall   这样就可以在页面直接使用 不用在引入了
<template>
        <view class="m-waterfall">
                <view id="m-left-column" class="m-column">
                        <slot name="left" :leftList="leftList"></slot>
                </view>
                <view id="m-right-column" class="m-column">
                        <slot name="right" :rightList="rightList"></slot>
                </view>

        </view>
</template>

<script setup>
/**
* @param value瀑布流数据
* @param addTime 插入数据的时间间隔
* @param keyIdData / id值,用于清除某一条数据时,根据此idKey名称找到并移除
*/

import {
                computed,
                defineProps,
                toRefs
        } from "vue"

       
        const props=defineProps({
                // 瀑布流数据
                value: {
                       required: true,
                        type: Array,
                        default: ()=>[]
                },
                // 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
                addTime: {
                        type: ,
                        default: 200
                },
                // id值,用于清除某一条数据时,根据此idKey名称找到并移除
                keyIdData: {
                        type: String,
                        default: 'id'
                }
        })
        const {
                value,
                addTime,
                keyIdData
        } = toRefs(props)
        const leftList = ref([])
        const rightList = ref([])
        const tempList = ref([])
        const copyFlowList= computed (()=> {
                return cloneData(value.value);
        })

       
        watch(()=>value.value,(nVal,oVal)=>{
                let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
               
                tempList.value = tempList.value.concat(cloneData(nVal.slice(startIndex)));
                splitData();
       
        })
        onMounted(()=>{
       
                tempList.value = cloneData(copyFlowList.value);
                setTimeout(()=>{
                        splitData();
                },200)
        })
       
const instance = getCurrentInstance()
        const getysHeigth=(classd)=>{
                return new Promise((resolve, reject) => {
                        uni.createSelectorQuery().in(instance)
                            .select(classd)
                            .boundingClientRect(data => {
                              if (data) {
                                    resolve(data);
                              } else {
                                    reject(new Error('获取节点信息失败'));
                              }
                            })
                            .exec();
                  });
        }
        const splitData = async () => {
       
                if (tempList.value.length==0) return;
                let leftRect = await getysHeigth('#m-left-column');
                let rightRect = await getysHeigth('#m-right-column');
                // 如果左边小于或等于右边,就添加到左边,否则添加到右边
                let item =tempList.value;
                // 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰
                // 数组可能变成[],导致此item值可能为undefined
                if (!item) return;
                if (leftRect.height < rightRect.height) {
                        leftList.value.push(item);
                } else if (leftRect.height > rightRect.height) {
                        rightList.value.push(item);
                } else {
                        // 这里是为了保证第一和第二张添加时,左右都能有内容
                        // 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
                        if (leftList.value.length <= rightList.value.length) {
                                leftList.value.push(item);
                        } else {
                                rightList.value.push(item);
                        }
                }
                // 移除临时列表的第一项
                tempList.value.splice(0, 1);
                // 如果临时数组还有数据,继续循环
                if (tempList.value.length) {
                        setTimeout(() => {
                                splitData();
                        }, addTime)
                }

        }
        // 复制而不是引用对象和数组
        const cloneData=(data)=>{
                if(data){
                        return JSON.parse(JSON.stringify(data));
                }else{
                        return [];
                }
               
        }
       
</script>

<style lang="scss" scoped>

.m-waterfall {
        display: flex;
        flex-direction: row;
        align-items: flex-start;
}

.m-column {
        display: flex;
        flex: 1;
        flex-direction: column;
        height: auto;
}

.m-image {
        width: 100%;

}
</style>
组件利用

<m-waterfall :value="product">
                                <!-- 左边数据 -->
                                <template v-slot:left="{leftList}">
                                        <view @click="addDta" class="prodecutitem" v-for="(item,index) in leftList" :key="index">
                                                <view style="width: 100%;">
                                                        <m-imgage :url="item.image"></m-imgage>
                                                </view>
                                                <view class="title">{{item.title}}</view>
                                                <view class="desc">{{item.title}}</view>
                                        </view>
                                </template>
                                <!-- 右边数据 -->
                                <template v-slot:right="{rightList}">
                                        <view class="prodecutitem" v-for="(item,index) in rightList" :key="index">
                                                <view>
                                                        <m-imgage :url="item.image"></m-imgage>
                                                </view>
                                                <view class="title">{{item.title}}</view>
                                                <view class="desc">{{item.title}}</view>
                                        </view>
                                </template>
                        </m-waterfal>

数据
        const product=ref([
                {
                        title:'水果蔬菜1',
                        image:imgSrc.value
                },
                {
                        title:'水果蔬菜2',
                        image:"https://img2.baidu.com/it/u=3893165480,918722033&fm=253&fmt=auto&app=120&f=JPEG?w=729&h=1215"
                },
                {
                        title:'水果蔬菜3',
                        image:imgSrc.value
                },
                {
                        title:'水果蔬菜1',
                        image:imgSrc.value
                },
                {
                        title:'水果蔬菜3',
                        image:imgSrc.value
                }
        ])

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: vue3 uniapp封装一个瀑布流组件