商品列表页一次添加多个规格

可能题目的表述不是特别清晰,具体有一下截图看这会比较明显

页面上的功能描述
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="100%;">
16             <li>
17                 <div class="f_l text-align" style=" 50%;">Description</div>
18                 <div class="f_l text-align" style=" 12%;">TF Model</div>
19                 <div class="f_l text-align" style=" 12%;">Availability</div>
20                 <div class="f_l text-align" style=" 12%;">Price</div>
21                 <div class="f_l text-align" style=" 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="100%;">
31                         <!--{foreach from=$spec.values item=value key=key} -->
32                         <li class="noprivate" style="border:0px;">
33                             <div class="f_l" style=" 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=" 12%;">1212</div>
38                             <div class="f_l text-align" style=" 12%;">Availability</div>
39                             <div class="f_l text-align" style=" 12%;">
40                                 <font id="price_{$value.id}">{$goods.shop_price_formated}</font>
41                             </div>
42                             <div class="f_l text-align" style=" 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=" 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=" 12%;">1212</div>
    <div class="f_l text-align" style=" 12%;">Availability</div>
    <div class="f_l text-align" style=" 12%;">
        <font id="price_{$value.id}">{$goods.shop_price_formated}</font>
    </div>
    <div class="f_l text-align" style=" 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=" 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=" 12%;">1212</div>
    <div class="f_l text-align" style=" 12%;">Availability</div>
    <div class="f_l text-align" style=" 12%;">
        <font id="price_{$value.id}">{$goods.shop_price_formated}</font>
    </div>
    <div class="f_l text-align" style=" 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的那些行即可
所以把提交到购物车按钮点击事件的函数换一下

<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));
}

到这这个功能算是完成,后面的就是一些页面上的美化工作,这个功能做了一天半才完全搞定,有点慢了

原文地址:https://www.cnblogs.com/azhw/p/4379044.html