s-score-card.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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. import goodsCollectVue from '@/pages/user/goods-collect.vue';
  179. /**
  180. * 订单卡片
  181. *
  182. * @property {String} img - 图片
  183. * @property {String} title - 标题
  184. * @property {Number} titleWidth = 0 - 标题宽度,默认0,单位rpx
  185. * @property {String} skuText - 规格
  186. * @property {String | Number} score - 积分
  187. * @property {String | Number} price - 价格
  188. * @property {String | Number} originalPrice - 单购价
  189. * @property {String} priceColor - 价格颜色
  190. * @property {Number | String} num - 数量
  191. *
  192. */
  193. const props = defineProps({
  194. goodsFields: {
  195. type: [Array, Object],
  196. default() {
  197. return {
  198. title: { show: true },
  199. subtitle: { show: true },
  200. price: { show: true },
  201. original_price: { show: true },
  202. sales: { show: true },
  203. stock: { show: true },
  204. };
  205. },
  206. },
  207. tagStyle: {
  208. type: Object,
  209. default: {},
  210. },
  211. data: {
  212. type: Object,
  213. default: {},
  214. },
  215. size: {
  216. type: String,
  217. default: 'sl',
  218. },
  219. background: {
  220. type: String,
  221. default: '',
  222. },
  223. topRadius: {
  224. type: Number,
  225. default: 0,
  226. },
  227. bottomRadius: {
  228. type: Number,
  229. default: 0,
  230. },
  231. titleWidth: {
  232. type: Number,
  233. default: 0,
  234. },
  235. titleColor: {
  236. type: String,
  237. default: '#333',
  238. },
  239. priceUnit: {
  240. type: String,
  241. default: '¥',
  242. },
  243. subTitleColor: {
  244. type: String,
  245. default: '#999999',
  246. },
  247. });
  248. // 组件样式
  249. const elStyles = computed(() => {
  250. return {
  251. background: props.background,
  252. 'border-top-left-radius': props.topRadius + 'px',
  253. 'border-top-right-radius': props.topRadius + 'px',
  254. 'border-bottom-left-radius': props.bottomRadius + 'px',
  255. 'border-bottom-right-radius': props.bottomRadius + 'px',
  256. };
  257. });
  258. const emits = defineEmits(['click', 'getHeight']);
  259. const onClick = () => {
  260. emits('click');
  261. };
  262. // 格式化销量、库存信息
  263. const salesAndStock = computed(() => {
  264. let text = [];
  265. text.push(formatSales(props.data.sales_show_type, props.data.sales));
  266. text.push(formatStock(props.data.stock_show_type, props.data.stock));
  267. return text.join(' | ');
  268. });
  269. // 获取实时卡片高度
  270. const { proxy } = getCurrentInstance();
  271. const elId = `sheep_${Math.ceil(Math.random() * 10e5).toString(36)}`;
  272. function calculatePanelHeight(e) {
  273. if (props.size === 'md') {
  274. const view = uni.createSelectorQuery().in(proxy);
  275. view.select(`#${elId}`).fields({ size: true, scrollOffset: true });
  276. view.exec((data) => {
  277. const goodsPriceCard = data[0];
  278. const card = {
  279. width: goodsPriceCard.width,
  280. height: (goodsPriceCard.width / e.detail.width) * e.detail.height + goodsPriceCard.height,
  281. };
  282. emits('getHeight', card.height);
  283. });
  284. }
  285. }
  286. </script>
  287. <style lang="scss" scoped>
  288. .price-unit {
  289. margin-right: -4px;
  290. }
  291. .sales-text {
  292. display: table;
  293. font-size: 24rpx;
  294. transform: scale(0.8);
  295. margin-left: -16rpx;
  296. color: #c4c4c4;
  297. }
  298. // md
  299. .md-goods-card {
  300. overflow: hidden;
  301. width: 100%;
  302. position: relative;
  303. z-index: 1;
  304. background-color: $white;
  305. position: relative;
  306. .md-img-box {
  307. width: 100%;
  308. }
  309. .md-goods-title {
  310. font-size: 26rpx;
  311. color: #333;
  312. width: 100%;
  313. }
  314. .md-goods-subtitle {
  315. font-size: 24rpx;
  316. font-weight: 400;
  317. color: #999999;
  318. }
  319. .md-goods-price {
  320. font-size: 30rpx;
  321. color: $red;
  322. line-height: 36rpx;
  323. }
  324. .cart-box {
  325. width: 54rpx;
  326. height: 54rpx;
  327. background: linear-gradient(90deg, #fe8900, #ff5e00);
  328. border-radius: 50%;
  329. position: absolute;
  330. bottom: 50rpx;
  331. right: 20rpx;
  332. z-index: 2;
  333. .cart-icon {
  334. width: 30rpx;
  335. height: 30rpx;
  336. }
  337. }
  338. }
  339. // lg
  340. .lg-goods-card {
  341. overflow: hidden;
  342. position: relative;
  343. z-index: 1;
  344. background-color: $white;
  345. height: 280rpx;
  346. .lg-img-box {
  347. width: 280rpx;
  348. height: 280rpx;
  349. margin-right: 20rpx;
  350. }
  351. .lg-goods-title {
  352. font-size: 28rpx;
  353. font-weight: 500;
  354. color: #333333;
  355. // line-height: 36rpx;
  356. // width: 410rpx;
  357. }
  358. .lg-goods-subtitle {
  359. font-size: 24rpx;
  360. font-weight: 400;
  361. color: #999999;
  362. line-height: 30rpx;
  363. // width: 410rpx;
  364. }
  365. .lg-goods-price {
  366. font-size: 30rpx;
  367. color: $red;
  368. line-height: 36rpx;
  369. }
  370. .buy-box {
  371. position: absolute;
  372. bottom: 20rpx;
  373. right: 20rpx;
  374. z-index: 2;
  375. width: 120rpx;
  376. height: 50rpx;
  377. background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
  378. border-radius: 25rpx;
  379. font-size: 24rpx;
  380. color: #ffffff;
  381. }
  382. .tag-box {
  383. width: 100%;
  384. }
  385. }
  386. .sl-goods-card {
  387. overflow: hidden;
  388. position: relative;
  389. z-index: 1;
  390. width: 100%;
  391. background-color: $white;
  392. .sl-img-box {
  393. width: 100%;
  394. height: 360rpx;
  395. }
  396. .sl-goods-title {
  397. font-size: 26rpx;
  398. color: #333;
  399. width: 100%;
  400. box-sizing: border-box;
  401. }
  402. .sl-goods-subtitle {
  403. font-size: 24rpx;
  404. font-weight: 400;
  405. color: #999999;
  406. line-height: 30rpx;
  407. width: 100%;
  408. box-sizing: border-box;
  409. }
  410. .sl-goods-price {
  411. font-size: 30rpx;
  412. color: $red;
  413. }
  414. .buy-box {
  415. position: absolute;
  416. bottom: 20rpx;
  417. right: 20rpx;
  418. z-index: 2;
  419. width: 148rpx;
  420. height: 50rpx;
  421. background: linear-gradient(90deg, #fe8900, #ff5e00);
  422. border-radius: 25rpx;
  423. font-size: 24rpx;
  424. color: #ffffff;
  425. }
  426. }
  427. .goods-origin-price {
  428. font-size: 20rpx;
  429. color: #c4c4c4;
  430. text-decoration: line-through;
  431. }
  432. .score-img {
  433. width: 36rpx;
  434. height: 36rpx;
  435. margin: 0 4rpx;
  436. }
  437. </style>