s-score-card.vue 13 KB

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