[TOC]
## 步驟 1 : 先運行,看到效果,再學習
先將完整的 tmall_ssm 項目(向老師要相關資料),配置運行起來,確認可用之后,再學習做了哪些步驟以達到這樣的效果。
## 步驟 2 : 模仿和排錯
在確保可運行項目能夠正確無誤地運行之后,再嚴格照著教程的步驟,對代碼模仿一遍。
模仿過程難免代碼有出入,導致無法得到期望的運行結果,此時此刻通過比較**正確答案** ( 可運行項目 ) 和自己的代碼,來定位問題所在。
采用這種方式,**學習有效果,排錯有效率**,可以較為明顯地提升學習速度,跨過學習路上的各個檻。
## 步驟 3 : 界面效果
訪問如下地址:
`http://127.0.0.1:8080/tmall_ssm/foreproduct?pid=844`

目錄結構圖:

## 步驟 4 : Product
修改Product,增加如下屬性
1. 單個產品圖片集合
`private List<ProductImage> productSingleImages;`
2. 詳情產品圖片集合
`private List<ProductImage> productDetailImages;`
3. 銷量
`private int saleCount;`
4. 累計評價
`private int reviewCount;`
~~~
package com.dodoke.tmall.pojo;
import java.util.Date;
import java.util.List;
public class Product {
private Integer id;
private String name;
private String subTitle;
private Float originalPrice;
private Float promotePrice;
private Integer stock;
private Date createDate;
private Integer categoryId;
/* 非數據庫字段 */
private Category category;
private ProductImage firstProductImage;
private List<ProductImage> productSingleImages;
private List<ProductImage> productDetailImages;
private int saleCount;
private int reviewCount;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public String getSubTitle() {
return subTitle;
}
public void setSubTitle(String subTitle) {
this.subTitle = subTitle == null ? null : subTitle.trim();
}
public Float getOriginalPrice() {
return originalPrice;
}
public void setOriginalPrice(Float originalPrice) {
this.originalPrice = originalPrice;
}
public Float getPromotePrice() {
return promotePrice;
}
public void setPromotePrice(Float promotePrice) {
this.promotePrice = promotePrice;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public ProductImage getFirstProductImage() {
return firstProductImage;
}
public void setFirstProductImage(ProductImage firstProductImage) {
this.firstProductImage = firstProductImage;
}
public List<ProductImage> getProductSingleImages() {
return productSingleImages;
}
public void setProductSingleImages(List<ProductImage> productSingleImages) {
this.productSingleImages = productSingleImages;
}
public List<ProductImage> getProductDetailImages() {
return productDetailImages;
}
public void setProductDetailImages(List<ProductImage> productDetailImages) {
this.productDetailImages = productDetailImages;
}
public int getSaleCount() {
return saleCount;
}
public void setSaleCount(int saleCount) {
this.saleCount = saleCount;
}
public int getReviewCount() {
return reviewCount;
}
public void setReviewCount(int reviewCount) {
this.reviewCount = reviewCount;
}
}
~~~
## 步驟 5 : Review
修改自動生成的評價類Review, 增加User屬性
`private User user;`
~~~
package com.dodoke.tmall.pojo;
import java.util.Date;
public class Review {
private Integer id;
private String content;
private Date createDate;
private Integer productId;
private Integer userId;
/* 非數據庫字段 */
private User user;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content == null ? null : content.trim();
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
~~~
## 步驟 6 : ReviewService
增加ReviewService,提供CURD以及通過產品獲取評價方法:
List list(int pid);
int getCount(int pid);
~~~
package com.dodoke.tmall.service;
import java.util.List;
import com.dodoke.tmall.pojo.Review;
public interface ReviewService {
void add(Review c);
void delete(int id);
void update(Review c);
Review get(int id);
/**
* 通過產品獲取評價
* @param pid 產品id
* @return List
*/
List list(int pid);
/**
* 獲取產品有多少評價
* @param pid 產品id
* @return int
*/
int getCount(int pid);
}
~~~
## 步驟 7 : User
修改User, 增加一個getAnonymousName方法,用于在顯示評價者的時候,進行匿名顯示
~~~
package com.dodoke.tmall.pojo;
public class User {
private Integer id;
private String name;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
/**
* 在顯示評價者的時候進行匿名顯示
*
* @return String
*/
public String getAnonymousName() {
if (null == name)
return null;
if (name.length() <= 1)
return "*";
if (name.length() == 2)
return name.substring(0, 1) + "*";
char[] cs = name.toCharArray();
for (int i = 1; i < cs.length - 1; i++) {
cs[i] = '*';
}
return new String(cs);
}
}
~~~
## 步驟 8 : ReviewServiceImpl
增加ReviewServiceImpl,實現對應方法
~~~
package com.dodoke.tmall.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dodoke.tmall.mapper.ReviewMapper;
import com.dodoke.tmall.pojo.Review;
import com.dodoke.tmall.pojo.ReviewExample;
import com.dodoke.tmall.pojo.User;
import com.dodoke.tmall.service.ReviewService;
import com.dodoke.tmall.service.UserService;
@Service
public class ReviewServiceImpl implements ReviewService {
@Autowired
ReviewMapper reviewMapper;
@Autowired
UserService userService;
@Override
public void add(Review c) {
reviewMapper.insert(c);
}
@Override
public void delete(int id) {
reviewMapper.deleteByPrimaryKey(id);
}
@Override
public void update(Review c) {
reviewMapper.updateByPrimaryKeySelective(c);
}
@Override
public Review get(int id) {
return reviewMapper.selectByPrimaryKey(id);
}
public List<Review> list(int pid) {
ReviewExample example = new ReviewExample();
example.createCriteria().andProductIdEqualTo(pid);
example.setOrderByClause("id desc");
List<Review> result = reviewMapper.selectByExample(example);
setUser(result);
return result;
}
public void setUser(List<Review> reviews) {
for (Review review : reviews) {
setUser(review);
}
}
private void setUser(Review review) {
int uid = review.getUserId();
User user = userService.get(uid);
review.setUser(user);
}
@Override
public int getCount(int pid) {
return list(pid).size();
}
}
~~~
## 步驟 9 : OrderItemService
修改OrderItemService,增加根據產品獲取銷售量的方法:
`int getSaleCount(int pid);`
~~~
package com.dodoke.tmall.service;
import java.util.List;
import com.dodoke.tmall.pojo.Order;
import com.dodoke.tmall.pojo.OrderItem;
public interface OrderItemService {
void add(OrderItem c);
void delete(int id);
void update(OrderItem c);
OrderItem get(int id);
List list();
void fill(List<Order> os);
void fill(Order o);
int getSaleCount(int pid);
}
~~~
## 步驟 10 : OrderItemServiceImpl
修改OrderItemServiceImpl,實現getSaleCount方法
~~~
package com.dodoke.tmall.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dodoke.tmall.mapper.OrderItemMapper;
import com.dodoke.tmall.pojo.Order;
import com.dodoke.tmall.pojo.OrderItem;
import com.dodoke.tmall.pojo.OrderItemExample;
import com.dodoke.tmall.pojo.Product;
import com.dodoke.tmall.service.OrderItemService;
import com.dodoke.tmall.service.ProductService;
@Service
public class OrderItemServiceImpl implements OrderItemService {
@Autowired
OrderItemMapper orderItemMapper;
@Autowired
ProductService productService;
@Override
public void add(OrderItem c) {
orderItemMapper.insert(c);
}
@Override
public void delete(int id) {
orderItemMapper.deleteByPrimaryKey(id);
}
@Override
public void update(OrderItem c) {
orderItemMapper.updateByPrimaryKeySelective(c);
}
@Override
public OrderItem get(int id) {
OrderItem result = orderItemMapper.selectByPrimaryKey(id);
setProduct(result);
return result;
}
public List<OrderItem> list() {
OrderItemExample example = new OrderItemExample();
example.setOrderByClause("id desc");
return orderItemMapper.selectByExample(example);
}
@Override
public void fill(List<Order> os) {
// 遍歷每個訂單,然后挨個調用fill(Order order)。
for (Order o : os) {
fill(o);
}
}
public void fill(Order o) {
// 1. 根據訂單id查詢出其對應的所有訂單項
OrderItemExample example = new OrderItemExample();
example.createCriteria().andOrderIdEqualTo(o.getId());
example.setOrderByClause("id desc");
List<OrderItem> ois = orderItemMapper.selectByExample(example);
// 2. 通過setProduct為所有的訂單項設置Product屬性
setProduct(ois);
float total = 0;
int totalNumber = 0;
// 3. 遍歷所有的訂單項,然后計算出該訂單的總金額和總數量
for (OrderItem oi : ois) {
total += oi.getNumber() * oi.getProduct().getPromotePrice();
totalNumber += oi.getNumber();
}
o.setTotal(total);
o.setTotalNumber(totalNumber);
// 4. 最后再把訂單項設置在訂單的orderItems屬性上。
o.setOrderItems(ois);
}
public void setProduct(List<OrderItem> ois) {
for (OrderItem oi : ois) {
setProduct(oi);
}
}
private void setProduct(OrderItem oi) {
Product p = productService.get(oi.getProductId());
oi.setProduct(p);
}
@Override
public int getSaleCount(int pid) {
OrderItemExample example = new OrderItemExample();
example.createCriteria().andProductIdEqualTo(pid).andOrderIdIsNotNull();
List<OrderItem> ois = orderItemMapper.selectByExample(example);
int result = 0;
for (OrderItem oi : ois) {
result += oi.getNumber();
}
return result;
}
}
~~~
> 銷量顯示問題
把商品加入購物車,還沒完成付款,而產品的銷量理應完成付款才算一筆銷量。應該在OrderItemExample后面加上`.andOidIsNotNull()`這個條件。
## 步驟 11 : ProductService
修改ProductService,增加為產品設置銷量和評價數量的方法:
~~~
void setSaleAndReviewNumber(Product p);
void setSaleAndReviewNumber(List<Product> ps);
~~~
~~~
package com.dodoke.tmall.service;
import java.util.List;
import com.dodoke.tmall.pojo.Category;
import com.dodoke.tmall.pojo.Product;
public interface ProductService {
void add(Product c);
void delete(int id);
void update(Product c);
Product get(int id);
List list(int categoryId);
void setFirstProductImage(Product p);
/**
* 為分類填充產品集合
*
* @param categorys
*/
public void fill(List<Category> categorys);
/**
* 為多個分類填充產品集合
*
* @param category
*/
public void fill(Category category);
/**
* 為多個分類填充推薦產品集合,即把分類下的產品集合,按照8個為一行,拆成多行,以利于后續頁面上進行顯示
*
* @param categorys
*/
public void fillByRow(List<Category> categorys);
/**
* 為產品設置銷量和評價數量
* @param p 產品對象
*/
void setSaleAndReviewNumber(Product p);
void setSaleAndReviewNumber(List<Product> ps);
}
~~~
## 步驟 12 : ProductServiceImpl
修改ProductServiceImpl,實現為產品設置銷量和評價數量的方法:
void setSaleAndReviewNumber(Product p);
void setSaleAndReviewNumber
~~~
package com.dodoke.tmall.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dodoke.tmall.mapper.ProductMapper;
import com.dodoke.tmall.pojo.Category;
import com.dodoke.tmall.pojo.Product;
import com.dodoke.tmall.pojo.ProductExample;
import com.dodoke.tmall.pojo.ProductImage;
import com.dodoke.tmall.service.CategoryService;
import com.dodoke.tmall.service.OrderItemService;
import com.dodoke.tmall.service.ProductImageService;
import com.dodoke.tmall.service.ProductService;
import com.dodoke.tmall.service.ReviewService;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
ProductMapper productMapper;
@Autowired
CategoryService categoryService;
@Autowired
ProductImageService productImageService;
@Autowired
OrderItemService orderItemService;
@Autowired
ReviewService reviewService;
@Override
public void add(Product p) {
p.setCreateDate(new Date());
productMapper.insert(p);
}
@Override
public void delete(int id) {
productMapper.deleteByPrimaryKey(id);
}
@Override
public void update(Product p) {
productMapper.updateByPrimaryKeySelective(p);
}
@Override
public Product get(int id) {
Product p = productMapper.selectByPrimaryKey(id);
setFirstProductImage(p);
setCategory(p);
return p;
}
private void setCategory(Product p) {
int categoryId = p.getCategoryId();
Category category = categoryService.get(categoryId);
p.setCategory(category);
}
@Override
public List list(int categoryId) {
ProductExample example = new ProductExample();
example.createCriteria().andCategoryIdEqualTo(categoryId);
example.setOrderByClause("id desc");
List result = productMapper.selectByExample(example);
setFirstProductImage(result);
setCategory(result);
return result;
}
public void setCategory(List<Product> ps) {
for (Product p : ps) {
setCategory(p);
}
}
/**
* 根據productId和圖片類型查詢出所有的單個圖片,然后把第一個取出來放在firstProductImage上。
*
* @param p
* 產品
*/
@Override
public void setFirstProductImage(Product p) {
List<ProductImage> pis = productImageService.list(p.getId(), ProductImageService.type_single);
if (!pis.isEmpty()) {
ProductImage pi = pis.get(0);
p.setFirstProductImage(pi);
}
}
/**
* 給多個產品設置圖片
*
* @param ps
* 產品集合
*/
public void setFirstProductImage(List<Product> ps) {
for (Product p : ps) {
setFirstProductImage(p);
}
}
/**
* 為分類填充產品集合
*
* @param categorys
*/
@Override
public void fill(Category c) {
List<Product> ps = list(c.getId());
c.setProducts(ps);
}
/**
* 為多個分類填充產品集合
*
* @param category
*/
@Override
public void fill(List<Category> cs) {
for (Category c : cs) {
fill(c);
}
}
/**
* 為多個分類填充推薦產品集合,即把分類下的產品集合,按照8個為一行,拆成多行,以利于后續頁面上進行顯示
*
* @param categorys
*/
@Override
public void fillByRow(List<Category> cs) {
// 把分類下的產品集合,按照8個為一行,拆成多行,以利于后續頁面上進行顯示
int productNumberEachRow = 8;
// 將categorylist中每個category拿出來循環
for (Category c : cs) {
// 獲取每個分類中對應的產品,在使用fillByRow(List<Category>
// cs)這個方法前,需要先使用fill方法,注入分類中的所有產品,因此在這里才可以取出產品
List<Product> products = c.getProducts();
// 設置每一頁的頭圖
setFirstProductImage(products);
// 每一行產品的list
List<List<Product>> productsByRow = new ArrayList<>();
for (int i = 0; i < products.size(); i += productNumberEachRow) {
int size = i + productNumberEachRow;
// 界限判斷
size = size > products.size() ? products.size() : size;
// 該方法返回的是父list的一個子集合,從fromIndex(包含),到toIndex(不包含)
List<Product> productsOfEachRow = products.subList(i, size);
productsByRow.add(productsOfEachRow);
}
c.setProductsByRow(productsByRow);
}
}
@Override
public void setSaleAndReviewNumber(Product p) {
int saleCount = orderItemService.getSaleCount(p.getId());
p.setSaleCount(saleCount);
int reviewCount = reviewService.getCount(p.getId());
p.setReviewCount(reviewCount);
}
@Override
public void setSaleAndReviewNumber(List<Product> ps) {
for (Product p : ps) {
setSaleAndReviewNumber(p);
}
}
}
~~~
## 步驟 13 :`ForeController.product()`
通過訪問地址
`http://127.0.0.1:8080/tmall_ssm/foreproduct?pid=844`
導致`ForeController.product() `方法被調用
1. 獲取參數pid
2. 根據pid獲取Product 對象p
3. 根據對象p,獲取這個產品對應的單個圖片集合
4. 根據對象p,獲取這個產品對應的詳情圖片集合
5. 獲取產品的所有屬性值
6. 獲取產品對應的所有的評價
7. 設置產品的銷量和評價數量
8. 把上述取值放在request屬性上
9. 服務端跳轉到 "product.jsp" 頁面
~~~
package com.dodoke.tmall.controller;
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.util.HtmlUtils;
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";
}
}
~~~
## 步驟 14 : product.jsp
與 register.jsp 相仿,product.jsp也包含了header.jsp, top.jsp, simpleSearch.jsp, footer.jsp 等公共頁面。
中間是產品業務頁面 productPage.jsp
~~~
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@include file="../include/fore/header.jsp"%>
<%@include file="../include/fore/top.jsp"%>
<%@include file="../include/fore/simpleSearch.jsp"%>
<%@include file="../include/fore/product/productPage.jsp"%>
<%@include file="../include/fore/footer.jsp"%>
~~~
## 步驟 15 : productPage.jsp
productPage.jsp 又由3個頁面組成
1. imgAndInfo.jsp
單個圖片和基本信息
2. productReview.jsp
評價信息
3. productDetail.jsp
詳情圖片
~~~
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<title>模仿天貓官網 ${p.name}</title>
<div class="categoryPictureInProductPageDiv">
<img class="categoryPictureInProductPage" src="img/category/${p.category.id}.jpg">
</div>
<div class="productPageDiv">
<%@include file="imgAndInfo.jsp" %>
<%@include file="productReview.jsp" %>
<%@include file="productDetail.jsp" %>
</div>
~~~
## 步驟 16 : imgAndInfo.jsp
1. 左側顯示5張單個圖片
(1)默認顯示第一張圖片
~~~
<img src="img/productSingle/${p.firstProductImage.id}.jpg" class="bigImg">
~~~
(2)5張小圖片
~~~
<c:forEach items="${p.productSingleImages}" var="pi">
<img src="img/productSingle_small/${pi.id}.jpg" bigImageURL="img/productSingle/${pi.id}.jpg" class="smallImage">
</c:forEach>
~~~
2. 右邊顯示基本信息
(1)標題和小標題
~~~
<div class="productTitle">
${p.name}
</div>
<div class="productSubTitle">
${p.subTitle}
</div>
~~~
(2)原始價格和促銷價
~~~
<fmt:formatNumber type="number" value="${p.originalPrice}" minFractionDigits="2"/>
<fmt:formatNumber type="number" value="${p.promotePrice}" minFractionDigits="2"/>
~~~
(3)銷量和累計評價
~~~
<div>銷量 <span class="redColor boldWord"> ${p.saleCount }</span></div>
<div>累計評價 <span class="redColor boldWord"> ${p.reviewCount}</span></div>
~~~
(4)庫存
`<span>庫存${p.stock}件</span>`

