樂優商城(三十二)——購物車
目錄
一、搭建購物車微服務
1.1 建立module
1.2 pom依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>leyou</artifactId> <groupId>com.leyou.parent</groupId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.leyou.cart</groupId> <artifactId>leyou-cart</artifactId> <version>1.0.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> </project>
1.3 配置檔案
server: port: 8088 spring: application: name: cart-service redis: host: 192.168.19.121 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka registry-fetch-interval-seconds: 10 instance: instance-id: ${spring.application.name}:${server.port} prefer-ip-address: true #當你獲取host時,返回的不是主機名,而是ip ip-address: 127.0.0.1 lease-expiration-duration-in-seconds: 10 #10秒不傳送九過期 lease-renewal-interval-in-seconds: 5 #每隔5秒發一次心跳
1.4 啟動類
package com.leyou.cart;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @Author: 98050
* @Time: 2018-10-24 20:46
* @Feature:購物車啟動器
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LyCartApplication {
public static void main(String[] args) {
SpringApplication.run(LyCartApplication.class,args);
}
}
二、購物車功能分析
2.1 需求
需求描述:
-
使用者可以在登入狀態下將商品新增到購物車
-
放入資料庫
-
放入redis(採用)
-
-
使用者可以在未登入狀態下將商品新增到購物車
-
放入localstorage
-
-
使用者可以使用購物車一起結算下單
-
使用者可以查詢自己的購物車
-
使用者可以在購物車中修改購買商品的數量。
-
使用者可以在購物車中刪除商品。
-
在購物車中展示商品優惠資訊
-
提示購物車商品價格變化
2.2 流程圖
這幅圖主要描述了兩個功能:新增商品到購物車、查詢購物車。
新增商品:
-
判斷是否登入
-
是:則新增商品到後臺Redis中
-
否:則新增商品到本地的Localstorage
-
無論哪種新增,完成後都需要查詢購物車列表:
-
判斷是否登入
-
否:直接查詢localstorage中資料並展示
-
是:已登入,則需要先看本地是否有資料,
-
有:需要提交到後臺新增到redis,合併資料,而後查詢
-
否:直接去後臺查詢redis,而後返回
-
-
三、未登入購物車
3.1 準備
3.1.1 購物車的資料結構
首先分析一下未登入購物車的資料結構。
我們看下頁面展示需要什麼資料:
因此每一個購物車資訊,都是一個物件,包含:
{
skuId:2131241,
title:"小米6",
image:"",
price:190000,
num:1,
ownSpec:"{"機身顏色":"陶瓷黑尊享版","記憶體":"6GB","機身儲存":"128GB"}"
}
另外,購物車中不止一條資料,因此最終會是物件的陣列。即:
[
{...},{...},{...}
]
3.1.2 web本地儲存
知道了資料結構,下一個問題,就是如何儲存購物車資料。前面分析過,可以使用Localstorage來實現。Localstorage是web本地儲存的一種,那麼,什麼是web本地儲存呢?
什麼是web本地儲存
web本地儲存主要有兩種方式:
-
LocalStorage:localStorage 方法儲存的資料沒有時間限制。第二天、第二週或下一年之後,資料依然可用。
-
SessionStorage:sessionStorage 方法針對一個 session 進行資料儲存。當用戶關閉瀏覽器視窗後,資料會被刪除。
LocalStorage的用法
語法非常簡單:
localStorage.setItem("key","value"); // 儲存資料
localStorage.getItem("key"); // 獲取資料
localStorage.removeItem("key"); // 刪除資料
注意:localStorage和SessionStorage都只能儲存字串。
不過,在common.js中,已經對localStorage進行了簡單的封裝:
示例:
3.1.3 獲取num
新增購物車需要知道購物的數量,所以需要獲取數量大小。在Vue中定義num,儲存數量:
然後將num與頁面的input框繫結,同時給+
和-
的按鈕繫結事件:
編寫方法:
3.2 新增購物車
3.2.1 點選事件
商品詳情頁:
現在點選加入購物車會跳轉到購物車成功頁面。
不過不這麼做,先繫結點選事件,然後實現新增購物車功能。
addCart方法中判斷使用者的登入狀態:
addCart(){
ly.http.get("/auth/verify").then(res => {
//已經登入傳送訊息到後臺,儲存到redis中
}).catch(() =>{
//未登入儲存在瀏覽器本地localStorage中
})
}
3.2.2 獲取數量、新增購物車
addCart(){
ly.http.get("/auth/verify").then(res => {
//已經登入傳送訊息到後臺,儲存到redis中
}).catch(() =>{
//未登入儲存在瀏覽器本地localStorage中
//1.查詢本地購物車
let carts = ly.store.get("carts") || [];
let cart = carts.find(c => c.skuId === this.sku.id);
//2.判斷是否存在
if (cart){
//3.存在更新數量
cart.num += this.num;
} else{
//4.不存在,新增
cart={
skuId:this.sku.id,
title:this.sku.title,
price:this.sku.price,
image:this.sku.image,
num:this.num,
ownSpec:JSON.stringify(this.ownSpec)
};
carts.push(cart);
}
//5.把carts寫回到localStorage
ly.store.set("carts",carts);
//6.跳轉
window.location.href = "http://www.leyou.com/cart.html";
});
}
結果:
新增完成後,頁面會跳轉到購物車結算頁面:cart.html
3.3 查詢購物車
3.3.1 校驗使用者登入
因為校驗使用者會多次進行,所以在common.js中將其封裝為一個方法:
然後呼叫:
3.3.2 查詢購物車
頁面載入時,就應該去查詢購物車。
var cartVm = new Vue({
el: "#cartApp",
data: {
ly,
carts:[], //購物車資料
},
components: {
shortcut: () => import("/js/pages/shortcut.js")
},
created(){
this.loadCarts();
},
methods:{
loadCarts(){
//1.先判斷登入狀態
ly.verifyUser().then(() => {
//2.已經登入
}).catch(() => {
//3.未登陸
this.carts = ly.store.get("carts") || [];
})
}
}
})
重新整理頁面,檢視資料:
3.3.3 渲染到頁面
效果:
3.4 修改數量
給頁面的 +
和 -
繫結點選事件,修改num 的值:
increment(c){
c.num++;
ly.verifyUser().then(() =>{
//已經登入,向後臺發起請求
}).catch(() => {
//未登入,直接操作本地資料庫
ly.store.set("carts",this.carts);
})
},
decrement(c){
if (c.num <= 1){
return;
}
c.num--;
ly.verifyUser().then(() =>{
//已經登入,向後臺發起請求
}).catch(() => {
//未登入,直接操作本地資料庫
ly.store.set("carts",this.carts);
})
},
3.5 刪除商品
給刪除按鈕繫結事件:
deleteCart(i){
ly.verifyUser().then(() =>{
//已經登入,向後臺發起請求
}).catch(() => {
//未登入,直接操作本地資料庫
this.carts.splice(i,1);
ly.store.set("carts",this.carts);
})
}
3.6 選中商品
在頁面中,每個購物車商品左側,都有一個複選框,使用者可以選擇部分商品進行下單,而不一定是全部:
定義一個變數,記錄所有被選中的商品:
3.6.1 選中一個
給商品前的複選框與selected繫結,並且指定其值為當前購物車商品:
3.6.2 全部選中
分析
頁面方法繫結
上:
中:
下:
程式碼
loadCarts: function () {
//1.先判斷登入狀態
ly.verifyUser().then(() => {
//2.已經登入
}).catch(() => {
//3.未登陸
this.carts = ly.store.get("carts") || [];
this.selected = this.carts;
this.$refs.selectAllTop.checked = true;
this.$refs.selectAllBottom.checked = true;
})
}
初始化是全選的。
selectAll(){
if(this.selected.length !== ly.store.get("carts").length){
this.selected = this.carts;
}else {
this.selected = [];
}
},
selectSingle(){
console.log(this.selected.length);
console.log(ly.store.get("carts").length);
if(this.selected.length !== ly.store.get("carts").length - 1){
this.$refs.selectAllTop.checked=false;
this.$refs.selectAllBottom.checked=false;
}else{
this.$refs.selectAllTop.checked=true;
this.$refs.selectAllBottom.checked=true;
}
}
注意:為什麼要減1?
因為selected和商品選擇按鈕是雙向繫結,當單擊按鈕時,獲取的selected的長度是改變前的長度,所以要進行修正,可自行實驗。
3.6.3 初始化全選
載入完成購物車查詢後,初始化全選:
3.6.4 總價格
編寫計算屬性,計算選中商品的總價格:
computed:{
totalPrice(){
return ly.formatPrice(this.selected.reduce((c1,c2) => c1+c2.num*c2.price,0))
}
}
3.6.5 總數量
編寫計算屬性:
totalNum(){
return this.selected.reduce((c1,c2) => c1+c2.num,0);
}
效果: