v1.0
This commit is contained in:
24
uni_modules/uv-waterfall/changelog.md
Normal file
24
uni_modules/uv-waterfall/changelog.md
Normal file
@ -0,0 +1,24 @@
|
||||
## 1.0.8(2023-08-17)
|
||||
1. 修复只有一条数据切换时可能存在位置错误的BUG
|
||||
## 1.0.7(2023-07-22)
|
||||
1. 避免快速切换报错的BUG
|
||||
## 1.0.6(2023-07-17)
|
||||
1. 优化文档
|
||||
2. 优化其他
|
||||
## 1.0.5(2023-07-14)
|
||||
1. 优化changeList未处理数据时,正确返回对应列的数据,避免误导
|
||||
## 1.0.4(2023-05-27)
|
||||
1. 修复在百度小程序中可能存在的BUG
|
||||
2. 去掉原有的slot方式
|
||||
## 1.0.3(2023-05-23)
|
||||
1. 修复在百度/头条小程序显示异常等BUG
|
||||
2. 增加changeList回调函数处理数据
|
||||
3. 更新示例
|
||||
## 1.0.2(2023-05-16)
|
||||
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
|
||||
2. 优化部分功能
|
||||
## 1.0.1(2023-05-12)
|
||||
1. 增加clear回调函数
|
||||
2. 增加remove回调函数
|
||||
## 1.0.0(2023-05-10)
|
||||
uv-waterfall 瀑布流
|
69
uni_modules/uv-waterfall/components/uv-waterfall/props.js
Normal file
69
uni_modules/uv-waterfall/components/uv-waterfall/props.js
Normal file
@ -0,0 +1,69 @@
|
||||
export default {
|
||||
props: {
|
||||
// 瀑布流数据
|
||||
// #ifdef VUE2
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// #endif
|
||||
// 数据的id值,根据id值对数据执行删除操作
|
||||
// 如数据为:{id: 1, name: 'uv-ui'},那么该值设置为id
|
||||
idKey: {
|
||||
type: String,
|
||||
default: 'id'
|
||||
},
|
||||
// 每次插入数据的事件间隔,间隔越长能保证两列高度相近,但是用户体验不好,单位ms
|
||||
addTime: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
// 瀑布流的列数,默认2,最高为5
|
||||
columnCount: {
|
||||
type: [Number, String],
|
||||
default: 2
|
||||
},
|
||||
// 列与列的间隙,默认20
|
||||
columnGap: {
|
||||
type: [Number, String],
|
||||
default: 20
|
||||
},
|
||||
// 左边和列表的间隙
|
||||
leftGap: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 右边和列表的间隙
|
||||
rightGap: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 是否显示滚动条,仅nvue生效
|
||||
showScrollbar: {
|
||||
type: [Boolean],
|
||||
default: false
|
||||
},
|
||||
// 列宽,nvue生效
|
||||
columnWidth: {
|
||||
type: [Number, String],
|
||||
default: 'auto'
|
||||
},
|
||||
// 瀑布流的宽度,nvue生效
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 瀑布流的高度,nvue生效
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
...uni.$uv?.props?.waterfall
|
||||
}
|
||||
}
|
@ -0,0 +1,265 @@
|
||||
<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>
|
89
uni_modules/uv-waterfall/package.json
Normal file
89
uni_modules/uv-waterfall/package.json
Normal file
@ -0,0 +1,89 @@
|
||||
{
|
||||
"id": "uv-waterfall",
|
||||
"displayName": "uv-waterfall 瀑布流 全面兼容vue3+2、app、h5、小程序等多端",
|
||||
"version": "1.0.8",
|
||||
"description": "该组件主要用于瀑布流式布局显示,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,同时集成nvue的原生瀑布流。",
|
||||
"keywords": [
|
||||
"uv-waterfall",
|
||||
"uvui",
|
||||
"uv-ui",
|
||||
"waterfall",
|
||||
"瀑布流"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"uv-ui-tools",
|
||||
"uv-image",
|
||||
"uv-loading-icon"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
uni_modules/uv-waterfall/readme.md
Normal file
19
uni_modules/uv-waterfall/readme.md
Normal file
@ -0,0 +1,19 @@
|
||||
## Waterfall 瀑布流
|
||||
|
||||
> **组件名:uv-waterfall**
|
||||
|
||||
该组件主要用于瀑布流式布局显示,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,同时集成`nvue`的原生瀑布流用于`app-nvue`。常用于一些电商商品展示等,如某宝首页、x红书等。
|
||||
|
||||
研究uniapp瀑布流多年,**该方式是目前小程序端最佳方案**,灵活配置,简单易用,开箱即用。
|
||||
|
||||
该插件请根据文档耐心查看,`vue`的写法稍微麻烦点,但是效果是很好的,比之前上线的两个版本的瀑布流适用,更有扩展性,我自己的上线项目也是用的此插件。
|
||||
|
||||
# <a href="https://www.uvui.cn/components/waterfall.html" target="_blank">查看文档</a>
|
||||
|
||||
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
|
||||
|
||||
### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
|
||||
|
||||

|
||||
|
||||
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
|
Reference in New Issue
Block a user