Vue学习:22-组件之购物车案例

main.js

// import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'


const app = createApp(App)


app.mount('#app')

App.vue

<script setup>
  import { computed,ref } from 'vue'

  //引入产品组件
  import ProductVue from './components/Product.vue';
  
  //引入汇总组件
  import CollectVue from './components/Collect.vue';

  // 购物车中的产品数据
let shopCar = ref([
    {
        id: 89,
        title: '四川爱媛38号果冻橙 当季时令应季彩箱装甜桔橘子新鲜水果专区 净重2斤小果尝鲜装(力荐大果,口感更好更实惠)',
        subtitle: '由初逐旗舰店从 四川眉山市 发货, 并提供售后服务. 现在至明日17:00前完成下单,预计11月15日19:30前发货',
        image: 'https://img12.360buyimg.com/n1/jfs/t1/39198/22/19565/188868/634a3bc4Ea15f2eee/2bb232b36cdd285c.jpg',
        price: 10,
        count: 1,
        selected: false
    },
    {
        id: 102,
        title: '【现货速发】新鲜四季青柠檬 无籽香水柠檬当季生鲜小青柠檬奶茶店水果 有籽青柠檬1斤装试吃【50-80克】',
        subtitle: '由朵艾美水果旗舰店发货, 并提供售后服务. 现在至明日16:00前完成下单,预计11月16日23:30前发货',
        image: 'https://img12.360buyimg.com/n1/jfs/t1/191077/5/6346/108268/60beea0dEc3a6d2ad/15db7dd619a0bc4f.jpg',
        price: 9,
        count: 3,
        selected: true
    },
    {
        id: 108,
        title: '新疆阿克苏冰糖心苹果 新鲜时令水果 阿克苏苹果红富士 10斤礼盒装 单果75-85mm 净重9斤多',
        subtitle: '由阿克苏苹果旗舰店发货, 并提供售后服务. 现在至明日16:00前完成下单,预计11月16日20:30前发货',
        image: 'https://img13.360buyimg.com/n1/jfs/t1/64647/33/22918/106322/6360afb1E9bab1003/a82bda0aeae6e953.png',
        price: 80,
        count: 2,
        selected: false
    }
])


//产品的状态发生改变
function changeShopCarProductChecked(checked,id) {
  //循环购物车中的每个产品
  shopCar.value.some(product => {
    //判断更改的是哪一个产品的checked状态
    if(id === product.id) {
      product.selected = checked  //改变购物车中指定产品的选中的值
      return true  //结束循环
    }
  })
}

//产品数量更改
function changeShopCarProductCount(count,id) {
  //循环购物车中的每个产品
  shopCar.value.some(product => {
    //判断更改的是哪一个产品的count
    if(id === product.id) {
      product.count += count  //改变购物车中指定产品的数量
      return true  //结束循环
    }
  })
}

//是否全选购物车产品
let isFullSelectProduct = computed(()=>{
  return shopCar.value.every(product => product.selected)
})

//改变购物车所有产品的选中状态
function changeShopCarAllProductCheckedState(checked) {
  shopCar.value.forEach(product => product.selected=checked)
}

//总金额
let total = computed(()=>{
  return shopCar.value.filter(item => item.selected).reduce((money,item)=>(money+=item.price*item.count),0)
})

//购买总数量
let countSum = computed(()=>{
  return shopCar.value.filter(item => item.selected).reduce((count,item)=>(count+=item.count),0)
})

</script>

<!-- 视图区域(view) -->
<template>
  <!-- 产品组件 -->
  <ProductVue 
    v-for="product in shopCar" 
    :key="product.id"
    :id="product.id"
    :picture="product.image"
    :title="product.title"
    :subtitle="product.subtitle"
    :price="product.price"
    :count="product.count"
    :is-checked="product.selected"
    @change-product-checked="changeShopCarProductChecked"
    @change-product-count="changeShopCarProductCount"
  />

  <hr>

  <!-- 使用汇总组件 -->
  <CollectVue 
    :is-all-checked="isFullSelectProduct"
    :all-money="total"
    :all-count="countSum"
    @change-all-checked-state="changeShopCarAllProductCheckedState"
  />
