VueDemo-12.加入购物车

12.加入购物车

思路:

  • 1.点击加入购物车,需要判断用户的登录状态(先前端校验,再接口后端校验)

  • 2.调用加入购物车的接口,完成

    • 1.如果购物车中有这个产品,更新购物车,数量加1即可

    • 2.如果购物车中没有这个产品,插入一条记录即可

  • 前端可以通过 loginState 校验登录

  • 后端通过前端传递的 token 校验登录状态

// src/api/cart.js
import request from '../utils/request'
​
// 加入购物车
export function addCart (params) {
  return request.post('/cart/add', params)
}
// 获取购物车数据
export function getCartList (params) {
  return request.post('/cart/list', params)
}
// 删除当前用户的所有的购物车中的数据
export function removeAll (params) {
  return request.post('/cart/removeall', params)
}
// 删除购物车中的单条数据
export function remove (params) {
  return request.post('/cart/remove', params)
}
​
// 更新购物车中的单条数据的复选框的 选中状态
export function selectOne (params) {
  return request.post('/cart/selectone', params)
}
​
// 更新购物车中的全选的选中状态
export function selectAll (params) {
  return request.post('/cart/selectall', params)
}
​
// 更新购物车单条数据的数量
export function updateNum (params) {
  return request.post('/cart/updatenum', params)
}
​

12.1 封装axios 拦截器

axios中文文档|axios中文网 | axios

请求拦截器:给所有的请求的头信息中携带token信息

响应拦截器:先判断token的有效性,如果失效 直接跳转至登录页面

s r c/utils/request.js

import axios from 'axios'
import router from '../router' // +++++++++++++++++++
// 自定义axios
// const ins = axios.create({
//   // baseURL 以后调用接口可以不用写这个,程序会自动拼接
//   baseURL: 'http://121.89.205.189:3001/api',
//   timeout: 6000 // 请求的超时时间
// })
​
// 环境 - 开发环境 | 测试环境 | 生产环境
//       localhost |  ip地址  |  域名
//      yarn serve | yarn build  | yarn build
//      development |       |  production
// process 属于nodejs自带的一个全局的模块 ---- 进程
// http://nodejs.cn/api/process.html#processenv
// process.env ---- 判断环境
// process.env 本身并没有 NODE_ENV 这个变量,程序脚手架设置的 -- webpack 设置
const isDev = process.env.NODE_ENV === 'development' // 判断当前的运行环境的
​
const ins = axios.create({
  baseURL: isDev ? 'http://121.89.205.189:3001/api' : 'http://121.89.205.189:3001/api',
  timeout: 6000 // 请求的超时时间
})
​
// 请求拦截器
ins.interceptors.request.use((config) => {
  // 比如登录的验证 - token
  config.headers.token = localStorage.getItem('token') // +++++++++++++++++++
  return config
}, (error) => {
  return Promise.reject(error)
})
​
// 响应拦截器
ins.interceptors.response.use((response) => {
  // 验证登录,如果验证不通过,可以跳转到登录页面,验证通过,返回想要的数据
  if (response.data.code === '10119') { // +++++++++++++++++++
    // 后端校验登录状态 失败,需要跳转至 登录页面
    router.push('/login')
    return response
  }
  return response
}, (error) => {
  return Promise.reject(error)
})
​
export default ins
​

12.2 加入购物车

Src/views/detail/index.vue

<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        v-if="scrollTop < 50"
        left-arrow
        @click-left="$router.back()"
      >
        <template #right>
          <van-popover
            v-model="showPopover"
            trigger="click"
            placement="bottom-end"
            theme="dark"
            :actions="actions"
            @select="onSelect"
          >
            <template #reference>
              <van-icon name="ellipsis" size="32"/>
            </template>
          </van-popover>
        </template>
      </van-nav-bar>
      <transition name="fade">
      <van-nav-bar
        v-if="scrollTop >= 50"
        :title="proname"
        left-arrow
        @click-left="$router.back()"
      >
        <template #right>
          <van-popover
            v-model="showPopover"
            trigger="click"
            placement="bottom-end"
            theme="dark"
            :actions="actions"
            @select="onSelect"
          >
            <template #reference>
              <van-icon name="ellipsis" size="32"/>
            </template>
          </van-popover>
        </template>
      </van-nav-bar>
      </transition>
    </header>
    <div class="content" ref="content">
      <div class="my-detail-swiper">
        <van-swipe :show-indicators = "false" @change="changeSwiper" :initial-swipe="index - 1">
          <van-swipe-item v-for="(item, index) of banners" :key="index" @click="previewImage">
            <van-image  fit="scale-down" :src="item" />
          </van-swipe-item>
        </van-swipe>
        <div class="my-indicators">
          {{ index }} / {{ banners.length }}
        </div>
        <div class="video-btn" @click="show = true">
          <van-icon name="play-circle-o" size="18" color="#f66"/> {{ time | timeFilter }}
        </div>
      </div>
      <div class="proname">
        <van-tag type="danger">{{ category }}</van-tag> {{ proname }}
      </div>
      <div class="price">
        ¥{{ discount !== 0 ? originprice * discount / 10 : originprice}}
        <del>¥{{ originprice }}</del>
      </div>
