Files
t/components/thorui/tui-waterfall/tui-waterfall.vue
T
2025-03-21 09:38:06 +08:00

224 lines
5.5 KiB
Vue

<template>
<view class="tui-waterfall__box"
:style="{ paddingLeft: leftGap, paddingRight: rightGap, background: backgroundColor, borderRadius: radius }">
<view class="tui-waterfall__list" id="tui-waterfall__left" :style="{ marginRight: columnGap }">
<view v-for="(item, index) in leftList" :key="index">
<slot name="left" :entity="item" :index="index" :isList="columnCount==1"></slot>
</view>
</view>
<view v-if="columnCount > 1" class="tui-waterfall__list" id="tui-waterfall__right">
<view v-for="(item, index) in rightList" :key="index">
<slot name="right" :entity="item" :index="index" :isList="columnCount==1"></slot>
</view>
</view>
</view>
</template>
<script>
/**
* 列表中图片可以使用懒加载或者其他方式
* 优先计算出图片高度,以达到最佳效果
* App端建议使用weex的waterfall组件,使用案例详见:
* [ThorUI组件库->pages->index->productNvue]
*/
export default {
name: 'tuiWaterfall',
props: {
//列表数据,不建议一次性加载过多数据
listData: {
type: Array,
default () {
return [];
}
},
//每页数据条数(固定条数),当数据长度小于等于该数时表示第一页数据,刷新重置
pageSize: {
type: Number,
default: 10
},
//数据分组类型:1-简单左右分组,按顺序排列,伪瀑布流 2-计算左右容器高度进行分组
type: {
type: Number,
default: 1
},
//瀑布流列数,目前支持最大值为2
columnCount: {
type: Number,
default: 2
},
//列与列的间隙
columnGap: {
type: String,
default: '10rpx'
},
//左侧和列表的间隙
leftGap: {
type: String,
default: '0'
},
//右侧和列表的间隙
rightGap: {
type: String,
default: '0'
},
//列表背景色,可使用渐变色
backgroundColor: {
type: String,
default: 'transparent'
},
//列表外层容器圆角值
radius: {
type: String,
default: '0'
}
},
data() {
return {
leftListConst: [],
leftList: [],
rightList: [],
running: false,
};
},
watch: {
listData(val) {
this.columnChange();
},
columnCount(val) {
this.columnChange(val);
}
},
mounted() {
this.columnChange();
},
methods: {
columnChange(val) {
if (this.columnCount < 2) {
this.leftList = [...this.listData];
} else {
if (val && val == 2) {
this.leftList = [...this.leftListConst]
}
this.initData()
}
},
initData() {
if (this.type == 1) {
this.getSubGroup();
} else {
this.getArrayByHeight();
}
},
getDiffList() {
let diffList = [];
let total = this.listData.length;
if (total <= this.pageSize) {
console.info('running clean func')
this.leftListConst = [];
this.leftList = [];
this.rightList = [];
}
let sum = this.leftListConst.length + this.rightList.length;
let diff = total - sum;
if (diff > 0) {
diffList = [...this.listData].filter((item, index) => {
return index >= sum;
});
}
return diffList;
},
getSubGroup() {
//type=1时执行简单数据分组
if (!this.listData && this.listData.length === 0) return;
let leftList = [];
let rightList = [];
let data = this.getDiffList();
data.forEach((item, index) => {
if (index % 2 === 0) {
leftList.push(item);
} else {
rightList.push(item);
}
});
this.leftList = this.leftList.concat(leftList);
this.leftListConst = this.leftListConst.concat(leftList);
this.rightList = this.rightList.concat(rightList);
},
async getArrayByHeight() {
// 如果在计算了,则跳过
if (this.running === true) {
// 如果重置页面,则不再加载;
if (this.listData.length <= this.pageSize) return false;
setTimeout(() => {this.getArrayByHeight()}, 100);
return false;
}
this.running = true;
if (!this.listData && this.listData.length === 0) return;
console.info('getArrayByHeight');
let data = this.getDiffList();
for (let item of data) {
await this.render(item);
}
this.running = false;
},
async render(item) {
console.info(item.id);
let obj = await this.getContainerHeight();
return await new Promise((resolve, reject) => {
if (obj && typeof obj.leftHeight === 'number') {
if (obj.leftHeight <= obj.rightHeight) {
this.leftList.push(item);
this.leftListConst.push(item);
} else {
this.rightList.push(item);
}
this.$nextTick(() => {
setTimeout(() => {
resolve(true);
}, 30);
});
} else {
reject(false);
}
});
},
async getContainerHeight() {
//type=2
return await new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
let nodes = query.selectAll('#tui-waterfall__left, #tui-waterfall__right');
nodes.boundingClientRect().exec(res => {
if (res && res[0]) {
const rects = res[0];
const leftHeight = rects[0].height;
const rightHeight = rects[1].height;
resolve({
leftHeight: leftHeight,
rightHeight: rightHeight
});
} else {
reject(res);
}
});
});
}
}
};
</script>
<style scoped>
.tui-waterfall__box {
width: 100%;
display: inline-flex;
justify-content: space-between;
flex-direction: row;
flex-wrap: wrap;
box-sizing: border-box;
align-items: flex-start;
}
.tui-waterfall__list {
flex: 1;
}
</style>