企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] ## 步骤 1 : 先运行,看到效果,再学习 先将完整的 tmall_ssm 项目(向老师要相关资料),配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。 ## 步骤 2 : 模仿和排错 在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。 模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较**正确答案** ( 可运行项目 ) 和自己的代码,来定位问题所在。 采用这种方式,**学习有效果,排错有效率**,可以较为明显地提升学习速度,跨过学习路上的各个槛。 ## 步骤 3 : 界面效果 访问地址:`http://localhost:8080/tmall_ssm/forecategory?cid=74` ![](https://box.kancloud.cn/4ead14506fa41ef7ee9e282b6b8e3d35_1551x957.png) ## 步骤 4 : 5个Comparator比较器 分类这个页面有排序功能,所以在讲解ForeController之前,先准备5个Comparator比较器 1. ProductAllComparator 综合比较器 把 销量x评价 高的放前面 2. ProductReviewComparator 人气比较器 把 评价数量多的放前面 3. ProductDateComparator 新品比较器 把 创建日期晚的放前面 4. ProductSaleCountComparator 销量比较器 把 销量高的放前面 5. ProductPriceComparator 价格比较器 把 价格低的放前面 ProductAllComparator.java ~~~ package com.dodoke.tmall.comparator; import java.util.Comparator; import com.dodoke.tmall.pojo.Product; public class ProductAllComparator implements Comparator<Product> { @Override public int compare(Product p1, Product p2) { return p2.getReviewCount() * p2.getSaleCount() - p1.getReviewCount() * p1.getSaleCount(); } } ~~~ ProductReviewComparator.java ~~~ package com.dodoke.tmall.comparator; import java.util.Comparator; import com.dodoke.tmall.pojo.Product; public class ProductReviewComparator implements Comparator<Product> { @Override public int compare(Product p1, Product p2) { return p2.getReviewCount() - p1.getReviewCount(); } } ~~~ ProductDateComparator.java ~~~ package com.dodoke.tmall.comparator; import java.util.Comparator; import com.dodoke.tmall.pojo.Product; public class ProductDateComparator implements Comparator<Product> { @Override public int compare(Product p1, Product p2) { return p2.getCreateDate().compareTo(p1.getCreateDate()); } } ~~~ ProductSaleCountComparator.java ~~~ package com.dodoke.tmall.comparator; import java.util.Comparator; import com.dodoke.tmall.pojo.Product; public class ProductSaleCountComparator implements Comparator<Product> { @Override public int compare(Product p1, Product p2) { return p2.getSaleCount() - p1.getSaleCount(); } } ~~~ ProductPriceComparator.java ~~~ package com.dodoke.tmall.comparator; import java.util.Comparator; import com.dodoke.tmall.pojo.Product; public class ProductPriceComparator implements Comparator<Product> { @Override public int compare(Product p1, Product p2) { return (int) (p1.getPromotePrice() - p2.getPromotePrice()); } } ~~~ ## 步骤 5 : ForeController.category() 1. 获取参数cid 2. 根据cid获取分类Category对象 c 3. 为c填充产品 4. 为产品填充销量和评价数据 5. 获取参数sort 5.1 如果`sort==null`,即不排序 5.2 如果`sort!=null`,则根据sort的值,从5个Comparator比较器中选择一个对应的排序器进行排序 6. 把c放在model中 7. 服务端跳转到 category.jsp ~~~ package com.dodoke.tmall.controller; import java.util.Collections; import java.util.List; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.util.HtmlUtils; import com.dodoke.tmall.comparator.ProductAllComparator; import com.dodoke.tmall.comparator.ProductDateComparator; import com.dodoke.tmall.comparator.ProductPriceComparator; import com.dodoke.tmall.comparator.ProductReviewComparator; import com.dodoke.tmall.comparator.ProductSaleCountComparator; import com.dodoke.tmall.pojo.Category; import com.dodoke.tmall.pojo.Product; import com.dodoke.tmall.pojo.ProductImage; import com.dodoke.tmall.pojo.PropertyValue; import com.dodoke.tmall.pojo.Review; import com.dodoke.tmall.pojo.User; import com.dodoke.tmall.service.CategoryService; import com.dodoke.tmall.service.OrderItemService; import com.dodoke.tmall.service.OrderService; import com.dodoke.tmall.service.ProductImageService; import com.dodoke.tmall.service.ProductService; import com.dodoke.tmall.service.PropertyValueService; import com.dodoke.tmall.service.ReviewService; import com.dodoke.tmall.service.UserService; @Controller @RequestMapping("") public class ForeController { @Autowired CategoryService categoryService; @Autowired ProductService productService; @Autowired UserService userService; @Autowired ProductImageService productImageService; @Autowired PropertyValueService propertyValueService; @Autowired OrderService orderService; @Autowired OrderItemService orderItemService; @Autowired ReviewService reviewService; @RequestMapping("forehome") public String home(Model model) { List<Category> cs = categoryService.list(); productService.fill(cs); productService.fillByRow(cs); model.addAttribute("cs", cs); return "fore/home"; } @RequestMapping("foreregister") public String register(Model model, User user) { String name = user.getName(); // 把账号里的特殊符号进行转义 name = HtmlUtils.htmlEscape(name); user.setName(name); boolean exist = userService.isExist(name); if (exist) { String m = "用户名已经被使用,不能使用"; model.addAttribute("msg", m); model.addAttribute("user", null); return "fore/register"; } userService.add(user); return "redirect:registerSuccessPage"; } @RequestMapping("forelogin") public String login(@RequestParam("name") String name, @RequestParam("password") String password, Model model, HttpSession session) { name = HtmlUtils.htmlEscape(name); User user = userService.get(name, password); if (null == user) { model.addAttribute("msg", "账号密码错误"); return "fore/login"; } session.setAttribute("user", user); return "redirect:forehome"; } @RequestMapping("forelogout") public String logout(HttpSession session) { session.removeAttribute("user"); return "redirect:forehome"; } @RequestMapping("foreproduct") public String product(int pid, Model model) { Product p = productService.get(pid); // 根据对象p,获取这个产品对应的单个图片集合 List<ProductImage> productSingleImages = productImageService.list(p.getId(), ProductImageService.type_single); // 根据对象p,获取这个产品对应的详情图片集合 List<ProductImage> productDetailImages = productImageService.list(p.getId(), ProductImageService.type_detail); p.setProductSingleImages(productSingleImages); p.setProductDetailImages(productDetailImages); // 获取产品的所有属性值 List<PropertyValue> pvs = propertyValueService.list(p.getId()); // 获取产品对应的所有的评价 List<Review> reviews = reviewService.list(p.getId()); // 设置产品的销量和评价数量 productService.setSaleAndReviewNumber(p); model.addAttribute("reviews", reviews); model.addAttribute("p", p); model.addAttribute("pvs", pvs); return "fore/product"; } @RequestMapping("forecheckLogin") @ResponseBody public String checkLogin(HttpSession session) { User user = (User) session.getAttribute("user"); if (null != user) { return "success"; } return "fail"; } @RequestMapping("foreloginAjax") @ResponseBody public String loginAjax(@RequestParam("name") String name, @RequestParam("password") String password, HttpSession session) { name = HtmlUtils.htmlEscape(name); User user = userService.get(name, password); if (null == user) { return "fail"; } session.setAttribute("user", user); return "success"; } @RequestMapping("forecategory") public String category(int cid, String sort, Model model) { Category c = categoryService.get(cid); productService.fill(c); productService.setSaleAndReviewNumber(c.getProducts()); if (null != sort) { switch (sort) { case "review": Collections.sort(c.getProducts(), new ProductReviewComparator()); break; case "date": Collections.sort(c.getProducts(), new ProductDateComparator()); break; case "saleCount": Collections.sort(c.getProducts(), new ProductSaleCountComparator()); break; case "price": Collections.sort(c.getProducts(), new ProductPriceComparator()); break; case "all": Collections.sort(c.getProducts(), new ProductAllComparator()); break; } } model.addAttribute("c", c); return "fore/category"; } } ~~~ ![](https://box.kancloud.cn/7b25d56539b33f2a258a51031cc1dc88_573x528.png) ## 步骤 6 : category.jsp 与 register.jsp 相仿,category.jsp也包含了header.jsp, top.jsp, simpleSearch.jsp, footer.jsp 等公共页面。 中间是分类业务页面 categoryPage.jsp ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@include file="../include/header.jsp"%> <%@include file="../include/top.jsp"%> <%@include file="../include/search.jsp"%> <%@include file="../include/category/categoryPage.jsp"%> <%@include file="../include/footer.jsp"%> ~~~ ## 步骤 7 : categoryPage.jsp categoryPage.jsp 里有3个内容 1. 显示当前分类图片 `<img src="img/category/${c.id}.jpg">` 2. 排序条 sortBar.jsp 3. 产品列表 productsByCategory.jsp ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <title>模仿天猫官网-${c.name}</title> <div id="category"> <div class="categoryPageDiv"> <img src="img/category/${c.id}.jpg"> <%@include file="sortBar.jsp"%> <%@include file="productsByCategory.jsp"%> </div> </div> ~~~ ## 步骤 8 : sortBar.jsp sortBar.jsp 即排序条,做了如下几个与数据相关的事情 1. 根据sort参数判断哪个排序按钮高亮 ~~~ <td <c:if test="${'all'==param.sort||empty param.sort}">class="grayColumn"</c:if> ><a href="?cid=${c.id}&sort=all">综合<span class="glyphicon glyphicon-arrow-down"></span></a></td> <td <c:if test="${'review'==param.sort}">class="grayColumn"</c:if> ><a href="?cid=${c.id}&sort=review">人气<span class="glyphicon glyphicon-arrow-down"></span></a></td> <td <c:if test="${'date'==param.sort}">class="grayColumn"</c:if>><a href="?cid=${c.id}&sort=date">新品<span class="glyphicon glyphicon-arrow-down"></span></a></td> <td <c:if test="${'saleCount'==param.sort}">class="grayColumn"</c:if>><a href="?cid=${c.id}&sort=saleCount">销量<span class="glyphicon glyphicon-arrow-down"></span></a></td> <td <c:if test="${'price'==param.sort}">class="grayColumn"</c:if>><a href="?cid=${c.id}&sort=price">价格<span class="glyphicon glyphicon-resize-vertical"></span></a></td> ~~~ 2. 每个排序按钮提交到本页面,即/forecategory,并带上参数sort ![](https://box.kancloud.cn/f6807fed2d38cdfa21110a2094baa925_382x55.png) ### js代码讲解 ~~~ <input class="sortBarPrice beginPrice" type="text" placeholder="请输入"> <input class="sortBarPrice endPrice" type="text" placeholder="请输入"> ~~~ 首先开始价格和结束价格的输入框,都使用了sortBarPrice, 这样通过 `$("input.sortBarPrice").keyup(function(){})` 就可以给这两个输入框添加keyup事件 ~~~ var num= $(this).val(); if(num.length==0){ $("div.productUnit").show(); return; } ~~~ 然后是获取数值,如果是空的,那么就让所有的产品项都显示出来,因为此时输入的价格是无效的。 ~~~ num = parseInt(num); if(isNaN(num)) num= 1; if(num<=0) num = 1; $(this).val(num); ~~~ 接着判断输入的值,是否是数字,如果不是数字,或者是负数就显示1. ~~~ var begin = $("input.beginPrice").val(); var end = $("input.endPrice").val(); if(!isNaN(begin) && !isNaN(end)){ $("div.productUnit").hide(); $("div.productUnit").each(function(){ var price = $(this).attr("price"); price = new Number(price); if(price<=end && price>=begin) $(this).show(); }); } ~~~ 紧跟着获取开始和结束价格 `<div price="799.2" class="productUnit">` 首先,产品项上有一个自定义属性price,标注了该产品项的价格 接着,隐藏所有的产品项,然后遍历每一个产品项,当产品的价格在开始和结束区间的话,就把它显示出来。 > `price = new Number(price);`这一句的作用,是确保price变量是数字类型,否则就有可能是字符串类型,导致后续的运算出现偏差了 ## 步骤 9 : productsByCategory.jsp productsByCategory.jsp显示当前分类下的所有产品 通过forEach遍历c.products集合里的每个产品,并把产品标题,价格,图片,评价数,成交数打印出来 ~~~ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <%@ taglib prefix='fmt' uri="http://java.sun.com/jsp/jstl/fmt" %> <div class="categoryProducts"> <c:forEach items="${c.products}" var="p" varStatus="stc"> <div class="productUnit" price="${p.promotePrice}"> <div class="productUnitFrame"> <a href="foreproduct?pid=${p.id}"> <img class="productImage" src="img/productSingle_middle/${p.firstProductImage.id}.jpg"> </a> <span class="productPrice">¥<fmt:formatNumber type="number" value="${p.promotePrice}" maxFractionDigits="2" minFractionDigits="2"/></span> <a class="productLink" href="foreproduct?pid=${p.id}"> ${fn:substring(p.name, 0, 50)} </a> <a class="tmallLink" href="foreproduct?pid=${p.id}">天猫专卖</a> <div class="show1 productInfo"> <span class="monthDeal ">月成交 <span class="productDealNumber">${p.saleCount}笔</span></span> <span class="productReview">评价<span class="productReviewNumber">${p.reviewCount}</span></span> <span class="wangwang"> <a class="wangwanglink" href="#nowhere"> <img src="img/site/wangwang.png"> </a> </span> </div> </div> </div> </c:forEach> <div style="clear:both"></div> </div> ~~~ > 当然这里也可以使用SQL语句排序,实际工作多半都是在数据库的基础上做的排序,甚至多字段排序。这里是演示比较器的这种用法。 > 自己可以尝试使用SQL语句排序。