​
      <div class="space">1111</div>
      <van-goods-action  v-if="issale === 0">
        <van-goods-action-icon icon="chat-o" text="客服" color="#ee0a24" @click="chat"/>
        <van-goods-action-icon icon="cart-o" text="购物车" />
        <van-goods-action-button type="warning" disabled text="商品已下架" />
      </van-goods-action>
      <van-goods-action v-else>
        <van-goods-action-icon icon="chat-o" text="客服" color="#ee0a24" @click="chat"/>
        <van-goods-action-icon icon="cart-o" text="购物车" @click="toCart"/>
        <van-goods-action-button type="warning" text="加入购物车" @click="addCartFn"/>
        <van-goods-action-button type="danger" text="立即购买" />
      </van-goods-action>
​
    </div>
    <van-share-sheet
      v-model="showShare"
      title="立即分享给好友"
      :options="options"
    />
    <van-overlay :show="show" @click="show = false">
      <video controls class="my-video" ref="vdo" :src="vdoSrc"></video>
    </van-overlay>
  </div>
</template>
​
<script>
import Vue from 'vue'
import { Toast, Tag, NavBar, Popover, ShareSheet, Icon, Swipe, SwipeItem, Image as VanImage, ImagePreview, Overlay, GoodsAction, GoodsActionIcon, GoodsActionButton } from 'vant'
import { getProDetailData } from './../../api/detail'
import { addCart } from './../../api/cart'
Vue.use(Toast)
Vue.use(Tag)
Vue.use(NavBar)
Vue.use(Popover)
Vue.use(ShareSheet)
Vue.use(Icon)
Vue.use(Swipe)
Vue.use(SwipeItem)
Vue.use(VanImage)
Vue.use(ImagePreview)
Vue.use(Overlay)
Vue.use(GoodsAction)
Vue.use(GoodsActionIcon)
Vue.use(GoodsActionButton)
export default {
  data () {
    return {
      showPopover: false,
      // 通过 actions 属性来定义菜单选项 <van-icon name="location-o" />
      actions: [
        { index: 0, text: '首页', icon: 'location-o' },
        { index: 1, text: '分类', icon: 'like-o' },
        { index: 2, text: '购物车', icon: 'shopping-cart-o' },
        { index: 3, text: '个人中心', icon: 'friends-o' },
        { index: 4, text: '分享', icon: 'share-o' }
      ],
      showShare: false, // 分享面板
      options: [
        { name: '微信', icon: 'wechat' },
        { name: '微博', icon: 'weibo' },
        { name: '复制链接', icon: 'link' },
        { name: '分享海报', icon: 'poster' },
        { name: '二维码', icon: 'qrcode' }
      ],
      proid: '',
      banners: [], // 轮播图数据
      proname: '', // 产品名称
      originprice: 0, // 原价
      category: '', // 分类
      discount: 0, // 折扣
      issale: 0, // 是否在售卖
      index: 1,
      show: false, // 视频播放器
      vdoSrc: 'https://vod.300hu.com/4c1f7a6atransbjngwcloud1oss/2f9ff93e332543321040232449/v.f20.mp4?dockingId=dd606c09-5aa5-449d-9faa-442ac07456f2&storageSource=3',
      // vdoSrc: 'https://jvod.300hu.com/vod/product/c3cf94d5-6fb9-4354-9f18-cc01c19350ff/f98fc5d1f64d40b2b667940ebc36bae3.mp4',
      time: 0, // 视频的播放时长
      scrollTop: 0
    }
  },
  mounted () {
    const proid = this.$route.params.proid
    getProDetailData(proid).then(res => {
      console.log(res.data.data)
      const banners = res.data.data.banners[0]
      this.proid = proid
      this.banners = banners.split(',') // 特殊处理,并不是所有项目都需要
      this.proname = res.data.data.proname
      this.originprice = res.data.data.originprice
      this.category = res.data.data.category
      this.discount = res.data.data.discount
      this.issale = res.data.data.issale
      console.log(this.$refs.vdo.duration)
    })
    setTimeout(() => {
      this.time = this.$refs.vdo.duration // 计算视频的总时长
    }, 1000)
    this.$refs.content.addEventListener('scroll', () => {
      this.scrollTop = this.$refs.content.scrollTop
    }, false)
  },
  filters: {
    timeFilter (val) { // 过滤器处理时间显示格式
      let a = Math.floor(val / 60)
      let b = Math.floor(val % 60)
      a = a < 10 ? '0' + a : a
      b = b < 10 ? '0' + b : b
      return a + '‘' + b
    }
  },
  methods: {
    toCart () {
      this.$router.push('/cart')
    },
    addCartFn () {
      // 加入购物车
      const userid = localStorage.getItem('userid')
      const loginState = localStorage.getItem('loginState') === 'true'
      if (loginState) {
        // 前端校验为登录状态,接下来后端校验
        addCart({
          userid,
          proid: this.proid,
          num: 1
        }).then(res => {
          if (res.data.code !== '10119') {
            Toast('加入购物车数据')
          }
        })
      } else {
        // 前端校验为未登录状态
        this.$router.push('/login')
      }
    },
    chat () {
      window._MEIQIA('metadata', {
        name: '张津曦', // 美洽默认字段
        address: '吧啦吧啦吧啦', // 美洽默认字段
        sex: '男', // 自定义字段
        hobby: '女' // 自定义字段
      })
      window._MEIQIA('showPanel')
    },
    previewImage () {
      ImagePreview({
        images: this.banners,
        startPosition: this.index - 1,
        onChange: (index) => {
          console.log('111', index)
          this.index = index + 1
        }
      })
    },
    changeSwiper (index) {
      this.index = index + 1
    },
    onSelect ({ index }) {
      switch (index) {
        case 0:
          this.$router.push('/home')
          break
        case 1:
          this.$router.push('/kind')
          break
        case 2:
          this.$router.push('/cart')
          break
        case 3:
          this.$router.push('/user')
          break
        case 4:
          this.showShare = true
      }
    }
  },
  watch: {
    show (newVal) { // 监听视频播放
      if (newVal) {
        this.$refs.vdo.currentTime = 0 // 播放时长为0
        this.$refs.vdo.play()
      } else {
        this.$refs.vdo.pause()
      }
      // newVal ? this.$refs.vdo.play() : this.$refs.vdo.pause()
    }
  }
}
</script>
​
<style lang="stylus" scoped>
.van-swipe
  height 3.3rem
  background-color #fff
  .van-swipe-item
    width 100%
    .van-image
      width 100%
      img
        width 100%
