s-point-card.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <template>
  2. <view>
  3. <!-- md卡片:竖向,一行放两个,图上内容下 -->
  4. <view v-if="size === 'md'" class="md-goods-card ss-flex-col" :style="[elStyles]" @tap="onClick">
  5. <image
  6. class="md-img-box"
  7. :src="sheep.$url.cdn(data.image)"
  8. mode="widthFix"
  9. @load="calculatePanelHeight"
  10. ></image>
  11. <view
  12. class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
  13. :id="elId"
  14. >
  15. <view
  16. v-if="goodsFields.title?.show"
  17. class="md-goods-title ss-line-1"
  18. :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
  19. >
  20. {{ data.title }}
  21. </view>
  22. <view
  23. v-if="goodsFields.subtitle?.show"
  24. class="md-goods-subtitle ss-m-t-16 ss-line-1"
  25. :style="[{ color: subTitleColor }]"
  26. >
  27. {{ data.subtitle }}
  28. </view>
  29. <view class="ss-col-bottom">
  30. <view
  31. v-if="goodsFields.score_price?.show"
  32. class="md-goods-price ss-m-t-16 font-OPPOSANS ss-m-r-10 ss-flex"
  33. :style="[{ color: goodsFields.score_price.color }]"
  34. >
  35. <view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
  36. <image
  37. :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
  38. class="score-img"
  39. ></image>
  40. {{ data.score }}
  41. </view>
  42. <view
  43. v-if="goodsFields.price?.show && data.original_price > 0"
  44. class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
  45. :style="[{ color: goodsFields.price.color }]"
  46. >
  47. <text class="price-unit ss-font-20">{{ priceUnit }}</text>
  48. <view class="ss-m-l-8">{{ data.original_price }}</view>
  49. </view>
  50. </view>
  51. <view class="ss-m-t-16 ss-flex ss-col-center ss-flex-wrap">
  52. <view class="sales-text">{{ salesAndStock }}</view>
  53. </view>
  54. </view>
  55. <slot name="cart">
  56. <view class="cart-box ss-flex ss-col-center ss-row-center">
  57. <image class="cart-icon" src="/static/img/shop/tabbar/category2.png" mode=""></image>
  58. </view>
  59. </slot>
  60. </view>
  61. <!-- lg卡片:横向型,一行放一个,图片左内容右边 -->
  62. <view
  63. v-if="size === 'lg'"
  64. class="lg-goods-card ss-flex ss-col-stretch"
  65. :style="[elStyles]"
  66. @tap="onClick"
  67. >
  68. <image class="lg-img-box" :src="sheep.$url.cdn(data.image)" mode="aspectFill"></image>
  69. <view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
  70. <view class="ss-m-r-20">
  71. <view
  72. v-if="goodsFields.title?.show"
  73. class="lg-goods-title ss-line-2"
  74. :style="[{ color: titleColor }]"
  75. >
  76. {{ data.title }}
  77. </view>
  78. <view
  79. v-if="goodsFields.subtitle?.show"
  80. class="lg-goods-subtitle ss-m-t-10 ss-line-1"
  81. :style="[{ color: subTitleColor }]"
  82. >
  83. {{ data.subtitle }}
  84. </view>
  85. </view>
  86. <view>
  87. <view class="ss-m-t-10">
  88. <view
  89. v-if="goodsFields.score_price?.show"
  90. class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
  91. :style="[{ color: goodsFields.score_price.color }]"
  92. >
  93. <view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
  94. <image
  95. :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
  96. class="score-img"
  97. ></image>
  98. {{ data.score }}
  99. </view>
  100. <view
  101. v-if="goodsFields.price?.show && data.original_price > 0"
  102. class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
  103. :style="[{ color: goodsFields.price.color }]"
  104. >
  105. <text class="price-unit ss-font-20">{{ priceUnit }}</text>
  106. <view class="ss-m-l-8">{{ data.original_price }}</view>
  107. </view>
  108. </view>
  109. <view class="ss-m-t-16 ss-flex ss-col-center ss-flex-wrap">
  110. <view class="sales-text">{{ salesAndStock }}</view>
  111. </view>
  112. </view>
  113. </view>
  114. <slot name="cart"
  115. ><view class="buy-box ss-flex ss-col-center ss-row-center">去兑换</view></slot
  116. >
  117. </view>
  118. <!-- sl卡片:竖向型,一行放一个,图片上内容下边 -->
  119. <view v-if="size === 'sl'" class="sl-goods-card ss-flex-col" @tap="onClick">
  120. <image class="sl-img-box" :src="sheep.$url.cdn(data.image)" mode="aspectFill"></image>
  121. <view class="sl-goods-content ss-flex-col ss-row-between ss-p-b-20 ss-p-t-20">
  122. <view class="ss-m-b-20">
  123. <view class="sl-goods-title ss-line-1 ss-p-l-16 ss-p-r-16">
  124. {{ data.title }}
  125. </view>
  126. <view v-if="data.subtitle" class="sl-goods-subtitle ss-p-l-16 ss-p-r-16 ss-m-t-16">
  127. {{ data.subtitle }}
  128. </view>
  129. </view>
  130. <view>
  131. <slot name="activity">
  132. <view
  133. v-if="data.promos?.length"
  134. class="tag-box ss-flex ss-col-center ss-flex-wrap ss-p-l-16 ss-p-r-16"
  135. >
  136. <view
  137. class="activity-tag ss-m-r-10 ss-m-t-16"
  138. v-for="item in data.promos"
  139. :key="item.id"
  140. >
  141. {{ item.title }}
  142. </view>
  143. </view>
  144. </slot>
  145. <view class="ss-flex ss-col-bottom ss-p-l-16 ss-p-r-16 font-OPPOSANS">
  146. <view class="sl-goods-price ss-m-r-12 ss-flex">
  147. <view>{{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}</view>
  148. <image
  149. :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
  150. class="score-img"
  151. ></image>
  152. <view>{{ data.score ? data.score : '' }}</view>
  153. </view>
  154. <view
  155. v-if="data.original_price > 0"
  156. class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
  157. >
  158. <text class="price-unit ss-font-20">¥</text>
  159. <view class="ss-m-l-8">{{ data.original_price }}</view>
  160. </view>
  161. </view>
  162. <view class="ss-p-l-16 ss-p-r-16 ss-m-t-16 ss-flex ss-flex-wrap">
  163. <view class="sales-text">{{ salesAndStock }}</view>
  164. </view>
  165. </view>
  166. </view>
  167. <slot name="cart"
  168. ><view class="buy-box ss-flex ss-col-center ss-row-center">去兑换</view></slot
  169. >
  170. </view>
  171. </view>
  172. </template>
  173. <script setup>
  174. import { computed, getCurrentInstance } from 'vue';
  175. import sheep from '@/sheep';
  176. import { formatSales } from '@/sheep/hooks/useGoods';
  177. import { formatStock } from '@/sheep/hooks/useGoods';
  178. /**
  179. * 订单卡片
  180. *
  181. * @property {String} img - 图片
  182. * @property {String} title - 标题
  183. * @property {Number} titleWidth = 0 - 标题宽度,默认0,单位rpx
  184. * @property {String} skuText - 规格
  185. * @property {String | Number} score - 积分
  186. * @property {String | Number} price - 价格
  187. * @property {String | Number} originalPrice - 单购价
  188. * @property {String} priceColor - 价格颜色
  189. * @property {Number | String} num - 数量
  190. *
  191. */
  192. const props = defineProps({
  193. goodsFields: {
  194. type: [Array, Object],
  195. default() {
  196. return {
  197. title: { show: true },
  198. subtitle: { show: true },
  199. price: { show: true },
  200. original_price: { show: true },
  201. sales: { show: true },
  202. stock: { show: true },
  203. };
  204. },
  205. },
  206. tagStyle: {
  207. type: Object,
  208. default: {},
  209. },
  210. data: {
  211. type: Object,
  212. default: {},
  213. },
  214. size: {
  215. type: String,
  216. default: 'sl',
  217. },
  218. background: {
  219. type: String,
  220. default: '',
  221. },
  222. topRadius: {
  223. type: Number,
  224. default: 0,
  225. },
  226. bottomRadius: {
  227. type: Number,
  228. default: 0,
  229. },
  230. titleWidth: {
  231. type: Number,
  232. default: 0,
  233. },
  234. titleColor: {
  235. type: String,
  236. default: '#333',
  237. },
  238. priceUnit: {
  239. type: String,
  240. default: '¥',
  241. },
  242. subTitleColor: {
  243. type: String,
  244. default: '#999999',
  245. },
  246. });
  247. // 组件样式
  248. const elStyles = computed(() => {
  249. return {
  250. background: props.background,
  251. 'border-top-left-radius': props.topRadius + 'px',
  252. 'border-top-right-radius': props.topRadius + 'px',
  253. 'border-bottom-left-radius': props.bottomRadius + 'px',
  254. 'border-bottom-right-radius': props.bottomRadius + 'px',
  255. };
  256. });
  257. const emits = defineEmits(['click', 'getHeight']);
  258. const onClick = () => {
  259. emits('click');
  260. };
  261. // 格式化销量、库存信息
  262. const salesAndStock = computed(() => {
  263. let text = [];
  264. text.push(formatSales(props.data.sales_show_type, props.data.sales));
  265. text.push(formatStock(props.data.stock_show_type, props.data.stock));
  266. return text.join(' | ');
  267. });
  268. // 获取实时卡片高度
  269. const { proxy } = getCurrentInstance();
  270. const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
  271. function calculatePanelHeight(e) {
  272. if (props.size === 'md') {
  273. const view = uni.createSelectorQuery().in(proxy);
  274. view.select(`#${elId}`).fields({ size: true, scrollOffset: true });
  275. view.exec((data) => {
  276. const goodsPriceCard = data[0];
  277. const card = {
  278. width: goodsPriceCard.width,
  279. height: (goodsPriceCard.width / e.detail.width) * e.detail.height + goodsPriceCard.height,
  280. };
  281. emits('getHeight', card.height);
  282. });
  283. }
  284. }
  285. </script>
  286. <style lang="scss" scoped>
  287. .price-unit {
  288. margin-right: -4px;
  289. }
  290. .sales-text {
  291. display: table;
  292. font-size: 24rpx;
  293. transform: scale(0.8);
  294. margin-left: -16rpx;
  295. color: #c4c4c4;
  296. }
  297. // md
  298. .md-goods-card {
  299. overflow: hidden;
  300. width: 100%;
  301. position: relative;
  302. z-index: 1;
  303. background-color: $white;
  304. position: relative;
  305. .md-img-box {
  306. width: 100%;
  307. }
  308. .md-goods-title {
  309. font-size: 26rpx;
  310. color: #333;
  311. width: 100%;
  312. }
  313. .md-goods-subtitle {
  314. font-size: 24rpx;
  315. font-weight: 400;
  316. color: #999999;
  317. }
  318. .md-goods-price {
  319. font-size: 30rpx;
  320. color: $red;
  321. line-height: 36rpx;
  322. }
  323. .cart-box {
  324. width: 54rpx;
  325. height: 54rpx;
  326. background: linear-gradient(90deg, #fe8900, #ff5e00);
  327. border-radius: 50%;
  328. position: absolute;
  329. bottom: 50rpx;
  330. right: 20rpx;
  331. z-index: 2;
  332. .cart-icon {
  333. width: 30rpx;
  334. height: 30rpx;
  335. }
  336. }
  337. }
  338. // lg
  339. .lg-goods-card {
  340. overflow: hidden;
  341. position: relative;
  342. z-index: 1;
  343. background-color: $white;
  344. height: 280rpx;
  345. .lg-img-box {
  346. width: 280rpx;
  347. height: 280rpx;
  348. margin-right: 20rpx;
  349. }
  350. .lg-goods-title {
  351. font-size: 28rpx;
  352. font-weight: 500;
  353. color: #333333;
  354. // line-height: 36rpx;
  355. // width: 410rpx;
  356. }
  357. .lg-goods-subtitle {
  358. font-size: 24rpx;
  359. font-weight: 400;
  360. color: #999999;
  361. line-height: 30rpx;
  362. // width: 410rpx;
  363. }
  364. .lg-goods-price {
  365. font-size: 30rpx;
  366. color: $red;
  367. line-height: 36rpx;
  368. }
  369. .buy-box {
  370. position: absolute;
  371. bottom: 20rpx;
  372. right: 20rpx;
  373. z-index: 2;
  374. width: 120rpx;
  375. height: 50rpx;
  376. background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
  377. border-radius: 25rpx;
  378. font-size: 24rpx;
  379. color: #ffffff;
  380. }
  381. .tag-box {
  382. width: 100%;
  383. }
  384. }
  385. .sl-goods-card {
  386. overflow: hidden;
  387. position: relative;
  388. z-index: 1;
  389. width: 100%;
  390. background-color: $white;
  391. .sl-img-box {
  392. width: 100%;
  393. height: 360rpx;
  394. }
  395. .sl-goods-title {
  396. font-size: 26rpx;
  397. color: #333;
  398. width: 100%;
  399. box-sizing: border-box;
  400. }
  401. .sl-goods-subtitle {
  402. font-size: 24rpx;
  403. font-weight: 400;
  404. color: #999999;
  405. line-height: 30rpx;
  406. width: 100%;
  407. box-sizing: border-box;
  408. }
  409. .sl-goods-price {
  410. font-size: 30rpx;
  411. color: $red;
  412. }
  413. .buy-box {
  414. position: absolute;
  415. bottom: 20rpx;
  416. right: 20rpx;
  417. z-index: 2;
  418. width: 148rpx;
  419. height: 50rpx;
  420. background: linear-gradient(90deg, #fe8900, #ff5e00);
  421. border-radius: 25rpx;
  422. font-size: 24rpx;
  423. color: #ffffff;
  424. }
  425. }
  426. .goods-origin-price {
  427. font-size: 20rpx;
  428. color: #c4c4c4;
  429. text-decoration: line-through;
  430. }
  431. .score-img {
  432. width: 36rpx;
  433. height: 36rpx;
  434. margin: 0 4rpx;
  435. }
  436. </style>