uview-plus组件库全面升级更新,订单结算判断支付方式是否可用代码调整,公众号关注二维码修改
This commit is contained in:
@@ -1,25 +1,41 @@
|
||||
<template>
|
||||
<view class="u-waterfall">
|
||||
<view ref="u-left-column" id="u-left-column" class="u-column">
|
||||
<slot name="left" :leftList="leftList"></slot>
|
||||
</view>
|
||||
<view ref="u-right-column" id="u-right-column" class="u-column">
|
||||
<slot name="right" :rightList="rightList"></slot>
|
||||
<!-- 新增支持多列布局 -->
|
||||
<view
|
||||
v-for="(column, index) in columnList"
|
||||
:key="index"
|
||||
:ref="`u-column-${index}`"
|
||||
:id="`u-column-${index}`"
|
||||
class="u-column"
|
||||
>
|
||||
<slot name="column"
|
||||
:colIndex="index"
|
||||
:colList="column">
|
||||
</slot>
|
||||
<slot name="left"
|
||||
:colIndex="index"
|
||||
:leftList="column">
|
||||
</slot>
|
||||
<template v-if="!$slots['left'] && !$slots['column']" v-for="(item, itemIndex) in column" :key="itemIndex">
|
||||
<slot :item="item" :itemIndex="itemIndex"></slot>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* waterfall 瀑布流
|
||||
* @description 这是一个瀑布流形式的组件,内容分为左右两列,结合uview的懒加载组件效果更佳。相较于某些只是奇偶数左右分别,或者没有利用vue作用域插槽的做法,uview的瀑布流实现了真正的 组件化,搭配LazyLoad 懒加载和loadMore 加载更多组件,让您开箱即用,眼前一亮。
|
||||
* waterfall 瀴布流
|
||||
* @description 这是一个瀑布流形式的组件,对原组件进行升级已经支持自定义列数模式,便于适配不同屏幕。搭配loadMore 加载更多组件,让您开箱即用,眼前一亮。
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/waterfall.html
|
||||
* @property {Array} flow-list 用于渲染的数据
|
||||
* @property {String Number} add-time 单条数据添加到队列的时间间隔,单位ms,见上方注意事项说明(默认200)
|
||||
* @property {String Number} columns 瀑布流列数,默认为2,设置为auto时自动根据屏幕宽度调整列数
|
||||
* @example <u-waterfall :flowList="flowList"></u-waterfall>
|
||||
*/
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
import { sleep } from '../../libs/function/index';
|
||||
export default {
|
||||
name: "u-waterfall",
|
||||
props: {
|
||||
@@ -43,7 +59,7 @@
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
// 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
|
||||
// 每次向结构插入数据的时间间隔,单位ms
|
||||
// 单位ms
|
||||
addTime: {
|
||||
type: [Number, String],
|
||||
@@ -54,41 +70,98 @@
|
||||
idKey: {
|
||||
type: String,
|
||||
default: 'id'
|
||||
},
|
||||
// 瀑布流列数
|
||||
columns: {
|
||||
type: [Number, String],
|
||||
default: 2
|
||||
},
|
||||
// 瀑布流最小列数
|
||||
columnsMin: {
|
||||
type: [Number, String],
|
||||
default: 2
|
||||
},
|
||||
// 最小列宽
|
||||
minColumnWidth: {
|
||||
type: Number,
|
||||
default: 230
|
||||
}
|
||||
},
|
||||
mixins: [mpMixin, mixin],
|
||||
data() {
|
||||
return {
|
||||
leftList: [],
|
||||
rightList: [],
|
||||
tempList: [],
|
||||
children: []
|
||||
columnList: [[]], // 存储每列的数据
|
||||
children: [],
|
||||
// 用于标记是否已经初始化
|
||||
initialized: false,
|
||||
windowWidth: 375,
|
||||
windowHeight: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
copyFlowList(nVal, oVal) {
|
||||
if (!nVal || nVal.length == 0) {
|
||||
this.clear(false);
|
||||
// console.log('clear');
|
||||
} else {
|
||||
// 取差值,即这一次数组变化新增的部分
|
||||
let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
|
||||
// 拼接上原有数据
|
||||
this.tempList = this.tempList.concat(this.cloneData(nVal.slice(startIndex)));
|
||||
this.splitData();
|
||||
}
|
||||
copyFlowList: {
|
||||
handler(nVal, oVal) {
|
||||
if (!nVal || nVal.length == 0) {
|
||||
this.clear(false);
|
||||
} else {
|
||||
if (this.columnList.length == 1) {
|
||||
this.initColumnList()
|
||||
}
|
||||
// 取差值,即这一次数组变化新增的部分
|
||||
let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
|
||||
// 直接处理数据,不再使用tempList和splitData
|
||||
this.handleData(nVal.slice(startIndex));
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
columns: {
|
||||
handler() {
|
||||
this.initColumnList();
|
||||
// 重新分配数据
|
||||
if (this.copyFlowList.length > 0) {
|
||||
this.redistributeData();
|
||||
}
|
||||
},
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.tempList = this.cloneData(this.copyFlowList);
|
||||
this.splitData();
|
||||
created() {
|
||||
this.initColumnList();
|
||||
},
|
||||
mounted() {
|
||||
this.initialized = true;
|
||||
// 添加窗口大小变化监听
|
||||
// #ifdef H5
|
||||
if (this.columns === 'auto') {
|
||||
uni.onWindowResize(this.handleWindowResize);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// 添加beforeUnmount生命周期清理事件监听
|
||||
// #ifdef VUE3
|
||||
beforeUnmount() {
|
||||
// #ifdef H5
|
||||
if (this.columns === 'auto') {
|
||||
uni.offWindowResize(this.handleWindowResize);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE2
|
||||
beforeDestroy() {
|
||||
// #ifdef H5
|
||||
if (this.columns === 'auto') {
|
||||
window.removeEventListener('resize', this.handleWindowResize);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// #endif
|
||||
computed: {
|
||||
// 破坏flowList变量的引用,否则watch的结果新旧值是一样的
|
||||
copyFlowList() {
|
||||
// #ifdef VUE3
|
||||
if (!this.modelValue || this.modelValue.length == 0) {
|
||||
this.clear(false);
|
||||
// console.log('clear');
|
||||
return [];
|
||||
} else {
|
||||
@@ -100,47 +173,138 @@
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
emits: ['update:modelValue', 'after-add-one', 'after-add-all'],
|
||||
methods: {
|
||||
async splitData() {
|
||||
if (!this.tempList.length) return;
|
||||
let leftRect = await this.$uGetRect('#u-left-column');
|
||||
let rightRect = await this.$uGetRect('#u-right-column');
|
||||
// 如果左边小于或等于右边,就添加到左边,否则添加到右边
|
||||
let item = this.tempList[0];
|
||||
// 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰
|
||||
// 数组可能变成[],导致此item值可能为undefined
|
||||
if (!item) return;
|
||||
if (leftRect.height < rightRect.height) {
|
||||
this.leftList.push(item);
|
||||
} else if (leftRect.height > rightRect.height) {
|
||||
this.rightList.push(item);
|
||||
} else {
|
||||
// 这里是为了保证第一和第二张添加时,左右都能有内容
|
||||
// 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
|
||||
if (this.leftList.length <= this.rightList.length) {
|
||||
this.leftList.push(item);
|
||||
} else {
|
||||
this.rightList.push(item);
|
||||
// 初始化列数据数组
|
||||
initColumnList() {
|
||||
this.windowWidth = uni.getSystemInfoSync().windowWidth;
|
||||
const cols = this.getColumnsCount();
|
||||
// console.log(cols)
|
||||
this.columnList = Array.from({ length: cols }, () => []);
|
||||
},
|
||||
|
||||
// 获取列数,支持auto模式
|
||||
getColumnsCount() {
|
||||
if (this.columns === 'auto') {
|
||||
// 列间距为10rpx(约等于7px)
|
||||
const columnGap = 7;
|
||||
// 计算可容纳的列数
|
||||
let columnCount = Math.max(1, Math.floor(this.windowWidth / (this.minColumnWidth + columnGap)));
|
||||
if (columnCount < this.columnsMin) {
|
||||
columnCount = this.columnsMin
|
||||
}
|
||||
return columnCount;
|
||||
}
|
||||
return parseInt(this.columns) || 2;
|
||||
},
|
||||
|
||||
// 窗口大小变化处理函数
|
||||
handleWindowResize(res) {
|
||||
this.windowWidth = res.size.windowWidth
|
||||
this.windowHeight = res.size.windowHeight
|
||||
// 防抖处理,避免频繁触发
|
||||
if (this.resizeTimer) {
|
||||
clearTimeout(this.resizeTimer);
|
||||
}
|
||||
this.resizeTimer = setTimeout(() => {
|
||||
const newColumnsCount = this.getColumnsCount();
|
||||
const oldColumnsCount = this.columnList.length;
|
||||
|
||||
// 只有列数发生变化时才重新分配数据
|
||||
if (newColumnsCount !== oldColumnsCount) {
|
||||
this.redistributeData();
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
|
||||
// 重新分配所有数据
|
||||
async redistributeData() {
|
||||
// 清空所有列
|
||||
this.initColumnList();
|
||||
// 保存所有数据
|
||||
const allData = this.cloneData(this.copyFlowList);
|
||||
// 重新分配数据
|
||||
this.handleData(allData);
|
||||
},
|
||||
|
||||
// 处理新增数据
|
||||
async handleData(newData) {
|
||||
if (!newData || newData.length === 0) return;
|
||||
|
||||
// 初始化列高度数组
|
||||
const columnHeights = new Array(this.columnList.length).fill(0);
|
||||
|
||||
// 获取各列当前高度
|
||||
for (let i = 0; i < this.columnList.length; i++) {
|
||||
try {
|
||||
const rect = await this.$uGetRect(`#u-column-${i}`);
|
||||
// console.log(`#u-column-${i}`, rect.height)
|
||||
columnHeights[i] = rect.height || 0;
|
||||
} catch (e) {
|
||||
columnHeights[i] = 0;
|
||||
}
|
||||
}
|
||||
// 移除临时列表的第一项
|
||||
this.tempList.splice(0, 1);
|
||||
// 如果临时数组还有数据,继续循环
|
||||
if (this.tempList.length) {
|
||||
setTimeout(() => {
|
||||
this.splitData();
|
||||
}, this.addTime)
|
||||
|
||||
// 分配新数据到最短的列
|
||||
for (let item of newData) {
|
||||
const minHeightIndex = columnHeights.indexOf(Math.min(...columnHeights));
|
||||
// console.log('this.columnList', this.columnList)
|
||||
this.columnList[minHeightIndex].push(item);
|
||||
|
||||
// 获取实际渲染后的元素高度而不是估算
|
||||
await sleep(this.addTime)
|
||||
await this.$nextTick(async () => {
|
||||
try {
|
||||
const rect = await this.$uGetRect(`#u-column-${minHeightIndex}`);
|
||||
// console.log(`#u-column-${minHeightIndex}`, rect.height)
|
||||
if (rect.height) {
|
||||
columnHeights[minHeightIndex] = rect.height;
|
||||
// 加载一个后置事件
|
||||
this.$emit('after-add-one', {
|
||||
...item,
|
||||
height: rect.height
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// console.log(e)
|
||||
// columnHeights[i] = 0;
|
||||
}
|
||||
});
|
||||
// this.$nextTick(async () => {
|
||||
// try {
|
||||
// // 等待DOM更新后获取实际高度
|
||||
// const lastIndex = this.columnList[minHeightIndex].length - 1;
|
||||
// const el = this.$refs[`u-column-${minHeightIndex}`][0].children[lastIndex];
|
||||
// if (el) {
|
||||
// const rect = await this.$uGetRect(el);
|
||||
// const actualHeight = rect.height || 100;
|
||||
// columnHeights[minHeightIndex] += actualHeight;
|
||||
// } else {
|
||||
// // 备用方案:如果无法获取实际高度,则使用默认值
|
||||
// columnHeights[minHeightIndex] += 100;
|
||||
// }
|
||||
// } catch (e) {
|
||||
// // 出错时使用默认高度
|
||||
// console.log(e)
|
||||
// columnHeights[minHeightIndex] += 100;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
// 加载所有后置事件
|
||||
this.$emit('after-add-all', {
|
||||
columnHeights: columnHeights,
|
||||
newData: newData
|
||||
});
|
||||
},
|
||||
|
||||
// 复制而不是引用对象和数组
|
||||
cloneData(data) {
|
||||
return JSON.parse(JSON.stringify(data));
|
||||
},
|
||||
|
||||
// 清空数据列表
|
||||
clear(bak = true) {
|
||||
this.leftList = [];
|
||||
this.rightList = [];
|
||||
this.initColumnList();
|
||||
// 同时清除父组件列表中的数据
|
||||
if (bak) {
|
||||
// #ifdef VUE2
|
||||
@@ -150,69 +314,73 @@
|
||||
this.$emit('update:modelValue', []);
|
||||
// #endif
|
||||
}
|
||||
this.tempList = [];
|
||||
},
|
||||
|
||||
// 清除某一条指定的数据,根据id实现
|
||||
remove(id) {
|
||||
// 如果findIndex找不到合适的条件,就会返回-1
|
||||
let index = -1;
|
||||
index = this.leftList.findIndex(val => val[this.idKey] == id);
|
||||
if (index != -1) {
|
||||
// 如果index不等于-1,说明已经找到了要找的id,根据index索引删除这一条数据
|
||||
this.leftList.splice(index, 1);
|
||||
} else {
|
||||
// 同理于上方面的方法
|
||||
index = this.rightList.findIndex(val => val[this.idKey] == id);
|
||||
if (index != -1) this.rightList.splice(index, 1);
|
||||
// 遍历所有列查找并删除数据
|
||||
for (let i = 0; i < this.columnList.length; i++) {
|
||||
const index = this.columnList[i].findIndex(val => val[this.idKey] == id);
|
||||
if (index !== -1) {
|
||||
this.columnList[i].splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 同时清除父组件的数据中的对应id的条目
|
||||
// #ifdef VUE2
|
||||
index = this.value.findIndex(val => val[this.idKey] == id);
|
||||
if (index != -1) this.$emit('input', this.value.splice(index, 1));
|
||||
const valueIndex = this.value.findIndex(val => val[this.idKey] == id);
|
||||
if (valueIndex !== -1) {
|
||||
const newValue = this.cloneData(this.value);
|
||||
newValue.splice(valueIndex, 1);
|
||||
this.$emit('input', newValue);
|
||||
}
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
index = this.modelValue.findIndex(val => val[this.idKey] == id);
|
||||
if (index != -1) this.$emit('update:modelValue', this.modelValue.splice(index, 1));
|
||||
const modelValueIndex = this.modelValue.findIndex(val => val[this.idKey] == id);
|
||||
if (modelValueIndex !== -1) {
|
||||
const newModelValue = this.cloneData(this.modelValue);
|
||||
newModelValue.splice(modelValueIndex, 1);
|
||||
this.$emit('update:modelValue', newModelValue);
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 修改某条数据的某个属性
|
||||
modify(id, key, value) {
|
||||
// 如果findIndex找不到合适的条件,就会返回-1
|
||||
let index = -1;
|
||||
index = this.leftList.findIndex(val => val[this.idKey] == id);
|
||||
if (index != -1) {
|
||||
// 如果index不等于-1,说明已经找到了要找的id,修改对应key的值
|
||||
this.leftList[index][key] = value;
|
||||
} else {
|
||||
// 同理于上方面的方法
|
||||
index = this.rightList.findIndex(val => val[this.idKey] == id);
|
||||
if (index != -1) this.rightList[index][key] = value;
|
||||
let found = false;
|
||||
let targetItem = null;
|
||||
|
||||
// 在所有列中查找数据
|
||||
for (let i = 0; i < this.columnList.length; i++) {
|
||||
const index = this.columnList[i].findIndex(val => val[this.idKey] == id);
|
||||
if (index !== -1) {
|
||||
// 修改对应key的值
|
||||
this.columnList[i][index][key] = value;
|
||||
targetItem = this.columnList[i][index];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 修改父组件的数据中的对应id的条目
|
||||
// #ifdef VUE2
|
||||
index = this.value.findIndex(val => val[this.idKey] == id);
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
index = this.modelValue.findIndex(val => val[this.idKey] == id);
|
||||
// #endif
|
||||
if (index != -1) {
|
||||
// 首先复制一份value的数据
|
||||
|
||||
if (found && targetItem) {
|
||||
// 修改父组件的数据中的对应id的条目
|
||||
// #ifdef VUE2
|
||||
let data = this.cloneData(this.value);
|
||||
const valueIndex = this.value.findIndex(val => val[this.idKey] == id);
|
||||
if (valueIndex !== -1) {
|
||||
let data = this.cloneData(this.value);
|
||||
data[valueIndex][key] = value;
|
||||
this.$emit('input', data);
|
||||
}
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
let data = this.cloneData(this.modelValue);
|
||||
const modelValueIndex = this.modelValue.findIndex(val => val[this.idKey] == id);
|
||||
if (modelValueIndex !== -1) {
|
||||
let data = this.cloneData(this.modelValue);
|
||||
data[modelValueIndex][key] = value;
|
||||
this.$emit('update:modelValue', data);
|
||||
}
|
||||
// #endif
|
||||
// 修改对应索引的key属性的值为value
|
||||
data[index][key] = value;
|
||||
// 修改父组件通过v-model绑定的变量的值
|
||||
// #ifdef VUE2
|
||||
this.$emit('input', data);
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
this.$emit('update:modelValue', data);
|
||||
// #endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,8 +388,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-waterfall {
|
||||
@include flex;
|
||||
flex-direction: row;
|
||||
@@ -236,6 +402,10 @@
|
||||
/* #ifndef APP-NVUE */
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
// 添加列之间的间距
|
||||
&:not(:first-child) {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.u-image {
|
||||
@@ -243,4 +413,4 @@
|
||||
max-width: 100%;
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user