文章目录
一、前言
环境:VScode+Chrome
技术栈:HTML+CSS+Vue(软件开发定制组件之前的知识运用—Vue基础语法)
软件开发定制将购物车实现分成三部软件开发定制分分别实现:
第一部分:软件开发定制简单购物车功能实现;
第二部分:软件开发定制将商品序号改为勾选框;
第三部分:软件开发定制添加商店对商品进行分类。
二、第一部分
软件开发定制页面效果图:
思路:
软件开发定制分为两部分,软件开发定制上半部分用于添加商品到购物车;软件开发定制下半部分用于展示购物车详情:
1、软件开发定制先把界面的框架(ui效果),软件开发定制大致画出来—这里使用table标签
2、软件开发定制先完成下半部分的逻辑
①循环数组:软件开发定制书籍名单是从服务器传来的,所以用for软件开发定制循环遍历获得;
②软件开发定制格式化价格:实现价格¥xx.00软件开发定制的表示方法(有两种);
③完成 + 和 – 按钮:软件开发定制购买数量的±软件开发定制按钮功能通过点击事件写js实现(注意传入index软件开发定制值知道是哪一行书籍)软件开发定制并且使用按钮的disabled功能使<=1软件开发定制时不能再点(软件开发定制解决会为负数的小bug);
④加移除:软件开发定制移除功能使用splice函数,且用ifelse判断当购物车为空时显示购物车为空;
⑤算总价:书籍总价格通过计算属性实时计算获得,最后返回一个值,通过第二步的过滤器实现最后的样子;
2、完成上半部分逻辑
①v-model绑定每个表单:在点击添加按钮时,要判断每个表单是否为空,有一个表单为空则提示请输入xx内容,然后return false;
②添加至购物车的逻辑:点击添加按钮,商品添加至购物车,并清空此时上半部分的input内容
三、第二部分
页面效果图:
1、把购物车的序列号,改为勾选框,当勾选起来的时候,才会计算总价;默认不勾选;列头有全选勾选框,可以实现全选功能。
2、添加商品的时候,增加判断,如果新增商品ID已经在购物车中,那么这个时候应该是把对应商品的数量增加;否则在购物车中新增商品行。
四、第三部分
页面效果图:
1、每个商品都有对应的商店:在data中用二维数组存放商品数据
(1)“添加要购买商品” 新增2个输入框,商店后台编号和商店名称。
(2)“购物车”按商店先划分,再按商品划分。
(3) 同样需要勾选框。这里的勾选框有3种:
a.全选勾选框,b.某个商店全选勾选框,c.某个商品的勾选框。勾选上的才算总价。默认不勾选。
大概属性定义思路:checkbox的属性是一个布尔属性,用v-model双向绑定一个布尔属性,即可在方法中用来实现各种功能;
这里分成三种复选框,一是isAllChecked(代表全选复选框,决定商店商品所有复选框的状态);二是isStChecked(代表商店复选框,决定某商店下所有商品的复选框状态);三是isChecked(代表商品复选框,决定该商品复选框的状态)
大概功能实现思路:
①先实现全选复选框(商店复选框)让下面所有商品复选框选中和取消选中
思路:让下面所有复选框的checked属性(选中状态) 跟随 全选复选框(商店复选框)即可
②下面商品复选框需要全部选中,全选复选框(商店复选框)才会选中做法:每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面就不选中。
(4) 同样添加商品的时候要加上判断。分3种情况:
a.新增商品已经在购物车中有了,则该商品数量增加;
b.新增商品不在购物车中,但是其对应的商店在购物车中,则在商店后面新增该商品;
c.新增商品不在购物车中,且对应商店也不在购物车中,新增商店和商品。
(可看问题总结5)
2、页面结构:
页面结构实现使用二维数组来实现商店商品,因为这里使用的是table来做,我的思路是之前的thead不变,对tbody进行更改:
在tbody中再嵌入一个table-外层for循环商店,在table中再分为thead和tbody,tbody的tr再内层for循环商店下的商品即可实现上面效果图。
注意:在table标签中进行v-if判断该商店是否有商品,没有则不显示该商店,另外,这里不显示商品并没有删除存进data中的商店的数据,要在每次对商品进行移除时判断该商店商品是否为空,为空则用splice对该商店数据进行删除。
五、问题总结
这里报错的原因是:
细节问题,Price的input框为text,导致数据类型是字符串,所以报错;将数据类型转换成数字类型的就不会报这个错了;
解决方法:
①在JS中将return ‘¥’ + price.toFixed(2); 改成 return '¥ ’ + parseFloat(val).toFixed(2);
②在html中利用v-model的number修饰符,即v-model.number = “good.price”
2.注意不能直接进行this.goods.push(this.good);.这个方法会把good的地址也push进去,而不是拷贝;要构建一个新对象,再push进去
// (parse必须是json格式,所以先用stringify转换成json字符串类型再用parse转换成对象)
let goodJson = JSON.stringify(this.good);
let addGood = JSON.parse(goodJson);
this.goods.push(addGood);
3.按钮的全选和取消全选
大概属性定义思路:checkbox的属性是一个布尔属性,用v-model双向绑定一个布尔属性,即可在方法中用来实现各种功能;
这里分成三种复选框,一是isAllChecked(代表全选复选框,决定商店商品所有复选框的状态);二是isStChecked(代表商店复选框,决定某商店下所有商品的复选框状态);三是isChecked(代表商品复选框,决定该商品复选框的状态)
大概功能实现思路:
①先实现全选复选框(商店复选框)让下面所有商品复选框选中和取消选中
思路:让下面所有复选框的checked属性(选中状态) 跟随 全选复选框(商店复选框)即可
②下面商品复选框需要全部选中,全选复选框(商店复选框)才会选中做法:每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面就不选中。
4.在实现当添加的商品编号已经在购物车存在,进行增加购物车商品数量的功能时:
1)我在对购物车商品编号遍历对比的时候用‘ ===’ 等同符时,两边值类型不相等,但是我分别打印后得到的都是number类型,不知道什么原因;所以改为使用等值符,只对值进行比较。
2)/对购物车的商品进行遍历,如果新加入的商品编号已存在就直接进行商品数量增加,否则直接进行push ,有两种实现方法:
①使用flag判断是否进行了数量增加的操作,当进行了购物车商品数量增加时,flag=false,就不会执行push操作;(不推荐此方法,在加入商店对商品进行分类后,此方法不好写)
②直接return,当进行了购物车商品数量增加时,return;此操作return接下来的所有代码都不会执行(推荐此方法,方便又快捷)
1)当商店存在,但是商品不存在,在该商店下新增商品时,新增商品addGood的类型与goods类型不同,所以需要再次构造新增对象addG,使addG的类型与goods相同,然后再在该商店下进行push
2)当商店商品不存在购物车时,要先创建商店,然后再在商店下创建商品,一样类型要一致
六、核心代码
html:
<div id="app"> <div class="add"> <h3>添加要购买的商品:</h3> <table> <tr> <td>商店后台编号:</td> <td><input type="text" placeholder="请填写商店编号" v-model="good.storeId"></td> </tr> <tr> <td>商店名称:</td> <td><input type="text" placeholder="请填写商店名称" v-model="good.storeName"></td> </tr> <tr> <td>商品后台编号:</td> <td><input type="text" placeholder="请填写商品编号" v-model="good.id"></td> </tr> <tr> <td>商品名称:</td> <td><input type="text" placeholder="请填写商品名称" v-model="good.name"></td> </tr> <tr> <td>商品价格:</td> <td><input type="text" placeholder="请填写商品价格" v-model="good.price"></td> </tr> <tr> <td>商品数量:</td> <td><input type="text" placeholder="请填写商品数量" v-model.number="good.count"></td> </tr> <tr> <td colspan="2"><button @click="addToCar()">添加</button></td> </tr> </table> </div> <!-- 当购物车没有商店时,意味着没有商品,那么就显示购物车为空 --> <div v-if="infos.length"> <h3 >购物车:</h3> <table> <thead> <tr> <th class="weightSmall"><input type="checkbox" v-model="isAllChecked" @click="clickAllChecked"></th> <th class="weightBig">书籍名称</th> <th class="weightMid">价格</th> <th class="weightMid">购买数量</th> <th class="weightMid">操作</th> </tr> </thead> <tbody> <!-- 外层for循环商店,如果该商店无商品,则不显示 --> <table v-for="(items, indexs) in infos" v-if="infos[indexs].goods.length"> <thead> <th colspan="5"><input type="checkbox" v-model="items.isStChecked" @click="clickStChecked(indexs)">{{items.storeName}}</th> </thead> <tbody> <!-- 内层for循环商店下的商品 --> <tr v-for="(item, index) in items.goods"> <td class="weightSmall"><input type="checkbox" v-model="item.isChecked" @click="clickChecked(indexs,index)"></td> <td class="weightBig">{{item.name}}</td> <td class="weightMid">{{item.price | getFinalPrice}}</td> <td class="weightMid"> <button @click="decrement(indexs,index)" :disabled="item.count <= 1">-</button> {{item.count}} <button @click="increment(indexs,index)">+</button> </td> <td class="weightMid"> <!-- 每次移除商品时判断该商店商品是否为空,为空就删除该商店 --> <button @click="removeHandle(indexs,index)" >移除</button> </td> </tr> </tbody> </table> <table> <tr> <td class="final">总价格:{{totalPrice | getFinalPrice}}</td> </tr> </table> </tbody> </table> </div> <h2 v-else>购物车为空</h2> </div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
JS:
const app = new Vue({ el: '#app', data: { // 创建一个对象用于存放要添加的商品信息 good: { storeId: '', storeName: '', id: '', name: '', price: '', count: '', }, //checkbox用布尔值表示是否选中,在data里绑定该属性,然后每次点击对该属性进行操作 isAllChecked: false, infos: [ { storeId: 1, storeName: '华为手机店', isStChecked: false, goods: [ { id: 1, name: '华为手机', price: 2000, count: 1, isChecked: false, }, { id: 2, name: '华为Mate30手机', price: 5000, count: 1, isChecked: false, }, ] }, { storeId: 2, storeName: '体育用品店', isStChecked: false, goods: [ { id: 3, name: '篮球', price: 200, count: 1, isChecked: false, }, ] }, { storeId: 3, storeName: '手机用品店', isStChecked: false, goods: [ { id: 4, name: '手机膜', price: 30, count: 1, isChecked: false, }, ] } ], }, methods: { // 方法①调用时用getFinalPrice(item.price) // getFinalPrice(price) { // return '¥' + price.toFixed(2); // }, decrement(indexs, index) { this.infos[indexs].goods[index].count--; }, increment(indexs, index) { this.infos[indexs].goods[index].count++; }, removeHandle(indexs, index) { this.infos[indexs].goods.splice(index, 1); if (this.infos[indexs].goods.length === 0) { this.infos.splice(indexs, 1); } }, // 二、添加到购物车的功能 addToCar() { //如果有一个表单元素输入空,就返回空值,不会在进行在购物车添加的操作 if (!this.judgeGood()) { return; } else { //对购物车的商品进行遍历,有三种情况: for (let j = 0; j < this.infos.length; j++) { if (this.good.storeId == this.infos[j].storeId) { for (let i = 0; i < this.infos[j].goods.length; i++) { //a 商店存在且新增商品在购物车中存在,商品数量增加; if (this.good.id == this.infos[j].goods[i].id) { this.infos[j].goods[i].count += this.good.count; this.clearGood(); return; } } //b 商店存在但新增商品不在购物车中,则在该商店下push新增商品; //先构造新增的对象 let goodJson = JSON.stringify(this.good); let addGood = JSON.parse(goodJson); let addG = { id: addGood.id, name: addGood.name, price: addGood.price, count: addGood.count, isChecked: false, }; this.infos[j].goods.push(addG); this.clearGood(); } } /*1)这个方法会把good的地址也push进去,而不是拷贝 this.goods.push(this.good); 2)构建一个新对象,再push进去 (parse必须是json格式,所以先用stringify转换成json字符串类型再用parse转换成对象)*/ //c 商店不存在且新增商品不在购物车中,则新增商店商品 //先构造新增的对象 let goodJson = JSON.stringify(this.good); let addGood = JSON.parse(goodJson); let addS = { storeId: addGood.storeId, storeName: addGood.storeName, isStChecked: false, goods: [], }; let addG = { id: addGood.id, name: addGood.name, price: addGood.price, count: addGood.count, isChecked: false, }; this.infos.push(addS); addS.goods.push(addG); this.clearGood(); } }, //判断每个输入框的内容是否输入,让表单每个元素必填 judgeGood() { if (this.good.storeId == '' || this.good.storeId == undefined || this.good.storeId == null) { alert('请输入商店后台编号'); return false; }; if (this.good.storeName == '' || this.good.storeName == undefined || this.good.storeName == null) { alert('请输入商店名称'); return false; }; if (this.good.id == '' || this.good.id == undefined || this.good.id == null) { alert('请输入商品编号'); return false; }; if (this.good.name == '' || this.good.name == undefined || this.good.name == null) { alert('请输入商品名称'); return false; }; if (this.good.price == '' || this.good.price == undefined || this.good.price == null) { alert('请输入商品价格'); return false; }; if (this.good.count == '' || this.good.count == undefined || this.good.count == null) { alert('请输入商品数量'); return false; }; return true; }, //当点击添加时,输入框内的内容同时为空 clearGood() { this.good.storeId = ''; this.good.storeName = ''; this.good.id = ''; this.good.name = ''; this.good.price = ''; this.good.count = ''; }, // 1. 全选复选框选中,让所有商店所有商品被选中 clickAllChecked() { let afterClickChecked = !this.isAllChecked; for (let i = 0; i < this.infos.length; i++) { this.infos[i].isStChecked = afterClickChecked; for (let j = 0; j < this.infos[i].goods.length; j++) { this.infos[i].goods[j].isChecked = afterClickChecked; } } }, // 2.点击商店复选框,该商店下面所有商品的复选框被选中 clickStChecked(indexs) { this.infos[indexs].isStChecked = !this.infos[indexs].isStChecked;//没有这句,4运行不起作用,因为商店isStChecked没有被更改 let afterClickChecked = this.infos[indexs].isStChecked; for (let i = 0; i < this.infos[indexs].goods.length; i++) { this.infos[indexs].goods[i].isChecked = afterClickChecked; } //4.解决3c中的小bug,如果点击了商店复选框,就调用此方法对商店进行循环判断 let flagSt = true; for (let j = 0; j < this.infos.length; j++) { if (!this.infos[j].isStChecked) { flagSt = false; break; } } this.isAllChecked = flagSt; }, //3. //a.每次点击商品复选框,都循环查看该商店下所有商品复选框是否全部选中,只要有一个商品没选中,商店就不会被选中; //b.同时,也循环查看商店的复选框是否全部选中,商店复选框全选中,则全选复选框选中 /*c.另外有个小bug,如果所有商店的复选框已全部选中,但最后一次点击的复选框不是商品复选框,全选复选框不会被选中 因为如果最后一次的点击后,所有商店商品被选中,但点击的不是商品复选框,则不会调用此方法(判断商店是否全部选中的循环没有执行)*/ clickChecked(indexs, index) { //决定商店复选框是否选中 let flag = true; this.infos[indexs].goods[index].isChecked = !this.infos[indexs].goods[index].isChecked; for (let i = 0; i < this.infos[indexs].goods.length; i++) { if (!this.infos[indexs].goods[i].isChecked) { flag = false; break; } } this.infos[indexs].isStChecked = flag; //决定全选复选框是否选中 let flagSt = true; for (let j = 0; j < this.infos.length; j++) { if (!this.infos[j].isStChecked) { flagSt = false; break; } } this.isAllChecked = flagSt; }, }, computed: { totalPrice() { let totalPrice = 0; for (let i = 0; i < this.infos.length; i++) { for (let j = 0; j < this.infos[i].goods.length; j++) { //被选中的商品才会把其价格加入总价格中 if (this.infos[i].goods[j].isChecked) { totalPrice += this.infos[i].goods[j].price * this.infos[i].goods[j].count; } } } return totalPrice; } }, filters: { // 方法②调用时用item.price | getFinalPrice getFinalPrice(price) { return '¥' + parseFloat(price).toFixed(2); }, }});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
以上是我学习Vue的阶段性练习案例,如有不足,欢迎提出指正以及互相学习交流。