目录
一、题目要求
题目要求:
1.作出如图的展示效果
2.点击购买加号则数量增加,总价格随之更变
3.如果书本数量为1则不能减少
4.点击移除,删除该行,总价格随之更变
5.使用VUE + HTML 实现该案例说明:
题目中没有要求实现添加订单的功能,添加订单的实现是我自己想练手,所以添加了这个模块,当然实际开发中不可能是这种手动输入来添加订单的方式,我这里的实现纯粹是用于学习(用原生HTML和jQuery实现布局和交互效果),没有刻意引入UI框架。不喜勿喷喔
二、效果演示
三、完整代码
1、shopping-cart.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车案例</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="./jquery-3.5.1.js"></script>
<link rel="stylesheet" href="./shopping-cart.css">
</head>
<body>
<!--
1、页面布局
2、添加数量功能
3、减少数量功能
4、删除书籍功能
5、总价计算更新功能
6、添加订单功能
6.1 校验信息功能
6.2 取消添加功能
6.3 确认添加功能
-->
<div id="app">
<div v-show="isShowOrderDiv">
<table class="order">
<caption>
<h1>购物车清单管理</h1>
</caption>
<tr class="options">
<td colspan="6">
<span class="add" @click="openAdd">十</span>
</td>
</tr>
<tr>
<th scope="col" class="num"></th>
<th scope="col" class="book_name">书籍名称</th>
<th scope="col" class="publish_date">出版日期</th>
<th scope="col" class="book_price">价格</th>
<th scope="col" class="buy_count">购买数量</th>
<th scope="col" class="action">操作</th>
</tr>
<tr class="th" v-for="(book,index) in book_list">
<td class="num">{{book.num}}</td>
<td class="book_name">《{{book.book_name}}》</td>
<td class="publish_date">{{book.publish_date}}</td>
<td class="book_price">¥{{book.book_price}}</td>
<td class="buy_count">
<span class="minus" v-bind:class="[book.isMore?'more':'one']"
@click="[book.buy_count > 1 ? minusCount(index):'']">-</span>
<span class="count_value">{{book.buy_count}}</span>
<span class="plus more" @click="addCount(index)">+</span>
</td>
<td class="action">
<span class="delete" @click="deleteBook(index)">
<svg t="1609162597621" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="2030" width="20" height="20">
<path
d="M677.35552 204.8l0-61.44c0-33.91488-29.61408-61.44-66.1504-61.44l-198.49216 0c-36.57728 0-66.19136 27.52512-66.19136 61.44l0 61.44-264.64256 0 0 61.44 99.24608 0 0 614.4c0 33.95584 29.65504 61.44 66.1504 61.44l529.32608 0c36.57728 0 66.19136-27.48416 66.19136-61.44l0-614.4 99.24608 0 0-61.44L677.35552 204.8 677.35552 204.8zM412.71296 143.36l198.49216 0 0 61.44-198.49216 0L412.71296 143.36 412.71296 143.36zM776.6016 880.64 247.27552 880.64l0-614.4 529.32608 0L776.6016 880.64 776.6016 880.64zM346.5216 358.4l66.19136 0 0 430.08-66.19136 0L346.5216 358.4 346.5216 358.4zM478.86336 358.4l66.1504 0 0 430.08-66.1504 0L478.86336 358.4 478.86336 358.4zM611.20512 358.4l66.1504 0 0 430.08-66.1504 0L611.20512 358.4 611.20512 358.4zM611.20512 358.4"
p-id="2031"></path>
</svg>
</span>
</td>
</tr>
</table>
<p class="sum_price">总价格:¥<span>{{sum_price}}</span></p>
</div>
<div v-show="isShowAddDiv" id="add-wrapper">
<table>
<span class="titlebar">添加订单</span>
<tr scope="col">
<td>
<label for="book_name"><span style="color: red;">*</span>书名:</label>
</td>
<td>
<input type="text" id="book_name">
</td>
</tr>
<tr scope="col">
<td>
<label for="publish_date"><span style="color: red;">*</span>出版日期:</label>
</td>
<td>
<input type="text" id="publish_date">
</td>
</tr>
<tr scope="col">
<td>
<label for="book_price"><span style="color: red;">*</span>价格:</label>
</td>
<td>
<input type="text" id="book_price">
</td>
</tr>
<tr scope="col">
<td>
<label for="buy_count"><span style="color: red;">*</span>购买数量:</label>
</td>
<td>
<input type="text" id="buy_count">
</td>
</tr>
<tr style="color: red;height:30px;line-height: 30px; text-align: right;padding-right: 20px;">
<td colspan="2">{{warn_info}}</td>
</tr>
</table>
<div class="btnDiv">
<span id="cancelAddBtn" @click="cancelAdd">×</span>
<span id="confirmAddBtn" @click="addBook">√</span>
</div>
</div>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
sum_price: 0,
book_num: 0,
book_list: [],
isShowOrderDiv: true, //默认展示订单页面
isShowAddDiv: false, //默认不展示添加页面
warn_info: "" //警告信息
},
methods: {
addCount(index) {
//vue的forEach循环
var list = this.book_list;
list.forEach((item, i) => {
if (index == i) {
//将减少按钮置为可点击样式
if (item.buy_count == 1) {
item.isMore = true
}
//将购买数量+1
item.buy_count += 1;
//将总价格更新
this.sum_price = this.roundFun(this.sum_price + item.book_price)
}
})
this.book_list = list;
},
minusCount(index) {
//vue的forEach循环
var list = this.book_list;
list.forEach((item, i) => {
if (index == i) {
//将购买数量-1
item.buy_count -= 1;
//将减少按钮置为可点击样式
if (item.buy_count == 1) {
item.isMore = false
}
//将总价格更新
this.sum_price = this.roundFun(this.sum_price - item.book_price)
}
})
this.book_list = list;
},
//删除订单记录的方法
deleteBook(index) {
//将book从book_list中移除
let list = this.book_list.filter((item, i) => {
if (index == i) {
//将总价更新
this.sum_price = this.roundFun(this.sum_price - item.book_price * item.buy_count);
//将总订单数更新
this.book_num -= item.buy_count
}
return index != i
})
//将删除后的数组序号更新
list.forEach((item, i) => {
item.num = i + 1
})
this.book_list = list
},
//打开添加订单页面
openAdd() {
this.isShowOrderDiv = false;
this.isShowAddDiv = true;
},
//添加订单的方法
addBook() {
this.warn_info = ""
let book_name = $("#book_name").val();
let publihs_date = $("#publish_date").val();
let book_price = $("#book_price").val();
let buy_count = $("#buy_count").val();
if (book_name === "" || publihs_date === "" || book_price === "" || buy_count === "") {
this.warn_info = "必填项不允许空值!"
return
}
//校验发布日期是否符合日期规范
if (this.isFormatDate(publihs_date) === false) {
this.warn_info = "日期应为yyyy-mm-dd或者yyyy/mm/dd的格式!"
return
}
//校验书籍价格是否为非负数字
if (this.isPrice(book_price) === false) {
this.warn_info = "价格应该为正数,且最多包含2位小数!"
return
}
//校验购买数量是否为正整数
if (this.isBuyCount(buy_count) === false) {
this.warn_info = "购买数量应该为正整数!"
return
}
//创建一个JSON对象存储新的书籍信息
let book = {
num: this.book_num + 1,
book_name: book_name,
publish_date: publihs_date.replaceAll("/", "-"),
book_price: parseFloat(book_price),
buy_count: parseInt(buy_count),
isMore: buy_count > 1 ? true : false
}
//将book添加到book_list中
this.book_list.push(book)
//将总价更新
this.sum_price = this.roundFun(this.sum_price + book.book_price * book.buy_count);
//将总订单数更新
this.book_num += 1
//关闭添加订单页面,展示订单列表页面,关闭警告信息提示
this.isShowOrderDiv = true;
this.isShowAddDiv = false;
this.initInput();
this.warn_info = "";
},
//取消添加订单的方法
cancelAdd() {
this.isShowOrderDiv = true;
this.isShowAddDiv = false;
this.initInput();
},
//清空输入框
initInput() {
this.warn_info = "";
$("#book_name").val("");
$("#publish_date").val("");
$("#book_price").val("");
$("#buy_count").val("");
},
//价格是否为正数,且最多有2位小数
isPrice(val) {
var regPos = /^\d+(\.\d{1,2})?$/; //非负整数,或者有1-2位小数
if (regPos.test(val)) {
return true;
} return false;
},
//日期是否符合日期规范yyyy-mm-dd或者yyyy/mm/dd格式
isFormatDate(val) {
var reg = /^\d{4}(-|\/)\d{2}(-|\/)\d{2}$/;
if (reg.test(val)) {
return true;
} return false;
},
//购买数量是否为正整数
isBuyCount(val) {
var reg = /^[1-9]{1}(\d*)$/; //第一位必须为1-9,后面的数可以为0-n个任意数字
if (reg.test(val)) {
return true;
} return false;
},
//保留俩位小数
roundFun(value, n = 2) {
return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
}
}
});
</script>
</html>
2、shopping-cart.css
#app{
margin:auto;
width:850px;
}
table.order h1{
color:burlywood;
}
table.order{
border-collapse: collapse;
border-spacing: 0px;
}
table.order td,th{
border:1px solid #ccc;
text-align: center;
font-family: 'STHeiTi';
}
table.order tr{
height:40px;
}
table.order tr:first-child td{
border: none;
text-align: left;
}
table.order th{
background-color:whitesmoke;
font-weight: bold;
color:grey;
}
.num{
width:50px;
}
.book_name{
width:300px;
}
.publish_date{
width:130px;
}
.book_price{
width:120px;
}
.buy_count{
width:200px;
}
.action{
width:100px;
}
.one,.more{
display: inline-block;
width:20px;
height:20px;
line-height: 20px;
text-align: center;
border: 1px solid;
}
.one{
color:grey;
border-color:gainsboro;
}
.more{
color:black;
cursor:pointer;
}
.count_value{
display:inline-block;
width:50px;
height:20px;
line-height: 20px;
text-align: center;
border:0px;
outline: none;
}
.delete,.options .add{
display: inline-block;
height:30px;
border-radius: 5px;
line-height: 30px;
cursor:pointer;
color:white;
}
.delete{
width:50px;
background-color: red;
}
.options .add{
font-weight: bold;
width:60px;
background-color: deepskyblue;
text-align: center;
}
.options td{
text-align: left;
}
table.order span>svg{
margin:auto;
margin-top:5px;
fill:currentColor;
color:white;
}
.sum_price{
text-align: right;
font-weight: bold;
font-size: 20px;
}
/*设置添加订单div的样式*/
#add-wrapper{
width:500px;
margin:100px auto;
height:370px;
/* border:1px solid grey; */
box-shadow:#ccc 5px 5px 5px 5px;
position: relative;
}
#add-wrapper span.titlebar{
display: inline-block;
width:500px;
height:50px;
line-height: 50px;
text-align: center;
background-color: deepskyblue;
color: white;
font-size: 20px;
font-weight: bold;
}
#add-wrapper tr{
width:400px;
}
/* #add-wrapper span{
display: block;
width:80px;
height:30px;
margin-top:10px;
} */
#add-wrapper table{
padding: 50px 50px 0px 50px;
}
#add-wrapper tr{
height: 40px;
line-height: 40px;
}
/* #add-wrapper td{
border:1px solid red;
} */
#add-wrapper tr td label{
display: inline-block;
width:100px;
text-align: right;
}
#add-wrapper tr td input{
height:30px;
width:230px;
line-height: 30px;
font-size: 15px;
}
/* #add-wrapper table tr:last-child{
margin-top:50px;
text-align: right;
} */
.btnDiv{
width:100%;
height:50px;
float: left;
position: relative;
top:20px;
right:20px;
text-align: right;
}
#confirmAddBtn{
display: inline-block;
height:30px;
width:30px;
border-radius: 50%;
line-height: 30px;
cursor:pointer;
color:white;
background-color: deepskyblue;
text-align: center;
font-weight: bold;
font-size: 20px;
}
#cancelAddBtn{
display: inline-block;
height:30px;
width:30px;
border-radius: 50%;
line-height: 30px;
cursor:pointer;
color:white;
background-color:crimson;
text-align: center;
font-weight: bold;
font-size: 20px;
margin-right: 10px;
}
四、问题总结
1、取消table第一行的外边框不生效
-
取消table第一行的外边框,希望达到下方的效果
但是发现始终不生效,最后一点点清除代码验证效果后,发现是body中table标签多写了一个border="1“的属性,同时tr:first-child后面的td也老是忘加,导致一直不生效 -
这是不生效的效果图:
-
错误代码如下:
2、总价格的小数点超过2位也会展示
- 最开始在计算和更新总价格的时候,使用的是-=、+=运算符,但是后续测试中发现:当价格与数量乘积超过2位小数时,也会直接展示,所以参考网上方法,将-=、+=运算符改为普通 = 运算符,并且将最终计算的结果精确到了2位小数
- 参考资料:js保留两位小数的方法
3、H5的table没有cellspacing属性
- 因为H5中取消了一些属性,代替的是使用CSS实现,其中就包括cellspacing属性
- 解决方法:
table{
border-collapse: collapse;
border-spacing: 0px;
}
4、实现div的显示与隐藏效果
- 因为没有引入市面的UI框架,比如elementUI等,所以就得自己实现div的显示与隐藏
- 这里采用的是vue的v-show属性,用于控制标签是否展示,值需要在vue对象的data中给出
- 示例:
html代码
vue代码:
5、日期、价格、数量的格式校验
- 因为本身这个案例只是用来学习,实际情况不会出现订单需要手动输入的情况,所以这里的校验只是对数据格式进行校验,并没有对数据内容进行严格的控制
- 采用的是js的正则表达式进行字符串校验
- 价格后续要进行计算,最后用parseFloat方法转为float类型
- 数量也要进行计算,最后用parseInt方法转为int类型
6、如何处理每一个订单信息
- 起初我没太想明白如何处理每一个订单信息,也是因为基础不扎实,最开始尝试使用生成tr标签的方式去添加订单信息
- 后来意识到行不通,回想了一下学习的课程中老师讲的知识点,确定了要使用v-for属性
- 使用v-for也就必须要提供一个数组,然后就想到,每一个订单信息,都可以当成一个JSON对象,依次存入到一个数组中,然后v-for进行遍历取出每一条订单信息进行展示
- 当然,html中要写好展示订单信息的模板,如下:
7、如何传递指定的一条记录进行删除操作
- 最开始,没考虑将每一条订单信息当作一个JSON对象处理,然后就一直想着用this对象去修改数据
- 但是因为一个HTML中只创建了一个Vue对象,this对象的值只能从vue的data中取,也就意味着如果要用this对象来直接处理数据,一个vue对象,只能保存一条订单的信息,这样多条订单就很难实现了。
- 后来采用数组存放订单对象的方式实现,然后顺其自然就想到了索引。
- 根据数组的索引进行修改数组中指定索引对象的属性值,相比之前的思路,这种方案才有较好的可行性