【基础练习】Bootstrap+Mybatis创建一个网上购物商城的Maven项目

前言:

1、基本的功能包括登录、商品展示、商品添加、购物车展示、总价动态展示。

2、前端的框架这次采用了Bootstrap,说一下用它的优点是什么 --- Bootstrap table可以自动分页、有搜索的按钮,都是基于前端就能实现的。

3、mybatis逆向工程文件生成所需要的dao、mapper和entity。

 

前端实现:

1、login.html文件用于做登录功能,这里使用到Bootstrap的form表单。

2、index.html文件用于展示商品,这里使用到Bootstrap的table插件,后台再对它进行渲染。

3、cart.html用于展示购物车,在主页添加的商品都会在这里生成对应的订单。

4、每个表格的后面都要能添加监听事件,用于监听行事件,例如主页就是 监听添加的事件选定的行信息,购物车页面则是修改和删除

登录页面:

首页:

 

购物车页面:

(上面的计算总价还有一个bug还没解决,后续会给出解决的方法)

 

实现过程

1、登录功能我们可以参照我的上一篇博客。https://blog.csdn.net/qq_34093082/article/details/99551022,在springboot的应用中,登录功能的写法是最常见的其中一种,使用form表单提交信息,后台控制层判断用户是否存在,如果用户存在,再判断用户的密码是否正确,若全部正确,就返回首页。这里给出这部分控制层的代码。

LoginController.java

package com.txy.omall.controller;
import com.txy.omall.model.User;
import com.txy.omall.service.IUser;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/login")
public class LoginController {
    private static final String SUCCESS_CODE = "200";
    private static final String FAIL_CODE = "500";
    private static final String ERROR_CODE = "400";
    @Autowired
    private IUser UserService;

    @RequestMapping("/page")
    public String  getLoginPage(){
        return  "login.html";
    }

    @PostMapping("/checkLogin")
    @ResponseBody
    public String checkLoginInfo(@Param("userName") String userName , @Param("password") String password){
        User user =UserService.getUserByName(userName);
        String userPassword = "";
        if(user != null){
            userPassword = user.getUserPwd();
        }
        if(user == null){
            System.out.println("用户不存在");
            return FAIL_CODE;
        }else if (userPassword.equals(password)){
            System.out.println("成功登陆");
            return SUCCESS_CODE;
        }else{
            System.out.println("密码错误");
            return ERROR_CODE;
        }
    }

}

 

2、首页功能的实现,包括商品展示,商品添加到购物车。

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Zmall商城主页</title>
    <!-- Bootstrap -->
    <link href="../bootstrap/css/bootstrap.css" rel="stylesheet">
    <!-- 引入bootstrap-table样式 -->
    <link href="../css/bootstrap-table.css" rel="stylesheet">
    <!--    引入bootstrap的css样式-->
    <link href="../css/editTable.css" rel="stylesheet">

</head>
<body>
    <h1>Zmall网上商城</h1>
    <a href="/login/page" class="btn btn-success  active" role="button">登录</a>
    <a href="/cart/page" class="btn btn-success  active" role="button">购物车</a>
    <br/>
    <table class="table table-striped table-bordered table-hover" id="goodsTable"></table>
    <div class="modal fade" id="addContent">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">购买情况</h4>
             </div>
                <form id="form_data"  onsubmit="return addCartItem()" method="post">
                 <div class="modal-body">
                        <label for="goodsId">商品编号</label>
                        <input type="text" id="goodsId" value="" readonly="readonly"/><br/>
                        <label for="goodsName">商品名称</label>
                        <input type="text" id="goodsName" value="" readonly="readonly"/><br/>
                        <label for="goodsPrice">商品价格</label>
                        <input type="text" id="goodsPrice" value="" readonly="readonly"/><br/>
                        <label for="amount">购买数量</label>
                        <input type="text" id="amount" value=""/><br/>
                 </div>
               <div class="modal-footer">
                   <button type="submit" class="btn btn-default"  >提交</button>
               </div>
                </form>
          </div>
      </div>
    </div>

    <a href = "/Goods/list">显示数据</a>

    <script src="../js/jquery.min.js"></script>
    <script src="../bootstrap/js/bootstrap.js"></script>
    <!-- 引入中文语言包 -->
    <script src="../js/bootstrap-table.js"></script>
    <!-- bootstrap-table.min.js -->
    <script src="../js/bootstrap-table-editable.js"></script>
    <script src="../js/bootstrap-table-zh-CN.js"></script>

    <script src="../js/bootstrap-table-export.js"></script>
    <script src="../js/index.js"></script>
