<template>
|
<view class="waterfalls-flow">
|
<view v-for="(item,index) in data.column" :key="index" class="waterfalls-flow-column" :id="`waterfalls_flow_column_${index+1}`" :msg="msg" :style="{'width':w,'margin-left':index==0?0:m}">
|
<view :class="['column-value',{'column-value-show':item2.o}]" v-for="(item2,index2) in columnValue(index)" :key="index2" :style="[s1]" @click.stop="wapperClick(item2)">
|
<view class="inner" v-if="data.seat==1">
|
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef VUE2 -->
|
<slot name="slot{{item2.index}}"></slot>
|
<!-- #endif -->
|
<!-- #ifdef VUE3 -->
|
<slot :name="`slot${item2.index}`"></slot>
|
<!-- #endif -->
|
<!-- #endif -->
|
<!-- #ifndef MP-WEIXIN -->
|
<slot v-bind="item2"></slot>
|
<!-- #endif -->
|
</view>
|
<image :class="['img',{'img-hide':item2[hideImageKey]==true||item2[hideImageKey]==1},{'img-error':!item2[data.imageKey]}]" :src="item2[data.imageKey]" mode="widthFix" @load="imgLoad(item2,index+1)" @error="imgError(item2,index+1)" @click.stop="imageClick(item2)"></image>
|
<view class="inner" v-if="data.seat==2">
|
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef VUE2 -->
|
<slot name="slot{{item2.index}}"></slot>
|
<!-- #endif -->
|
<!-- #ifdef VUE3 -->
|
<slot :name="`slot${item2.index}`"></slot>
|
<!-- #endif -->
|
<!-- #endif -->
|
<!-- #ifndef MP-WEIXIN -->
|
<slot v-bind="item2"></slot>
|
<!-- #endif -->
|
</view>
|
</view>
|
</view>
|
</view>
|
</template>
|
<script>
|
export default {
|
props: {
|
value: Array,
|
column: { // 列的数量
|
type: [String, Number],
|
default: 2
|
},
|
maxColumn: { // 最大列数
|
type: [String, Number],
|
default: 5
|
},
|
columnSpace: { // 列之间的间距 百分比
|
type: [String, Number],
|
default: 2
|
},
|
imageKey: { // 图片key
|
type: [String],
|
default: 'image'
|
},
|
hideImageKey: { // 隐藏图片key
|
type: [String],
|
default: 'hide'
|
},
|
seat: { // 文本的位置,1图片之上 2图片之下
|
type: [String, Number],
|
default: 2
|
},
|
listStyle: { // 单个展示项的样式:eg:{'background':'red'}
|
type: Object
|
}
|
},
|
data() {
|
return {
|
data: {
|
list: this.value ? this.value : [],
|
column: this.column < 2 ? 2 : this.column,
|
columnSpace: this.columnSpace <= 5 ? this.columnSpace : 5,
|
imageKey: this.imageKey,
|
seat: this.seat
|
},
|
msg: 0,
|
listInitStyle: {
|
'border-radius': '12rpx',
|
'margin-bottom': '20rpx',
|
'background-color': '#fff'
|
},
|
adds: [], //预置数据
|
isLoaded: true,
|
curIndex: 0,
|
isRefresh: true,
|
flag: false,
|
refreshDatas: []
|
}
|
},
|
computed: {
|
// 计算列宽
|
w() {
|
const column_rate = `${100 / this.data.column - (+this.data.columnSpace)}%`;
|
return column_rate;
|
},
|
// 计算margin
|
m() {
|
const column_margin = `${(100-(100 / this.data.column - (+this.data.columnSpace)).toFixed(5)*this.data.column)/(this.data.column-1)}%`;
|
return column_margin;
|
},
|
// list样式
|
s1() {
|
return { ...this.listInitStyle, ...this.listStyle };
|
}
|
},
|
created() {
|
// 初始化
|
this.refresh();
|
},
|
methods: {
|
// 预加载图片
|
loadImages(idx = 0) {
|
let count = 0;
|
const newList = this.data.list.filter((item, index) => index >= idx);
|
for (let i = 0; i < newList.length; i++) {
|
// #ifndef APP-PLUS
|
uni.getImageInfo({
|
src: `${newList[i][this.imageKey]}.jpg`,
|
complete: res => {
|
count++;
|
if (count == newList.length) this.initValue(idx);
|
}
|
})
|
// #endif
|
// #ifdef APP-PLUS
|
plus.io.getImageInfo({
|
src: `${newList[i][this.imageKey]}.jpg`,
|
complete: res => {
|
count++;
|
if (count == newList.length) this.initValue(idx);
|
}
|
})
|
// #endif
|
}
|
},
|
// 刷新
|
refresh() {
|
if (!this.isLoaded) {
|
this.refreshDatas = this.value;
|
return false;
|
};
|
setTimeout(() => {
|
this.refreshDatas = [];
|
this.isRefresh = true;
|
this.adds = [];
|
this.data.list = this.value ? this.value : [];
|
this.data.column = this.column < 2 ? 2 : this.column >= this.maxColumn ? this.maxColumn : this.column;
|
this.data.columnSpace = this.columnSpace <= 5 ? this.columnSpace : 5;
|
this.data.imageKey = this.imageKey;
|
this.data.seat = this.seat;
|
this.curIndex = 0;
|
// 每列的数据初始化
|
for (let i = 1; i <= this.data.column; i++) {
|
this.data[`column_${i}_values`] = [];
|
this.msg++;
|
}
|
this.$nextTick(() => {
|
this.initValue(this.curIndex, 'refresh==>');
|
})
|
}, 1)
|
},
|
columnValue(index) {
|
return this.data[`column_${index+1}_values`];
|
},
|
change(newValue) {
|
for (let i = 0; i < this.data.list.length; i++) {
|
const cv = this.data[`column_${this.data.list[i].column}_values`];
|
for (let j = 0; j < cv.length; j++) {
|
if (newValue[i] && i === cv[j].index) {
|
this.data[`column_${this.data.list[i].column}_values`][j] = Object.assign(cv[j], newValue[i]);
|
this.msg++;
|
break;
|
}
|
}
|
}
|
},
|
getMin(a, s) {
|
let m = a[0][s];
|
let mo = a[0];
|
for (var i = a.length - 1; i >= 0; i--) {
|
if (a[i][s] < m) {
|
m = a[i][s];
|
}
|
}
|
mo = a.filter(i => i[s] == m);
|
return mo[0];
|
},
|
// 计算每列的高度
|
getMinColumnHeight() {
|
return new Promise(resolve => {
|
const heightArr = [];
|
for (let i = 1; i <= this.data.column; i++) {
|
const query = uni.createSelectorQuery().in(this);
|
query.select(`#waterfalls_flow_column_${i}`).boundingClientRect(data => {
|
heightArr.push({ column: i, height: data.height });
|
}).exec(() => {
|
if (this.data.column <= heightArr.length) {
|
resolve(this.getMin(heightArr, 'height'));
|
}
|
});
|
}
|
})
|
},
|
async initValue(i, from) {
|
this.isLoaded = false;
|
if (i >= this.data.list.length || this.refreshDatas.length) {
|
this.msg++;
|
this.loaded();
|
return false;
|
}
|
const minHeightRes = await this.getMinColumnHeight();
|
const c = this.data[`column_${minHeightRes.column}_values`];
|
this.data.list[i].column = minHeightRes.column;
|
c.push({ ...this.data.list[i], cIndex: c.length, index: i, o: 0 });
|
this.msg++;
|
},
|
// 图片加载完成
|
imgLoad(item, c) {
|
const i = item.index;
|
item.o = 1;
|
this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
|
this.initValue(i + 1);
|
},
|
// 图片加载失败
|
imgError(item, c) {
|
const i = item.index;
|
item.o = 1;
|
item[this.data.imageKey] = null;
|
this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
|
this.initValue(i + 1);
|
},
|
// 渲染结束
|
loaded() {
|
if (this.refreshDatas.length) {
|
this.isLoaded = true;
|
this.refresh();
|
return false;
|
}
|
this.curIndex = this.data.list.length;
|
if (this.adds.length) {
|
this.data.list = this.adds[0];
|
this.adds.splice(0, 1);
|
this.initValue(this.curIndex);
|
} else {
|
if (this.data.list.length) this.$emit('loaded');
|
this.isLoaded = true;
|
this.isRefresh = false;
|
}
|
},
|
// 单项点击事件
|
wapperClick(item) {
|
this.$emit('wapperClick', item);
|
},
|
// 图片点击事件
|
imageClick(item) {
|
this.$emit('imageClick', item);
|
}
|
},
|
watch: {
|
value: {
|
deep: true,
|
handler(newValue, oldValue) {
|
setTimeout(() => {
|
this.$nextTick(() => {
|
if (this.isRefresh) return false;
|
if (this.isLoaded) {
|
// if (newValue.length <= this.curIndex) return this.refresh();
|
if (newValue.length <= this.curIndex) return this.change(newValue);
|
this.data.list = newValue;
|
this.$nextTick(() => {
|
this.initValue(this.curIndex, 'watch==>');
|
})
|
} else {
|
this.adds.push(newValue);
|
}
|
})
|
}, 10)
|
}
|
},
|
column(newValue) {
|
this.refresh();
|
}
|
}
|
}
|
</script>
|
<style lang="scss" scoped>
|
.waterfalls-flow {
|
overflow: hidden;
|
|
&-column {
|
float: left;
|
}
|
}
|
|
.column-value {
|
width: 100%;
|
font-size: 0;
|
overflow: hidden;
|
transition: opacity .4s;
|
opacity: 0;
|
|
&-show {
|
opacity: 1;
|
}
|
|
.inner {
|
font-size: 30rpx;
|
}
|
|
.img {
|
width: 100%;
|
|
&-hide {
|
display: none;
|
}
|
|
&-error {
|
background: #f2f2f2 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAiAQMAAAAatXkPAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAAIZJREFUCNdlzjEKwkAUBNAfEGyCuYBkLyLuxRYW2SKlV1JSeA2tUiZg4YrLjv9PGsHqNTPMSAQuyAJgRDHSyvBPwtZoSJXakeJI9iuRLGDygdl6V0yKDtyMAeMPZySj8yfD+UapvRPj2JOwkyAooSV5IwdDjPdCPspe8LyTl9IKJvDETKKRv6vnlUasgg0fAAAAAElFTkSuQmCC) no-repeat center center;
|
}
|
}
|
}
|
</style>
|