商品SKU选择实现效果
在客户购买商品的时候,若这个商品存在多种”规格”(SKU),就需要客户手动选择自己想要的哪款。当时并不是每种我们都有库存, 所有就需要提供提供一个筛选功能,当客户选中一个条件的时候,需要设置其他条件中的一些值不可选。 这样就能保证不论客户怎么选择,到最后的选择都是有库存的”规格”(SKU)
开始之前先定义几种术语
属性集: 一个商品全部属性的集合
属性: 商品中的 尺码 颜色 就是两个属性
属性值: 尺码、颜色可有多个值 比如 衣服的尺码:S M XL 衣服的颜色:黑色 白色 蓝色 其中 XL 黑色就是属性值
SKU: Stock Keeping Uint(库存量单位) 由属性值组合而成(这些属性值属于不通的属性)如 一件衣服 (XL,白色) 它能确定商品的 唯一性 ,同一款式的衣服 可能有不通的颜色和大小 ,把颜色大小限制住,就能确定这件‘商品’了。
为了简化 我们虚拟出一个的商品, 下面是它的属性集和SKU:
var keys = { "attr1":["10","11"], "attr2":["20","21","22","23"], "attr3":["30","31","32"], "attr4":["40","41"] }; /*num是库存*/ var sku_list=[ {"attrs":"10|20|30|40","num":120}, {"attrs":"10|21|30|40","num":10}, {"attrs":"10|22|30|40","num":28}, {"attrs":"10|22|31|41","num":220}, {"attrs":"10|22|32|40","num":130}, {"attrs":"11|23|32|41","num":120}, ];
为选择之前是这种显示状态
attr1 10 11 attr2 20 21 22 23 attr3 30 31 32 attr4 40 41
attr1-attr4中有 2x4x3x2=42种组合,但是有库存的只有上面6种组合
我们要做的就是,在选择某一个属性的时候,把其他为选择的属性中的一些值设置为block(锁定状态) 因为它们和已选属性的组合是没有库存的(有库存的只有上面6种)
比如:
选择 10 和 21后, 有库存的SKU(组合) 只有
{"attrs":"10|21|30|40","num":10},
31
32
以及attr4中的 41
就要设置为blockattr1 *10* 11 attr2 20 *21* 22 23 attr3 30 -31- -32- attr4 40 -41-
总结1:在所有库存组合(sku_list)
中筛选出包含选中属性值(10,21)
的组合(10|21|30|40)
, 未选的属性(attr3、attr4)
中的所有属性值若不在这个组合
中就设置为block
还有一个问题,在已选属性(attr1,attr2)
中 , 我可以把21
换成20
或22
; 因为 10|21
10|22
也是一种可行组合(有库存),10|23
不可行,所以需要20
、22
也可选,23
设置为block
或者21
不变,修改attr1中的属性值 11|21
不可行,11
设置block。
attr1 *10* -11- attr2 20 *21* 22 -23- attr3 30 -31- -32- attr4 40 -41-
总结2: 已选属性
(attr1,attr2) 中任意一个属性(attr1)的属性值(10,11), 若不能和其他属性
(attr2)中的选中属性值(21)
组合成有效(有库存)组合, 则设置该属性(11)
为block
好了,接下来就是根据上面总结的两条,来进行程序的实现 点击 商品SKU选择DEMO 可查看demo
<!DOCTYPE HTML> |
|
<html lang="en-US"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<title>商品SKU选择DEMO</title> |
|
</head> |
|
<body> |
|
<style type="text/css"> |
|
ul,li{ padding:0px; margin:0px;} |
|
#panel{ width:500px; margin:30px auto;} |
|
.goods_attr{ overflow:hidden;} |
|
.goods_attr .label {font: 12px/30px "宋体";color: #777;width: 50px;;padding-right: 10px;float: left; display:block;} |
|
.goods_attr ul {float:left;width:300px;} |
|
.goods_attr li{color:#333;overflow:hidden;position:relative;float:left;text-align:center; vertical-align:middle; border:1px solid #999;text-indent:0; cursor:pointer} |
|
.goods_attr li.b{border:1px dotted #CCC;color:#DDD; pointer:none;} |
|
.goods_attr li.b img {opacity:0.4;} |
|
.goods_attr li.sel{ border:1px solid #c80a28;color:#333;} |
|
.goods_attr li.text{margin:5px 10px 5px 0; height:23px;line-height:23px;text-indent:0;padding:0 23px;font-style:normal;} |
|
.goods_attr li.img{ margin-right:10px;width:35px;height:35px; line-height:35px;text-align:center;} |
|
</style> |
|
<div id="panel"> |
|
<div id="panel_sku_list"><pre></pre></div> |
|
<div id="panel_sel"> |
|
</div> |
|
</div> |
|
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script> |
|
<script type="text/javascript"> |
|
/* |
|
属性集 |
|
下面一共有4个属性 |
|
属性item1 下面有 2个属性值 分别是 10,11 |
|
(举个常见的例子 属性尺码 下有 S M L XL 4个属性值 ) |
|
*/ |
|
var keys = { |
|
"attr1":["10","11"], |
|
"attr2":["20","21","22","23"], |
|
"attr3":["30","31","32"], |
|
"attr4":["40","41"] |
|
}; |
|
//SKU,Stock Keeping Uint(库存量单位) |
|
var sku_list=[ |
|
{"attrs":"10|20|30|40","num":120}, |
|
{"attrs":"10|21|30|40","num":10}, |
|
{"attrs":"10|22|30|40","num":28}, |
|
{"attrs":"10|22|31|41","num":220}, |
|
{"attrs":"10|22|32|40","num":130}, |
|
{"attrs":"11|23|32|41","num":120}, |
|
]; |
|
/**init start */ |
|
//显示html结构 |
|
function show_attr_item(){ |
|
var html=""; |
|
for(k in keys){ |
|
html+="<div class="goods_attr" > <span class="label">"+k+"</span>"; |
|
html+="<ul>" |
|
for(k2 in keys[k]){ |
|
_attr_id=keys[k][k2]; |
|
html+="<li class="text" val=""+_attr_id+"" >"; |
|
html+="<span>"+_attr_id+"</span>"; |
|
html+="<s></s>"; |
|
html+="</li>" |
|
} |
|
html+="</ul>"; |
|
html+="</div>"; |
|
} |
|
$("#panel_sel").html(html); |
|
} |
|
//显示数据 |
|
function show_data(sku_list){ |
|
var str=""; |
|
for( k in sku_list){ |
|
str+=sku_list[k]["attrs"]+" "+sku_list[k]["num"]+" "; |
|
} |
|
$("#panel_sku_list pre").html(str); |
|
} |
|
show_data(sku_list); |
|
show_attr_item() |
|
/**init end */ |
|
//获取所有包含指定节点的路线 |
|
function filterProduct(ids){ |
|
var result=[]; |
|
$(sku_list).each(function(k,v){ |
|
_attr="|"+v["attrs"]+"|"; |
|
_all_ids_in=true; |
|
for( k in ids){ |
|
if(_attr.indexOf("|"+ids[k]+"|")==-1){ |
|
_all_ids_in=false; |
|
break; |
|
} |
|
} |
|
if(_all_ids_in){ |
|
result.push(v); |
|
} |
|
}); |
|
return result; |
|
} |
|
//获取 经过已选节点 所有线路上的全部节点 |
|
// 根据已经选择得属性值,得到余下还能选择的属性值 |
|
function filterAttrs(ids){ |
|
var products=filterProduct(ids); |
|
//console.log(products); |
|
var result=[]; |
|
$(products).each(function(k,v){ |
|
result=result.concat(v["attrs"].split("|")); |
|
}); |
|
return result; |
|
} |
|
//已选择的节点数组 |
|
function _getSelAttrId(){ |
|
var list=[]; |
|
$(".goods_attr li.sel").each(function(){ |
|
list.push($(this).attr("val")); |
|
}); |
|
return list; |
|
} |
|
$(".goods_attr li").click(function(){ |
|
if($(this).hasClass("b")){ |
|
return ;//被锁定了 |
|
} |
|
if($(this).hasClass("sel")){ |
|
$(this).removeClass("sel"); |
|
}else{ |
|
$(this).siblings().removeClass("sel"); |
|
$(this).addClass("sel"); |
|
} |
|
var select_ids=_getSelAttrId(); |
|
//已经选择了的规格 |
|
var $_sel_goods_attr=$("li.sel").parents(".goods_attr"); |
|
// step 1 |
|
var all_ids=filterAttrs(select_ids); |
|
//获取未选择的 |
|
var $other_notsel_attr=$(".goods_attr").not($_sel_goods_attr); |
|
//设置为选择属性中的不可选节点 |
|
$other_notsel_attr.each(function(){ |
|
set_block($(this),all_ids); |
|
}); |
|
//step 2 |
|
//设置已选节点的同级节点是否可选 |
|
$_sel_goods_attr.each(function(){ |
|
update_2($(this)); |
|
}); |
|
}); |
|
function update_2($goods_attr){ |
|
// 若该属性值 $li 是未选中状态的话,设置同级的其他属性是否可选 |
|
var select_ids=_getSelAttrId(); |
|
var $li=$goods_attr.find("li.sel"); |
|
var select_ids2=del_array_val(select_ids,$li.attr("val")); |
|
var all_ids=filterAttrs(select_ids2); |
|
set_block($goods_attr,all_ids); |
|
} |
|
function set_block($goods_attr,all_ids){ |
|
//根据 $goods_attr下的所有节点是否在可选节点中(all_ids) 来设置可选状态 |
|
$goods_attr.find("li").each(function(k2,li2){ |
|
if($.inArray($(li2).attr("val"),all_ids)==-1){ |
|
$(li2).addClass("b"); |
|
}else{ |
|
$(li2).removeClass("b"); |
|
} |
|
}); |
|
} |
|
function del_array_val(arr,val){ |
|
//去除 数组 arr中的 val ,返回一个新数组 |
|
var a=[]; |
|
for(k in arr){ |
|
if(arr[k]!=val){ |
|
a.push(arr[k]); |
|
} |
|
} |
|
return a; |
|
} |
|
</script> |
|
</body> |
|
</html> |
- 上一篇: [李景山php] php高级程序员简历的要点
- 下一篇: 其实你距离PHP高级工程师只有一步之遥!