|
|
- <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>
|