.my-detail-swiper
  position relative
  .my-indicators
    position absolute
    bottom 20px
    right 0
    width 50px
    height 20px
    background-color #cccc
    text-align  center
    line-height 20px
    border-radius 10px 0 0 10px
  .video-btn
    position absolute
    bottom 20px
    left 50%
    transform translateX(-50%)
    width 83px
    height 30px
    background-color #ffffff
    border-radius 30px
    display flex
    justify-content center
    align-items center
.my-video
  position fixed
  width 100%
  top 50%
  transform translateY(-50%)
.proname
  padding 5px 10px
  font-size 16px
  font-weight bold
.price
  color #ff6666
  del
    color #999
.space
  height 1000px
.container .box .header
  background-color transparent
.fade-enter-active{
  transition: opacity .5s;
}
.fade-leave-active {
  transition: opacity 0s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}
</style>
​

12.3 查看购物车

查看购物车,必须得是登录状态,如果是为登录状态,点击购物车直接跳转到登录页面

如果是登录状态,没有请求到数据,显示购物车空空如也

如果有数据,展示购物车的数据

<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        title="登录"
        left-arrow
        @click-left="$router.back()"
      />
    </header>
    <div class="content">
      <div class="noShop" v-if="flag">
        <van-empty description="购物车空空如也">
          <van-button @click="$router.push('/kind')" round type="danger" class="bottom-button">立即购物</van-button>
        </van-empty>
      </div>
      <div v-else class="hasShop">
        <van-swipe-cell
          v-for="item of cartList"
          :key="item.cartid">
          <van-card
            :num="item.num"
            :price="item.originprice"
            :title="item.proname"
            :thumb="item.img1"
          >
            <template #num>
              <van-stepper v-model="item.num" theme="round"/>
            </template>
          </van-card>
          <template #right>
            <van-button square type="danger" text="删除" class="delete-button"/>
          </template>
        </van-swipe-cell>
