珠宝小程序前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

265 lines
8.7 KiB

<template>
<view class="uv-waterfall">
<!-- #ifndef APP-NVUE -->
<view class="uv-waterfall__gap_left" :style="[gapLeftStyle]"></view>
<template v-if="columnNum>=1">
<view id="uv-waterfall-1" class="uv-waterfall__column">
<slot name="list1"></slot>
</view>
</template>
<template v-if="columnNum>=2">
<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]"></view>
<view id="uv-waterfall-2" class="uv-waterfall__column">
<slot name="list2"></slot>
</view>
</template>
<template v-if="columnNum>=3">
<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]"></view>
<view id="uv-waterfall-3" class="uv-waterfall__column">
<slot name="list3"></slot>
</view>
</template>
<template v-if="columnNum>=4">
<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]">
</view>
<view id="uv-waterfall-4" class="uv-waterfall__column">
<slot name="list4"></slot>
</view>
</template>
<template v-if="columnNum>=5">
<view class="uv-waterfall__gap_center" :style="[gapCenterStyle]">
</view>
<view id="uv-waterfall-5" class="uv-waterfall__column">
<slot name="list5"></slot>
</view>
</template>
<view class="uv-waterfall__gap_right" :style="[gapRightStyle]">
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="waterfall-warapper">
<waterfall :column-count="columnNum" :show-scrollbar="false" column-width="auto" :column-gap="columnGap" :left-gap="leftGap" :right-gap="rightGap" :always-scrollable-vertical="true" :style="[nvueWaterfallStyle]"
@loadmore="scrolltolower">
<slot></slot>
</waterfall>
</view>
<!-- #endif -->
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
/**
* 瀑布流
* @description 该组件兼容所有端,nvue参考https://uniapp.dcloud.net.cn/component/waterfall.html
* @tutorial https://www.uvui.cn/components/list.html
* @property {Array} value/modelValue 瀑布流数组数据,非nvue生效 (默认 [] )
* @property {String} idKey 数据的id值,根据id值对数据执行删除操作,如数据为:{id: 1, name: 'uv-ui'},那么该值设置为id,非nvue有效 (默认 '' )
* @property {String | Number} addTime 每次插入数据的事件间隔,间隔越长能保证两列高度相近,但是用户体验不好,单位ms,非nvue生效(默认 200 )
* @property {String | Number} columnCount 瀑布流的列数(默认 2 )
* @property {String | Number} columnGap 列与列的间隙(默认 0 )
* @property {String | Number} leftGap 左边和列表的间隙(默认 0 )
* @property {String | Number} rightGap 右边和列表的间隙(默认 0 )
* @property {Boolean} showScrollbar 控制是否出现滚动条,仅nvue有效 (默认 false )
* @property {String | Number} columnWidth 描述瀑布流每一列的列宽,nvue生效 (默认 auto)
* @property {String | Number} width 瀑布流的宽度,nvue生效 (默认 屏幕宽 )
* @property {String | Number} height 瀑布流的高度,nvue生效 (默认 屏幕高 )
* @property {Object} customStyle 定义需要用到的外部样式
*
* @example <uv-waterfall v-model="list"></uv-waterfall>
*/
export default {
name: 'uv-waterfall',
mixins: [mpMixin, mixin, props],
data() {
return {
list1: [],
list2: [],
list3: [],
list4: [],
list5: [],
// 临时列表
tempList: []
}
},
computed: {
// 破坏value变量引用,否则数据会保持不变
copyValue() {
// #ifdef VUE2
return this.$uv.deepClone(this.value)
// #endif
// #ifdef VUE3
return this.$uv.deepClone(this.modelValue)
// #endif
},
columnNum() {
return this.columnCount <= 0 ? 0 : this.columnCount >= 5 ? 5 : this.columnCount;
},
gapLeftStyle() {
const style = {}
style.width = this.$uv.addUnit(this.leftGap)
return style;
},
gapRightStyle() {
const style = {}
style.width = this.$uv.addUnit(this.rightGap)
return style;
},
gapCenterStyle() {
const style = {}
style.width = this.$uv.addUnit(this.columnGap)
return style;
},
nvueWaterfallStyle() {
const style = {};
if (this.width != 0) style.width = this.$uv.addUnit(this.width)
if (this.height != 0) style.height = this.$uv.addUnit(this.height)
// 如果没有定义列表高度,则默认使用屏幕高度
if (!style.width) style.width = this.$uv.addUnit(this.$uv.sys().windowWidth, 'px')
if (!style.height) style.height = this.$uv.addUnit(this.$uv.sys().windowHeight, 'px')
return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
}
},
watch: {
copyValue(nVal, oVal) {
// #ifndef APP-NVUE
if (nVal.length != 0) {
// 取出数组发生变化的部分
let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0
// 拼接原有数据
this.tempList = this.tempList.concat(this.$uv.deepClone(nVal.slice(startIndex)))
this.splitData()
}
// #endif
}
},
mounted() {
// #ifndef APP-NVUE
this.tempList = this.$uv.deepClone(this.copyValue)
this.splitData()
// #endif
},
methods: {
// 滚动到底部触发事件
scrolltolower(e) {
this.$uv.sleep(30).then(() => {
this.$emit('scrolltolower')
})
},
// 拆分数据
async splitData() {
let rectArr = [];
let emitList = {};
if (!this.tempList.length) return
for (let i = 1; i <= this.columnNum; i++) {
const rect = await this.$uvGetRect(`#uv-waterfall-${i}`);
rectArr.push({ ...rect, name: i });
}
let item = this.tempList[0]
// 因为经过上面两个await节点查询和定时器,数组有可能会变成空[],导致item的值为undefined
// 解决多次快速滚动会导致数据乱的问题
if (!item) return
const minCol = this.getMin(rectArr);
// 列宽可能使用的到
item.width = minCol.width;
this[`list${minCol.name}`].push(item);
emitList.name = `list${minCol.name}`;
emitList.value = item;
this.$emit('changeList', emitList);
// 移除临时数组中已处理的数据
this.tempList.splice(0, 1)
// 如果还有数据则继续执行
if (this.tempList.length) {
let _timeout = this.addTime;
// 部分平台在延时较短的情况会出现BUG
// #ifdef MP-BAIDU
_timeout = _timeout < 200 ? 200 : _timeout;
// #endif
await this.$uv.sleep(_timeout);
this.splitData()
} else {
this.$emit('finish')
}
},
getMin(arr) {
let result = null;
const filter = arr.filter(item => item.height == 0);
if (!filter.length) {
const min = Math.min.apply(Math, arr.map(item => {
return item.height;
}))
const [item] = arr.filter(item => item.height == min);
result = item;
} else {
let newArr = [];
arr.map((item, index) => {
newArr.push({ len: this[`list${index+1}`].length, item: item });
});
const minLen = Math.min.apply(Math, newArr.map(item => {
return item.len;
}))
try {
const { item } = newArr.find(item => item.len == minLen && item.item.height == 0);
result = item;
} catch (e) {
const { item } = newArr.find(item => item.item.height == 0);
result = item;
}
}
return result;
},
// 清空数据列表
async clear() {
// 清除数据
for (let i = 0; i < this.columnCount; i++) {
this[`list${i+1}`] = [];
}
// #ifdef VUE2
this.$emit('input', [])
// #endif
// #ifdef VUE3
this.$emit('update:modelValue', [])
// #endif
this.tempList = []
await this.$uv.sleep(300);
this.$emit('clear');
},
// 清除指定的某一条数据,根据id来实现
remove(id) {
let index = -1
// 删除组件数据
for (let i = 1; i <= this.columnCount; i++) {
index = this[`list${i}`].findIndex(item => item[this.idKey] == id)
if (index != -1) {
this[`list${i}`].splice(index, 1)
}
}
// 同时删除父组件对应的数据
// #ifdef VUE2
index = this.value.findIndex(item => item[this.idKey] == id)
if (index != -1) this.$emit('input', this.value.splice(index, 1))
// #endif
// #ifdef VUE3
index = this.modelValue.findIndex(item => item[this.idKey] == id)
if (index != -1) this.$emit('update:modelValue', this.modelValue.splice(index, 1))
// #endif
this.$emit('remove', id);
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
.uv-waterfall {
@include flex(row);
align-items: flex-start;
&__column {
@include flex(column);
flex: 1;
// #ifndef APP-NVUE
height: auto;
// #endif
}
}
</style>