案例-水果购物车

1.7k 词
案例-水果购物车
选中
图片
单价
个数
小计
操作
{{ item.price }}
{{ item.num }}
{{ item.num*item.price }}
总价  :  ¥  {{ totalPrice }}
🛒空空如也

案例-水果购物车#

需求说明:

  1. 渲染功能
  2. 删除功能
  3. 修改个数
  4. 全选反选
  5. 统计选中的总价和总数量
  6. 持久化到本地

业务技术点总结:

  1. 渲染: v-if/v-else v-for :class
  2. 删除: 点击传参 filter 过滤覆盖原数组
  3. 修改个数: 点击传参 find (未使用)找对象
  4. 全选反选:计算属性 computed 完整写法 get/set
  5. 统计总价/总数:计算属性 computed reduce 条件求和
  6. 持久化到本地: watch 监视, localStroage JSON.stringify JSON.parse

渲染v-for#

<div class="tr" v-for="(item, index) in fruitList" :key="item.id" v-bind:class="{active:item.isChecked}">
    <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
    <div class="td"><img :src="item.icon" alt="" /></div>
    <div class="td">{{ item.price }}</div>
    <div class="td">
        <div class="my-input-number">
            <button class="decrease">-</button>
            <span class="my-input__inner">{{ item.num }}</span>
            <button class="increase">+</button>
        </div>
    </div>
    <div class="td">{{ item.num*item.price }}</div>
    <div class="td"><button>删除</button></div>
</div>

删除#

删除按钮点击传递当前id,根据id筛选出其他项并覆盖原对象列表,数据更新视图更新

<div class="td"><button @click="del(item.id)">删除</button></div>
methods: {
    del(id) {
        this.fruitList = this.fruitList.filter(item => item.id !== id);
    },
},

修改个数#

减号自减一,加号自增一

减到数量为一时禁用减号,加号可根据商品库存设限

<div class="my-input-number">
    <button class="decrease" @click="item.num--" :disabled="item.num<=1">-</button>
    <span class="my-input__inner">{{ item.num }}</span>
    <button class="increase" @click="item.num++">+</button>
</div>

全选反选#

全选框绑定 isCheckAll 计算属性

get返回所有小选框是否都被选中的布尔值,同步给全选框

set将全选框状态同步给所有小选框

<label class="check-all">
    <input type="checkbox" v-model="isCheckAll" />
    全选
</label>
computed: {
    isCheckAll: {
        // 需要设置值,所以用完整写法
        get() {
            // 必须所有小选框都选中,全选框才选中--every
            return this.fruitList.every(item => item.isChecked);
        },
        set(value) {
            // 基于拿到的全选框值同步所有小选框
            this.fruitList.forEach(item => {
                item.isChecked = value;
            });
        },
    },
},

统计总价/总数#

总价/总数部分计算的是:勾选上的商品的价格及数量

需要判断 isChecked 为true才计算

<div class="right-box">
    <!-- 所有商品总价 -->
    <span class="price-box">
        总价  :  ¥ 
        <span class="price">{{ totalPrice }}</span>
    </span>
    <!-- 结算按钮 -->
    <button class="pay">结算( {{ totalCount }} )</button>
</div>
computed: {
    // ...
    // 统计选中的总数
    totalCount() {
        return this.fruitList.reduce((sum, item) => (item.isChecked ? sum + item.num : sum + 0), 0);
    },
    // 统计选中的总价num*price
    totalPrice() {
        return this.fruitList.filter(item => item.isChecked).reduce((sum, item) => (sum += item.num * item.price), 0);
    },
},

持久化到本地#

用户操作的数据需要存到后台,但这里没有提供接口,所以存到本地

任何一个数据变化都要重新存储,所以用到 watch

将原本的商品数据换成存入本地的数据,为了避免本地没有数据,用逻辑或 || 的短路原理使表达式在左侧为空时取右边表达式默认数据,原本的数据存在 defaultArr

// ...
data: {
    // 水果列表
    fruitList: JSON.parse(localStorage.getItem('list')) || defaultArr,
},
// ...
watch: {
    fruitList: {
        deep: true,
        handler(newValue) {
            // 需要将变化后的数据newValue存入本地,记得转JSON格式
            localStorage.setItem('list', JSON.stringify(newValue));
        },
    },
},