​
        <van-submit-bar :price="3050" button-text="提交订单" @submit="onSubmit">
          <van-checkbox v-model="checked">全选</van-checkbox>
        </van-submit-bar>
      </div>
    </div>
  </div>
</template>
​
<script>
import Vue from 'vue'
import { NavBar, Empty, Button, Card, Stepper, SubmitBar, Checkbox, SwipeCell } from 'vant'
import { getCartList } from './../../api/cart'
Vue.use(NavBar)
Vue.use(Empty)
Vue.use(Button)
Vue.use(Card)
Vue.use(Stepper)
Vue.use(SubmitBar)
Vue.use(Checkbox)
Vue.use(SwipeCell)
export default {
  data () {
    return {
      cartList: [],
      flag: true,
      checked: false
    }
  },
  methods: {
    onSubmit () {
    }
  },
  mounted () {
    if (localStorage.getItem('isLogin') === 'true') {
      getCartList({ userid: localStorage.getItem('userid') }).then(res => {
        console.log(res.data)
        if (res.data.code === '10020') {
          this.flag = true
        } else {
          this.flag = false
          this.cartList = res.data.data
        }
      })
    } else {
      this.$router.push('/login')
    }
  }
}
</script>
​
<style lang="stylus" scoped>
.delete-button {
  height: 100%;
}
</style>
​

完善购物车页面(删除,修改数量,提示信息,底部的提交订单栏)

<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        title = '购物车'
        left-arrow
        @click-left="$router.back()"
      ></van-nav-bar>
    </header>
    <div class="content">
      <div class="npShop" v-if="empty">
        <van-empty description="购物车空空如也">
          <van-button round type="danger" class="bottom-button">立即购物</van-button>
        </van-empty>
      </div>
      <div class="shop" v-else>
        <van-notice-bar
          mode="closeable"
          left-icon="volume-o"
          text="亲爱的用户,像左滑动视图可以展示删除按钮,祝您在嗨购商场中购物愉快"
        />
        <van-swipe-cell
          v-for="item of cartList"
          :key="item.cartid"
        >
          <van-card
            :price="item.originprice"
            :title="item.proname"
            :thumb="item.img1"
          >
            <template #num>
              <van-stepper v-model="item.num" theme="round" button-size="22"/>
            </template>
          </van-card>
          <template #right>
            <van-button square text="删除" type="danger" class="delete-button" />
          </template>
        </van-swipe-cell>
        <van-submit-bar :price="3050" button-text="提交订单" @submit="onSubmit">
          <van-checkbox v-model="checked">全选</van-checkbox>
        </van-submit-bar>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import { NavBar, Empty, Button, Card, Stepper, SwipeCell, NoticeBar, SubmitBar, Checkbox } from 'vant'
import { getCartList } from '../../api/cart'
Vue.use(NavBar)
Vue.use(Empty)
Vue.use(Button)
Vue.use(Card)
Vue.use(Stepper)
Vue.use(SwipeCell)
Vue.use(NoticeBar)
Vue.use(SubmitBar)
Vue.use(Checkbox)
export default {
  data () {
    return {
      empty: true,
      cartList: [],
      checked: false // 全选
    }
  },
  computed: {
  },
  methods: {
    onSubmit () {
      console.log('提交订单')
    },
    getCartListData () {
      getCartList({
        userid: localStorage.getItem('userid')
      }).then(res => {
        console.log(res.data)
        if (res.data.code === '10020') {
          this.empty = true
        } else {
          this.empty = false
          this.cartList = res.data.data
        }
      })
    }
  },
  mounted () {
    if (localStorage.getItem('loginState') === 'true') {
      // 前端校验为登录状体啊
      this.getCartListData()
    } else {
      this.$router.push('/login')
    }
  }
}
</script>
​
<style lang="stylus">
.delete-button
  height 100%
</style>
​

12.4 删除数据

