购物车中列表功能,也是购物车的一个重要功能,购物车页面的商品列表是一个List对象和一些总览性字段。以下主要讲述购物车列表功能的主要实现步骤和代码。
一、数据格式的定义
购物项列表数据中的商品标题字段、商品预览图字段、商品价格字段可以通过购物项表中的goods_id 来关联和查询,而其商品数量字段通过购物项表来查询,列表还有一个删除按钮,因此需要返回购物项的id字段。总览性数据包括加购总量字段和总价字段。返回数据的格式为购物项列表数据+加购总量字段+总价字段。购物项VO对象编码如下所示:
public class NewBeeMallShoppingCartItemVO implements Serializable {
private Long cartItemId;
private Long goodsId;
private Integer goodsCount;
private String goodsName;
private String goodsCoverImg;
private Integer sellingPrice;
}
二、购物车列表数据的获取
购物车列表中的字段可以分别通过查询shopping_cart_item购物项表和goods_info商品表获取。
实体层
(1)Mapper接口
首先在购物项实体Mapper接口的NewBeeMallShoppingCartItemMapper.java中增加如下方法:
//根据userId和number字段获取固定数量的购物项列表数据
List<NewBeeMallShoppingCartItem> selectByUserId(
@Param("newBeeMallUserId") Long newBeeMallUserId
, @Param("number") int number);
(2)Mapper映射文件
映射文件NewBeeMallShoppingCartItemMapper.xml添加具体的SQL语句,代码如下:
<select id="selectByUserId" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from tb_newbee_mall_shopping_cart_item
where user_id = #{newBeeMallUserId,jdbcType=BIGINT} and is_deleted = 0
limit #{number}
</select>
业务层
在业务类中增加getMyShoppingCartItems方法,用以获取购物项列表数据,
在NewBeeMallShoppingCartService.java中增加方法,主要代码如下:
//获取我的购物车中的列表数据
List<NewBeeMallShoppingCartItemVO> getMyShoppingCartItems(Long newBeeMallUserId);
在NewBeeMallShoppingCartServiceImpl类中实现方法,主要代码如下:
@Override
public List<NewBeeMallShoppingCartItemVO> getMyShoppingCartItems(Long newBeeMallUserId) {
List<NewBeeMallShoppingCartItemVO> newBeeMallShoppingCartItemVOS = new ArrayList<>();
List<NewBeeMallShoppingCartItem> newBeeMallShoppingCartItems = newBeeMallShoppingCartItemMapper.selectByUserId(newBeeMallUserId, Constants.SHOPPING_CART_ITEM_TOTAL_NUMBER);
if (!CollectionUtils.isEmpty(newBeeMallShoppingCartItems)) {
//查询商品信息并做数据转换
List<Long> newBeeMallGoodsIds = newBeeMallShoppingCartItems.stream().map(NewBeeMallShoppingCartItem::getGoodsId).collect(Collectors.toList());
List<NewBeeMallGoods> newBeeMallGoods = newBeeMallGoodsMapper.selectByPrimaryKeys(newBeeMallGoodsIds);
Map<Long, NewBeeMallGoods> newBeeMallGoodsMap = new HashMap<>();
if (!CollectionUtils.isEmpty(newBeeMallGoods)) {
newBeeMallGoodsMap = newBeeMallGoods.stream().collect(Collectors.toMap(NewBeeMallGoods::getGoodsId, Function.identity(), (entity1, entity2) -> entity1));
}
for (NewBeeMallShoppingCartItem newBeeMallShoppingCartItem : newBeeMallShoppingCartItems) {
NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO = new NewBeeMallShoppingCartItemVO();
BeanUtil.copyProperties(newBeeMallShoppingCartItem, newBeeMallShoppingCartItemVO);
if (newBeeMallGoodsMap.containsKey(newBeeMallShoppingCartItem.getGoodsId())) {
NewBeeMallGoods newBeeMallGoodsTemp = newBeeMallGoodsMap.get(newBeeMallShoppingCartItem.getGoodsId());
newBeeMallShoppingCartItemVO.setGoodsCoverImg(newBeeMallGoodsTemp.getGoodsCoverImg());
String goodsName = newBeeMallGoodsTemp.getGoodsName();
// 字符串过长导致文字超出的问题
if (goodsName.length() > 28) {
goodsName = goodsName.substring(0, 28) + "...";
}
newBeeMallShoppingCartItemVO.setGoodsName(goodsName);
newBeeMallShoppingCartItemVO.setSellingPrice(newBeeMallGoodsTemp.getSellingPrice());
newBeeMallShoppingCartItemVOS.add(newBeeMallShoppingCartItemVO);
}
}
}
return newBeeMallShoppingCartItemVOS;
}
代码通过传入userid字段作为参数,然后通过SQL查询出当前userid下的购物项列表数据。因为购物车页面需要展示商品信息,所以通过购物项表中的goods_id 获取每个购物项对应的商品信息。接着填充数据,即将相关字段封装到NewBeeMallShoppingCartItemVO对象中。
三、购物车列表数据的渲染
控制层
数据最终通过Thymeleaf语法渲染到前端页面上,首先需要将获取的数据转发到对应模板页面中,就必须在Controller层方法中将查询到的数据放入request请求中。
ShoppingCartController中新增cartListPage()方法,代码如下:
@GetMapping("/shop-cart")
public String cartListPage(HttpServletRequest request,
HttpSession httpSession) {
NewBeeMallUserVO user = (NewBeeMallUserVO) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);
int itemsTotal = 0;
int priceTotal = 0;
List<NewBeeMallShoppingCartItemVO> myShoppingCartItems = newBeeMallShoppingCartService.getMyShoppingCartItems(user.getUserId());
if (!CollectionUtils.isEmpty(myShoppingCartItems)) {
//购物项总数
itemsTotal = myShoppingCartItems.stream().mapToInt(NewBeeMallShoppingCartItemVO::getGoodsCount).sum();
if (itemsTotal < 1) {
NewBeeMallException.fail("购物项不能为空");
}
//总价
for (NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO : myShoppingCartItems) {
priceTotal += newBeeMallShoppingCartItemVO.getGoodsCount() * newBeeMallShoppingCartItemVO.getSellingPrice();
}
if (priceTotal < 1) {
NewBeeMallException.fail("购物项价格异常");
}
}
request.setAttribute("itemsTotal", itemsTotal);
request.setAttribute("priceTotal", priceTotal);
request.setAttribute("myShoppingCartItems", myShoppingCartItems);
return "mall/cart";
}
在代码中首先调用业务层的方法,把当前用户添加到购物车中的购物项数据全部读取出来,然后计算商品总数和总价,并将三个对象都放到request对象中,最后跳转到mall目录下的cart.html模板页面进行数据渲染。
前端
在resource/templates/mall目录中增加cart.html,模板代码如下所示:
<!-- Copyright (c) 2019-2020 十三 all rights reserved. -->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>NewBee商城-购物车</title>
<link rel="stylesheet" th:href="@{mall/css/iconfont.css}">
<link rel="stylesheet" th:href="@{mall/css/common.css}">
<link rel="stylesheet" th:href="@{mall/styles/header.css}">
<link rel="stylesheet" th:href="@{mall/styles/cart.css}">
<link rel="stylesheet" th:href="@{/admin/plugins/sweetalert2/sweetalert2.min.css}"/>
</head>
<body>
<div id="cart">
<div class="banner_x center">
<a th:href="@{/index}" target="_blank">
<div class="logo fl">
<img src="mall/image/new-bee-logo-3.png"/>
</div>
</a>
<div class="wdgwc fl ml20">购物车</div>
<div class="wxts fl ml20">温馨提示:产品是否购买成功,以最终下单为准哦,请尽快结算</div>
<div class="clear"></div>
</div>
<div class="cart_line"></div>
<div class="cart_bg">
<th:block th:if="${#lists.isEmpty(myShoppingCartItems)}">
<div class="list center">
<img style="position: absolute;margin-top: 16px;left: 45%;" th:src="@{/mall/image/null-content.png}">
</div>
</th:block>
<th:block th:unless="${#lists.isEmpty(myShoppingCartItems)}">
<div class="list center">
<div class="top2 center">
<div class="sub_top fl">
</div>
<div class="sub_top fl">商品名称</div>
<div class="sub_top fl">单价</div>
<div class="sub_top fl">数量</div>
<div class="sub_top fl">小计</div>
<div class="sub_top fr">操作</div>
<div class="clear"></div>
</div>
<th:block th:each="item : ${myShoppingCartItems}">
<div class="content2 center">
<div class="sub_content fl ">
</div>
<div class="sub_content cover fl"><img th:src="@{${item.goodsCoverImg}}"></div>
<div class="sub_content fl ft20" th:text="${item.goodsName}">商品名称</div>
<div class="sub_content fl" th:text="${item.sellingPrice+'元'}">1299元</div>
<div class="sub_content fl">
<input class="goods_count" th:id="${'goodsCount'+item.cartItemId}" type="number"
th:onblur="'updateItem('+${item.cartItemId}+')'"
th:value="${item.goodsCount}" step="1" min="1"
max="5">
</div>
<div class="sub_content fl" th:text="${item.goodsCount*item.sellingPrice+'元'}">1299元</div>
<div class="sub_content fl"><a href="##" th:onclick="'deleteItem('+${item.cartItemId}+')'">×</a>
</div>
<div class="clear"></div>
</div>
</th:block>
</div>
</th:block>
<div class="pre_order mt20 center">
<div class="tips fl ml20">
<ul>
<li><a th:href="@{/index}">继续购物</a></li>
<li>|</li>
<li>共<span th:text="${itemsTotal}">13</span>件商品</li>
<div class="clear"></div>
</ul>
</div>
<div class="order_div fr">
<div class="order_total fl">合计(不含运费):<span th:text="${priceTotal}+'.00元'">1299.00元</span></div>
<div class="order_button fr">
<th:block th:if="${itemsTotal == 0}">
<input class="order_button_c" type="button" name="tip"
onclick="tip()"
value="去结算"/>
</th:block>
<th:block th:unless="${itemsTotal == 0}">
<input class="order_button_d" type="button" name="settle"
onclick="settle()"
value="去结算"/>
</th:block>
</div>
<div class="clear"></div>
</div>
<div class="clear"></div>
</div>
</div>
</div>
<div th:replace="mall/footer::footer-fragment"></div>
</body>
<!-- jQuery -->
<script th:src="@{/admin/plugins/jquery/jquery.min.js}"></script>
<script th:src="@{/admin/plugins/sweetalert2/sweetalert2.all.min.js}"></script>
<script type="text/javascript">
/**
* 购物车中数量为0时提示
*/
function tip() {
Swal.fire({
text: "购物车中无数据,无法结算",
icon: "error",iconColor:"#f05b72",
});
}
/**
* 跳转至结算页面
*/
function settle() {
window.location.href = '/shop-cart/settle'
}
/**
*更新购物项
*/
function updateItem(id) {
var domId = 'goodsCount' + id;
var goodsCount = $("#" + domId).val();
if (goodsCount > 5) {
Swal.fire({
text: "单个商品最多可购买5个",
icon: "error",iconColor:"#f05b72",
});
return;
}
if (goodsCount < 1) {
Swal.fire({
text: "数量异常",
icon: "error",iconColor:"#f05b72",
});
return;
}
var data = {
"cartItemId": id,
"goodsCount": goodsCount
};
$.ajax({
type: 'PUT',
url: '/shop-cart',
contentType: 'application/json',
data: JSON.stringify(data),
success: function (result) {
if (result.resultCode == 200) {
window.location.reload();
} else {
Swal.fire({
text: "操作失败",
icon: "error",iconColor:"#f05b72",
});
}
},
error: function () {
Swal.fire({
text: "操作失败",
icon: "error",iconColor:"#f05b72",
});
}
});
}
/**
* * 删除购物项
* @param id
*/
function deleteItem(id) {
Swal.fire({
title: "确认弹框",
text: "确认要删除数据吗?",
icon: "warning",iconColor:"#dea32c",
showCancelButton: true,
confirmButtonText: '确认',
cancelButtonText: '取消'
}).then((flag) => {
if (flag.value) {
$.ajax({
type: 'DELETE',
url: '/shop-cart/' + id,
success: function (result) {
if (result.resultCode == 200) {
window.location.reload();
} else {
Swal.fire({
text: "操作失败",
icon: "error",iconColor:"#f05b72",
});
}
},
error: function () {
Swal.fire({
text: "操作失败",
icon: "error",iconColor:"#f05b72",
});
}
});
}
}
)
;
}
</script>
</html>
在购物项列表区域对应的位置读取myShoppingCartItems数据,首先使用th:each循环语法将商品标题字段、商品预览图字段、商品价格字段、商品数量字段、单个商品的总价字段进行渲染,然后读取读取底部两个小计字段并渲染至页面中。