s-goods-card.vue 8.0 KB

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