<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        title = '购物车'
        left-arrow
        @click-left="$router.back()"
      ></van-nav-bar>
    </header>
    <div class="content">
      <div class="npShop" v-if="empty">
        <van-empty description="购物车空空如也">
          <van-button round type="danger" class="bottom-button">立即购物</van-button>
        </van-empty>
      </div>
      <div class="shop" v-else>
        <van-notice-bar
          mode="closeable"
          left-icon="volume-o"
          text="亲爱的用户,像左滑动视图可以展示删除按钮,祝您在嗨购商场中购物愉快"
        />
        <van-swipe-cell
          v-for="item of cartList"
          :key="item.cartid"
        >
          <van-card
            :price="item.originprice"
            :title="item.proname"
            :thumb="item.img1"
          >
            <template #num>
              <van-stepper v-model="item.num" theme="round" button-size="22"/>
            </template>
          </van-card>
          <template #right>
            <van-button square text="删除" type="danger" class="delete-button" @click="deleteCart(item.cartid)"/>
          </template>
        </van-swipe-cell>
        <van-submit-bar :price="3050" button-text="提交订单" @submit="onSubmit">
          <van-checkbox v-model="checked">全选</van-checkbox>
        </van-submit-bar>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import { NavBar, Empty, Button, Card, Stepper, SwipeCell, NoticeBar, SubmitBar, Checkbox } from 'vant'
import { getCartList, remove } from '../../api/cart'
Vue.use(NavBar)
Vue.use(Empty)
Vue.use(Button)
Vue.use(Card)
Vue.use(Stepper)
Vue.use(SwipeCell)
Vue.use(NoticeBar)
Vue.use(SubmitBar)
Vue.use(Checkbox)
export default {
  data () {
    return {
      empty: true,
      cartList: [],
      checked: false // 全选
    }
  },
  computed: {
  },
  methods: {
    onSubmit () {
      console.log('提交订单')
    },
    deleteCart (cartid) {
      remove({ cartid }).then(() => this.getCartListData())
    },
    getCartListData () {
      getCartList({
        userid: localStorage.getItem('userid')
      }).then(res => {
        console.log(res.data)
        if (res.data.code === '10020') {
          this.empty = true
        } else {
          this.empty = false
          this.cartList = res.data.data
        }
      })
    }
  },
  mounted () {
    if (localStorage.getItem('loginState') === 'true') {
      // 前端校验为登录状体啊
      this.getCartListData()
    } else {
      this.$router.push('/login')
    }
  }
}
</script>
​
<style lang="stylus">
.delete-button
  height 100%
</style>
​

12.5 计算总价和总数量

<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        :title = 'totalNum > 0 ? "购物车-" + totalNum : "购物车"'
        left-arrow
        @click-left="$router.back()"
      ></van-nav-bar>
    </header>
    <div class="content">
      <div class="npShop" v-if="empty">
        <van-empty description="购物车空空如也">
          <van-button round type="danger" class="bottom-button">立即购物</van-button>
        </van-empty>
      </div>
      <div class="shop" v-else>
        <van-notice-bar
          mode="closeable"
          left-icon="volume-o"
          text="亲爱的用户,像左滑动视图可以展示删除按钮,祝您在嗨购商场中购物愉快"
        />
        <van-swipe-cell
          v-for="item of cartList"
          :key="item.cartid"
        >
          <van-card
            :price="item.originprice"
            :title="item.proname"
            :thumb="item.img1"
          >
            <template #num>
              <van-stepper v-model="item.num" theme="round" button-size="22"/>
            </template>
          </van-card>
          <template #right>
            <van-button square text="删除" type="danger" class="delete-button" @click="deleteCart(item.cartid)"/>
          </template>
        </van-swipe-cell>
        <van-submit-bar :price="totalPrice" button-text="提交订单" @submit="onSubmit">
          <van-checkbox v-model="checked">全选</van-checkbox>
        </van-submit-bar>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import { NavBar, Empty, Button, Card, Stepper, SwipeCell, NoticeBar, SubmitBar, Checkbox } from 'vant'
import { getCartList, remove } from '../../api/cart'
Vue.use(NavBar)
Vue.use(Empty)
Vue.use(Button)
Vue.use(Card)
Vue.use(Stepper)
Vue.use(SwipeCell)
Vue.use(NoticeBar)
Vue.use(SubmitBar)
Vue.use(Checkbox)
export default {
  data () {
    return {
      empty: true,
      cartList: [],
      checked: false // 全选
    }
  },
  computed: {
    totalNum () {
      return this.cartList.reduce((sum, item) => {
        return sum + item.num
      }, 0)
    },
    totalPrice () {
      return this.cartList.reduce((sum, item) => {
        return sum + item.num * item.originprice
      }, 0) * 100
    }
  },
  methods: {
    onSubmit () {
      console.log('提交订单')
    },
    deleteCart (cartid) {
      remove({ cartid }).then(() => this.getCartListData())
    },
    getCartListData () {
      getCartList({
        userid: localStorage.getItem('userid')
      }).then(res => {
        console.log(res.data)
        if (res.data.code === '10020') {
          this.empty = true
          this.cartList = [] // 临界值变化 - 否则删除完所有的商品,商品总数量有问题
        } else {
          this.empty = false
          this.cartList = res.data.data
        }
      })
    }
  },
  mounted () {
    if (localStorage.getItem('loginState') === 'true') {
      // 前端校验为登录状体啊
      this.getCartListData()
    } else {
      this.$router.push('/login')
    }
  }
}
</script>
​
<style lang="stylus">
.delete-button
  height 100%
