預(yù)覽
功能說明
SKU。存貨單位(英語:stock keeping unit劲装,SKU)胧沫,也翻譯為庫存單元,是一個(gè)會(huì)計(jì)學(xué)名詞占业,定義為庫存管理中的最小可用單元绒怨。例如紡織品中一個(gè)SKU通常表示規(guī)格、顏色谦疾、款式南蹂,而在連鎖零售門店中有時(shí)稱單品為一個(gè)SKU。
-
以IPhone為例念恍,SKU指的是具體規(guī)格單品(銀色 64GB)六剥,SKU屬性就是會(huì)影響到庫存和價(jià)格的屬性,又叫銷售屬性峰伙,與商品是多對(duì)一的關(guān)系疗疟,比如
- 顏色:銀色、深空灰
- 容量:64GB瞳氓、128GB策彤、 512GB
- 因此所有屬性規(guī)格的排列組合則會(huì)生成 2 * 3 = 6 個(gè)SKU
需求與細(xì)節(jié)分析
- 僅有一種(一維,如顏色:白匣摘、黑)屬性時(shí)店诗,默認(rèn)選中該屬性第一個(gè)值(如白)
- 載入數(shù)據(jù)后即需判斷所有組合情況是否有庫存,設(shè)置規(guī)格屬性是否可點(diǎn)擊
- 每選中或取消選中后音榜,需判斷所有組合情況庞瘸,設(shè)置規(guī)格屬性是否可點(diǎn)擊
- 應(yīng)提供是否選中所有維度方法,判斷是否選了所有規(guī)格赠叼,用于進(jìn)行購(gòu)買或加入購(gòu)物車時(shí)判斷
- 僅第一種維度中每個(gè)值可提供不同商品圖片
- 選中所有維度后更新該規(guī)格屬性對(duì)應(yīng)價(jià)格等商品信息恕洲,否則顯示默認(rèn)商品信息
數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)
- 每種屬性規(guī)格組合應(yīng)有唯一id
- 每種維度需提供
ks
值用于標(biāo)明該維度塔橡,如顏色為s1
、容量為s2
依次類推 - 所有排列組合情況和對(duì)應(yīng)庫存應(yīng)全部由后端提供
- 需記錄當(dāng)前選中的規(guī)格屬性霜第,且一開始初始化所有維度葛家,默認(rèn)值為空
商品
product: {
id: 0,
picUrl: "https://img11.360buyimg.com/n1/s450x450_jfs/t1/62813/33/2131/584186/5d079803E03084b0d/2b4970456b7bf49f.png", // 默認(rèn)商品圖片
promotion: 1, // 促銷活動(dòng) 0無 1限時(shí)購(gòu) 2領(lǐng)劵
gallery: [{
picUrl: "https://img11.360buyimg.com/n1/s450x450_jfs/t1/62813/33/2131/584186/5d079803E03084b0d/2b4970456b7bf49f.png",
sortOrder: 1
},
{
picUrl: "https://img10.360buyimg.com/n1/s450x450_jfs/t1/4176/23/3653/281477/5b9a15d4E97e09d00/887e76e6c525324c.jpg",
sortOrder: 2
}
], // 輪播圖
title: "Apple iPhone XS Max (A2104)",
description: "A12仿生芯片流暢體驗(yàn),支持雙卡泌类!",
defaultPrice: 1.00, // 默認(rèn)顯示價(jià)格
price: 1.00,
originPrice: 9588.00,
detail: '<div><img src="https://img14.360buyimg.com/cms/jfs/t1/25195/1/9487/388554/5c7f80a5E8b8f8f0c/46818404849d6ec6.jpg"><img src="https://img12.360buyimg.com/cms/jfs/t1/15853/18/9628/325164/5c7f80a5E7172b236/ba9f3f63a83a9b65.jpg"></div>', // 商品詳情
tags: [{
id: 1,
title: "官方自營(yíng)品牌"
},
{
id: 2,
title: "新品"
}
],
serviceList: [{
id: 1,
title: "48小時(shí)快速退款",
desc: "收到退貨包裹并確認(rèn)無誤后癞谒,將在48小時(shí)內(nèi)辦理退款,退款將原路返回刃榨,不同銀行處理時(shí)間不同弹砚,預(yù)計(jì)1-5個(gè)工作日到賬。"
},
{
id: 2,
title: "滿88元免郵費(fèi)",
desc: "單筆訂單金額(不含運(yùn)費(fèi))枢希,大陸地區(qū)滿88元免郵桌吃,不滿88元收取10元郵費(fèi);港澳臺(tái)地區(qū)滿500元免郵苞轿,不滿500元收取30元運(yùn)費(fèi)茅诱;海外地區(qū)以下單頁提示運(yùn)費(fèi)為準(zhǔn)。"
}
],
comment: {
goodCommentRate: 100, // 好評(píng)率
count: 3986, // 評(píng)論計(jì)數(shù)
goodComment: {
nickname: "Exrick",
avatar: "https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJTqQ5hNKicCNEwW3cATfOTaXk6xMlNEfh1gm0kicPDtJrXwTf5YEqQXYea3m5vsuPyJUXc3U0OicXtA/132",
content: "很好搬卒,手機(jī)很有質(zhì)感瑟俭,值得購(gòu)買。",
rate: 5,
time: "2019.06.18",
spec: "銀色 64G",
pics: [
"https://img30.360buyimg.com/shaidan/s616x405_jfs/t1/65005/20/4818/92581/5d2ffdb6Ebcbf3018/35411a583e29d52d.jpg",
"https://img30.360buyimg.com/shaidan/s616x405_jfs/t1/74460/28/4830/96562/5d2ffdb7Ed5e9ce7a/e764b3daa92a9c67.jpg"
]
} // 精選評(píng)論
}
SKU
sku: {
show: false, // 顯示屬性規(guī)格
noneSku: false, // 有無規(guī)格選擇
quota: 100, // 限購(gòu)數(shù)量
productId: 1, // 商品id
picUrl: "", // 當(dāng)前選擇圖片
specText: "", // 所選規(guī)格屬性
specTextNoCount: "", // 所選規(guī)格屬性 無數(shù)量
tree: [
{
k: '顏色', // skuKeyName:規(guī)格類目名稱
v: [
{
id: 1, // skuValueId:規(guī)格值 id
name: '銀色', // skuValueName:規(guī)格值名稱
picUrl: 'https://img11.360buyimg.com/n1/s450x450_jfs/t1/62813/33/2131/584186/5d079803E03084b0d/2b4970456b7bf49f.png', // 規(guī)格類目圖片契邀,只有第一個(gè)規(guī)格類目可以定義圖片
selected: false, // 是否選擇
disabled: false // 禁用
},
{
id: 2,
name: '深空灰色',
picUrl: 'https://img14.360buyimg.com/n0/jfs/t1/3/15/4536/138660/5b997bf8Ed72ebce7/819dcf182d743897.jpg',
selected: false,
disabled: false
}
],
ks: 's1' // skuKeyStr:sku 組合列表(下方 list)中當(dāng)前類目對(duì)應(yīng)的 key 值摆寄,value 值會(huì)是從屬于當(dāng)前類目的一個(gè)規(guī)格值 id
},
{
k: '內(nèi)存',
v: [
{
id: 3,
name: '64GB',
picUrl: '',
selected: false,
disabled: false
},
{
id: 4,
name: '256GB',
picUrl: '',
selected: false,
disabled: false
},
{
id: 5,
name: '512GB',
picUrl: '',
selected: false,
disabled: false
}
],
ks: 's2'
}
],
// 所有 sku 的組合列表,比如紅色坯门、M 碼為一個(gè) sku 組合微饥,紅色、S 碼為另一個(gè)組合
list: [
{
id: 1, // skuId古戴,下單時(shí)后端需要
price: 1.00, // 價(jià)格
s1: 1, // 規(guī)格類目 ks 為 s1 的對(duì)應(yīng)規(guī)格值 id
s2: 3, // 規(guī)格類目 ks 為 s2 的對(duì)應(yīng)規(guī)格值 id
stockNum: 50 // 當(dāng)前 sku 組合對(duì)應(yīng)的庫存
},
{
id: 2,
price: 2.00,
s1: 1,
s2: 4,
stockNum: 100
},
{
id: 3,
price: 3.00,
s1: 1,
s2: 5,
stockNum: 0
},
{
id: 4,
price: 4.00,
s1: 2,
s2: 3,
stockNum: 100
},
{
id: 5,
price: 5.00,
s1: 2,
s2: 4,
stockNum: 100
},
{
id: 6,
price: 6.00,
s1: 2,
s2: 5,
stockNum: 50
}
],
// 選擇的 sku 組合
selectedSku: {
},
count: 1 // 選擇的商品數(shù)量
}
關(guān)鍵實(shí)現(xiàn)方法
- 初始化
// 加載sku后初始化selectedSku
let tree = this.data.sku.tree;
for (let i = 0; i < tree.length; i++) {
let s = 'sku.selectedSku.' + tree[i].ks;
this.setData({
[s]: ''
})
}
// 只有一種 sku 規(guī)格值時(shí)默認(rèn)選中第一個(gè)
if (tree.length == 1) {
let k = 'sku.selectedSku.' + this.data.sku.tree[0].ks;
this.setData({
[`sku.tree[${0}].v[${0}].selected`]: true,
[k]: this.data.sku.tree[0].v[0].id
});
}
- 核心判斷所有屬性是否可選
- 每當(dāng)點(diǎn)擊一個(gè)規(guī)格屬性后畜号,循環(huán)判斷其他所有規(guī)格屬性,模擬依次點(diǎn)擊組合其他所有規(guī)格屬性允瞧,與所有排列組合匹配简软,統(tǒng)計(jì)庫存總數(shù),若庫存總數(shù)>0則可選述暂,詳見下方
isSkuChoosable
方法
- 每當(dāng)點(diǎn)擊一個(gè)規(guī)格屬性后畜号,循環(huán)判斷其他所有規(guī)格屬性,模擬依次點(diǎn)擊組合其他所有規(guī)格屬性允瞧,與所有排列組合匹配简软,統(tǒng)計(jì)庫存總數(shù),若庫存總數(shù)>0則可選述暂,詳見下方
// 循環(huán)判斷所有屬性是否可選
judgeAllItem: function () {
// 判斷庫存
let tree = this.data.sku.tree;
for (let i = 0; i < tree.length; i++) {
let v = tree[i].v;
for (let j = 0; j < v.length; j++) {
if (this.isSkuChoosable(tree[i].ks, v[j].id)) {
// 可點(diǎn)擊
this.setData({
[`sku.tree[${i}].v[${j}].disabled`]: false
})
} else {
// 不可點(diǎn)擊
this.setData({
[`sku.tree[${i}].v[${j}].disabled`]: true
})
}
}
}
... ...
}
- 關(guān)鍵判斷方法
isSkuChoosable: function (ks, vId) {
let selectedSku = this.data.sku.selectedSku;
let list = this.data.sku.list;
// 先假設(shè)sku已選中痹升,拼入已選中sku對(duì)象中
let matchedSku = Object.assign({}, selectedSku, {
[ks]: vId
});
// 再判斷剩余sku是否全部不可選,若不可選則當(dāng)前sku不可選中
let skusToCheck = Object.keys(matchedSku).filter(
skuKey => matchedSku[skuKey] != ''
);
let filteredSku = list.filter(sku => (
skusToCheck.every(
skuKey => String(matchedSku[skuKey]) == String(sku[skuKey])
)
));
let stock = filteredSku.reduce((total, sku) => {
total += sku.stockNum;
return total;
}, 0);
return stock > 0;
}
- 是否所有維度規(guī)格屬性已選
// 是否所有規(guī)格已選
isAllSelected: function () {
let selectedSku = this.data.sku.selectedSku;
let selected = Object.keys(selectedSku).filter(
skuKeyStr => selectedSku[skuKeyStr] != ""
);
return this.data.sku.tree.length == selected.length;
}