</template>
<style>
 * {
  margin: 0;
  padding: 0;
 }
</style>

Product.vue

<script setup>
//声明组件的自定义属性
let propsData = defineProps({
  id: {type: Number,required: true},  //产品编号
  isChecked: Boolean,  //是否被选中
  picture: {type: String,required: true},  //图像
  title: {type: String,required: true},  //标题
  subtitle: {type: String,required: true}, //副标题
  price: {type: Number,default: 0},  //单价
  count: {type: Number,default: 0}   //数量
})

//自定义事件
let emits = defineEmits([
  'changeProductChecked',  //改变复选框的状态事件
  'changeProductCount'  //改变产品数量
])

//改变产品选中的状态
function changeCheckedState(e) {
  let newCheckedState = e.target.checked  //复选框最新的状态
  emits('changeProductChecked',newCheckedState,propsData.id)  //触发自定义事件,并向父组件传值
}
</script>

<template>
  <div class="box">
    <!-- 复选框 -->
    <input type="checkbox" class="p_checkbox" :checked="isChecked" @change="changeCheckedState">
    <!-- 产品图 -->
    <img class="p_image" :src="picture">
    <!-- 产品内容 -->
    <div class="p_content">
      <!-- 标题 -->
      <h3 class="p_title" v-text="title"></h3>
      <!-- 副标题 -->
      <span class="p_subtitle" v-text="subtitle"></span>
      <!-- 价格 -->
      <h2 class="p_price">¥{{ price }}</h2>
      <!-- 产品数量区域 -->
      <div class="p_count_area">
        <button :disabled="count <= 1" @click="emits('changeProductCount',-1,id)">-</button>
        <!-- 购买数量 -->
        <span v-text="count"></span>
        <button @click="emits('changeProductCount',1,id)">+</button>
      </div>
    </div>
  </div>
</template>

<style>
.box {
  box-shadow: 0 0 8px gray;
  padding: 20px;
  margin: 15px;
  display: flex;
  align-items: center;
}

.p_checkbox {
  width: 25px;
  height: 25px;
}

.p_image {
  width: 120px;
  height: 120px;
  margin: 0 20px;
}

.p_content {
  align-self: start;
  position: relative;
  width: 100%;
}

.p_title {
  margin-bottom: 8px;
}

.p_subtitle {
  font-size: 14px;
  color: gray;
}

.p_price {
  margin-top: 20px;
  color: rgb(201,67,67);
}

.p_count_area {
  position: absolute;
  bottom: 0;
  right: 0;
}

.p_count_area button {
  width: 25px;
  height: 25px;
}

.p_count_area span {
  margin: 0 10px;
}

</style>

Collect.vue

<script setup>
//自定义属性
defineProps({
  isAllChecked: Boolean,  //是否全选
  allMoney: {type: Number,default: 0},  //总金额
  allCount: {type: Number,default: 0}   //总个数
})

//自定义事件
let emits = defineEmits([
  'changeAllCheckedState'  //改变全选状态的事件
])

</script>

<template>
  <div class="container">
    <!-- 全选 -->
    <label>
      <input 
        type="checkbox" 
        :checked="isAllChecked"
        @change="emits('changeAllCheckedState',$event.target.checked)"
      >
      全选
    </label>
    <!-- 总金额 -->
    <span>
      合计金额:
      <strong>¥{{ allMoney }}</strong>
    </span>
    <!-- 结算按钮 -->
    <button>
      结算【{{ allCount }}】
    </button>
  </div>
</template>

<style>
.container {
  padding: 20px;
  margin: auto 15px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.container input {
  width: 25px;
  height: 25px;
}

.container label {
  display: flex;
  align-items: center;
  width: 70px;
  justify-content: space-between;
}

.container strong {
  color: red;
}

.container button {
  border: none;
  padding: 15px 25px;
  background-color: rgb(50,140,192);
  color: white;
  border-radius: 8px;
  box-shadow: 0 0 5px gray;
}
</style>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值