u-waterfall.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <template>
  2. <view class="u-waterfall">
  3. <view id="u-left-column" class="u-column u-left-column">
  4. <view v-for="(item, index) in leftList" :key="index" class="goods-item" @click="route(item)">
  5. <view class="demo-img-wrap">
  6. <image class="demo-image" :src="item.cover_pic" mode="widthFix"></image>
  7. </view>
  8. <view class="goods-title t-omit-two">
  9. {{item.name}}
  10. </view>
  11. <view class="goods-vip" v-if="item.is_level == 1 && item.is_negotiable != 1">
  12. <app-member-price
  13. :price="item.level_price"
  14. :theme="theme"
  15. ></app-member-price>
  16. </view>
  17. <view class="goods-vip" v-if="item.vip_card_appoint.discount">
  18. <app-sup-vip
  19. :discount="item.vip_card_appoint.discount"
  20. :is_vip_card_user="item.vip_card_appoint.is_vip_card_user"
  21. ></app-sup-vip>
  22. </view>
  23. <view class="goods-content dir-left-nowrap main-between">
  24. <view>
  25. <view class="price" :style="{'color': theme.color}">{{item.price_content}}</view>
  26. <view class="sales">{{item.sales}}</view>
  27. </view>
  28. <view :style="{'background-color': theme.background}" :class="'app-button-icon'" v-if="item.goods_stock !== 0" @click.stop="buy(item)">
  29. </view>
  30. </view>
  31. </view>
  32. </view>
  33. <view id="u-right-column" class="u-column u-right-column">
  34. <view v-for="(item, index) in rightList" :key="index" class="goods-item" @click="route(item)" >
  35. <view class="demo-img-wrap">
  36. <image class="demo-image" :src="item.cover_pic" mode="widthFix"></image>
  37. </view>
  38. <view class="goods-title t-omit-two">
  39. {{item.name}}
  40. </view>
  41. <view class="goods-vip" v-if="item.is_level == 1 && item.is_negotiable != 1">
  42. <app-member-price
  43. :price="item.level_price"
  44. :theme="theme"
  45. ></app-member-price>
  46. </view>
  47. <view class="goods-vip" v-if="item.vip_card_appoint.discount">
  48. <app-sup-vip
  49. :discount="item.vip_card_appoint.discount"
  50. :is_vip_card_user="item.vip_card_appoint.is_vip_card_user"
  51. ></app-sup-vip>
  52. </view>
  53. <view class="goods-content dir-left-nowrap main-between">
  54. <view>
  55. <view class="price" :style="{'color': theme.color}">{{item.price_content}}</view>
  56. <view class="sales">{{item.sales}}</view>
  57. </view>
  58. <view :style="{'background-color': theme.background}" :class="'app-button-icon'" v-if="item.goods_stock !== 0" @click.stop="buy(item)">
  59. </view>
  60. </view>
  61. </view>
  62. </view>
  63. <app-attr :goods="goods" :attrGroupList="goods && goods.attr_groups" :theme="theme" :show="attrShow"></app-attr>
  64. </view>
  65. </template>
  66. <script>
  67. import appAttr from '../../components/page-component/app-attr/app-attr.vue';
  68. export default {
  69. name: "u-waterfall",
  70. props: {
  71. value: {
  72. type: Array,
  73. required: true,
  74. default: function() {
  75. return [];
  76. }
  77. },
  78. addTime: {
  79. type: [Number, String],
  80. default: 200
  81. },
  82. idKey: {
  83. type: String,
  84. default: 'id'
  85. },
  86. theme: Object
  87. },
  88. provide() {
  89. return {
  90. uWaterfall: this
  91. }
  92. },
  93. data() {
  94. return {
  95. leftList: [],
  96. rightList: [],
  97. tempList: [],
  98. children: [],
  99. attrShow: 0,
  100. goods: null
  101. }
  102. },
  103. watch: {
  104. copyFlowList(nVal, oVal) {
  105. let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
  106. this.tempList = this.tempList.concat(this.cloneData(nVal.slice(startIndex)));
  107. this.splitData();
  108. }
  109. },
  110. mounted() {
  111. this.tempList = this.cloneData(this.copyFlowList);
  112. this.splitData();
  113. },
  114. computed: {
  115. copyFlowList() {
  116. return this.cloneData(this.value);
  117. }
  118. },
  119. methods: {
  120. async splitData() {
  121. if (!this.tempList.length) return;
  122. let leftRect = await this.uGetRect('#u-left-column');
  123. let rightRect = await this.uGetRect('#u-right-column');
  124. let item = this.tempList[0];
  125. if(!item) return ;
  126. if (leftRect && leftRect.height < rightRect && rightRect.height) {
  127. this.leftList.push(item);
  128. } else if (leftRect && leftRect.height > rightRect && rightRect.height) {
  129. this.rightList.push(item);
  130. } else {
  131. if (this.leftList.length <= this.rightList.length) {
  132. this.leftList.push(item);
  133. } else {
  134. this.rightList.push(item);
  135. }
  136. }
  137. this.tempList.splice(0, 1);
  138. if (this.tempList.length) {
  139. setTimeout(() => {
  140. this.splitData();
  141. }, this.addTime);
  142. }
  143. },
  144. cloneData(data) {
  145. return JSON.parse(JSON.stringify(data));
  146. },
  147. uGetRect(element) {
  148. return new Promise((resolve) => {
  149. const query = uni.createSelectorQuery().in(this);
  150. query.select(element).boundingClientRect(data => {
  151. resolve(data);
  152. }).exec();
  153. })
  154. },
  155. buy(item) {
  156. this.goods = item;
  157. this.attrShow = Math.random();
  158. },
  159. route(item) {
  160. uni.navigateTo({
  161. url: item.page_url
  162. });
  163. },
  164. emptyList() {
  165. this.leftList = [];
  166. this.tempList = [];
  167. this.rightList = [];
  168. }
  169. },
  170. components: {
  171. appAttr
  172. }
  173. }
  174. </script>
  175. <style lang="scss" scoped>
  176. .u-waterfall {
  177. display: flex;
  178. flex-direction: row;
  179. align-items: flex-start;
  180. }
  181. .u-column {
  182. display: flex;
  183. flex: 1;
  184. flex-direction: column;
  185. height: auto;
  186. }
  187. .u-image {
  188. width: 100%;
  189. }
  190. .u-right-column {
  191. margin-right: 24upx;
  192. margin-left: 7upx;
  193. }
  194. .u-left-column {
  195. margin-left: 24upx;
  196. margin-right: 7upx;
  197. }
  198. .demo-image {
  199. width: 344rpx;
  200. }
  201. .goods-item {
  202. width: 344rpx;
  203. background-color: #fff;
  204. margin-top: 20upx;
  205. overflow: hidden;
  206. border-radius: 16upx;
  207. }
  208. .goods-title {
  209. font-size: 26upx;
  210. color: #373737;
  211. padding: 0 20upx;
  212. }
  213. .goods-vip {
  214. padding: 0 20upx;
  215. margin-top: 12upx;
  216. }
  217. .goods-content {
  218. padding: 0 20upx;
  219. margin: 12upx 0 28upx 0;
  220. }
  221. .price {
  222. font-size: 22upx;
  223. }
  224. .sales {
  225. font-size: 18upx;
  226. color: #b0b0b0;
  227. }
  228. .app-button-icon {
  229. width: 40rpx;
  230. height: 40rpx;
  231. display: block;
  232. background-repeat: no-repeat;
  233. background-size: cover;
  234. background-position: center;
  235. background-image: url('../../static/image/icon/goods-cart.png');
  236. }
  237. </style>