Cart.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. <template>
  2. <div class="cart" @click="couponAvailable = false">
  3. <BaseHeader></BaseHeader>
  4. <!-- LOGO 搜索 -->
  5. <div class="width_1200 logo">
  6. <div>
  7. <router-link to="/"><img :src="logoImg" alt="lili shop" title="lilishop" /></router-link>
  8. <div>
  9. 购物车(<span>{{ goodsTotal }}</span>)
  10. </div>
  11. </div>
  12. <Search :showTag="false" :showLogo="false"></Search>
  13. </div>
  14. <Divider />
  15. <!-- 购物车主体 -->
  16. <div class="cart-content width_1200 center">
  17. <div class="available-area">
  18. <div class="cart-steps">
  19. <span :class="stepIndex == 0 ? 'active' : ''">1.我的购物车</span>
  20. <Icon :class="stepIndex == 0 ? 'active-arrow' : ''" custom="icomoon icon-next"></Icon>
  21. <span :class="stepIndex == 1 ? 'active' : ''">2.填写订单信息</span>
  22. <Icon :class="stepIndex == 1 ? 'active-arrow' : ''" custom="icomoon icon-next"></Icon>
  23. <span :class="stepIndex == 2 ? 'active' : ''">3.成功提交订单</span>
  24. </div>
  25. </div>
  26. <!-- 购物车商品列表 -->
  27. <div class="cart-goods">
  28. <div class="cart-goods-title">
  29. <div class="width_60">
  30. <Checkbox v-model="allChecked" @on-change="changeChecked(allChecked, 'all')">全选</Checkbox>
  31. </div>
  32. <div class="goods-title">商品</div>
  33. <div class="width_150">单价(元)</div>
  34. <div class="width_100">数量</div>
  35. <div class="width_150">小计</div>
  36. <div class="width_100">操作</div>
  37. </div>
  38. <div v-if="cartList.length === 0" class="cart-empty">
  39. <p>购物车空空如也</p>
  40. <router-link to="/">去选购&gt;</router-link>
  41. </div>
  42. <div v-else class="cart-goods-items" v-for="(shop, index) in cartList" :key="index">
  43. <div class="shop-name">
  44. <div>
  45. <Checkbox v-model="shop.checked" @on-change="changeChecked(shop.checked, 'shop', shop.storeId)"></Checkbox>
  46. <span class="go-shop-page" @click="goShopPage(shop.storeId)">{{shop.storeName}}</span>
  47. </div>
  48. <span class="shop-coupon" v-if="shop.couponList.length" :class="couponAvailable === index ? 'shop-coupon-show' : ''" @click.stop="showCoupon(shop.id, index)">
  49. <!-- 优惠券模态框 -->
  50. <div v-if="couponAvailable === index">
  51. <div class="coupon-item" v-for="(item, index) in shop.couponList" :key="index">
  52. <span v-if="item.couponType === 'PRICE'">¥{{ item.price }}</span>
  53. <span v-if="item.couponType === 'DISCOUNT'">{{ item.couponDiscount }}折</span>
  54. <span>满{{item.consumeThreshold}}元可用</span>
  55. <Button class="coupon-btn" size="small" type="primary" @click="receiveShopCoupon(item)" :disabled="item.disabled">{{ item.disabled ? "已领取" : "领取" }}</Button>
  56. </div>
  57. </div>
  58. </span>
  59. <div class="promotion-notice">{{shop.promotionNotice}}</div>
  60. </div>
  61. <template v-for="(goods, goodsIndex) in shop.skuList">
  62. <div class="goods-item" :key="goodsIndex">
  63. <div class="width_60">
  64. <Checkbox v-model="goods.checked" @on-change="changeChecked(goods.checked, 'goods', goods.goodsSku.id)"></Checkbox>
  65. </div>
  66. <div class="goods-title" @click="goGoodsDetail(goods.goodsSku.id, goods.goodsSku.goodsId)">
  67. <img :src="
  68. goods.goodsSku.thumbnail || '../assets/images/goodsDetail/item-detail-1.jpg'
  69. " />
  70. <div>
  71. <p>{{ goods.goodsSku.goodsName }}</p>
  72. <template v-for="(promotion, promotionIndex) in goods.promotions">
  73. <div class="promotion" :key="promotionIndex" v-if="promotion.promotionType === 'SECKILL'">
  74. <span>秒杀</span>
  75. <promotion :time="promotion.endTime" type="cart"></promotion>
  76. </div>
  77. </template>
  78. </div>
  79. </div>
  80. <div class="width_150">
  81. {{ goods.goodsSku.price | unitPrice("¥") }}
  82. </div>
  83. <div class="width_100">
  84. <InputNumber :min="1" size="small" v-model="goods.num" @on-change="changeNum(goods.num, goods.goodsSku.id)"></InputNumber>
  85. <div class="fontsize_12">{{goods.goodsSku.quantity > 0 ? '有货' : '无货'}}</div>
  86. </div>
  87. <div class="width_150">
  88. {{ goods.num * goods.goodsSku.price | unitPrice("¥") }}
  89. </div>
  90. <div class="width_100">
  91. <span class="handle-btn" v-if="!goods.errorMessage" @click="delGoods(goods.goodsSku.id)">删除</span>
  92. <span class="handle-btn" v-if="!goods.errorMessage" @click="collectGoods(goods.goodsSku.id)">收藏</span>
  93. </div>
  94. <div class="error-goods" v-if="goods.errorMessage">
  95. <div>{{goods.errorMessage}}</div>
  96. <Button type="primary" @click="delGoods(goods.goodsSku.id)">删除</Button>
  97. </div>
  98. </div>
  99. </template>
  100. </div>
  101. <!-- 底部支付栏 -->
  102. <div class="cart-goods-footer">
  103. <div>
  104. <div class="width_60">
  105. <Checkbox v-model="allChecked" @on-change="changeChecked(allChecked, 'all')">全选</Checkbox>
  106. </div>
  107. <div class="width_100 handle-btn" @click="delGoods">删除选中商品</div>
  108. <!-- <div class="width_100 handle-btn" @click="collectGoods">移到我的收藏</div> -->
  109. <div class="width_100 handle-btn" @click="clearCart">清空购物车</div>
  110. </div>
  111. <div>
  112. <div class="selected-count">
  113. 已选择<span>{{ checkedNum }}</span>件商品
  114. </div>
  115. <div class="ml_20 save-price">
  116. 已节省<span>{{ priceDetailDTO.discountPrice | unitPrice("¥") }}</span>
  117. </div>
  118. <div class="ml_20 total-price">
  119. 总价(不含运费):<span>{{ priceDetailDTO.billPrice | unitPrice("¥") }}</span>
  120. </div>
  121. <div class="pay ml_20" @click="pay">去结算</div>
  122. </div>
  123. </div>
  124. </div>
  125. <Spin size="large" fix v-if="loading"></Spin>
  126. </div>
  127. <!-- 猜你喜欢 -->
  128. <!-- <div class="like">
  129. <div class="likeGoods">
  130. <ShowLikeGoods />
  131. </div>
  132. </div> -->
  133. <BaseFooter></BaseFooter>
  134. </div>
  135. </template>
  136. <script>
  137. import Promotion from '@/components/goodsDetail/Promotion'
  138. import Search from '@/components/Search';
  139. import ShowLikeGoods from '@/components/like';
  140. import * as APICart from '@/api/cart';
  141. import * as APIMember from '@/api/member';
  142. import {getLogo} from '@/api/common.js'
  143. export default {
  144. name: 'Cart',
  145. beforeRouteEnter (to, from, next) {
  146. window.scrollTo(0, 0);
  147. next();
  148. },
  149. components: {
  150. Search,
  151. ShowLikeGoods,
  152. Promotion
  153. },
  154. data () {
  155. return {
  156. logoImg: '', // logo图
  157. couponAvailable: false, // 展示优惠券
  158. stepIndex: 0, // 当前处于哪一步,购物车==0,填写订单信息==1,成功提交订单==2
  159. goodsTotal: 1, // 商品数量
  160. checkedNum: 0, // 选中数量
  161. allChecked: false, // 全选
  162. loading: false, // 加载状态
  163. cartList: [], // 购物车列表
  164. couponList: [], // 优惠券列表
  165. priceDetailDTO: {}, // 价格明细
  166. skuList: [] // sku列表
  167. };
  168. },
  169. computed: {},
  170. methods: {
  171. // 跳转商品详情
  172. goGoodsDetail (skuId, goodsId) {
  173. let routeUrl = this.$router.resolve({
  174. path: '/goodsDetail',
  175. query: { skuId, goodsId }
  176. });
  177. window.open(routeUrl.href, '_blank');
  178. },
  179. // 跳转店铺首页
  180. goShopPage (id) {
  181. let routeUrl = this.$router.resolve({
  182. path: '/Merchant',
  183. query: { id }
  184. });
  185. window.open(routeUrl.href, '_blank');
  186. },
  187. // 收藏商品
  188. collectGoods (id) {
  189. this.$Modal.confirm({
  190. title: '收藏',
  191. content: '<p>商品收藏后可在个人中心我的收藏查看</p>',
  192. onOk: () => {
  193. APIMember.collectGoods('GOODS', id).then((res) => {
  194. if (res.success) {
  195. this.$Message.success('收藏商品成功');
  196. this.getCartList();
  197. }
  198. });
  199. },
  200. onCancel: () => { }
  201. });
  202. },
  203. // 删除商品
  204. delGoods (id) {
  205. const idArr = [];
  206. if (!id) {
  207. const list = this.cartList;
  208. list.forEach((shop) => {
  209. shop.skuList.forEach((goods) => {
  210. idArr.push(goods.goodsSku.id);
  211. });
  212. });
  213. } else {
  214. idArr.push(id);
  215. }
  216. this.$Modal.confirm({
  217. title: '删除',
  218. content: '<p>确定要删除该商品吗?</p>',
  219. onOk: () => {
  220. APICart.delCartGoods({ skuIds: idArr.toString() }).then((res) => {
  221. if (res.success) {
  222. this.$Message.success('删除成功');
  223. this.getCartList();
  224. } else {
  225. this.$Message.error(res.message);
  226. }
  227. });
  228. }
  229. });
  230. },
  231. clearCart () { // 清空购物车
  232. this.$Modal.confirm({
  233. title: '提示',
  234. content: '<p>确定要清空购物车吗?清空后不可恢复</p>',
  235. onOk: () => {
  236. APICart.clearCart().then((res) => {
  237. if (res.success) {
  238. this.$Message.success('清空购物车成功');
  239. this.getCartList();
  240. } else {
  241. this.$Message.error(res.message);
  242. }
  243. });
  244. }
  245. });
  246. },
  247. // 跳转支付页面
  248. pay () {
  249. if (this.checkedNum) {
  250. this.$router.push({ path: '/pay', query: { way: 'CART' } });
  251. } else {
  252. this.$Message.warning('请至少选择一件商品');
  253. }
  254. },
  255. // 展示优惠券
  256. showCoupon (storeId, index) {
  257. this.couponAvailable = index;
  258. },
  259. changeNum (val, id) {
  260. // 设置购买数量
  261. console.log(val, id);
  262. APICart.setCartGoodsNum({ skuId: id, num: val }).then((res) => {
  263. console.log(res);
  264. if (res.success) {
  265. this.getCartList();
  266. }
  267. });
  268. },
  269. async changeChecked (status, type, id) {
  270. // 设置商品选中状态
  271. const check = status ? 1 : 0;
  272. if (type === 'all') {
  273. // 全选
  274. await APICart.setCheckedAll({ checked: check });
  275. } else if (type === 'shop') {
  276. // 选中店铺所有商品
  277. await APICart.setCheckedSeller({ checked: check, storeId: id });
  278. } else {
  279. // 单个商品
  280. await APICart.setCheckedGoods({ checked: check, skuId: id });
  281. }
  282. this.getCartList();
  283. },
  284. async receiveShopCoupon (item) { // 领取优惠券
  285. let res = await APIMember.receiveCoupon(item.id)
  286. if (res.success) {
  287. this.$set(item, 'disabled', true)
  288. this.$Message.success('领取成功')
  289. } else {
  290. this.$Message.error(res.message)
  291. }
  292. },
  293. async getCartList () {
  294. // 购物车列表
  295. this.loading = true;
  296. try {
  297. let res = await APICart.cartGoodsAll();
  298. this.loading = false;
  299. if (res.success) {
  300. this.cartList = res.result.cartList;
  301. this.priceDetailDTO = res.result.priceDetailDTO;
  302. this.skuList = res.result.skuList;
  303. this.checkedNum = 0;
  304. let allChecked = true;
  305. for (let k = 0; k < this.cartList.length; k++) {
  306. let shop = this.cartList[k]
  307. let list = await APIMember.couponList({storeId: shop.storeId})
  308. shop.couponList.push(...list.result.records)
  309. }
  310. for (let i = 0; i < this.skuList.length; i++) {
  311. if (this.skuList[i].checked) {
  312. this.checkedNum += this.skuList[i].num;
  313. } else {
  314. allChecked = false;
  315. }
  316. }
  317. this.$forceUpdate()
  318. this.allChecked = allChecked;
  319. }
  320. } catch (error) {
  321. this.loading = false;
  322. }
  323. }
  324. },
  325. mounted () {
  326. this.getCartList();
  327. APICart.cartCount().then(res => { // 购物车商品数量
  328. if (res.success) this.goodsTotal = res.result;
  329. });
  330. if (!this.Cookies.getItem('logo')) {
  331. getLogo().then(res => {
  332. if (res.success) {
  333. let logoObj = JSON.parse(res.result.settingValue)
  334. this.Cookies.setItem('logo', logoObj.buyerSideLogo)
  335. }
  336. })
  337. } else {
  338. this.logoImg = this.Cookies.getItem('logo')
  339. }
  340. }
  341. };
  342. </script>
  343. <style scoped lang="scss">
  344. /** logo 搜索 start **/
  345. .logo {
  346. height: 40px;
  347. display: flex;
  348. justify-content: space-between;
  349. align-items: center;
  350. margin: 20px auto 0;
  351. div:nth-child(1) {
  352. display: flex;
  353. justify-content: space-between;
  354. align-items: center;
  355. img {
  356. width: 150px;
  357. height: auto;
  358. cursor: pointer;
  359. }
  360. div:nth-child(2) {
  361. width: 200px;
  362. color: #999;
  363. font-size: 16px;
  364. margin: 0 20px;
  365. span {
  366. color: $theme_color;
  367. }
  368. }
  369. }
  370. }
  371. .cart-content {
  372. margin: 0 auto;
  373. width: 1200px;
  374. position: relative;
  375. }
  376. /** logo end */
  377. /** step步骤条 */
  378. .cart-steps {
  379. height: 30px;
  380. display: flex;
  381. align-items: center;
  382. margin-bottom: 15px;
  383. span {
  384. @include content_color($light_content_color);
  385. height: 30px;
  386. text-align: center;
  387. line-height: 30px;
  388. display: inline-block;
  389. padding: 0 15px;
  390. }
  391. .ivu-icon {
  392. @include content_color($light_content_color);
  393. font-size: 20px;
  394. margin: 0 15px;
  395. }
  396. .active {
  397. border-radius: 50px;
  398. background-color: #ff8f23;
  399. color: #fff;
  400. }
  401. .active-arrow {
  402. color: #ff8f23;
  403. }
  404. }
  405. /** 步骤条和配送区域总体 */
  406. .available-area {
  407. display: flex;
  408. align-items: center;
  409. justify-content: space-between;
  410. margin-bottom: 15px;
  411. }
  412. /** 商品列表 */
  413. .cart-goods {
  414. &-title {
  415. height: 50px;
  416. @include background_color($light_white_background_color);
  417. @include title_color($title_color);
  418. display: flex;
  419. align-items: center;
  420. padding: 0 20px;
  421. div {
  422. text-align: center;
  423. }
  424. .goods-title {
  425. flex: 1;
  426. }
  427. }
  428. .cart-empty {
  429. width: 100%;
  430. text-align: center;
  431. height: 300px;
  432. padding-top: 100px;
  433. }
  434. &-items {
  435. .shop-name {
  436. height: 50px;
  437. display: flex;
  438. align-items: center;
  439. padding: 0 20px;
  440. position: relative;
  441. @include title_color($light_title_color);
  442. > * {
  443. width: 50%;
  444. }
  445. .go-shop-page:hover {
  446. color: $theme_color;
  447. cursor: pointer;
  448. }
  449. .customer-service {
  450. margin-left: 5px;
  451. color: #fcc217;
  452. cursor: pointer;
  453. &:hover {
  454. color: $theme_color;
  455. }
  456. }
  457. /** 优惠券 */
  458. .shop-coupon {
  459. width: 80px;
  460. height: 24px;
  461. position: relative;
  462. background: url(../assets/images/cart-coupon-icons02.png) 0 0 no-repeat;
  463. > div {
  464. position: absolute;
  465. top: 35px;
  466. left: 0;
  467. width: 300px;
  468. height: 300px;
  469. background-color: #fff;
  470. border: 1px solid $theme_color;
  471. // border-radius: 3px;
  472. z-index: 1;
  473. padding: 10px 20px;
  474. &::before {
  475. content: "";
  476. display: block;
  477. background: url(../assets/images/cart-coupon-icons02.png) 0 -58px no-repeat;
  478. width: 80px;
  479. height: 12px;
  480. position: absolute;
  481. top: -12px;
  482. left: 0;
  483. }
  484. .coupon-item {
  485. margin-bottom: 10px;
  486. span:nth-child(1) {
  487. border: 1px solid #e33937;
  488. display: inline-block;
  489. padding: 3px 10px;
  490. color: $theme_color;
  491. border-radius: 3px;
  492. }
  493. span:nth-child(2) {
  494. font-size: 12px;
  495. margin-left: 5px;
  496. color: #999;
  497. }
  498. .coupon-btn {
  499. height: 26px;
  500. float: right;
  501. font-size: 12px;
  502. }
  503. &::after {
  504. display: block;
  505. content: "";
  506. clear: right;
  507. }
  508. }
  509. }
  510. }
  511. .promotion-notice {
  512. text-align: right;
  513. font-size: 12px;
  514. }
  515. .shop-coupon-show {
  516. background-position-y: -34px;
  517. }
  518. }
  519. .goods-item {
  520. position: relative;
  521. @extend .cart-goods-title;
  522. @include background_color($light_white_background_color);
  523. padding: 10px 20px;
  524. height: auto;
  525. > div:nth-child(1) {
  526. padding-left: 15px;
  527. width: 30px;
  528. }
  529. > div:nth-child(2) {
  530. cursor: pointer;
  531. display: flex;
  532. box-sizing: border-box;
  533. padding-left: 20px;
  534. position: relative;
  535. img {
  536. width: 70px;
  537. height: 70px;
  538. }
  539. >div>p {
  540. @include content_color($light_content_color);
  541. font-size: 13px;
  542. text-align: left;
  543. margin-left: 10px;
  544. &:hover {
  545. color: $theme_color;
  546. }
  547. }
  548. }
  549. > div:nth-child(5) {
  550. font-weight: bold;
  551. }
  552. .num-input {
  553. width: 60px;
  554. border: 1px solid #999;
  555. border-radius: 5px;
  556. padding: 0 5px;
  557. &:focus {
  558. outline-color: $theme_color;
  559. }
  560. }
  561. }
  562. .error-goods{
  563. position: absolute;
  564. width: 100%;
  565. height: 100%;
  566. margin-left: -20px;
  567. background-color: rgba($color: #999, $alpha: .5);
  568. z-index: 10;
  569. display: flex;
  570. align-items: center;
  571. justify-content: space-between;
  572. padding-left: 150px;
  573. color: #000;
  574. padding-right: 30px;
  575. }
  576. }
  577. &-footer {
  578. @extend .cart-goods-title;
  579. position: sticky;
  580. bottom: 0;
  581. border-top: 1px solid #ddd;
  582. margin-top: 10px;
  583. padding: 0 0 0 20px;
  584. line-height: 50px;
  585. justify-content: space-between;
  586. > div {
  587. display: flex;
  588. }
  589. .selected-count {
  590. span {
  591. color: $theme_color;
  592. }
  593. }
  594. .save-price span {
  595. color: #000;
  596. }
  597. .total-price span {
  598. color: $theme_color;
  599. font-size: 20px;
  600. }
  601. .pay {
  602. background-color: $theme_color;
  603. width: 150px;
  604. font-size: 20px;
  605. color: #fff;
  606. height: 100%;
  607. line-height: 50px;
  608. cursor: pointer;
  609. }
  610. }
  611. .handle-btn {
  612. font-size: 12px;
  613. color: $handle-btn-color;
  614. cursor: pointer;
  615. &:hover {
  616. color: $theme_color;
  617. }
  618. }
  619. }
  620. .like {
  621. width: 1200px;
  622. margin: 10px auto;
  623. // padding: 20px 0;
  624. @include white_background_color();
  625. }
  626. .likeGoods,
  627. .shop-nav-container {
  628. width: 1200px;
  629. margin: 0 auto;
  630. }
  631. .ivu-divider {
  632. background: $theme_color;
  633. height: 2px;
  634. }
  635. .width_150 {
  636. width: 150px;
  637. }
  638. .width_60 {
  639. width: 60px;
  640. }
  641. .promotion {
  642. display: flex;
  643. margin-top: 5px;
  644. margin-left: 5px;
  645. >span{
  646. border: 1px solid $theme_color;
  647. color: $theme_color;
  648. font-size: 12px;
  649. border-radius: 2px;
  650. padding: 0 2px;
  651. }
  652. >p{
  653. font-size: 12px;
  654. margin-left: 10px;
  655. color: #999;
  656. }
  657. }
  658. </style>
  659. <style>
  660. .ivu-input-number-input {
  661. text-align: center;
  662. }
  663. </style>