</body>
</html>

需要注意两点,第一点是 js的引入顺序,一定要保证基本的js要先引入,假如出现的是XX is not a function ,就查看一下自己的JS是否正确引入(可以用alert($)来查看jquery是否正确引入,如果是undefined就是没有引入,但是如果是有引入它会显示一个function出来),第二点是table的加载我们放在js渲染了,唯一的一个addContent的div是默认隐藏的,只有当点击表格的购买按钮,该部分的内容才会显示出来。

index.js

// alert($);
function operateBtns(value, row, index) {
    return [
        '<button id="addToCart" type="button" class="btn btn-default" >购买</button>',
        '<button id="deleteGoods" type="button" class="btn btn-default">删除</button>',
    ].join('');
}
function refresh(){
    $.ajax({
        url:"/cart/list",
        success: function(data){
            alert(data);
            return data;
        }
    })
}

//  显示弹框,并且将这一行的信息填入进去
function addOrder(goodsId,goodsPrice,goodsName){
    $('#goodsId').val(goodsId);
    $('#goodsName').val(goodsName);
    $('#goodsPrice').val(goodsPrice);
    $('#addContent').modal('show');
}

//  提交订单
function addCartItem(){
    $.post({
        url:"/Goods/addToCart",
        data:{
            "goodsId":$("#goodsId").val() ,
            "goodsName":$("#goodsName").val(),
            "goodsPrice":$("#goodsPrice").val(),
            "amount":$("#amount").val()
        },
        error : function () {
            alert("插入出错");
            window.location.href="/";
        },
        success: function(data){
            if (data == "200"){
                alert("插入成功");
                window.location.href="/";
            }
            else{
                alert("插入失败");
            }
            // $('#addContent').modal('hide');
        }
    })
}

window.operateEvents = {
    'click #deleteGoods': function (e, value, row, index) {
        $.ajax({
            url: "/goods/deleteGoods",
            data: {
                goodsId:row.goodsId
            },
            dataType: "json",
            async:true,
            success: function (goodsData) {
                if (goodsData != null) {
                    alert("删除成功");
                }else{
                    alert("删除失败");
                }
            }
        })
    },'click #addToCart': function (e, value, row, index) {
        addOrder(row.goodsId,row.goodsPrice,row.goodsName);
    }
};

$(function ( ) {
    $('#goodsTable').bootstrapTable('destroy');
    $('#goodsTable').bootstrapTable({//表格初始化
        url: '/Goods/list',
        search: true,
        searchOnEnterKey: true,
        searchAlign: "left",
        buttonsAlign: "left",
        editable: true,//开启编辑模式
        clickToSelect: true,
        clickEdit: true,
        method: 'get',
        pageSize: 10, //每页3条
        pageNumber: 1,  //第1页
        pageList: [8, 25],   //在使用过程中根据情况调整每页条数.虽然你现在定义的每页3条,但你可以随时调整为10条或25条。
        cache: false,   //不缓存
        striped: true,
        pagination: true,
        sidePagination: 'client',
        showRefresh: true,
        showExport: false,
        showFooter: true,
        showToggle: true,
        columns: [
            {field: 'goodsId', title: '商品号', sortable: true, align:"center",edit: {required: true, type: 'text'}},
            {field: 'type', title: '类型', sortable: true, align:"center"},
            {field: 'goodsName', title: '名称', sortable: true, align:"center"},
            {field: 'goodsPrice', title: '价格', sortable: true, align:"center"},
            {field: 'Button', title: '操作', align:"center", events: operateEvents, formatter: operateBtns, width: 180}]

    });
})

operateBtns() 用于展示按钮,通过 table 的 formatter 引入

