一、简介:
使用Vue.js实现简单的书籍购物车,包括以下功能:
- 书籍列表可显示当前商店里所上架书籍的信息:包括书名、价格和余量;点击书籍最后的“加入购物车”按钮,可将书籍加入到下方的书籍购物车;当余量为0时,不能进行加入购物车的操作;列表上方有可以搜索书籍的搜索框,输入书籍名包含的字即可实时显示相关书籍。
- 书籍购物车显示当前已经加入购物车内的书籍信息:包括书名、价格、数量;在数量中,有“+”和“-”两个按钮,可对书籍的数量进行增加或减少;点击书籍最后的“移除”按钮,可将该本书籍移除购物车;书籍购物车左上方有“清空购物车按钮”,点击该按钮会移除购物车中所有的书籍;当购物车中该本书的数量为1时,“-”操作不可用;当购物车中该本书的数量为该本书原来的余量,即现在书籍列表中该本书余量为0时,“+”操作不可用。
- 点击书籍后的“加入购物车”按钮,当购物车中已经存在相同的书籍时,“加入购物车”按钮和“+”按钮功能相同。
- 书籍列表和书籍购物车中的书籍数量会随着相应操作实时更新。
- 最下方显示当前书籍购物车中的总价格。
二、部分功能演示:
- 初始界面:
- 搜索书籍
- 加入书籍到购物车和增加数量
三、实现:
为阅读方便,将html、css和js代码放到一起。样式采用Bootstrap相关样式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>书籍购物车</title>
<!-- 导入 bootstrap.css 和 vue.js -->
<link rel="stylesheet" type="text/css" href="lib/bootstrap.css">
<script src="lib/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 创建书籍列表 -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">书籍列表</h3>
</div>
<div class="panel-body">
<label style="padding-left: 10px; margin-bottom: 10px" class="form-inline">
搜索书籍:
<!-- 将搜索框输入的内容使用v-model指令与keywords进行双向绑定 -->
<input type="text" v-model="keywords" class="form-control">
</label>
<table class="table table-bordered table-hover" style="width: 60%">
<thead>
<tr>
<th>书名</th>
<th>价格</th>
<th>余量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 使用v-for指令遍历search方法中所得到的数组,并显示到书籍列表中(search方法实现见vue实例) -->
<tr v-for="(item, index) in search(keywords)">
<td>{{item.name}}</td>
<td>{{item.price | showPrice}}</td>
<td>{{item.count}}</td>
<td>
<!-- 为加入购物车按钮绑定事件add(index), 同时动态绑定将disabled属性与书籍的数量绑定,当书籍的余量小于等于0时,该按钮不可用 -->
<button class="btn btn-primary" @click="add(index)" :disabled="item.count <= 0">加入购物车</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 创建书籍购物车 -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">书籍购物车</h3>
</div>
<div class="panel-body form-inline">
<!-- 将清空购物车按钮与事件clear()绑定 -->
<button class="btn btn-primary pull-right" @click="clear">清空购物车</button>
<table class="table table-bordered table-hover" style="width: 60%">
<thead>
<tr>
<th>书名</th>
<th>价格</th>
<th>数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 遍历用户所选书籍数组,将其显示到书籍购物车中 -->
<tr v-for="(item, index) in userBooks">
<td>{{item.name}}</td>
<!-- 使用过滤器显示价格 -->
<td>{{item.price | showPrice}}</td>
<td>
<!-- "-"按钮绑定decrement(index),同时动态绑定将disabled属性与书籍的数量绑定,当购物车中书籍的数量小于等于0时,该按钮不可用 -->
<button class="btn btn-primary" @click="decrement(index)" :disabled="item.count <= 1">-</button>
{{item.count}}
<!-- "+"按钮绑定increment(index),同时动态绑定将disabled属性与计算属性isAdd绑定,当该书籍余量为0时,该按钮不可用-->
<button class="btn btn-primary" @click="increment(index)" :disabled="isAdd(index)">+</button>
</td>
<td>
<!-- "移除"按钮绑定removeBook(index) -->
<button class="btn btn-primary" @click="removeBook(index)">移除</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 使用过滤器显示价格 -->
<h4>总价格:{{ totalPrice | showPrice }}</h4>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
keywords: '',
<!-- 书籍列表 -->
books: [
{id: 1, name: '人间失格', price: 25, count: 13},
{id: 2, name: '活着', price: 28, count: 10},
{id: 3, name: '三体(全三册)', price: 93, count: 3},
{id: 4, name: '白夜行', price: 28, count: 5},
{id: 5, name: '追风筝的人', price: 19, count: 11},
{id: 6, name: '人生海海', price:55, count: 6}
],
<!-- 书籍所选书籍列表 -->
userBooks: [],
},
methods: {
<!-- "+"按钮绑定事件:点击书籍数量加1 -->
increment(index) {
<!-- 找到index下标在书籍列表中所对应的书籍,并将其数量减1 -->
for(let i of this.books){
if(i.id === this.userBooks[index].id){
i.count --;
}
}
<!-- 将该书籍数量加1 -->
this.userBooks[index].count ++;
},
<!-- "-"按钮绑定事件:点击书籍数量减1 -->
decrement(index) {
for(let i of this.books){
if(i.id === this.userBooks[index].id){
i.count ++;
}
}
this.userBooks[index].count --;
},
<!-- "移除"按钮绑定事件:点击将该书籍从购物车中移除 -->
removeBook (index) {
<!-- 找到index下标在书籍列表中所对应的书籍,并将其数量改为现有数量加上购物车中的该书籍的数量 -->
for(let i of this.books){
if(i.id === this.userBooks[index].id){
i.count += this.userBooks[index].count;
}
}
<!-- 在用户所购书籍列表中删除该书籍 -->
this.userBooks.splice(index, 1);
},
<!-- "加入购物车"按钮绑定事件:点击将该书籍加入购物车 -->
add(index) {
<!-- 若购物车中已有该书籍,则该按钮功能和"+"按钮相同 -->
for (let item of this.userBooks){
if(item.id === this.books[index].id){
item.count ++;
this.books[index].count --;
return;
}
}
<!-- 将新书籍加入购物车 -->
let book = {};
const returnedTarget = Object.assign(book, this.books[index]);
book['count'] = 1;
this.userBooks.push(book);
this.books[index].count --;
},
<!-- "清空购物车"按钮绑定事件:点击将书籍购物车清空 -->
clear() {
<!-- 将购物车中的书籍数量重新加到书籍列表中 -->
for (let i of this.userBooks){
for(let j of this.books){
if(i.id === j.id){
j.count += i.count;
}
}
}
<!-- 清空用户所选书籍列表 -->
this.userBooks.splice(0, this.userBooks.length);
},
<!-- 搜索书籍:把搜索框中的关键字keywords作为参数传给search -->
<!-- 在search内部,通过for循环,把所有符合关键字的数据保存到一个新数组中返回 -->
search(keywords) {
return this.books.filter(item => {
if (item.name.includes(keywords))
return item;
})
},
},
computed: {
<!-- 计算属性:总价格 -->
<!-- 通过遍历用户所选书籍列表计算得到 -->
totalPrice() {
let price = 0;
for(let i of this.userBooks){
price += i.price * i.count;
}
return price;
},
<!-- 传入参数的计算属性:判断"+"按钮是否可用 -->
isAdd() {
return function (index) {
for(let i of this.books){
if(i.id === this.userBooks[index].id){
if(i.count <= 0) {
return true;
}
else {
return false;
}
}
}
}
}
},
<!-- 过滤器:价格显示为¥+保留两位小数的数字 -->
filters: {
showPrice(price) {
return '¥' + price.toFixed(2);
}
}
})
</script>
</body>
</html>