</style>
​

12.6 更新数量

<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        :title = 'totalNum > 0 ? "购物车-" + totalNum : "购物车"'
        left-arrow
        @click-left="$router.back()"
      ></van-nav-bar>
    </header>
    <div class="content">
      <div class="npShop" v-if="empty">
        <van-empty description="购物车空空如也">
          <van-button round type="danger" class="bottom-button">立即购物</van-button>
        </van-empty>
      </div>
      <div class="shop" v-else>
        <van-notice-bar
          mode="closeable"
          left-icon="volume-o"
          text="亲爱的用户,像左滑动视图可以展示删除按钮,祝您在嗨购商场中购物愉快"
        />
        <van-swipe-cell
          v-for="item of cartList"
          :key="item.cartid"
        >
          <van-card
            :price="item.originprice"
            :title="item.proname"
            :thumb="item.img1"
          >
            <template #num>
              <van-stepper v-model="item.num" theme="round" button-size="22" @change="updateCartNum(item.num, item.cartid)"/>
            </template>
          </van-card>
          <template #right>
            <van-button square text="删除" type="danger" class="delete-button" @click="deleteCart(item.cartid)"/>
          </template>
        </van-swipe-cell>
        <van-submit-bar :price="totalPrice" button-text="提交订单" @submit="onSubmit">
          <van-checkbox v-model="checked">全选</van-checkbox>
        </van-submit-bar>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import { NavBar, Empty, Button, Card, Stepper, SwipeCell, NoticeBar, SubmitBar, Checkbox } from 'vant'
import { getCartList, remove, updateNum } from '../../api/cart'
Vue.use(NavBar)
Vue.use(Empty)
Vue.use(Button)
Vue.use(Card)
Vue.use(Stepper)
Vue.use(SwipeCell)
Vue.use(NoticeBar)
Vue.use(SubmitBar)
Vue.use(Checkbox)
export default {
  data () {
    return {
      empty: true,
      cartList: [],
      checked: false // 全选
    }
  },
  computed: {
    totalNum () {
      return this.cartList.reduce((sum, item) => {
        return sum + item.num
      }, 0)
    },
    totalPrice () {
      return this.cartList.reduce((sum, item) => {
        return sum + item.num * item.originprice
      }, 0) * 100
    }
  },
  methods: {
    onSubmit () {
      console.log('提交订单')
    },
    deleteCart (cartid) {
      remove({ cartid }).then(() => this.getCartListData())
    },
    updateCartNum (num, cartid) {
      console.log(num, cartid)
      updateNum({
        num, cartid
      }).then(() => this.getCartListData())
    },
    getCartListData () {
      getCartList({
        userid: localStorage.getItem('userid')
      }).then(res => {
        console.log(res.data)
        if (res.data.code === '10020') {
          this.empty = true
          this.cartList = [] // 临界值变化 - 否则删除完所有的商品,商品总数量有问题
        } else {
          this.empty = false
          this.cartList = res.data.data
        }
      })
    }
  },
  mounted () {
    if (localStorage.getItem('loginState') === 'true') {
      // 前端校验为登录状体啊
      this.getCartListData()
    } else {
      this.$router.push('/login')
    }
  }
}
</script>
​
<style lang="stylus">
.delete-button
  height 100%
</style>
​

12.7全选

什么时候出现全选

使用change事件还是click事件

全选选中,列表所有的都要被选中,全选不选中,列表所有的都不被选中

列表当前数据没有被选中,全选一定不被选中,当前被选中,监测其余的有没有被选中,如果全被选中,全选一定被选中

建议使用click事件,因为change事件是数据的双向绑定

当前项没有被选中,全选一定没有被选中,就会触发一次change事件,而此时全选是没有被选中的,就会更改让所有的数据都没有被选中

12.8单选

12.9 计算选中的总价和总数量

只有选中的商品才会被计算总价和总数量