operateEvents() 用于获取行信息的点击事件,里头放了两个点击动作,一个是删除的点击动作,直接向后台传了条删除信息,一个是购买的点击动作,对应一个addOrder的函数,将我们事先隐藏的addContent的div显示出来,并且将行信息的数据赋值到里面的 input的框当中。

addCartItem() 用于处理input框的表单提交之后往后台传值的那部分动作。

$('#goodsTable').bootstrapTable('destroy');    //表格初始化前,需要先清空加载的内容,否则会出错
$('#goodsTable').bootstrapTable({})     //表格初始化

 从上面的代码可以看到,我们展示商品的功能是在表格初始化的时候进行的,bootstrap和layui在传数据的时候的区别是,layui除了data以外还需要status,message,total这几个属性加入,所以我常常多做一步数据的组合,其他几个属性我也能用到,就多做了一步操作其实也便利了很多(毕竟前端可以直接调用就可以),而bootstrap-table 它就是可以直接使用json格式来传,bootstrap的分页功能也很好用,它可以选择在client端分页或是在server端分页。

每步的操作都伴随着状态码的返回,不同的状态码对应了不同的查询(更新、插入)所碰到的情况。

后端的控制层有一个业务逻辑判断需要提一下:

添加到购物车的时候,如果购物车里头该商品已经存在,现在的添加应该是在原有的商品的数量上面再加上现在的数量,如果该商品之前没有存在,添加的动作就是新建一条购物车的条目。

因此它包含三条操作,查询订单是否存在,插入一条新的订单,更新订单的数量(后两者是互斥的)

GoodsController.java

package com.txy.omall.controller;

import com.txy.omall.model.CartItem;
import com.txy.omall.model.Goods;
import com.txy.omall.service.IGoods;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/Goods")
public class GoodsController {
    private final static String SUCCESS_CODE = "200";
    private final static String FAIL_CODE ="500";

    @Autowired
    private IGoods goodsService;

    @GetMapping("/list")
    @ResponseBody
    public List<Goods> getGoodsAll(){
        Map<String,Object> map = new HashMap<String,Object>();
        List<Goods> goodsList = goodsService.getGoodsAll();
        if(goodsList != null){
          map.put("data",goodsList);
        }
       return goodsList;
    }

    @PostMapping("/addToCart")
    @ResponseBody
    public String addToCart(@Param("goodsId") String goodsId ,@Param("goodsName") String goodsName ,@Param("goodsPrice") String goodsPrice,@Param("amount")String amount){
        int inputAmount = Integer.parseInt(amount);
        CartItem cartItem = new CartItem();
        cartItem = goodsService.getCartItemById(goodsId);
        boolean insertResult;
        if(cartItem == null){
            insertResult =  goodsService.addToCart(goodsId,goodsName,goodsPrice,amount);
        }else{
            int oldAmount = Integer.parseInt(cartItem.getAmount());
            int newAmount = inputAmount+oldAmount;
            String newAmountStr =String.valueOf(newAmount);
            insertResult = goodsService.addAmount( goodsId ,newAmountStr);
        }
        if (insertResult){
            return  SUCCESS_CODE;
        }else {
            return FAIL_CODE;
        }
    }
}

业务层和持久层的代码就不给了吧,它和layui的那篇博客的后台是差不多,大家可以参照它。

 

3、购物车功能

购物车功能的实现包括  删除购物车条目、更改购物车信息(主要是数量)、计算总价、显示总价

首先更改购物车信息这个功能和前面的(订单已经存在,再添加就是修改数量)功能是一样的,都是通过goodsId找到一条购物车的信息,然后update。

UPDATE cart_item SET amount= #{amount} where goodsId= #{goodsId}

这边主要是删除功能的区别。

cart.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Zmall商城购物车页</title>

    <!-- Bootstrap -->
    <link href="../bootstrap/css/bootstrap.css" rel="stylesheet">
    <!-- 引入bootstrap-table样式 -->
    <link href="../css/bootstrap-table.css" rel="stylesheet">
    <!--    引入bootstrap的css样式-->
    <link href="../css/editTable.css" rel="stylesheet">


