简单版购物车实例

本节通过购物车实例巩固 v-for 循环遍历数据、v-model 数据双向绑定、数据定义、方法定义、v-if 等知识点。

购物车实例简介

由于还没有介绍网络数据请求,本节采用模拟数据进行购物车讲解,在此实例中将会讲解购物车数量计算、价格计算、购物车多选和全选等操作,最终效果如图 2-1 所示。

image 2025 02 11 11 35 26 418
Figure 1. 图2-1 购物车实例效果图

静态页面布局

下列代码只包括视图层代码和 M 层代码,样式代码可在配套资源中下载。

视图层代码如下。

<div id="app">
    <div class="head">购物车</div>
    <table cellpadding="5" cellspacing="0">
        <tr>
            <td><input type="checkbox" /></td>
            <td><img src="p1.jpg" /></td>
            <td>五香瓜子<span>19</span></td>
            <td>
                <button>-</button>
                <input type="text" value="1" />
                <button>+</button>
            </td>
        </tr>
        <tr>
            <td><input type="checkbox" /></td>
            <td><img src="p1.jpg" /></td>
            <td>五香瓜子<span>19</span></td>
            <td>
                <button>-</button>
                <input type="text" value="1" />
                <button>+</button>
            </td>
        </tr>
        <tr>
            <td><input type="checkbox" /></td>
            <td><img src="p1.jpg" /></td>
            <td>五香瓜子<span>19</span></td>
            <td>
                <button>-</button>
                <input type="text" value="1" />
                <button>+</button>
                <button>删除</button>
            </td>
        </tr>
    </table>
    <div class="footer">
        <input type="checkbox" />全选
        <i>总数量:100</i>
        <i>总价:1000</i>
    </div>
</div>

Vue 初始代码如下。

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'hello'
        }
    });
</script>

渲染购物车列表

(1)在 data 中模拟购物车数据,代码如下。

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            // 模拟购物车数据
            cartlist: [
                {
                    id: 1,
                    imgurl: 'https://i.loli.net/2021/04/29/YqZCykhzotGmx6D.jpg',
                    title: '瓜子',
                    price: 30,
                    num: 1,
                    check: false // 记录是否选中
                },
                {
                    id: 2,
                    imgurl: 'https://i.loli.net/2021/04/29/dHNmEWJgCajtMr1.jpg',
                    title: '花生',
                    price: 30,
                    num: 1,
                    check: false
                },
                {
                    id: 3,
                    imgurl: 'https://i.loli.net/2021/04/29/12CdfSPayn5QOb6.jpg',
                    title: '西瓜子',
                    price: 30,
                    num: 2,
                    check: false
                }
            ]
        }
    });
</script>

代码解析如下。

对象中的 check 表示是否选中该商品,false 表示未选中,true 表示已选中。

(2)将 data 中定义的购物车数据渲染到视图层,代码如下。

<table cellpadding="5" cellspacing="0">
    <tr v-for="(item, i) in cartlist" :key="i">
        <td v-if="item.check">
            <input type="checkbox" checked />
        </td>
        <td v-else>
            <input type="checkbox" />
        </td>
        <td>
            <img :src="item.imgurl" />
        </td>
        <td>
            {{ item.title }}<span>{{ item.price }}</span>
        </td>
        <td>
            <button>-</button>
            <input type="text" :value="item.num" />
            <button>+</button>
        </td>
    </tr>
</table>

代码解析如下。

使用 v-if 和 v-else 控制 checkbox 选中状态,当 v-if 结果为 true 时,checkbox 为选中状态。

修改商品选中状态

前述实例只是把商品渲染完成,并根据 check 属性值显示商品是否被选中。但是当选中 checkbox 复选框时,并不能实时修改 check 属性值,本节将实现商品选中状态的实时修改。

在视图层给 checkbox 添加单击事件,代码如下。

<tr v-for="(item, i) in cartlist" :key="i">
    <td v-if="item.check">
        <input type="checkbox" checked @click="checkbtn(i)" />
    </td>
    <td v-else>
        <input type="checkbox" @click="checkbtn(i)" />
    </td>
</tr>

代码解析如下。

checkbtn(i) 为事件方法,需要传递数组索引作为参数,其目的是记录具体操作的是哪一种商品。

在 methods 中定义 checkbtn 方法,代码如下。

methods: {
    // 单击 checkbox 修改 check 值
    checkbtn(i) {
        // 打印选中商品
        console.log(this.cartlist[i]);

        // 修改 check 属性
        this.cartlist[i].check = !this.cartlist[i].check;
    }
}

记录选中商品的总数量和总价格

当前我们已经实现了商品选中和取消状态的实时操作,本节实现选中商品的总数量和总价格功能。

(1)在 data 中定义 allPrice 和 allNum 属性,记录商品的总数量和总价格,代码如下。

data() {
    return {
        allPrice: 0, // 总价格
        allNum: 0,   // 总数量
        // 模拟购物车数据
        cartlist: [
            // ...
        ]
    };
}

(2)在视图层渲染商品的总数量和总价格,代码如下。

<div class="footer">
    <input type="checkbox" />全选
    <i>总数量:{{ allNum }}</i>
    <i>总价格:{{ allPrice }}</i>
</div>

(3)在 methods 中定义 getAllNum() 方法,获取商品总数量

methods: {
    // 获取商品总数量
    getAllNum() {
        var num = 0;
        for (var i = 0; i < this.cartlist.length; i++) {
            if (this.cartlist[i].check == true) {
                // 选中商品的数量(可能买多个)
                num += parseInt(this.cartlist[i].num);
            }
        }
        this.allNum = num;
    }
}