> 其中模態框在下一個章節講解
### 顯示縮略圖效果js代碼講解
~~~
$("img.smallImage").mouseenter(function(){
var bigImageURL = $(this).attr("bigImageURL");
$("img.bigImg").attr("src",bigImageURL);
});
~~~
首先在小圖片上有一個自定義屬性bigImageURL,用于存放對應的圖片的位置
~~~
<img width="100px" class="smallImage" src="img/productSingle_small/8620.jpg" bigImageURL="img/productSingle/8620.jpg">
~~~
監聽小圖片的mouseenter事件,獲取小圖片的bigImageURL屬性,把大圖片的src修改為該圖片
預加載,因為圖片比較大,所以需要進行預加載。
~~~
$("img.bigImg").load(
function(){
$("img.smallImage").each(function(){
var bigImageURL = $(this).attr("bigImageURL");
img = new Image();
img.src = bigImageURL;
img.onload = function(){
console.log(bigImageURL);
$("div.img4load").append($(img));
};
});
}
);
~~~
> 當指定的元素(及子元素)已加載時,會發生 `load() `事件。
根據不同的瀏覽器(Firefox 和 IE),如果圖像已被緩存,則也許不會觸發 load 事件
在大圖片加載好之后,根據每個小圖片的bigImageURL ,創建一個Image對象,然后把這個image對象的src屬性,設置為bigImageURL。 當這個img對象加載完畢之后,再放到被隱藏的`div.img4load`中,從而達到預加載的效果。
>小圖片和大圖片的img,分別使用的是不同的圖片。
> 小圖片本身就是分辨率低的圖片,直接使用小圖的src,放大顯示就會看上去很糊了。
>
> 預加載是這樣的:如果不使用預加載,那么當上面的主圖片位置顯示大圖片的時候,瀏覽器"才"會到服務器去取大圖片,而大圖片加載比較慢,會影響用戶體驗。
>
> 使用了預加載,就是用戶沒有把鼠標移動到小圖片之前,瀏覽器已經去把大圖片加載了,當上面的主圖片位置顯示大圖片的時候,直接就顯示了,用戶不會感受到加載的時間,感覺一下就顯示出來了。
>
> 預加載的技術是通過js創建Image對象來實現,創建 Image對象,并設置其src,會導致瀏覽器去服務端獲取圖片,從而達到在未使用前,事先加載大圖片的緩存效果。 真正需要顯示大圖片的時候,用戶就覺得大圖片一下就出來了,不會感覺到大圖片加載的卡頓。
### 修改價格
1. 效果:
可以向上調整數量,但是不能超過最大庫存
可以向下調整數量,但是不能小于1
輸入任何非數字,都會恢復為原來數字
輸入的數字超過庫存,恢復為最大庫存
2. js代碼講解
~~~
$(".productNumberSetting").keyup(function(){
var num= $(".productNumberSetting").val();
num = parseInt(num);
if(isNaN(num))
num= 1;
if(num<=0)
num = 1;
if(num>stock)
num = stock;
$(".productNumberSetting").val(num);
});
~~~
監聽keyup鍵盤彈起事件
獲取輸入框的值
* 如果是非數字,那么就設置為1。
> 注: parseInt會把文本中的非數字前的數字解析出來,所以如果文本框的內容是22B,那么解析出來是22.
* 如果是負數,那么設置為1。
* 如果大于庫存,設置為最大庫存。
~~~
$(".increaseNumber").click(function(){
var num= $(".productNumberSetting").val();
num++;
if(num>stock)
num = stock;
$(".productNumberSetting").val(num);
});
~~~
點擊增加按鈕的時候,獲取當前的值,并在當前值的基礎上`+1`,如果超過了庫存就取庫存最大值
~~~
$(".decreaseNumber").click(function(){
var num= $(".productNumberSetting").val();
--num;
if(num<=0)
num=1;
$(".productNumberSetting").val(num);
});
~~~
點擊減少按鈕的時候,獲取當前的值,并在當前值的基礎上`-1`,如果`<=0`,則取1。
## 步驟 17 : productReview.jsp
暫時沒有數據,所以看不到截圖。
這里借助`c:forEach`遍歷request中的reviews。

