224 lines
5.5 KiB
Vue
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>
|