first commit
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<view class="waterfall-item-container">
|
||||
<view class="waterfall-item" @tap="onTap">
|
||||
<image :src="params.image" mode="widthFix" @load="emitHeight" @error="emitHeight"></image>
|
||||
<view class="content">
|
||||
<view>{{params.name}}</view>
|
||||
<view>{{params.msg}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name:"helangWaterfallItem",
|
||||
options:{
|
||||
virtualHost: true
|
||||
},
|
||||
props:{
|
||||
params:{
|
||||
type: Object,
|
||||
default(){
|
||||
return {}
|
||||
}
|
||||
},
|
||||
tag:{
|
||||
type:String | Number,
|
||||
default:''
|
||||
},
|
||||
index:{
|
||||
type:Number,
|
||||
default:-1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods:{
|
||||
// 发出组件高度信息,在此处可以区分正确和错误的加载,给予错误的提示图片
|
||||
emitHeight(e){
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.waterfall-item-container').boundingClientRect(data => {
|
||||
let height = Math.floor(data.height);
|
||||
this.$emit("height",height,this.$props.tag);
|
||||
}).exec();
|
||||
},
|
||||
onTap(){
|
||||
this.$emit("click",this.$props.index,this.$props.tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.waterfall-item{
|
||||
width: 100%; background: #fff; border-radius: 10rpx;
|
||||
image{ display: block; width: 100%; height: 350rpx; border-radius: 10rpx 10rpx 0 0; }
|
||||
.content{
|
||||
width: 100%; height: 160rpx; box-sizing: border-box; padding: 20rpx 20rpx 0;
|
||||
view{
|
||||
&:nth-child(1){ line-height: 40rpx; color: #333; font-size: 28rpx; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
&:nth-child(2){ margin-top: 10rpx; line-height: 34rpx; height: 68rpx; color: #999; font-size: 24rpx; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="waterfall-box h-flex-x h-flex-2" v-if="showList">
|
||||
<view>
|
||||
<view v-for="(item,index) in leftList" :key="item._render_id"
|
||||
class="list-item"
|
||||
:class="{'show': showPage > item._current_page }"
|
||||
>
|
||||
<helang-waterfall-item
|
||||
:params="item"
|
||||
tag="left"
|
||||
:index="index"
|
||||
@height="onHeight"
|
||||
@click="onClick"
|
||||
></helang-waterfall-item>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<view v-for="(item,index) in rightList" :key="item._render_id"
|
||||
class="list-item"
|
||||
:class="{'show': showPage > item._current_page }"
|
||||
>
|
||||
<helang-waterfall-item
|
||||
:params="item"
|
||||
@height="onHeight"
|
||||
@click="onClick"
|
||||
tag="right"
|
||||
:index="index"
|
||||
></helang-waterfall-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<slot name="default"></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import helangWaterfallItem from "./waterfall-item.vue"
|
||||
|
||||
export default {
|
||||
name:"helangWaterfallList",
|
||||
options:{
|
||||
virtualHost: true
|
||||
},
|
||||
components: {
|
||||
"helang-waterfall-item": helangWaterfallItem
|
||||
},
|
||||
props:{
|
||||
// 组件状态
|
||||
status:{
|
||||
type: String,
|
||||
default:''
|
||||
},
|
||||
// 待渲染的数据
|
||||
list:{
|
||||
type: Array,
|
||||
default(){
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 重置列表,设置为 true 时,瀑布流会自动重新渲染列表
|
||||
reset:{
|
||||
type: Boolean,
|
||||
default:false
|
||||
},
|
||||
},
|
||||
watch:{
|
||||
"$props.status"(newValue,oldValue){
|
||||
// 状态变更为 加载成功 时,执行瀑布流数据渲染
|
||||
if(newValue == 'success'){
|
||||
this.startRender();
|
||||
}else if(!this.showList){
|
||||
this.resetData();
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
showList(){
|
||||
return !["fail","empty"].includes(this.$props.status);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 左侧列表高度
|
||||
leftHeight: 0,
|
||||
// 右侧列表高度
|
||||
rightHeight: 0,
|
||||
// 左侧列表数据
|
||||
leftList: [],
|
||||
// 右侧列表数据
|
||||
rightList: [],
|
||||
// 待渲染列表
|
||||
awaitRenderList:[],
|
||||
// 当前展示页码数据
|
||||
showPage:1
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if(this.$props.status == 'success'){
|
||||
this.startRender();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 监听高度变化
|
||||
onHeight(height, tag) {
|
||||
/**
|
||||
* 这个为实际渲染后 CSS 中 margin-buttom 的值,本示例默认为20rpx
|
||||
* 用于解决实际渲染后因为数据条数关系,高度差计算偏差的问题
|
||||
* */
|
||||
let marginBottom = uni.upx2px(20);
|
||||
|
||||
// console.log(`左高:${this.leftHeight},右高:${this.rightHeight},当前高:${height},插入方向:${tag}`)
|
||||
|
||||
if (tag == 'left') {
|
||||
this.leftHeight += (height + marginBottom);
|
||||
} else {
|
||||
this.rightHeight += (height + marginBottom);
|
||||
}
|
||||
this.renderList();
|
||||
},
|
||||
// 组件点击事件
|
||||
onClick(index, tag){
|
||||
// 对应的数据
|
||||
if(tag == 'left'){
|
||||
this.$emit("click",this.leftList[index],index,tag);
|
||||
}else{
|
||||
this.$emit("click",this.rightList[index],index,tag);
|
||||
}
|
||||
},
|
||||
// 渲染列表,这里实现瀑布流的左右分栏
|
||||
renderList() {
|
||||
// 待渲染长度为 0 时表示已渲染完成
|
||||
if(this.awaitRenderList.length < 1){
|
||||
this.showPage++;
|
||||
this.$emit("done");
|
||||
|
||||
// 为防止 js 数值类型最大值溢出,当高度值大于 1亿时重置高度
|
||||
if(this.leftHeight > 100000000){
|
||||
if(this.leftHeight > this.rightHeight){
|
||||
this.leftHeight = 2;
|
||||
this.rightHeight = 1;
|
||||
}else{
|
||||
this.leftHeight = 1;
|
||||
this.rightHeight = 2;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
let item = {
|
||||
...this.awaitRenderList.splice(0,1)[0],
|
||||
// 当前数据添加当前页面标识
|
||||
_current_page:this.showPage,
|
||||
// 当前数据添加一个渲染id,解决 v-for 重复会出现不执行 load 的 BUG
|
||||
_render_id:new Date().getTime()
|
||||
};
|
||||
|
||||
if(this.leftHeight > this.rightHeight){
|
||||
this.rightList.push(item);
|
||||
}else{
|
||||
this.leftList.push(item);
|
||||
}
|
||||
},
|
||||
// 重置数据
|
||||
resetData(){
|
||||
this.leftHeight = 0;
|
||||
this.rightHeight = 0;
|
||||
this.leftList = [];
|
||||
this.rightList = [];
|
||||
this.awaitRenderList = [];
|
||||
// 当前展示页码数据
|
||||
this.showPage = 1;
|
||||
},
|
||||
// 启动渲染
|
||||
startRender(){
|
||||
if(!this.showList){
|
||||
this.resetData();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.$props.list || this.$props.list.length < 1){
|
||||
console.log('河浪瀑布流插件提示:当前数据为空,不会触发列表渲染');
|
||||
return;
|
||||
}
|
||||
|
||||
// 若本次渲染为 重置 则先恢复组件的默认参数
|
||||
if(this.$props.reset){
|
||||
this.resetData();
|
||||
}
|
||||
|
||||
this.awaitRenderList = [...this.$props.list];
|
||||
this.renderList();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.waterfall-box {
|
||||
width: 100%;
|
||||
|
||||
>view {
|
||||
width: 334rpx; margin-right: 18rpx;
|
||||
&:last-child{ margin-right: 0; }
|
||||
}
|
||||
|
||||
.list-item{
|
||||
margin-bottom: 0;
|
||||
// 设置透明,默认是可视的
|
||||
opacity: 0;
|
||||
// 默认超出隐藏,不影响加载中的文字显示效果
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
|
||||
&.show{
|
||||
margin-bottom: 20rpx;
|
||||
opacity: 1;
|
||||
overflow: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.h-flex-x {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
|
||||
&.h-flex-2 {
|
||||
>view {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user