</head>
<body>
    <h1>Zmall网上商城</h1>
    <a href="/" class="btn btn-success  active" role="button">主页</a>
    <!--<a href="/" class="btn btn-success  active" role="button" onclick="refresh()">表格数据</a>-->
    <br/>
    <table class="table table-striped table-bordered table-hover" id="cartTable"></table>

    <div class="modal fade" id="updateContent">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                    <h4 class="modal-title">修改订单</h4>
                </div>
                <form id="form_data"  onsubmit="return updateCartItem()" method="post">
                 <div class="modal-body">
                     <label for="goodsId">商品编号</label>
                     <input type="text" id="goodsId" value="" readonly="readonly"/><br/>
                     <label for="goodsName">商品名称</label>
                     <input type="text" id="goodsName" value="" readonly="readonly"/><br/>
                     <label for="amount">购买数量</label>
                     <input type="text" id="amount" value=""/><br/>
                 </div>
                 <div class="modal-footer">
                        <button type="submit" class="btn btn-default">提交</button>
                  </div>
                 </form>
            </div>
        </div>
    </div>
    <a href = "/cart/list">显示数据</a>

    <script src="../js/jquery.min.js"></script>

    <script src="../bootstrap/js/bootstrap.js"></script>

    <!-- 引入中文语言包 -->
    <script src="../js/bootstrap-table.js"></script>
    <!-- bootstrap-table.min.js -->
    <script src="../js/bootstrap-table-editable.js"></script>
    <script src="../js/bootstrap-table-zh-CN.js"></script>

    <script src="../js/bootstrap-table-export.js"></script>
    <script src="../js/cart.js"></script>
</body>
</html>

 

cart.js

// alert($);

function operateBtns(value, row, index) {
    return [
        '<button id="updateCartItem" type="button" class="btn btn-default" >修改</button>',
        '<button id="deleteCartItem" type="button" class="btn btn-default">删除</button>',
    ].join('');
}

// function refresh(){
//     $.ajax({
//         url:"/cart/list",
//         success: function(data){
//             alert(data);
//             return data;
//         }
// })
// }

//  显示弹框,并且将这一行的信息填入进去
function updateAmount(goodsId,goodsName,amount){
    $('#goodsId').val(goodsId);
    $('#goodsName').val(goodsName);
    $('#amount').val(amount);
    $('#updateContent').modal('show');
}

//  提交订单
function updateCartItem(){
    $.post({
        url:"/cart/updateCartItem",
        data:{
            "goodsId":$("#goodsId").val(),
            "amount":$("#amount").val()
        },
        error : function () {
            alert("插入出错");
        },
        success: function(data){
            if (data == "200"){
                alert("插入成功");
            }
            else{
                alert("插入失败");
            }
            // $('#addContent').modal('hide');
        }
    })
}

function sumOrderPrice(value){
    console.log(value);
    var count = 0; for (var i in value) { count += value[i].totalPrice; } return count;
}

window.operateEvents = {
   'click #deleteCartItem': function (e, value, row, index) {
       $.ajax({
           url: "/cart/deleteCartItem",
           data: {
               id:row.id
           },
           dataType: "json",
           async:true,
           success: function (cartData) {
               if (cartData != null) {
                   alert("删除成功");
               }else{
                   alert("删除失败");
               }
           }
       })
    },'click #updateCartItem': function (e, value, row, index) {
        updateAmount(row.goodsId,row.goodsName,row.amount);
    }
};