<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        :title = 'totalNum > 0 ? "购物车-" + totalNum : "购物车"'
        left-arrow
        @click-left="$router.back()"
      ></van-nav-bar>
    </header>
    <div class="content">
      <div class="npShop" v-if="empty">
        <van-empty description="购物车空空如也">
          <van-button round type="danger" class="bottom-button">立即购物</van-button>
        </van-empty>
      </div>
      <div class="shop" v-else>
        <van-notice-bar
          mode="closeable"
          left-icon="volume-o"
          text="亲爱的用户,像左滑动视图可以展示删除按钮,祝您在嗨购商场中购物愉快"
        />
        <van-swipe-cell
          v-for="item of cartList"
          :key="item.cartid"
        >
          <van-row>
            <van-col span="2">
              <div class="my-checkbox">
                <van-checkbox v-model="item.flag" @change="chooseOne(item.cartid, item.flag)"></van-checkbox>
              </div>
            </van-col>
            <van-col span="22">
              <van-card
                :price="item.originprice"
                :title="item.proname"
                :thumb="item.img1"
              >
                <template #num>
                  <van-stepper v-model="item.num" theme="round" button-size="22" @change="updateCartNum(item.num, item.cartid)"/>
                </template>
              </van-card>
            </van-col>
          </van-row>
​
          <template #right>
            <van-button square text="删除" type="danger" class="delete-button" @click="deleteCart(item.cartid)"/>
          </template>
        </van-swipe-cell>
        <van-submit-bar :price="totalPrice" button-text="提交订单" @submit="onSubmit">
          <van-checkbox v-model="checked" @click="chooseAll">全选</van-checkbox>
        </van-submit-bar>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import { NavBar, Empty, Button, Card, Stepper, SwipeCell, NoticeBar, SubmitBar, Checkbox, Col, Row } from 'vant'
import { getCartList, remove, updateNum, selectAll, selectOne } from '../../api/cart'
Vue.use(NavBar)
Vue.use(Empty)
Vue.use(Button)
Vue.use(Card)
Vue.use(Stepper)
Vue.use(SwipeCell)
Vue.use(NoticeBar)
Vue.use(SubmitBar)
Vue.use(Checkbox)
Vue.use(Col)
Vue.use(Row)
export default {
  data () {
    return {
      empty: true,
      cartList: [],
      checked: false // 全选
    }
  },
  computed: {
    totalNum () {
      return this.cartList.reduce((sum, item) => {
        return item.flag ? sum + item.num : sum + 0
      }, 0)
    },
    totalPrice () {
      return this.cartList.reduce((sum, item) => {
        return item.flag ? sum + item.num * item.originprice : sum + 0
      }, 0) * 100
    }
  },
  methods: {
    onSubmit () {
      console.log('提交订单')
    },
    chooseOne (cartid, flag) {
      console.log(cartid, flag)
      selectOne({ cartid, flag }).then(() => this.getCartListData())
    },
    chooseAll () {
      selectAll({
        userid: localStorage.getItem('userid'),
        type: this.checked // 数据的双向绑定
      }).then(() => this.getCartListData())
    },
    deleteCart (cartid) {
      remove({ cartid }).then(() => this.getCartListData())
    },
    updateCartNum (num, cartid) {
      console.log(num, cartid)
      updateNum({
        num, cartid
      }).then(() => this.getCartListData())
    },
    getCartListData () {
      getCartList({
        userid: localStorage.getItem('userid')
      }).then(res => {
        console.log(res.data)
        if (res.data.code === '10020') {
          this.empty = true
          this.cartList = [] // 临界值变化 - 否则删除完所有的商品,商品总数量有问题
        } else {
          this.empty = false
          this.cartList = res.data.data
          // 判断全选的状态
          this.checked = this.cartList.every(item => item.flag)
        }
      })
    }
  },
  mounted () {
    if (localStorage.getItem('loginState') === 'true') {
      // 前端校验为登录状体啊
      this.getCartListData()
    } else {
      this.$router.push('/login')
    }
  }
}
</script>
​
<style lang="stylus">
.delete-button
  height 100%
.my-checkbox
  height 1.04rem
  background-color #fff
  display flex
  justify-content center
  align-items center
</style>
​

12.10猜你喜欢

