2022/9/1-9/2 VUE实现购物车案例

APP

<template>
  <div class="app-container">
    <Heahder></Heahder>
    <!-- <p>{{fullState}}</p> -->
    <!-- 循环渲染每一个商品信息 -->
    <Goods v-for="item in list" :key="item.id" :id="item.id" 
    :title="item.goods_name" :pic="item.goods_img" 
    :price="item.goods_price" :state="item.goods_state" 
    @state-change='getNewState' :count="item.goods_count">
    </Goods>
    <Footer :isfull="fullState" @full-change="getFullState" :amount="amt" :all="total"></Footer>
  </div>
</template>

<script>
import Footer from './components/Footer/Footer.vue'
import axios from 'axios'
import Heahder from './components/Header/Header.vue'
import Goods from './components/Goods/Goods.vue'
import bus from '@/components/eventbus.js'
export default {
  data () {
    return {
      //存储购物车列表数据
      list : []
    }
  },
  created(){
  this.initCarList()
  bus.$on('share',val=>{
    this.list.some(item=>{
      if(item.id === val.id){
        item.goods_count = val.value
        return true
        }
      })
    })
  },
  //计算属性,计算布尔值
  computed : {
    //动态计算全选的状态true false
    fullState(){
    return this.list.every(item=>item.goods_state === true)
    },
    amt () {
        //计算价先过滤再累加
    return  this.list.filter(item=>item.goods_state).reduce((total,item)=> total+=item.goods_price*item.goods_count,0)
    },
    //已勾选商品总数量
    total(){
    return  this.list.filter(item=>item.status).reduce((t,item)=>{ return t+=item.goods_count},0)
    }
  },

  methods : {
  async initCarList(){
    const {data:res} = await axios.get('https://www.escook.cn/api/cart')
    //请求回来数据在页面渲染需要使用必须转存DATA内
    if (res.status === 200) {
      this.list = res.list
    }
  },
  //接受子组件传的数据,终止后续循环
  getNewState(val){
    this.list.some(item=>{
      if(item.id === val.id){
        item.goods_state = val.value
        return true
      }
    })
  },
  //接受Footer子组件传递过来的全选按钮状态
  getFullState(val){
    this.list.forEach(item=>item.goods_state=val)
  }
  },
  components : {Heahder,Goods,Footer}
}
</script>

<style lang="less" scoped>
.app-container {
  padding-top: 45px;
  padding-bottom: 50px;
}
</style>

 在components内创建以文件名的文件夹

Counter.vue

<template>
  <div class="number-container d-flex justify-content-center align-items-center">
    <!-- 减 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="sub">-</button>
    <!-- 购买的数量 -->
    <span class="number-box">{{num}}</span>
    <!-- 加 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="add">+</button>
  </div>
</template>

<script>
import bus from '@/components/eventbus.js'
export default {
  props : {
    //接受到的num数量值
    num : {
     type : Number,
     default : 1
    }
  },
  methods : {
    add(){
      //发送给APP数据格式为{id,value}
      //其中ID的商品id.value是商品的购买数量
     const obj = {id : this.id , value : this.num + 1}  //感觉是问题所在
      //通过eventbus把obj发给app.vue组件
      bus.$emit('share',obj)
    },
    sub(){
      if(this.num === 0) return
     const obj = {id:this.id , value :this.num - 1}  //感觉是问题所在
     bus.$emit('share',obj)
    }
    
  }
}
</script>

<style lang="less" scoped>
.number-box {
  min-width: 30px;
  text-align: center;
  margin: 0 5px;
  font-size: 12px;
}

.btn-sm {
  width: 30px;
}
</style>

Footer.vue

<template>
  <div class="number-container d-flex justify-content-center align-items-center">
    <!-- 减 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="sub">-</button>
    <!-- 购买的数量 -->
    <span class="number-box">{{num}}</span>
    <!-- 加 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="add">+</button>
  </div>
</template>

<script>
import bus from '@/components/eventbus.js'
export default {
  props : {
    //接受到的num数量值
    num : {
     type : Number,
     default : 1
    }
  },
  methods : {
    add(){
      //发送给APP数据格式为{id,value}
      //其中ID的商品id.value是商品的购买数量
     const obj = {id : this.id , value : this.num + 1}  //感觉是问题所在
      //通过eventbus把obj发给app.vue组件
      bus.$emit('share',obj)
    },
    sub(){
      if(this.num === 0) return
     const obj = {id:this.id , value :this.num - 1}  //感觉是问题所在
     bus.$emit('share',obj)
    }
    
  }
}
</script>

<style lang="less" scoped>
.number-box {
  min-width: 30px;
  text-align: center;
  margin: 0 5px;
  font-size: 12px;
}

.btn-sm {
  width: 30px;
}
</style>

 

 Goods.vue

<template>
  <div class="goods-container">
    <!-- 左侧图片 -->
    <div class="thumb">
      <div class="custom-control custom-checkbox">
        <!-- 复选框 -->
        <input type="checkbox" class="custom-control-input" :id="'cb'+id" :checked="state"  @change="stateChange"/>
        <label class="custom-control-label" :for="'cb'+id">
          <!-- 商品的缩略图 -->
          <img :src='pic' alt="" />
        </label>
      </div>
    </div>
    <!-- 右侧信息区域 -->
    <div class="goods-info">
      <!-- 商品标题 -->
      <h6 class="goods-title">{{ title }}</h6>
      <div class="goods-info-bottom">
        <!-- 商品价格 -->
        <span class="goods-price">{{price}}</span>
        <!-- 商品的数量 -->
        <Counter :num="count" :id="id"></Counter>
      </div>
    </div>
  </div>
</template>

<script>
import Counter from '@/components//Counter/Counter.vue'
export default {
  components : {Counter},
  //商品的ID
  //接受商品ID,使用Eventbus方案把数值传递到App.vue的组件,更新哪个商品的数量
  props :{
    id:{
      //必存在
      required : true,
      type : Number
    },
    title : {
      default : '',
      type : String
      },
    
    //渲染商品图片
    pic : {
      default : '',
      type : String
    },
    //商品单价
    price : {
      default : 0 ,
      type : Number 
    },
    //商品勾选状态
    state : {
     default : true,
     type : Boolean
    },
    count : {
      type : Number,
      default : 1
    }
  },
  methods : {
    //只要复选框状态发生了变化,就调用这个函数
    stateChange(e){
      const newState = e.target.checked
      //触发自定义事件
      this.$emit('state-change',{id:this.id,value:newState })
    },
  }
}
</script>

<style lang="less" scoped>
.goods-container {
  + .goods-container {
    border-top: 1px solid #efefef;
  }
  padding: 10px;
  display: flex;
  .thumb {
    display: flex;
    align-items: center;
    img {
      width: 100px;
      height: 100px;
      margin: 0 10px;
    }
  }

  .goods-info {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;
    .goods-title {
      font-weight: bold;
      font-size: 12px;
    }
    .goods-info-bottom {
      display: flex;
      justify-content: space-between;
      .goods-price {
        font-weight: bold;
        color: red;
        font-size: 13px;
      }
    }
  }
}
</style>

Header.vue

<template>
  <div class="header-container">标题</div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.header-container {
  font-size: 12px;
  height: 45px;
  width: 100%;
  background-color: #1d7bff;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #fff;
  position: fixed;
  top: 0;
  z-index: 999;
}
</style>

 

因为使用了子传子所以增加组件eventbus.js 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值