s-goods-card.vue 8.1 KB

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