s-goods-card.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <template>
  2. <!-- 商品卡片 -->
  3. <view>
  4. <!-- 布局1. 单列大图(上图,下内容)-->
  5. <view v-if="layoutType === LayoutTypeEnum.ONE_COL_BIG_IMG && state.goodsList.length" class="goods-sl-box">
  6. <view
  7. class="goods-box"
  8. v-for="item in state.goodsList"
  9. :key="item.id"
  10. :style="[{ marginBottom: data.space * 2 + 'rpx' }]"
  11. >
  12. <s-goods-column
  13. class=""
  14. size="sl"
  15. :goodsFields="data.fields"
  16. :tagStyle="data.badge"
  17. :data="item"
  18. :titleColor="data.fields.name?.color"
  19. :subTitleColor="data.fields.introduction.color"
  20. :topRadius="data.borderRadiusTop"
  21. :bottomRadius="data.borderRadiusBottom"
  22. @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
  23. >
  24. <!-- 购买按钮 -->
  25. <template v-slot:cart>
  26. <button class="ss-reset-button cart-btn" :style="[buyStyle]">
  27. {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
  28. </button>
  29. </template>
  30. </s-goods-column>
  31. </view>
  32. </view>
  33. <!-- 布局2. 双列(每一列:上图,下内容)-->
  34. <view
  35. v-if="layoutType === LayoutTypeEnum.TWO_COL && state.goodsList.length"
  36. class="goods-md-wrap ss-flex ss-flex-wrap ss-col-top"
  37. >
  38. <view class="goods-list-box">
  39. <view
  40. class="left-list"
  41. :style="[{ paddingRight: data.space + 'rpx', marginBottom: data.space + 'px' }]"
  42. v-for="item in state.leftGoodsList"
  43. :key="item.id"
  44. >
  45. <s-goods-column
  46. class="goods-md-box"
  47. size="md"
  48. :goodsFields="data.fields"
  49. :tagStyle="data.badge"
  50. :data="item"
  51. :titleColor="data.fields.name?.color"
  52. :subTitleColor="data.fields.introduction.color"
  53. :topRadius="data.borderRadiusTop"
  54. :bottomRadius="data.borderRadiusBottom"
  55. :titleWidth="330 - marginLeft - marginRight"
  56. @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
  57. @getHeight="calculateGoodsColumn($event, 'left')"
  58. >
  59. <!-- 购买按钮 -->
  60. <template v-slot:cart>
  61. <button class="ss-reset-button cart-btn" :style="[buyStyle]">
  62. {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
  63. </button>
  64. </template>
  65. </s-goods-column>
  66. </view>
  67. </view>
  68. <view class="goods-list-box">
  69. <view
  70. class="right-list"
  71. :style="[{ paddingLeft: data.space + 'rpx', marginBottom: data.space + 'px' }]"
  72. v-for="item in state.rightGoodsList"
  73. :key="item.id"
  74. >
  75. <s-goods-column
  76. class="goods-md-box"
  77. size="md"
  78. :goodsFields="data.fields"
  79. :tagStyle="data.badge"
  80. :data="item"
  81. :titleColor="data.fields.name?.color"
  82. :subTitleColor="data.fields.introduction.color"
  83. :topRadius="data.borderRadiusTop"
  84. :bottomRadius="data.borderRadiusBottom"
  85. :titleWidth="330 - marginLeft - marginRight"
  86. @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
  87. @getHeight="calculateGoodsColumn($event, 'right')"
  88. >
  89. <!-- 购买按钮 -->
  90. <template v-slot:cart>
  91. <button class="ss-reset-button cart-btn" :style="[buyStyle]">
  92. {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
  93. </button>
  94. </template>
  95. </s-goods-column>
  96. </view>
  97. </view>
  98. </view>
  99. <!-- 布局3. 单列小图(左图,右内容) -->
  100. <view v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.goodsList.length" class="goods-lg-box">
  101. <view
  102. class="goods-box"
  103. :style="[{ marginBottom: data.space + 'px' }]"
  104. v-for="item in state.goodsList"
  105. :key="item.id"
  106. >
  107. <s-goods-column
  108. class="goods-card"
  109. size="lg"
  110. :goodsFields="data.fields"
  111. :data="item"
  112. :tagStyle="data.badge"
  113. :titleColor="data.fields.name?.color"
  114. :subTitleColor="data.fields.introduction.color"
  115. :topRadius="data.borderRadiusTop"
  116. :bottomRadius="data.borderRadiusBottom"
  117. @tap="sheep.$router.go('/pages/goods/index', { id: item.id })"
  118. >
  119. <!-- 购买按钮 -->
  120. <template v-slot:cart>
  121. <button class="ss-reset-button cart-btn" :style="[buyStyle]">
  122. {{ btnBuy.type === 'text' ? btnBuy.text : '' }}
  123. </button>
  124. </template>
  125. </s-goods-column>
  126. </view>
  127. </view>
  128. </view>
  129. </template>
  130. <script setup>
  131. /**
  132. * 商品卡片
  133. */
  134. import { computed, reactive, onMounted } from 'vue';
  135. import sheep from '@/sheep';
  136. import SpuApi from '@/sheep/api/product/spu';
  137. // 布局类型
  138. const LayoutTypeEnum = {
  139. // 单列大图
  140. ONE_COL_BIG_IMG: 'oneColBigImg',
  141. // 双列
  142. TWO_COL: 'twoCol',
  143. // 单列小图
  144. ONE_COL_SMALL_IMG: 'oneColSmallImg',
  145. }
  146. const state = reactive({
  147. goodsList: [],
  148. leftGoodsList: [],
  149. rightGoodsList: [],
  150. });
  151. const props = defineProps({
  152. data: {
  153. type: Object,
  154. default() {},
  155. },
  156. styles: {
  157. type: Object,
  158. default() {},
  159. },
  160. });
  161. const { layoutType, btnBuy, spuIds } = props.data ?? {};
  162. const { marginLeft, marginRight } = props.styles ?? {};
  163. // 购买按钮样式
  164. const buyStyle = computed(() => {
  165. if (btnBuy.type === 'text') {
  166. // 文字按钮:线性渐变背景颜色
  167. return {
  168. background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
  169. };
  170. }
  171. if (btnBuy.type === 'img') {
  172. // 图片按钮
  173. return {
  174. width: '54rpx',
  175. height: '54rpx',
  176. background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
  177. backgroundSize: '100% 100%',
  178. };
  179. }
  180. });
  181. //region 商品瀑布流布局
  182. // 下一个要处理的商品索引
  183. let count = 0;
  184. // 左列的高度
  185. let leftHeight = 0;
  186. // 右列的高度
  187. let rightHeight = 0;
  188. /**
  189. * 计算商品在左列还是右列
  190. * @param height 商品的高度
  191. * @param where 添加到哪一列
  192. */
  193. function calculateGoodsColumn(height = 0, where = 'left') {
  194. // 处理完
  195. if (!state.goodsList[count]) return;
  196. // 增加列的高度
  197. if (where === 'left') leftHeight += height;
  198. if (where === 'right') rightHeight += height;
  199. // 添加到矮的一列
  200. if (leftHeight <= rightHeight) {
  201. state.leftGoodsList.push(state.goodsList[count]);
  202. } else {
  203. state.rightGoodsList.push(state.goodsList[count]);
  204. }
  205. // 计数
  206. count++;
  207. }
  208. //endregion
  209. /**
  210. * 根据商品编号列表,获取商品列表
  211. * @param ids 商品编号列表
  212. * @return {Promise<undefined>} 商品列表
  213. */
  214. async function getGoodsListByIds(ids) {
  215. const { data } = await SpuApi.getSpuListByIds(ids);
  216. return data;
  217. }
  218. // 初始化
  219. onMounted(async () => {
  220. // 加载商品列表
  221. state.goodsList = await getGoodsListByIds(spuIds.join(','));
  222. // 只有双列布局时需要
  223. if (layoutType === LayoutTypeEnum.TWO_COL){
  224. // 分列
  225. calculateGoodsColumn();
  226. }
  227. });
  228. </script>
  229. <style lang="scss" scoped>
  230. .goods-md-wrap {
  231. width: 100%;
  232. }
  233. .goods-list-box {
  234. width: 50%;
  235. box-sizing: border-box;
  236. .left-list {
  237. &:nth-last-child(1) {
  238. margin-bottom: 0 !important;
  239. }
  240. }
  241. .right-list {
  242. &:nth-last-child(1) {
  243. margin-bottom: 0 !important;
  244. }
  245. }
  246. }
  247. .goods-box {
  248. &:nth-last-of-type(1) {
  249. margin-bottom: 0 !important;
  250. }
  251. }
  252. .goods-md-box,
  253. .goods-sl-box,
  254. .goods-lg-box {
  255. position: relative;
  256. .cart-btn {
  257. position: absolute;
  258. bottom: 18rpx;
  259. right: 20rpx;
  260. z-index: 11;
  261. height: 50rpx;
  262. line-height: 50rpx;
  263. padding: 0 20rpx;
  264. border-radius: 25rpx;
  265. font-size: 24rpx;
  266. color: #fff;
  267. }
  268. }
  269. </style>