363 lines
12 KiB
Vue
363 lines
12 KiB
Vue
<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 0.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>
|