$(function ( ) {
    $('#cartTable').bootstrapTable('destroy');

    $('#cartTable').bootstrapTable({//表格初始化
        url: '/cart/list',
        search: true,      //用于开启搜索框
        searchAlign: "left",
        buttonsAlign: "left",
        editable: true,//开启编辑模式 ,bootstrap 可编辑表格没有开
        clickToSelect: true,
        clickEdit: true,
        method: 'get',
        pageSize: 10, //每页3条
        pageNumber: 1,  //第1页
        pageList: [8, 25],   //在使用过程中根据情况调整每页条数.虽然你现在定义的每页3条,但你可以随时调整为10条或25条。
        cache: false,   //不缓存
        striped: true,
        pagination: true,
        sidePagination: 'client',   //设置分页是在客户端分页,设置成server就是在服务器端分页
        showRefresh: true,      //刷新表格按钮
        showExport: false,  //导出表格按钮,需要引用export.js
        showFooter: true,   //如果列数过多以至于超过屏幕的话会出现滚动条
        showToggle: true,
        columns: [
            {field: 'id', title: '订单号', sortable: true, align:"center",edit: {required: true, type: 'text'}},
            {field: 'goodsId', title: '商品编号', sortable: true,visible:false, align:"center"},
            {field: 'goodsName', title: '名称', sortable: true, align:"center"},
            {field: 'goodsPrice', title: '单价', sortable: true, align:"center"},
            {field: 'amount', title: '数量', sortable: true,align:"center",footerFormatter: sumOrderPrice},
            {field: 'totalPrice',title: '总价', align:"center",formatter:function(value, row, index){return row.goodsPrice*row.amount},footerFormatter:function (value) {  console.log(value);
                    var count = 0; for (var i in value) { count += value[i].totalPrice; } return count; }},
            {field: 'Button', title: '操作', align:"center", events: operateEvents, formatter: operateBtns, width: 180}]
    });

})

cartController.java

package com.txy.omall.controller;
import com.txy.omall.model.Cart;
import com.txy.omall.model.CartItem;
import com.txy.omall.service.ICart;
import com.txy.omall.service.IGoods;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("/cart")
public class CartController {
    private static final String SUCCESS_CODE = "200";
    private static final String FAIL_CODE = "500";

    @RequestMapping("/page")
    public String getCartPage(){
        return  "cart.html";
    }

    @Autowired
    private ICart cartService;

    @Autowired
    private IGoods goodsService;

    @GetMapping("/list")
    @ResponseBody
    public List<CartItem> getCartList(){
        List<CartItem> cartItemList = new ArrayList<>();
        cartItemList = cartService.getCartItemList();
        return cartItemList;
    }

    @GetMapping("/deleteCartItem")
    @ResponseBody
    public List<CartItem> deleteCartItem(@Param("id") String id ){
        int inputId = Integer.parseInt(id);
        if(cartService.deleteCartItem(inputId)){
            List<CartItem> cartItemList = new ArrayList<>();
            cartItemList = cartService.getCartItemList();
            return cartItemList;
        }else{
            return null;
        }
    }

    @PostMapping("/updateCartItem")
    @ResponseBody
    public String updateCartItem(@Param("amount")String amount,@Param("goodsId")String goodsId){
        boolean updateResult = goodsService.addAmount(goodsId,amount);
        if(updateResult){
            return SUCCESS_CODE;
        }else{
            return FAIL_CODE;
        }
    }
}

为什么这里return的是一个list,当初,考虑到的是重新加载表格的时候,可以做到局部刷新,这个地方还有问题。

然后回到功能需求,计算总价是怎么做到的,我们先算行总价(单个订单的总价),再通过行总价计算列总价。

行总价是这样实现的:

{field: 'totalPrice',title: '总价', align:"center",formatter:function(value, row, index){return row.goodsPrice*row.amount}}

列总价还存在问题,获取data的时候获取不到totoalPrice这一列的值,其他的却都能获取得到。猜想也许是因为totalPrice不是从后端传来的值,而formatter和footformatter是同时加载的,所以得到的值为空。【已经通过其他方法解决,详情见https://blog.csdn.net/qq_34093082/article/details/100151900

 

总结:

1、bootstrap和layui都是很好的框架,二者使用的时候有太多相似的地方了,对layui熟悉之后再去看bootstrap有很多的相似的地方。

2、尽量使用对象来封装传值的结果,要考虑到代码的拓展性。

3、若传值对象叫做User,User为NULL ,此时使用User.get方法会报NULL_Exception,因此我们在获取对象的第一步是做判断是否为空。

4、bootstrap-table如果想做成可编辑的样式,需要引入bootstrap-table-editable的js文件和css样式。这个过程我没引入成功,因此最后修改的过程换成了弹出框的形式。

5、使用bootstrap做局部刷新,include一个页面将该table包括进去来刷新。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值