~~~
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<div class="productReviewDiv" >
<div class="productReviewTopPart">
<a href="#nowhere" class="productReviewTopPartSelectedLink">商品詳情</a>
<a href="#nowhere" class="selected">累計評價 <span class="productReviewTopReviewLinkNumber">${p.reviewCount}</span> </a>
</div>
<div class="productReviewContentPart">
<c:forEach items="${reviews}" var="r">
<div class="productReviewItem">
<div class="productReviewItemDesc">
<div class="productReviewItemContent">
${r.content }
</div>
<div class="productReviewItemDate"><fmt:formatDate value="${r.createDate}" pattern="yyyy-MM-dd"/></div>
</div>
<div class="productReviewItemUserInfo">
${r.user.anonymousName}<span class="userInfoGrayPart">(匿名)</span>
</div>
<div style="clear:both"></div>
</div>
</c:forEach>
</div>
</div>
~~~
## 步驟 18 : productDetail.jsp
如圖所示,productDetail.jsp做了兩件事
1. 顯示屬性值
~~~
<c:forEach items="${pvs}" var="pv">
<span>${pv.property.name}: ${fn:substring(pv.value, 0, 10)} </span>
</c:forEach>
~~~
2. 顯示詳情圖片
~~~
<c:forEach items="${p.productDetailImages}" var="pi">
<img src="img/productDetail/${pi.id}.jpg">
</c:forEach>
~~~
~~~
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<div class="productDetailDiv" >
<div class="productDetailTopPart">
<a href="#nowhere" class="productDetailTopPartSelectedLink selected">商品詳情</a>
<a href="#nowhere" class="productDetailTopReviewLink">累計評價 <span class="productDetailTopReviewLinkNumber">${p.reviewCount}</span> </a>
</div>
<div class="productParamterPart">
<div class="productParamter">產品參數:</div>
<div class="productParamterList">
<c:forEach items="${pvs}" var="pv">
<span>${pv.property.name}: ${fn:substring(pv.value, 0, 10)} </span>
</c:forEach>
</div>
<div style="clear:both"></div>
</div>
<div class="productDetailImagesPart">
<c:forEach items="${p.productDetailImages}" var="pi">
<img src="img/productDetail/${pi.id}.jpg">
</c:forEach>
</div>
</div>
~~~
- 項目簡介
- 功能一覽
- 前臺
- 后臺
- 開發流程
- 需求分析-展示
- 首頁
- 產品頁
- 分類頁
- 搜索結果頁
- 購物車查看頁
- 結算頁
- 確認支付頁
- 支付成功頁
- 我的訂單頁
- 確認收貨頁
- 確認收貨成功頁
- 評價頁
- 需求分析-交互
- 分類頁排序
- 立即購買
- 加入購物車
- 調整訂單項數量
- 刪除訂單項
- 生成訂單
- 訂單頁功能
- 確認付款
- 確認收貨
- 提交評價信息
- 登錄
- 注冊
- 退出
- 搜索
- 前臺需求列表
- 需求分析后臺
- 分類管理
- 屬性管理
- 產品管理
- 產品圖片管理
- 產品屬性設置
- 用戶管理
- 訂單管理
- 后臺需求列表
- 表結構設計
- 數據建模
- 表與表之間的關系
- 后臺-分類管理
- 可運行的項目
- 靜態資源
- JSP包含關系
- 查詢
- 分頁
- 增加
- 刪除
- 編輯
- 修改
- 做一遍
- 重構
- 分頁方式
- 分類逆向工程
- 所有逆向工程
- 后臺其他頁面
- 屬性管理實現
- 產品管理實現
- 產品圖片管理實現
- 產品屬性值設置
- 用戶管理實現
- 訂單管理實現
- 前端
- 前臺-首頁
- 可運行的項目
- 靜態資源
- ForeController
- home方法
- home.jsp
- homePage.jsp
- 前臺-無需登錄
- 注冊
- 登錄
- 退出
- 產品頁
- 模態登錄
- 分類頁
- 搜索
- 前臺-需要登錄
- 購物流程
- 立即購買
- 結算頁面
- 加入購物車
- 查看購物車頁面
- 登錄狀態攔截器
- 其他攔截器
- 購物車頁面操作
- 訂單狀態圖
- 生成訂單
- 我的訂單頁
- 我的訂單頁操作
- 評價產品
- 總結