sh-goods-card.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <!-- 横版大图,商品卡片 -->
  2. <template>
  3. <view class="big-goods u-flex u-p-20 u-col-top u-m-b-16" @tap="click"
  4. :style="type ? 'background-image: url(' + $IMG_URL + typeMap[type].goodsBg + ')' : ''">
  5. <image class="goods-img" lazy-load fade-show :src="image" mode="aspectFill"></image>
  6. <view class=" card-right u-m-l-20 u-flex-col u-row-between">
  7. <view class="">
  8. <view class="goods-title u-ellipsis-1 u-m-t-10 u-m-b-10">
  9. <view v-if="type" class=" sm cu-tag radius title-tag u-m-r-10"
  10. :style="{ backgroundColor: typeMap[type].tagBg, color: '#fff' }">{{ typeMap[type].text }}</view>
  11. {{ title }}
  12. </view>
  13. <view v-show="subtitle" class="subtitle-text u-m-b-10 u-ellipsis-1">{{ subtitle }}</view>
  14. </view>
  15. <view class="u-m-b-20" v-if="tagTextList.length">
  16. <view class="tag-box u-m-r-10" v-for="(item, index) in tagTextList" :key="index">{{ item }}</view>
  17. </view>
  18. <view class=" u-flex u-row-between u-col-center">
  19. <view class="font-OPPOSANS">
  20. <view class="price">{{ price }}</view>
  21. <view class="origin-price">{{ originPrice }}</view>
  22. </view>
  23. <!-- 加入购物车 -->
  24. <view class="cart-box">
  25. <!-- 单规格 -->
  26. <view class="" v-if="!detail.is_sku">
  27. <button v-if="!isCart(detail.id)" @tap.stop="addCart(detail.sku_price[0])"
  28. class="u-reset-button buy-btn u-flex u-row-center u-col-center"
  29. :style="type ? typeMap[type].btnBg : ''">
  30. {{ type ? typeMap[type].btnText : '去购买' }}
  31. </button>
  32. <view class="num-step" @tap.stop v-else>
  33. <u-number-box :value="checkCart[detail.id].num" :min="0" :step="1" :long-press="false"
  34. :max="detail.sku_price[0].stock > 999 ? 999 : detail.sku_price[0].stock" @min="onMin"
  35. @plus="plus($event, detail.sku_price[0])"
  36. @change="onChangeNum($event, detail.sku_price[0])"></u-number-box>
  37. </view>
  38. </view>
  39. <!-- 多规格 -->
  40. <button class="u-reset-button item-btn buy-btn" :style="type ? typeMap[type].btnBg : ''"
  41. @tap.stop="selSku(detail)" v-else>
  42. {{ type ? typeMap[type].btnText : '去购买' }}
  43. </button>
  44. </view>
  45. </view>
  46. </view>
  47. <!-- 规格弹窗 -->
  48. <shopro-sku v-if="showSku && goodsInfo.id" v-model="showSku" :goodsInfo="goodsInfo" buyType="cart"></shopro-sku>
  49. </view>
  50. </template>
  51. <script>
  52. /**
  53. *shoproGoodsCard - 商品列表卡片
  54. * @property {Object} detail - 商品详情
  55. * @property {String} type - 商品类型
  56. * @property {String} image - 商品图片
  57. * @property {String} title - 商品标题
  58. * @property {String} subtitle - 商品副标题
  59. * @property {String | Number} price - 商品价格
  60. * @property {String | Number} originPrice - 商品原价
  61. * @property {String | Number} sales - 商品销量
  62. * @property {Array} tagTextList - 活动标签
  63. * @event {Function} click 商品被点击
  64. */
  65. import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
  66. export default {
  67. components: {},
  68. data() {
  69. return {
  70. showSku: false,
  71. goodsInfo: {}, //商品详情
  72. typeMap: {
  73. seckill: {
  74. text: '秒杀',
  75. tagBg: '#FF5854',
  76. goodsBg: '/imgs/tag/seckill_x_bg.png',
  77. btnText: '去抢购',
  78. btnBg: 'background: linear-gradient(90deg, #D01325, #ED3C30);'
  79. },
  80. groupon: {
  81. text: '拼团',
  82. tagBg: '#FE832A',
  83. goodsBg: '/imgs/tag/groupon_x_bg.png',
  84. btnText: '马上拼',
  85. btnBg: 'background: linear-gradient(90deg, #FF6600 0%, #FE832A 100%);'
  86. }
  87. }
  88. };
  89. },
  90. computed: {
  91. ...mapGetters(['cartList', 'checkCart'])
  92. },
  93. props: {
  94. detail: {
  95. type: Object,
  96. default: () => {
  97. return {};
  98. }
  99. },
  100. type: {
  101. type: [String, null],
  102. default: ''
  103. },
  104. image: {
  105. type: String,
  106. default: ''
  107. },
  108. title: {
  109. type: String,
  110. default: ''
  111. },
  112. subtitle: {
  113. type: String,
  114. default: ''
  115. },
  116. price: {
  117. type: [String, Number],
  118. default: ''
  119. },
  120. originPrice: {
  121. type: [String, Number],
  122. default: ''
  123. },
  124. sales: {
  125. type: [String, Number],
  126. default: ''
  127. },
  128. tagTextList: {
  129. type: Array,
  130. default: () => []
  131. }
  132. },
  133. mounted() {},
  134. methods: {
  135. ...mapActions(['getCartList', 'changeCartList', 'addCartGoods']),
  136. //点击商品
  137. click() {
  138. this.$emit('click');
  139. },
  140. // 检测是否为购物车商品
  141. isCart(id) {
  142. return Object.keys(this.checkCart).includes(id + '');
  143. },
  144. // 检测商品在购物车中的下标
  145. checkGoodsIndex(id) {
  146. let cIndex = 0;
  147. this.cartList.forEach((item, index) => {
  148. if (id == item.goods_id) {
  149. cIndex = index;
  150. }
  151. });
  152. return cIndex;
  153. },
  154. // 更改商品数
  155. async onChangeNum(e, sku) {
  156. let gIndex = this.checkGoodsIndex(sku.goods_id);
  157. if (e.value != this.checkCart[sku.goods_id].num) {
  158. uni.showLoading({
  159. mask: true
  160. });
  161. this.$set(this.cartList[gIndex], 'goods_num', +e.value);
  162. await this.changeCartList({
  163. ids: [this.checkCart[sku.goods_id].cartOrderId],
  164. goodsNum: +e.value,
  165. art: 'change'
  166. });
  167. await uni.hideLoading();
  168. }
  169. },
  170. // 到达最小值
  171. onMin() {
  172. const that = this;
  173. let cartGoodId = 0;
  174. cartGoodId = this.cartList.filter(item => item.goods_id === that.detail.id)[0].id;
  175. uni.showModal({
  176. title: '删除提示',
  177. confirmColor: '#f0c785',
  178. content: `是否确认从购物车中删除此商品?`,
  179. success: res => {
  180. res.confirm && this.changeCartList({ ids: [cartGoodId], art: 'delete' });
  181. }
  182. });
  183. },
  184. // 增加
  185. plus(e, sku) {
  186. if (e.value >= sku.stock) {
  187. this.$u.toast('库存不足');
  188. return;
  189. }
  190. if (this.detail.activity_type === 'seckill' || this.detail.activity_type === 'groupon') {
  191. let rules = this.detail.activity.rules;
  192. if (rules.limit_buy != 0 && e.value >= rules.limit_buy) {
  193. this.$u.toast('本次活动最多购买' + rules.limit_buy + '件');
  194. return;
  195. }
  196. }
  197. },
  198. // 添加购物车,多规格
  199. async selSku(info) {
  200. if (this.detail.activity_type) {
  201. this.$Router.push({ path: '/pages/goods/detail', query: { id: this.detail.id } });
  202. return;
  203. }
  204. this.goodsInfo = {};
  205. this.getGoodsDetail(info.id);
  206. this.showSku = true;
  207. },
  208. // 商品详情
  209. getGoodsDetail(id) {
  210. let that = this;
  211. that.$http('goods.detail', {
  212. id: id
  213. }).then(res => {
  214. if (res.code === 1) {
  215. that.goodsInfo = res.data;
  216. }
  217. });
  218. },
  219. // 加入购物车
  220. addCart(sku) {
  221. if (sku.stock <= 0) {
  222. this.$u.toast('库存不足');
  223. return;
  224. }
  225. if (this.detail.activity_type) {
  226. this.$Router.push({ path: '/pages/goods/detail', query: { id: this.detail.id } });
  227. return;
  228. }
  229. let confirmGoodsList = {
  230. list: [{
  231. goods_id: sku.goods_id,
  232. goods_num: 1,
  233. sku_price_id: sku.id,
  234. goods_price: sku.price
  235. }],
  236. from: 'goods'
  237. };
  238. this.addCartGoods(confirmGoodsList).then(res => {
  239. if (res.code === 1) {
  240. this.$u.toast(res.msg);
  241. }
  242. });
  243. }
  244. }
  245. };
  246. </script>
  247. <style lang="scss">
  248. // 大商品卡片
  249. .big-goods {
  250. width: 710rpx;
  251. min-height: 260rpx;
  252. background: #ffffff;
  253. border-radius: 20rpx;
  254. background-repeat: no-repeat;
  255. background-size: 100% 100%;
  256. .goods-img {
  257. width: 260rpx;
  258. height: 260rpx;
  259. background-color: #f5f5f5;
  260. border-radius: 6rpx;
  261. }
  262. .card-right {
  263. width: 430rpx;
  264. height: 220rpx;
  265. }
  266. .goods-title {
  267. font-size: 26rpx;
  268. font-weight: 600;
  269. width: 400rpx;
  270. color: #000000;
  271. padding-top: 6rpx;
  272. .title-tag {
  273. transform: scale(0.9);
  274. position: relative;
  275. top: -6rpx;
  276. }
  277. }
  278. .subtitle-text {
  279. font-size: 22rpx;
  280. width: 400rpx;
  281. font-weight: 500;
  282. color: #666666;
  283. }
  284. .tag-box {
  285. border: 1rpx solid #ff0000;
  286. display: inline-block;
  287. font-size: 20rpx;
  288. line-height: 30rpx;
  289. padding: 0 10rpx;
  290. color: #ff0000;
  291. border-radius: 8rpx;
  292. }
  293. // 购物车
  294. .cart-box {
  295. .cart-btn {
  296. width: 54rpx;
  297. height: 54rpx;
  298. border-radius: 50%;
  299. padding: 0;
  300. background: linear-gradient(90deg, #e9b461, #eecc89);
  301. }
  302. .buy-btn {
  303. width: 120rpx;
  304. line-height: 50rpx;
  305. background: linear-gradient(90deg, #e9b461, #eecc89);
  306. border-radius: 25rpx;
  307. font-size: 24rpx;
  308. font-weight: 500;
  309. color: #ffffff;
  310. }
  311. }
  312. // 价格
  313. .price {
  314. color: #ff0000;
  315. font-weight: 600;
  316. &::before {
  317. content: '¥';
  318. font-size: 20rpx;
  319. }
  320. }
  321. .origin-price {
  322. color: #c4c4c4;
  323. font-size: 24rpx;
  324. text-decoration: line-through;
  325. &::before {
  326. content: '¥';
  327. font-size: 20rpx;
  328. }
  329. }
  330. }
  331. </style>