waterfall-list.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <template>
  2. <view>
  3. <view class="waterfall-box h-flex-x h-flex-2">
  4. <view>
  5. <view v-for="(item,index) in leftList" :key="item._render_id" class="list-item show">
  6. <helang-waterfall-item :datas="datas" :params="item" tag="left" :index="index" @height="onHeight"
  7. @click="onClick">
  8. </helang-waterfall-item>
  9. </view>
  10. </view>
  11. <view>
  12. <view v-for="(item,index) in rightList" :key="item._render_id" class="list-item show">
  13. <helang-waterfall-item :datas="datas" :params="item" @height="onHeight" @click="onClick" tag="right"
  14. :index="index"></helang-waterfall-item>
  15. </view>
  16. </view>
  17. </view>
  18. <view class="load-txt" v-if="statusText">{{statusText}}</view>
  19. </view>
  20. </template>
  21. <script>
  22. import helangWaterfallItem from "./waterfall-item.vue"
  23. export default {
  24. components: {
  25. "helang-waterfall-item": helangWaterfallItem
  26. },
  27. props: {
  28. // 组件状态
  29. status: {
  30. type: String,
  31. default: ''
  32. },
  33. // 只显示一行
  34. showlineText: {
  35. type: String,
  36. default: ''
  37. },
  38. // 等待加载文案
  39. awaitText: {
  40. type: String,
  41. default: '上拉加载更多'
  42. },
  43. // 加载中文案
  44. loadingText: {
  45. type: String,
  46. default: '加载中'
  47. },
  48. // 加载成功文案,一般与加载中保持一致即可,主要为提示组件可以开始渲染了
  49. successText: {
  50. type: String,
  51. default: '加载中'
  52. },
  53. // 加载结束文案,用于没有更多数据时候展示
  54. finishText: {
  55. type: String,
  56. default: '没有更多了'
  57. },
  58. // 加载失败文案,用于数据获取异常时展示
  59. failText: {
  60. type: String,
  61. default: '加载失败'
  62. },
  63. // 待渲染的数据
  64. list: {
  65. type: Array,
  66. default () {
  67. return [];
  68. }
  69. },
  70. // 重置列表,设置为 true 时,瀑布流会自动重新渲染列表
  71. reset: {
  72. type: Boolean,
  73. default: false
  74. },
  75. datas: {
  76. type: Object,
  77. default: ''
  78. }
  79. },
  80. watch: {
  81. "$props.status"(newValue, oldValue) {
  82. // 状态变更为 加载成功 时,执行瀑布流数据渲染
  83. if (newValue == 'success' || newValue == 'await' || newValue == 'showline') {
  84. if (!this.$props.list || this.$props.list.length < 1) {
  85. console.log('河浪瀑布流插件提示:当前数据无效');
  86. return;
  87. }
  88. // 若本次渲染为 重置 则先恢复组件的默认参数
  89. if (this.$props.reset) {
  90. this.leftHeight = 0;
  91. this.rightHeight = 0;
  92. this.leftList = [];
  93. this.rightList = [];
  94. this.awaitRenderList = [];
  95. // 当前展示页码数据
  96. this.showPage = 1;
  97. }
  98. this.awaitRenderList = [...this.$props.list];
  99. this.renderList();
  100. }
  101. }
  102. },
  103. computed: {
  104. statusText() {
  105. if (!this.$props.status) {
  106. return false;
  107. }
  108. let key = `${this.$props.status}Text`;
  109. return this.$props[key] || false;
  110. }
  111. },
  112. data() {
  113. return {
  114. // 左侧列表高度
  115. leftHeight: 0,
  116. // 右侧列表高度
  117. rightHeight: 0,
  118. // 左侧列表数据
  119. leftList: [],
  120. // 右侧列表数据
  121. rightList: [],
  122. // 待渲染列表
  123. awaitRenderList: [],
  124. // 当前展示页码数据
  125. showPage: 1
  126. }
  127. },
  128. methods: {
  129. // 监听高度变化
  130. onHeight(height, tag) {
  131. /**
  132. * 这个为实际渲染后 CSS 中 margin-buttom 的值,本示例默认为20rpx
  133. * 用于解决实际渲染后因为数据条数关系,高度差计算偏差的问题
  134. * */
  135. let marginBottom = uni.upx2px(20);
  136. // console.log(`左高:${this.leftHeight},右高:${this.rightHeight},当前高:${height},插入方向:${tag}`)
  137. if (tag == 'left') {
  138. this.leftHeight += (height + marginBottom);
  139. } else {
  140. this.rightHeight += (height + marginBottom);
  141. }
  142. this.renderList();
  143. },
  144. // 组件点击事件
  145. onClick(index, tag) {
  146. // 对应的数据
  147. if (tag == 'left') {
  148. this.$emit("click", this.leftList[index], index, tag);
  149. } else {
  150. this.$emit("click", this.rightList[index], index, tag);
  151. }
  152. },
  153. // 渲染列表,这里实现瀑布流的左右分栏
  154. renderList() {
  155. // 待渲染长度为 0 时表示已渲染完成
  156. if (this.awaitRenderList.length < 1) {
  157. this.showPage++;
  158. this.$emit("done");
  159. // 为防止 js 数值类型最大值溢出,当高度值大于 1亿时重置高度
  160. if (this.leftHeight > 100000000) {
  161. if (this.leftHeight > this.rightHeight) {
  162. this.leftHeight = 2;
  163. this.rightHeight = 1;
  164. } else {
  165. this.leftHeight = 1;
  166. this.rightHeight = 2;
  167. }
  168. }
  169. return;
  170. }
  171. let item = {
  172. ...this.awaitRenderList.splice(0, 1)[0],
  173. // 当前数据添加当前页面标识
  174. _current_page: this.showPage,
  175. // 当前数据添加一个渲染id,解决 v-for 重复会出现不执行 load 的 BUG
  176. _render_id: new Date().getTime()
  177. };
  178. if (this.leftHeight > this.rightHeight) {
  179. this.rightList.push(item);
  180. } else {
  181. this.leftList.push(item);
  182. }
  183. }
  184. }
  185. }
  186. </script>
  187. <style lang="scss" scoped>
  188. .waterfall-box {
  189. padding: 20rpx 10rpx;
  190. box-sizing: border-box;
  191. >view {
  192. padding: 0 10rpx;
  193. }
  194. .list-item {
  195. margin-bottom: 0;
  196. // 设置透明,默认是可视的
  197. opacity: 0;
  198. // 默认超出隐藏,不影响加载中的文字显示效果
  199. overflow: hidden;
  200. height: 0;
  201. &.show {
  202. margin-bottom: 20rpx;
  203. opacity: 1;
  204. overflow: auto;
  205. height: auto;
  206. }
  207. }
  208. }
  209. .h-flex-x {
  210. display: flex;
  211. flex-direction: row;
  212. flex-wrap: nowrap;
  213. justify-content: flex-start;
  214. align-items: flex-start;
  215. align-content: flex-start;
  216. &.h-flex-2 {
  217. >view {
  218. width: 50%;
  219. }
  220. }
  221. }
  222. .load-txt {
  223. padding: 0 0 20rpx 0;
  224. text-align: center;
  225. color: #999;
  226. font-size: 24rpx;
  227. }
  228. </style>