u-waterfall.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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="themeObject"
  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" :class="theme + '-m-text ' + theme">{{item.price_content}}</view>
  26. <view class="sales">{{item.sales}}</view>
  27. </view>
  28. <view :class="theme + '-m-back app-button-icon ' + theme" 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="themeObject"
  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" :class="theme + '-m-text ' + theme">{{item.price_content}}</view>
  56. <view class="sales">{{item.sales}}</view>
  57. </view>
  58. <view :class="theme + '-m-back app-button-icon ' + theme" 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.attr_groups" :theme="theme" :show="attrShow"></app-attr>
  64. </view>
  65. </template>
  66. <script>
  67. import appSupVip from '../../components/page-component/app-sup-vip/app-sup-vip.vue';
  68. import appMemberPrice from '../../components/page-component/app-member-mark/app-member-price.vue';
  69. import appAttr from '../../components/page-component/app-attr/app-attr.vue';
  70. import {mapState} from "vuex";
  71. export default {
  72. name: "u-waterfall",
  73. props: {
  74. value: {
  75. type: Array,
  76. required: true,
  77. default: function() {
  78. return [];
  79. }
  80. },
  81. addTime: {
  82. type: [Number, String],
  83. default: 200
  84. },
  85. idKey: {
  86. type: String,
  87. default: 'id'
  88. },
  89. theme: String,
  90. themeObject: Object
  91. },
  92. provide() {
  93. return {
  94. uWaterfall: this
  95. }
  96. },
  97. data() {
  98. return {
  99. leftList: [],
  100. rightList: [],
  101. tempList: [],
  102. children: [],
  103. attrShow: 0,
  104. goods: null
  105. }
  106. },
  107. watch: {
  108. copyFlowList(nVal, oVal) {
  109. let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
  110. this.tempList = this.tempList.concat(this.cloneData(nVal.slice(startIndex)));
  111. this.splitData();
  112. }
  113. },
  114. mounted() {
  115. this.tempList = this.cloneData(this.copyFlowList);
  116. this.splitData();
  117. },
  118. computed: {
  119. copyFlowList() {
  120. return this.cloneData(this.value);
  121. }
  122. },
  123. methods: {
  124. async splitData() {
  125. if (!this.tempList.length) return;
  126. let leftRect = await this.uGetRect('#u-left-column');
  127. let rightRect = await this.uGetRect('#u-right-column');
  128. let item = this.tempList[0];
  129. if(!item) return ;
  130. if (leftRect.height < rightRect.height) {
  131. this.leftList.push(item);
  132. } else if (leftRect.height > rightRect.height) {
  133. this.rightList.push(item);
  134. } else {
  135. if (this.leftList.length <= this.rightList.length) {
  136. this.leftList.push(item);
  137. } else {
  138. this.rightList.push(item);
  139. }
  140. }
  141. this.tempList.splice(0, 1);
  142. if (this.tempList.length) {
  143. setTimeout(() => {
  144. this.splitData();
  145. }, this.addTime);
  146. }
  147. },
  148. cloneData(data) {
  149. return JSON.parse(JSON.stringify(data));
  150. },
  151. uGetRect(element) {
  152. return new Promise((resolve) => {
  153. const query = uni.createSelectorQuery().in(this);
  154. query.select(element).boundingClientRect(data => {
  155. resolve(data);
  156. }).exec();
  157. })
  158. },
  159. buy(item) {
  160. this.goods = item;
  161. this.attrShow = Math.random();
  162. },
  163. route(item) {
  164. uni.navigateTo({
  165. url: item.page_url
  166. });
  167. },
  168. emptyList() {
  169. this.leftList = [];
  170. this.tempList = [];
  171. this.rightList = [];
  172. }
  173. },
  174. components: {
  175. appMemberPrice,
  176. appSupVip,
  177. appAttr
  178. }
  179. }
  180. </script>
  181. <style lang="scss" scoped>
  182. .u-waterfall {
  183. display: flex;
  184. flex-direction: row;
  185. align-items: flex-start;
  186. }
  187. .u-column {
  188. display: flex;
  189. flex: 1;
  190. flex-direction: column;
  191. height: auto;
  192. }
  193. .u-image {
  194. width: 100%;
  195. }
  196. .u-right-column {
  197. margin-right: 24upx;
  198. margin-left: 7upx;
  199. }
  200. .u-left-column {
  201. margin-left: 24upx;
  202. margin-right: 7upx;
  203. }
  204. .demo-image {
  205. width: 344rpx;
  206. }
  207. .goods-item {
  208. width: 344rpx;
  209. background-color: #fff;
  210. margin-top: 20upx;
  211. overflow: hidden;
  212. border-radius: 16upx;
  213. }
  214. .goods-title {
  215. font-size: 26upx;
  216. color: #373737;
  217. padding: 0 20upx;
  218. }
  219. .goods-vip {
  220. padding: 0 20upx;
  221. margin-top: 12upx;
  222. }
  223. .goods-content {
  224. padding: 0 20upx;
  225. margin: 12upx 0 28upx 0;
  226. }
  227. .price {
  228. font-size: 22upx;
  229. }
  230. .sales {
  231. font-size: 18upx;
  232. color: #b0b0b0;
  233. }
  234. .app-button-icon {
  235. width: 40rpx;
  236. height: 40rpx;
  237. display: block;
  238. background-repeat: no-repeat;
  239. background-size: cover;
  240. background-position: center;
  241. background-image: url('../../static/image/icon/goods-cart.png');
  242. }
  243. </style>