(4)在 checkbtn() 方法中调用 getAllNum() 方法

methods: {
    checkbtn(i) {
        console.log(this.cartlist[i]);
        this.cartlist[i].check = !this.cartlist[i].check;
        // 获取总数量
        this.getAllNum();
    }
}

(5)在 methods 中定义 getAllPrice() 方法,获取商品总价格

methods: {
    // 获取总价格
    getAllPrice() {
        var num = 0;
        for (var i = 0; i < this.cartlist.length; i++) {
            if (this.cartlist[i].check == true) {
                num += parseInt(this.cartlist[i].num) * this.cartlist[i].price;
            }
        }
        this.allPrice = num;
    }
}

代码解析如下。

循环遍历整个商品列表,当商品中的 check 属性为 true 时,表示选中了该商品,商品总价格为选中商品的数量×选中商品的价格,最后把遍历出来的商品总价赋值给 data 中的 allPrice。

(6)在 checkbtn() 方法中调用 getAllPrice() 方法,代码如下。

methods: {
    checkbtn(i) {
        console.log(this.cartlist[i]);
        this.cartlist[i].check = !this.cartlist[i].check;

        // 获取总数量
        this.getAllNum();

        // 获取总价格
        this.getAllPrice();
    },
}

全选状态

全选分为两种状态,即状态1:选中所有商品。当把所有的商品都选中时,最底部的 “全选” 复选框应该自动选中。状态2:选中底部的 “全选” 复选框。当选中最底部的 “全选” 复选框时,列表中所有的商品应该都被选中。

状态1:选中所有商品

(1)在 data 属性中,新增选中 allCheck 记录的总条数,如果选中的总条数等于商品列表的条数,表示选中了所有商品,代码如下。

data() {
    return {
        allCheck: 0, // 选中总条数
        allPrice: 0, // 总价格
        allNum: 0,   // 总数量
        cartlist: [  // 购物车商品列表
            // ...
        ]
    };
}

(2)在 methods 中新增 getAllCheck() 方法,获取选中商品的个数,代码如下。

methods: {
    // 判断全选,获取选中个数
    getAllCheck() {
        var num = 0;
        for (var i = 0; i < this.cartlist.length; i++) {
            if (this.cartlist[i].check == true) {
                num++;
            }
        }
        this.allCheck = num;
    }
}

(3)在 checkbtn() 方法中调用 getAllCheck() 方法

methods: {
    checkbtn(i) {
        console.log(this.cartlist[i]);
        this.cartlist[i].check = !this.cartlist[i].check;

        // 获取总数量
        this.getAllNum();

        // 获取总价格
        this.getAllPrice();

        // 获取选中个数
        this.getAllCheck();
    }
}

(4)使用 v-if 判断底部的全选按钮是否被选中,代码如下。

<div class="footer">
    <em v-if="allCheck == cartlist.length">
        <input type="checkbox" checked />全选
    </em>
    <em v-else>
        <input type="checkbox" />全选
    </em>
</div>

状态2:选中底部的“全选”复选框

(1)在视图层给 “全选” 复选框增加 allCheckbtn 单击事件,代码如下。

<div class="footer">
    <em v-if="allCheck == cartlist.length">
        <input type="checkbox" checked @click="allCheckbtn" />全选
    </em>
    <em v-else>
        <input type="checkbox" @click="allCheckbtn" />全选
    </em>
    <i>总数量:{{ allNum }}</i>
    <i>总价:{{ allPrice }}</i>
</div>

(2)在 methods 中新增 allCheckbtn() 方法,实现全选功能,并计算商品数量和商品价格,代码如下。

methods: {
    allCheckbtn(e) {
        // 判断“全选”复选框是否选中
        console.log(e.target.checked);

        if (e.target.checked) {
            // 如果选中,将所有商品的 check 属性设置为 true
            for (var i = 0; i < this.cartlist.length; i++) {
                this.cartlist[i].check = true;
            }
        } else {
            // 如果未选中,将所有商品的 check 属性设置为 false
            for (var i = 0; i < this.cartlist.length; i++) {
                this.cartlist[i].check = false;
            }
        }

        // 获取总数量
        this.getAllNum();

        // 获取总价格
        this.getAllPrice();
    }
}

代码解析如下。

为 allCheckbtn() 方法添加事件参数 e,“e.target.checked” 可以判断复选框是 true 还是 false,true 表示选中 “全选” 复选框,false 表示未选中 “全选” 复选框。

如果是选中状态,循环遍历整个商品列表,拿到每件商品的 check 属性,设置为选中状态 true,同时调用计算商品总数量和总价格的方法即可。

商品数量增加或减少

购物车的最后一个功能是商品数量的增加或减少,具体实现步骤如下。

(1)在视图层为 “增加” 和 “减少” 按钮处添加单击事件,并传入商品索引,表示具体操作哪条数据。

<button @click="min(i)">-</button>
<input type="text" :value="item.num" />
<button @click="add(i)">+</button>

(2)在 methods 中新增 add() 和 min() 方法

methods: {
    // 增加商品数量
    add(i) {
        this.cartlist[i].num++;

        // 获取总数量
        this.getAllNum();

        // 获取总价格
        this.getAllPrice();
    },

    // 减少商品数量
    min(i) {
        // 如果商品数量已经是 1,则不再减少
        if (this.cartlist[i].num == 1) {
            return;
        }
        this.cartlist[i].num--;

        // 获取总数量
        this.getAllNum();

        // 获取总价格
        this.getAllPrice();
    }
}