可能题目的表述不是特别清晰,具体有一下截图看这会比较明显
页面上的功能描述
1. 当页面加载完成后,需要根据不同规格的商品刷新出对应规格商品的价格,
2.重置默认数量,这个功能在input标签中设置默认的value值即可,或者添加一个js函数,重置对应class集合中的值,这个不多说,比较简单,
3.点击添加购物车,需要把例如上图中的不同规格中的商品能全部添加到购物车
解题思路,
要求1,这个任务比较简单,由于这种方式,后台里的商品规格选项必须用单选按钮来做,否则页面效果不容易实现
具体要实现什么样式自己来定义,我的代码如下
1 <style>
2 .text-align{ text-align: center;}
3 #table_title li div{border: 1px solid red; font-size: 18px; font-weight: bold;}
4 </style>
5 <!--备注,由于需求限制这里只对单选的单选按钮做出控制-->
6 <!--更改的购买流程开始-->
7 <div>
8 <!-- {* 开始循环所有可选属性 *} -->
9 <!-- {foreach from=$specification item=spec key=spec_key} -->
10
11 <!--不用了屏蔽掉-->
12 <div class="te" style="disply:none;">{$spec.name}</div>
13 <!--不用了屏蔽掉-->
14
15 <ul id="table_title" style="width:100%;">
16 <li>
17 <div class="f_l text-align" style="width: 50%;">Description</div>
18 <div class="f_l text-align" style="width: 12%;">TF Model</div>
19 <div class="f_l text-align" style="width: 12%;">Availability</div>
20 <div class="f_l text-align" style="width: 12%;">Price</div>
21 <div class="f_l text-align" style="width: 12%;">Quantity</div>
22 <div class="clear"></div>
23 </li>
24 </ul>
25
26 <!-- {* 判断属性是复选还是单选 *} -->
27 <!-- {if $spec.attr_type eq 1} --> <!--$spec.attr_type eq 1 是单选-->
28 <!-- {if $cfg.goodsattr_style eq 1} --> <!--单选按钮-->
29
30 <ul style="width:100%;">
31 <!--{foreach from=$spec.values item=value key=key} -->
32 <li class="noprivate" style="border:0px;">
33 <div class="f_l" style="width: 50%;">
34 {$value.label}
35 <input style="display:none" class="spec_value" id="spec_value_{$value.id}" type="radio" name="spec_{$spec_key}" value="{$value.id}" {if $key eq 0}checked{/if} />
36 </div>
37 <div class="f_l text-align" style="width: 12%;">1212</div>
38 <div class="f_l text-align" style="width: 12%;">Availability</div>
39 <div class="f_l text-align" style="width: 12%;">
40 <font id="price_{$value.id}">{$goods.shop_price_formated}</font>
41 </div>
42 <div class="f_l text-align" style="width: 12%;">
43 <font id="plus">
44 <var onclick="goods_cut(this);" class="imgl"><img src="images/classjj.gif"></var>
45 <input name="number" type="text" id="number" class="inum" value="1" size="4" onblur="changePrice();get_shipping_list(forms['ECS_FORMBUY'],{$goods.goods_id});"/>
46 <var onclick="goods_add(this);" class="imgr"><img src="images/classj.gif"></var>
47 </font>
48 </div>
49 <div class="clear"></div>
50 <input type="hidden" class="hidNum" name="hidNum" value="1">
51 </li>
52 <!-- {/foreach} -->
53 </ul>
54 <input type="hidden" name="spec_list" value="{$key}" />
55 <!-- {else} --><!--下拉菜单-->
56 <select name="spec_{$spec_key}" onchange="changePrice()">
57 <!-- {foreach from=$spec.values item=value key=key} -->
58 <option label="{$value.label}" value="{$value.id}">{$value.label} {if $value.price gt 0}{$lang.plus}{elseif $value.price lt 0}{$lang.minus}{/if}{if $value.price neq 0}{$value.format_price}{/if}</option>
59 <!-- {/foreach} -->
60 </select>
61 <input type="hidden" name="spec_list" value="{$key}" />
62 <!-- {/if} -->
63 <!-- {else} --> <!--多选-->
64 <!-- {foreach from=$spec.values item=value key=key} -->
65 <label for="spec_value_{$value.id}">
66 <input type="checkbox" name="spec_{$spec_key}" value="{$value.id}" id="spec_value_{$value.id}" onclick="changePrice()" />
67 {$value.label} [{if $value.price gt 0}{$lang.plus}{elseif $value.price lt 0}{$lang.minus}{/if} {$value.format_price|abs}] </label><br />
68 <!-- {/foreach} -->
69 <input type="hidden" name="spec_list" value="{$key}" />
70 <!-- {/if} -->
71 <!-- {/foreach} -->
72 <!-- {* 结束循环可选属性 *} -->
73 </div>
74
75 <!--更改的购买流程结束-->
76 <div class="blank"></div>
由于空间编辑器可能识别代码不是很好,建议复制出去看
这样上面的样式就出来了
这里去观察原版的ecshop中,对于价格的刷新是利用了一个changePrice2函数来做ajax,所以这里我们根据已有的结构
<!--{foreach from=$spec.values item=value key=key} -->
<li class="noprivate" style="border:0px;">
<div class="f_l" style="width: 50%;">
{$value.label}<input style="display:none" class="spec_value" id="spec_value_{$value.id}" type="radio" name="spec_{$spec_key}" value="{$value.id}" {if $key eq 0}checked{/if} />
</div>
<div class="f_l text-align" style="width: 12%;">1212</div>
<div class="f_l text-align" style="width: 12%;">Availability</div>
<div class="f_l text-align" style="width: 12%;">
<font id="price_{$value.id}">{$goods.shop_price_formated}</font>
</div>
<div class="f_l text-align" style="width: 12%;">
<font id="plus">
<var onclick="goods_cut(this);" class="imgl"><img src="images/classjj.gif"></var>
<input name="number" type="text" id="number" class="inum" value="1" size="4" onblur="changePrice();get_shipping_list(forms['ECS_FORMBUY'],{$goods.goods_id});"/>
<var onclick="goods_add(this);" class="imgr"><img src="images/classj.gif"></var>
</font>
</div>
<div class="clear"></div>
<input type="hidden" class="hidNum" name="hidNum" value="1">
</li>
<!-- {/foreach} -->
<!--{foreach from=$spec.values item=value key=key} -->
<li class="noprivate" style="border:0px;">
<div class="f_l" style="width: 50%;">
{$value.label}<input style="display:none" class="spec_value" id="spec_value_{$value.id}" type="radio" name="spec_{$spec_key}" value="{$value.id}" {if $key eq 0}checked{/if} />
</div>
<div class="f_l text-align" style="width: 12%;">1212</div>
<div class="f_l text-align" style="width: 12%;">Availability</div>
<div class="f_l text-align" style="width: 12%;">
<font id="price_{$value.id}">{$goods.shop_price_formated}</font>
</div>
<div class="f_l text-align" style="width: 12%;">
<font id="plus">
<var onclick="goods_cut(this);" class="imgl"><img src="images/classjj.gif"></var>
<input name="number" type="text" id="number" class="inum" value="1" size="4" onblur="changePrice();get_shipping_list(forms['ECS_FORMBUY'],{$goods.goods_id});"/>
<var onclick="goods_add(this);" class="imgr"><img src="images/classj.gif"></var>
</font>
</div>
<div class="clear"></div>
<input type="hidden" class="hidNum" name="hidNum" value="1">
</li>
<!-- {/foreach} -->
这里循环的是li
<li class="noprivate" style="border:0px;">
添加几个函数
function noprivate(good_id){
var noprivate = document.getElementsByClassName("noprivate");
//alert(noprivate.length);
for(i = 0; i < noprivate.length; i++){
changePrice2(noprivate[i]);
}
}
/*
* 专门用于处理页面初始化完成后,对于属性列表中的价格处理
* 仿照上面的changePrice函数来写
*/
function changePrice2(divElement){
var attr_arr = divElement.getElementsByClassName('spec_value');
var inum_arr = divElement.getElementsByClassName('hidNum');
var attr = attr_arr[0].value;
var inum = inum_arr[0].value;
Ajax.call('goods.php', 'act=price&id=' + {$goods_id} + '&attr=' + attr + '&number=' + inum, changePriceResponse2, 'GET', 'JSON');
}
function changePriceResponse2(res){
if (res.err_msg.length > 0)
{
alert(res.err_msg);
}
else
{
// document.forms['ECS_FORMBUY'].elements['number'].value = res.qty;
//
// if (document.getElementById('ECS_GOODS_AMOUNT'))
// document.getElementById('ECS_GOODS_AMOUNT').innerHTML = res.result;
document.getElementById('price_' + res.attr).innerHTML = res.result;
}
}
在返回的处理函数中需要 document.getElementById('price_' + res.attr).innerHTML = res.result;
但是这里的 具体这里为什么要这样写,需要研究一下那些input标签里的属性,观察一下会有发现
attr的值在原函数中是没有被返回的,这里在goods.php文件的if (!empty($_REQUEST['act']) && $_REQUEST['act'] == 'price')这里加入一行
$res['attr'] = $attr_id;这样
res.attr就有值了
noprivate 的参数根据页面变量来var goods_id = {$goods_id};也就是商品的id
这样做ajax循环,就可以刷新出每种规格的商品的价格了
还有一个就是加减商品数量,这里比较简单,稍微改造一下原函数就可以了
function goods_cut(obj){
var inum_arr = obj.parentNode.getElementsByClassName('inum');
var inum = inum_arr[0];
var new_num=inum.value;
if(isNaN(new_num)){alert('请输入数字');return false}
var Num = parseInt(new_num);
if(Num>0)Num=Num-1;
inum.value=Num;
}
function goods_add(obj){
var inum_arr = obj.parentNode.getElementsByClassName('inum');
var inum = inum_arr[0];
var new_num=inum.value;
var new_num=inum.value;
if(isNaN(new_num)){alert('请输入数字');return false}
var Num = parseInt(new_num);
Num=Num+1;
inum.value=Num;
}
下一步就是要根据不同规格商品的不同购买数量来提交到购物车,查看原函数可知,原本提交到购物撤是要在页面中查找商品的属性,然后来提交,这里只做了一个属性,所以会比较简单,如果是多个属性,看看原函数怎么弄的就行
当要提交的时候,根据用户需求,可能不是所有规格都会有一个数量》0 所以这里只要提交商品数量大于0的那些行即可
所以把提交到购物车按钮点击事件的函数换一下
<a href="javascript:;" onclick="check_car({$goods.goods_id})"><img src="images/bnt_cat.gif" /></a>
对应的函数如下
function check_car(good_id){
//获得被循环的li元素的集合
var noprivate = document.getElementsByClassName("noprivate");
var z = 0;
var rei = new Array();
//循环这个集合
for(i = 0; i < noprivate.length; i++){
var inum_arr = noprivate[i].getElementsByClassName('inum');
//得到某一行的选定的数量
var inum = inum_arr[0].value;
//如果数量大于零,那么就将这个行的行号存入数组中,用z变量来记录总共需要提交的行数
if(inum > 0){
rei[z] = i;
z++;
}
}
//alert(rei);
//这里来提交到购物车用上面的z来控制循环次数
for(y = 0; y < z; y++){
//rei[y] 里面存的是一个行号,这一行一定是购物数量大于0的,
//noprivate[x]变量就是集合中的具体某一个,不多废话了
var x = rei[y];
//如果条件成立,说明这是最后一次提交到购物车的操作,就传递给addToCart2一个(1)
if((y+1) == z){
addToCart2(1, noprivate[x], good_id);
}
else{
addToCart2(0, noprivate[x], good_id);
}
}
}
再来看addToCart2这个函数,也是要稍微改造一下的
/* *
* 使用与本站的添加商品到购物车
*/
function addToCart2(check_good, divElement, goodsId, parentId)
{
var attr_arr = divElement.getElementsByClassName('spec_value');
var inum_arr = divElement.getElementsByClassName('inum');
var attr = attr_arr[0].value;
var inum = inum_arr[0].value;
var goods = new Object();
var spec_arr = new Array();
//var fittings_arr = new Array();//这个变量好像原版里也没有用到。
var number = 0;
//var formBuy = document.forms['ECS_FORMBUY'];
var quick = 0;
//这里的处理逻辑需要改变,如果某一个属性的购买数量大于0,那么就做ajax提交,否则就不提交到购物车,所以goods.quick 可以直接设置为1,
if(parseInt(inum) > 0){
number = parseInt(inum);
spec_arr = attr;
quick = 1;
goods.quick = quick;
goods.spec = spec_arr;
goods.goods_id = goodsId;
goods.number = number;
goods.parent = (typeof(parentId) == "undefined") ? 0 : parseInt(parentId);
if(check_good == 1){
Ajax.call('flow.php?step=add_to_cart', 'goods=' + goods.toJSONString(), addToCartResponse, 'POST', 'JSON');
}
else if(check_good == 0){
Ajax.call('flow.php?step=add_to_cart', 'goods=' + goods.toJSONString(), function(res){return 2;}, 'POST', 'JSON');
}
}
}
把这个函数对比一下原函数找一下区别把,就是查找属性的时候不太一样,再有就是最后的ajax提交,的返回不同,如果是最后一次才用addToCartResponse作为回调函数,其他情况用一个匿名函数function(res){return 2;},这里我返回2,其实无所谓,就是不写都没事,主要是让客户端程序不要有操作就可以了,
再来看看回调函数
/* *
* 处理添加商品到购物车的反馈信息
*/
function addToCartResponse(result)
{
if (result.error > 0)
{
// 如果需要缺货登记,跳转
if (result.error == 2)
{
if (confirm(result.message))
{
location.href = 'user.php?act=add_booking&id=' + result.goods_id + '&spec=' + result.product_spec;
}
}
// 没选规格,弹出属性选择框
else if (result.error == 6)
{
openSpeDiv(result.message, result.goods_id, result.parent);
}
else
{
alert(result.message);
}
}
else
{
//这里会更新屏幕上面的购物车数据
var cartInfo = document.getElementById('ECS_CARTINFO');
var cart_url = 'flow.php?step=cart';
//这里我屏蔽了,再下面会用到,其中result.content; 中的content元素在php文件中
//是这么定义的$result['content'] = insert_cart_info();至于insert_cart_info函数的作用自己看咯
// if (cartInfo)
// {
// cartInfo.innerHTML = result.content;
// }
if (result.one_step_buy == '1')
{
location.href = cart_url;
}
else
{
switch(result.confirm_type)
{
case '1' :
//如果成功了就会进入到这里原函数中只是调用了opencartDiv(,,,,....)这个函数,
//为什么用下面的ajax下面会解释
Ajax.call(
'flow.php?step=re_select_cart',
'goods_id=' + result.goods_id,
function(result){
if (cartInfo)
{
cartInfo.innerHTML = result.content;
}
opencartDiv(result.shop_price,result.goods_name,result.goods_thumb,result.goods_brief,result.goods_id,result.goods_price,result.goods_number);
},
'POST',
'JSON');
//opencartDiv(result.shop_price,result.goods_name,result.goods_thumb,result.goods_brief,result.goods_id,result.goods_price,result.goods_number);
break;
case '2' :
if (!confirm(result.message)) location.href = cart_url;
break;
case '3' :
location.href = cart_url;
break;
default :
break;
}
}
}
}
if(check_good == 1){
Ajax.call('flow.php?step=add_to_cart', 'goods=' + goods.toJSONString(), addToCartResponse, 'POST', 'JSON');
}
else if(check_good == 0){
Ajax.call('flow.php?step=add_to_cart', 'goods=' + goods.toJSONString(), function(res){return 2;}, 'POST', 'JSON');
}
这里按照道理来说是最后一个属性提交后才会调用ajax,但是在实际中可能存在一个线程问题,就是前面的ajax还没有执行完成,后面的ajax请求已经来了,所以前面的可能被服务器挂起,这样当最后调用回调函数的时候,调用的数据就不是应该有的最后一次的执行结果,
又由于js本身没有一个合适的休眠函数,好像就算是休眠也不见得很好用,所以这里在回调函数中,判断执行成功后重新发起一次ajax请求,重新获得页面上想要的数据所以
case '1' :
Ajax.call(
'flow.php?step=re_select_cart',
'goods_id=' + result.goods_id, //要记得传递商品的id,这个id是从当前的回调数据中取得
function(result){
if (cartInfo)
{
cartInfo.innerHTML = result.content;
}
opencartDiv(result.shop_price,result.goods_name,result.goods_thumb,result.goods_brief,result.goods_id,result.goods_price,result.goods_number);
},
'POST',
'JSON');
//opencartDiv(result.shop_price,result.goods_name,result.goods_thumb,result.goods_brief,result.goods_id,result.goods_price,result.goods_number);
break;
case ‘1’ 内部的这个ajax请求就诞生了
对应的php代码 flow.php文件中找到$_REQUEST['step'] == 'add_to_cart'
在这个代码段的下面添加一段查询代码
elseif($_REQUEST['step'] == 're_select_cart')
{
//加载json类,ecshop里面都没有用php自带的json函数,可能是由于历史原因
include_once('includes/cls_json.php');
$goods_id = $_REQUEST['goods_id'];//接收参数
$json = new JSON;//实例化json类
$rows = $GLOBALS['db']->getRow("select
goods_brief,shop_price,goods_name,goods_thumb,promote_price from
".$GLOBALS['ecs']->table('goods')." where goods_id=".$goods_id);
$result['shop_price'] = price_format($rows['shop_price']);
$result['goods_name'] = $rows['goods_name'];
$result['goods_thumb'] = $rows['goods_thumb'];
$result['goods_brief'] = $rows['goods_brief'];
if ($rows['promote_price'] > 0)
{
$result['shop_price'] = price_format($rows['promote_price']);
}
else
{
$result['shop_price'] = price_format($rows['shop_price']);
}
$result['goods_id'] = $goods_id;
$sql = 'SELECT SUM(goods_number) AS number, SUM(goods_price * goods_number) AS amount' .
' FROM ' . $GLOBALS['ecs']->table('cart') .
" WHERE session_id = '" . SESS_ID . "' AND rec_type = '" . CART_GENERAL_GOODS . "'";
$rowss = $GLOBALS['db']->GetRow($sql);
$result['goods_price'] = price_format($rowss['amount']);
$result['goods_number'] = $rowss['number'];
$result['confirm_type'] = !empty($_CFG['cart_confirm']) ? $_CFG['cart_confirm'] : 2;
//这里要重新执行一边$result['content'] = insert_cart_info(); //这个数据是提供给给页面头部里的购物车数据
$result['content'] = insert_cart_info();
die($json->encode($result));
}
到这这个功能算是完成,后面的就是一些页面上的美化工作,这个功能做了一天半才完全搞定,有点慢了