day12 书城项目第五阶段
1. 显示购物车页面
1.1目标
把购物车信息在专门的页面显示出来
1.2思路
1.3代码实现
1.3.1 购物车超链接
登录状态和未登录状态
<div class="topbar-right" th:if="${username} == null">
<a href="user?method=toLoginPage" class="login">登录</a>
<a href="user?method=toRegistPage" class="register">注册</a>
<a
href="cart?method=toCartPage"
class="cart iconfont icon-gouwuche
"
>
购物车
<div class="cart-num">{{totalCount}}</div>
</a>
<a th:href="admin" class="admin">后台管理</a>
</div>
<div class="topbar-right" th:unless="${username} == null">
<span>欢迎你 <b th:text="${username}">张总</b> </span>
<a th:href="@{/user(method=logout)}">注销</a>
<a
href="cart?method=toCartPage"
class="cart iconfont icon-gouwuche
"
>
购物车
<div class="cart-num">{{totalCount}}</div>
</a>
<a th:href="admin" class="admin">后台管理</a>
</div>
1.3.2 CartServlet添加跳转到cart.html页面的代码
// 前往cart页面
protected void toCartPage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
processTemplate("cart/cart", req, resp);
}
1.3.3 cart.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="@{/}">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<script src="static/script/axios.min.js"></script>
<script src="static/script/vue.js"></script>
</head>
<body>
<div id="box">
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>我的购物车</h1>
</div>
<div class="header-right">
<h3>欢迎<span>张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order/order.html">我的订单</a></div>
<div class="destory"><a href="index.html">注销</a></div>
<div class="gohome">
<a href="index.html">返回</a>
</div>
</div>
</div>
</div>
<div class="list">
<div class="w">
<table>
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(e,index) in cart.cartList">
<td>
<img :src="e.imgPath" alt="" />
</td>
<td>{{e.title}}</td>
<td>
<span class="count">-</span>
<input class="count-num" type="text" v-model="e.count" />
<span class="count">+</span>
</td>
<td>{{e.price}}</td>
<td>{{e.amount}}</td>
<td><a href="" >删除</a></td>
</tr>
</tbody>
</table>
<div class="footer">
<div class="footer-left">
<a href="" class="clear-cart">清空购物车</a>
<a href="#">继续购物</a>
</div>
<div class="footer-right">
<div>共<span>{{cart.totalCount}}</span>件商品</div>
<div class="total-price">总金额<span>{{cart.totalAmount}}</span>元</div>
<a class="pay" href="checkout.html">去结账</a>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="" />
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="" />
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="" />
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.atguigu.com</dd>
<dd></dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</div>
<script>
var vue = new Vue({
"el":"#box",
"data":{
"cart":{
"cartList":[
{
"imgPath": "static/uploads/huozhe.jpg",
"title": "活着",
"count": 2,
"price": "36.8",
"amount": "73.6"
},
{
"imgPath": "static/uploads/huozhe.jpg",
"title": "活着真好",
"count": 2,
"price": "36.8",
"amount": "73.6"
}
],
"totalCount": "4",
"totalAmount": "147.2"
}
},
"created":function () {
// 发起异步请求 获取购物车详情数据
axios({
"url":"cart",
"method":"post",
"params":{
"method":"getCart"
}
}).
then(function (value) {
// 获取后台发送的数据
console.log(value.data);
if (value.data.flag){
vue.cart = value.data.resultData;
}
})
}
});
</script>
</body>
</html>
1.3.4 修改Cart类添加getCartItem方法
// 获取所有的购物项
public List<CartItem> getCartItem(){
Collection<CartItem> values = cartMap.values();
// 将Collection转为集合
ArrayList<CartItem> cartItems = new ArrayList<>(values);
return cartItems;
}
1.3.5 CartServlet中添加getCart()方法
// 获取购物车内容
protected void getCart(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
CommonResult result;
// 获取购物车
try {
HttpSession session = req.getSession();
Cart cart = (Cart) session.getAttribute(Constants.SESSION_CART_KEY);
Map map = new HashMap<>();
// 2.判断购物车是否存在
if (cart != null) {
// 获取对应的数据
List<CartItem> cartItem = cart.getCartItem();
int totalCount = cart.getTotalCount();
Double totalAmount = cart.getTotalAmount();
map.put("cartList", cartItem);
map.put("totalCount", totalCount);
map.put("totalAmount", totalAmount);
}
// 4.将数据封装到CommonResult
result = CommonResult.ok(map);
} catch (Exception e) {
e.printStackTrace();
result = CommonResult.error(e.getMessage());
}
JSONUtils.writeJson(resp, result);
}
2. 清空购物车
2.1 目标
当用户确定点击清空购物车,将Session域中的Cart对象移除。
2.2 思路
cart.html→清空购物车超链接→点击事件→confirm()确认→确定→CartServlet.clearCart()→从Session域移除Cart对象→跳转回到cart.html页面
2.3 代码实现
2.3.1 前端页面代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="@{/}">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<script src="static/script/axios.min.js"></script>
<script src="static/script/vue.js"></script>
</head>
<body>
<div id="box">
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>我的购物车</h1>
</div>
<div class="header-right">
<h3>欢迎<span>张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order/order.html">我的订单</a></div>
<div class="destory"><a href="index.html">注销</a></div>
<div class="gohome">
<a href="index.html">返回</a>
</div>
</div>
</div>
</div>
<div class="list">
<div class="w">
<table>
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(e,index) in cart.cartList">
<td>
<img :src="e.imgPath" alt="" />
</td>
<td>{{e.title}}</td>
<td>
<span class="count" @click="subItem(e.id,e.title,e.count,index)">-</span>
<input class="count-num" type="text" v-model="e.count"/>
<span class="count">+</span>
</td>
<td>{{e.price}}</td>
<td>{{e.amount}}</td>
<td><a href="">删除</a></td>
</tr>
</tbody>
</table>
<div class="footer">
<div class="footer-left">
<a href="cart?method=clearCart" @click="clearCart()" class="clear-cart">清空购物车</a>
<a href="#">继续购物</a>
</div>
<div class="footer-right">
<div>共<span>{{cart.totalCount}}</span>件商品</div>
<div class="total-price">总金额<span>{{cart.totalAmount}}</span>元</div>
<a class="pay" href="checkout.html">去结账</a>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="" />
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="" />
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="" />
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.atguigu.com</dd>
<dd></dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</div>
<script>
var vue = new Vue({
"el":"#box",
"data":{
"cart":{
"cartList":[
{
"imgPath": "static/uploads/huozhe.jpg",
"title": "活着",
"count": 2,
"price": "36.8",
"amount": "73.6"
},
{
"imgPath": "static/uploads/huozhe.jpg",
"title": "活着真好",
"count": 2,
"price": "36.8",
"amount": "73.6"
}
],
"totalCount": "4",
"totalAmount": "147.2"
}
},
"methods":{
"clearCart":function () {
var falg = confirm("请问您真的要清空购物车吗?");
// 点击取消清空 则阻止默认行为
if(!falg){
event.preventDefault();
}
},
"created":function () {
// 发起异步请求 获取购物车详情数据
axios({
"url":"cart",
"method":"post",
"params":{
"method":"getCart"
}
}).
then(function (value) {
// 获取后台发送的数据
console.log(value.data);
if (value.data.flag){
vue.cart = value.data.resultData;
}
})
}
});
</script>
</body>
</html>
2.3.2 CartServlet.cleanCart()
/**
* 清空购物车
* @param request
* @param response
*/
public void cleanCart(HttpServletRequest request, HttpServletResponse response) throws IOException {
//1. 将cart从session中移除
request.getSession().removeAttribute(BookStoreConstants.CARTSESSIONKEY);
//2. 跳转回到cart.html页面
processTemplate("cart/cart",request,response);
}
3. 减号
3.1 目标
- 在大于1的数值基础上-1:执行-1的逻辑
- 在1的基础上-1:执行删除item的逻辑
3.2 思路
3.3 前端代码
3.3.1 给减号绑定点击事件
<span class="count" @click="subItem(e.id,e.title,e.count,index)">-</span>
3.3.2 Vue代码
"subItem":function (id,title,count,index){
console.log(id + "," + title + "," + count + "," + index);
// 1.判断数量是否为1
if (count==1){
// 2. 提示是否删除
let flag = confirm("请问您要删除【" + title + "】吗?");
if (!flag){
// 不删除的话则什么都不做
return ;
}
}
// 3.不是1 发起异步请求 减少购物车数量
axios({
"url":"cart",
"method":"post",
"params":{
"method":"subItem",
"id":id
}
}).then(function (value) {
console.log(value.data);
if (value.data.flag){
if (count == 1){
vue.cart.cartList.splice(index, 1);
}else{
// 更新指定下标的金额数量
vue.cart.cartList[index].count = value.data.resultData.count;
vue.cart.cartList[index].amount = value.data.resultData.amount;
}
// 更新总金额总数量
vue.cart.totalCount = value.data.resultData.totalCount;
vue.cart.totalAmount = value.data.resultData.totalAmount;
}
})
}
3.4 后端代码
CartServlet.subItem()方法
protected void subItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
CommonResult result;
try {
// 1.获取请求参数id
String id = req.getParameter("id");
int intId = Integer.parseInt(id);
// 2.获取购物车
HttpSession session = req.getSession();
Cart cart = (Cart) session.getAttribute(Constants.SESSION_CART_KEY);
// 3.对指定的id的条目进行数量-1
cart.cartItemCountDecrease(intId);
// 4.封装反馈的数据
Map map = getMap(intId, cart);
result = CommonResult.ok(map);
} catch (NumberFormatException e) {
e.printStackTrace();
result = CommonResult.error(e.getMessage());
}
JSONUtils.writeJson(resp, result);
}
CartServlet.getMap()方法
// 增加 减少 改变值 通用的反馈结果方法
private Map getMap(int intId, Cart cart) {
// 封装反馈的结果
Map map = new HashMap();
// 获取指定的购物项
CartItem cartItem = cart.getCartMap().get(intId);
if (cartItem != null) {
map.put("count", cartItem.getCount());
map.put("amount", cartItem.getAmount());
}
map.put("totalCount", cart.getTotalCount());
map.put("totalAmount", cart.getTotalAmount());
return map;
}
4. 加号
4.1 目标
告诉Servlet将Session域中Cart对象里面对应的CartItem执行count+1操作
4.2 思路
4.3 代码实现
4.3.1 前端代码
给加号绑定点击事件
<span class="count" @click="addItem(e.id, index)">+</span>
vue代码
"addItem":function (id, index) {
axios({
"url":"cart",
"method":"post",
"params":{
"method":"addItem",
"id":id
}
}).then(function (value) {
console.log(value.data)
if (value.data.flag){
vue.cart.cartList[index].count = value.data.resultData.count;
vue.cart.cartList[index].amount = value.data.resultData.amount;
vue.cart.totalCount = value.data.resultData.totalCount;
vue.cart.totalAmount = value.data.resultData.totalAmount;
}
})
}
4.3.2 后端代码
CartServlet.addItem()
protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
CommonResult result;
try {
// 1.获取id
String id = req.getParameter("id");
int intId = Integer.parseInt(id);
// 2.获取购物车
HttpSession session = req.getSession();
Cart cart = (Cart) session.getAttribute(Constants.SESSION_CART_KEY);
// 3.对指定的id的条目进行数量+1
cart.cartItemCountIncrease(intId);
// 4.封装反馈的数据
Map map = getMap(intId, cart);
result = CommonResult.ok(map);
} catch (NumberFormatException e) {
e.printStackTrace();
result = CommonResult.error(e.getMessage());
}
JSONUtils.writeJson(resp, result);
}
5. 删除
5.1 目标
点击删除超链接后,把对应的CartItem从Cart中删除
5.2 思路
5.3 代码实现
5.3.1 前端代码
给删除按钮绑定点击事件
<td><a href="" @click="removeItem(e.title,index,e.id)">删除</a></td>
vue和axios代码
"removeItem":function (title,index,id){
event.preventDefault(); // 取消默认行为
// console.log(title + "--->" + index + "--->" + id);
var flag = confirm("请问你真的要删除【" + title + "】吗?");
if(!flag){
// 不删除则return
return ;
}
// 确认删除 则像后端发送请求
axios({
"url":"cart",
"method":"post",
"params":{
"method":"delCartItem",
"id":id
}
}).then(function (value) {
console.log(value.data);
// 请求成功
if (value.data.flag){
// splice(下标,数量);
// 删除指定下标开始的元素 1个
vue.cart.cartList.splice(index, 1);
vue.cart.totalCount = value.data.resultData.totalCount;
vue.cart.totalAmount = value.data.resultData.totalAmount;
}
})
}
}
5.3.2 后端代码
CartServlet.delCartItem()
protected void delCartItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
CommonResult result;
try {
// 获取id
String id = req.getParameter("id");
int intId = Integer.parseInt(id);
// 获取session
HttpSession session = req.getSession();
// 获取购物车
Cart cart = (Cart) session.getAttribute(Constants.SESSION_CART_KEY);
// 删除购物项
cart.removeItem(intId);
// 4.封装数据进行返回
Map map = new HashMap();
// 将数据封装到map内
map.put("totalCount", cart.getTotalCount());
map.put("totalAmount", cart.getTotalAmount());
result = CommonResult.ok(map);
} catch (Exception e) {
e.printStackTrace();
result = CommonResult.error(e.getMessage());
}
JSONUtils.writeJson(resp, result);
}
6. 文本框修改
6.1 目标
用户在文本框输入新数据后,根据用户输入在Session中的Cart中修改CartItem中的count
6.2 思路
6.3 代码实现
6.3.1 前端代码
绑定失去change事件
<input class="count-num" type="text" v-model="e.count" @change="updateItem(e.id,e.count,index)"/>
vue和axios代码
"updateItem":function (id, count, index) {
// 判断count是否符合规范
var reg = /[1-9][0-9]*/
if (reg.test(count)){
// 符合规范 发送axios请求
axios({
"url":"cart",
"method":"post",
"params":{
"method":"updateItem",
"id":id,
"count":count
}
}).then(function (value){
console.log(value.data)
if (value.data.flag){
vue.cart.cartList[index].count = value.data.resultData.count;
vue.cart.cartList[index].amount = value.data.resultData.amount;
vue.cart.totalCount = value.data.resultData.totalCount;
vue.cart.totalAmount = value.data.resultData.totalAmount;
}
})
}
}
6.3.2后端代码
CartServlet.updateCartItemCount()
protected void updateItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
CommonResult result;
try {
// 1.获取id
String id = req.getParameter("id");
String count = req.getParameter("count");
int intId = Integer.parseInt(id);
int intCount = Integer.parseInt(count);
// 2.获取购物车
HttpSession session = req.getSession();
Cart cart = (Cart) session.getAttribute(Constants.SESSION_CART_KEY);
// 3.对指定的条目进行数量修改
cart.cartItemSetCount(intId, intCount);
// 4.封装反馈的数据
Map map = getMap(intId, cart);
result = CommonResult.ok(map);
} catch (NumberFormatException e) {
e.printStackTrace();
result = CommonResult.error(e.getMessage());
}
JSONUtils.writeJson(resp, result);
}
7. Double数据运算过程中精度调整
7.1 问题现象
7.2 解决方案
- 使用BigDecimal类型来进行Double类型数据运算
- 创建BigDecimal类型对象时将Double类型的数据转换为字符串
Cart类:
// 获取总金额
public Double getTotalAmount(){
// this.totalAmount = 0.0;
BigDecimal bigDecimalTotalAmount = new BigDecimal(0.0);
// 获取购物车中所有的购物项
Collection<CartItem> values = cartMap.values();
for (CartItem item : values) {
// 计算所有购物项的总金额
// totalAmount += item.getAmount();
bigDecimalTotalAmount = bigDecimalTotalAmount.add(new BigDecimal(item.getAmount() + ""));
}
// 将totalAmount转成double类型
totalAmount = bigDecimalTotalAmount.doubleValue();
return totalAmount;
}
CartItem类:
public Double getAmount() {
// return amount * count;
BigDecimal bigDecimalPrice = new BigDecimal(price + "");
BigDecimal bigDecimalCount = new BigDecimal(count + "");
amount = bigDecimalCount.multiply(bigDecimalPrice).doubleValue();
return amount;
}