// src/api/cart.js
// 猜你喜欢数据
export function getRecommendList (params) {
  return request.get('/pro/recommendlist', params)
}
<template>
  <div class="box">
    <header class="header">
      <van-nav-bar
        :title = 'totalNum > 0 ? "购物车-" + totalNum : "购物车"'
        left-arrow
        @click-left="$router.back()"
      ></van-nav-bar>
    </header>
    <div class="content">
      <div class="npShop" v-if="empty">
        <van-empty description="购物车空空如也">
          <van-button round type="danger" class="bottom-button">立即购物</van-button>
        </van-empty>
      </div>
      <div class="shop" v-else>
        <van-notice-bar
          mode="closeable"
          left-icon="volume-o"
          text="亲爱的用户,像左滑动视图可以展示删除按钮,祝您在嗨购商场中购物愉快"
        />
        <van-swipe-cell
          v-for="item of cartList"
          :key="item.cartid"
        >
          <van-row>
            <van-col span="2">
              <div class="my-checkbox">
                <van-checkbox v-model="item.flag" @change="chooseOne(item.cartid, item.flag)"></van-checkbox>
              </div>
            </van-col>
            <van-col span="22">
              <van-card
                :price="item.originprice"
                :title="item.proname"
                :thumb="item.img1"
              >
                <template #num>
                  <van-stepper v-model="item.num" theme="round" button-size="22" @change="updateCartNum(item.num, item.cartid)"/>
                </template>
              </van-card>
            </van-col>
          </van-row>
​
          <template #right>
            <van-button square text="删除" type="danger" class="delete-button" @click="deleteCart(item.cartid)"/>
          </template>
        </van-swipe-cell>
        <van-submit-bar :price="totalPrice" button-text="提交订单" @submit="onSubmit">
          <van-checkbox v-model="checked" @click="chooseAll">全选</van-checkbox>
        </van-submit-bar>
      </div>
      <van-divider>猜你喜欢</van-divider>
      <ProList :proList="proList"></ProList>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import { Divider, NavBar, Empty, Button, Card, Stepper, SwipeCell, NoticeBar, SubmitBar, Checkbox, Col, Row } from 'vant'
import { getCartList, remove, updateNum, selectAll, selectOne, getRecommendList } from '../../api/cart'
import ProList from '@/components/ProList.vue'
Vue.use(Divider)
Vue.use(NavBar)
Vue.use(Empty)
Vue.use(Button)
Vue.use(Card)
Vue.use(Stepper)
Vue.use(SwipeCell)
Vue.use(NoticeBar)
Vue.use(SubmitBar)
Vue.use(Checkbox)
Vue.use(Col)
Vue.use(Row)
export default {
  components: {
    ProList
  },
  data () {
    return {
      empty: true,
      cartList: [],
      checked: false, // 全选
      proList: []
    }
  },
  computed: {
    totalNum () {
      return this.cartList.reduce((sum, item) => {
        return item.flag ? sum + item.num : sum + 0
      }, 0)
    },
    totalPrice () {
      return this.cartList.reduce((sum, item) => {
        return item.flag ? sum + item.num * item.originprice : sum + 0
      }, 0) * 100
    }
  },
  methods: {
    onSubmit () {
      console.log('提交订单')
    },
    chooseOne (cartid, flag) {
      console.log(cartid, flag)
      selectOne({ cartid, flag }).then(() => this.getCartListData())
    },
    chooseAll () {
      selectAll({
        userid: localStorage.getItem('userid'),
        type: this.checked // 数据的双向绑定
      }).then(() => this.getCartListData())
    },
    deleteCart (cartid) {
      remove({ cartid }).then(() => this.getCartListData())
    },
    updateCartNum (num, cartid) {
      console.log(num, cartid)
      updateNum({
        num, cartid
      }).then(() => this.getCartListData())
    },
    getCartListData () {
      getCartList({
        userid: localStorage.getItem('userid')
      }).then(res => {
        console.log(res.data)
        if (res.data.code === '10020') {
          this.empty = true
          this.cartList = [] // 临界值变化 - 否则删除完所有的商品,商品总数量有问题
        } else {
          this.empty = false
          this.cartList = res.data.data
          // 判断全选的状态
          this.checked = this.cartList.every(item => item.flag)
        }
      })
    }
  },
  mounted () {
    if (localStorage.getItem('loginState') === 'true') {
      // 前端校验为登录状体啊
      this.getCartListData()
    } else {
      this.$router.push('/login')
    }
    getRecommendList().then(res => {
      this.proList = res.data.data
    })
  }
}
</script>
​
<style lang="stylus">
.delete-button
  height 100%
.my-checkbox
  height 1.04rem
  background-color #fff
  display flex
  justify-content center
  align-items center
</style>
​
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值