博客列表 >商品sku,笛卡尔积实现商品多规格。

商品sku,笛卡尔积实现商品多规格。

有什么是忘不了的的博客
有什么是忘不了的的博客原创
2020年03月24日 18:05:302552浏览

在做商品添加的时候,最常见的多规格产品数据。如何来实现呢?

分享一个我自己学习做出来的小栗子(tp5.1&layui)。如图:

sku.png

代码写的比较啰嗦,但是清楚逻辑就可以。

思路:

    但选择分类时,去生成对应的规格table,这里的数据写在spec.html 页面了。所以就是发送请求返回一个页面,放到指定位置。

    当添加规格值时,加载生成规格数据表。需要把规格排序,值最少排在前面。获取每一个规格值ID(我是在添加的时候直接入库,并判断是否存在改值,返回这条规格值的id),获取去所有的规格值id生成数组,规格项id为下标,规格值id为数组值的二维数组。然后在通过笛卡尔积得到所有的规格在进行循环拼接table 字符串返回。

思路写的可能不大好:不过可以看我下面粘的代码。

笛卡尔积php实现代码:

//多个数组的笛卡尔积 调用这个
function combineDika() {
    $data = func_get_args();//获取参数
    $data = current($data);
    $cnt = count($data);
    $result = array();
    $arr1 = array_shift($data);
    foreach($arr1 as $key=>$item){
        $result[] = array($item);
    }

    foreach($data as $key=>$item){
        $result = combineArray($result,$item);
    }
    return $result;
}

//两个数组的笛卡尔积
function combineArray($arr1,$arr2) {
    $result = array();
    foreach ($arr1 as $item1){
        foreach ($arr2 as $item2){
            $temp = $item1;
            $temp[] = $item2;
            $result[] = $temp;
        }
    }
    return $result;
}

js合并单元格,同一个列连续相同的两个值合并在一起

// 合并单元格
function merge() {
    //要合并的table的ID
    var tab = document.getElementById("spec_input_table");//这里的ID是关键,一定要相互对应
    //maxCol:合并单元格作用到多少列
    var maxCol = 2, val, count, start;
    if (tab != null) {
        for (var col = maxCol - 1; col >= 0; col--) {
            count = 1;
            val = "";
            for (var i = 0; i < tab.rows.length; i++) {
                if (val == tab.rows[i].cells[col].innerHTML) {
                    count++;
                } else {
                    if (count > 1) { //合并
                        start = i - count;
                        tab.rows[start].cells[col].rowSpan = count;
                        for (var j = start + 1; j < i; j++) {
                            tab.rows[j].cells[col].style.display = "none";
                        }
                        count = 1;
                    }
                    val = tab.rows[i].cells[col].innerHTML;
                }
            }
            if (count > 1) {
                //合并,最后几行相同的情况下
                start = i - count;
                tab.rows[start].cells[col].rowSpan = count;
                for (var j = start + 1; j < i; j++) {
                    tab.rows[j].cells[col].style.display = "none";
                }
            }
        }
    }
}

//获取选中商品规格和规格值的ID来生成商品sku
    public function getSpecItem(){
        //获取到的数组
        //[2=>[10,11],3=>[12,13,14],5=>[15]] 下标是规格项id=>值为该规格项规格值id
        $specArr = Request::post('spec_arr');
        //获取数组中每个子数组的元素个数,主要是用来排序的判断那个规则项在前
        //循环后[2=>2,3=>3,5=>1]
        foreach ($specArr as $k => $v){
            $specArrSort[$k] = count($specArr[$k]);
        }
        //保存键名的升序排序,这个时候[5=>1,2=>2,3=>3]
        //这里排序的意义:在输出表头的时候规格值少的在前
        asort($specArrSort);
        //根据排序的下标进行数组的排序
        //排序后[[15],[10,11],[12,13,14]] //这时候数组的下标丢失不过没有关系
        foreach ($specArrSort as $k=>$v){
            $specArr1[] = $specArr[$k];
        }
        //获取规格id
        //[2,3,5]
        $clo_name = array_keys($specArr);
//        var_dump($specArr1);
        //笛卡尔积
        //
        $specSort = combineDika($specArr1);
        //规格表 获取所有数据
        $specCatsList = SpecCats::where('dataFlag',1)->field('catName,catId')->select()->toArray();
        //对获取的数据进行重构,以catId为键catName为值。这里主要是为了配合我们排序好的$specArrSort数据来获取规格名称
        $specCatsList = array_column($specCatsList,'catName','catId');
        //规格项值表  获取所有固定规格的规格值
        $specItemList = SpecItem::where('spec_id','IN',$clo_name)->select()->toArray();
        //数组重构,获取数组的id值,在以id数组为键规格值数组为值重构数组。
        $specItemListKey = array_column($specItemList,'id');
        $specItemList = array_combine($specItemListKey,$specItemList);
        //生成table表格字符串
        $str = '<table id="spec_input_table" lay-filter="demo" class="layui-table">';
        $str .='<tr>';
        //表头开始 根据排序好的$specArrSort的数组[5=>1,2=>2,3=>3]根据他的下标循环输出规格项的名称。
        foreach ($specArrSort as $k=>$v){
            $str .='<td><b>'.$specCatsList[$k].'</b></td>';
        }
         $str .='<td><b>价格</b></td><td><b>库存</b></td><td><b>预警值</b></td></tr>';
        //表头结束
//        var_dump($clo_name);
//        halt($specItemList);

        //循环笛卡尔积后的数组
        //表身体开始
        foreach ($specSort as $k => $v){
            $str .= '<tr>';
            $item_key_name = [];
                foreach ($v as $k1=>$v1){
                    //因为进行笛卡尔积的是规格项值的id,所以需要从重构后的规格项值数组中获取规格值名称
                    $str .='<td>'.$specItemList[$v1]['item'].'</td>';
                    //获取 规格项:规格值 的数据格式
                    $item_key_name[$v1] = $specCatsList[$specItemList[$v1]['spec_id']] .":".$specItemList[$v1]['item'];
                }
                //根据键排序[45=>'内存:222',46 =>'颜色:333',50 =>'型号:666']
                ksort($item_key_name);
                $item_key = implode('_',array_keys($item_key_name));//45_16_50
                $item_name = implode(' ',$item_key_name);//内存:222 颜色:333 型号:666
                $str .= '<td><input type="text" value="0" name="item['.$item_key.'][shopPrice]" lay-verify="required|number" autocomplete="off" class="layui-input" ></td>';
                $str .= '<td><input type="text" value="0" name="item['.$item_key.'][goodsStock]" lay-verify="required|number" autocomplete="off" class="layui-input"></td>';
                $str .= '<td><input type="text" value="0" name="item['.$item_key.'][warnStock]" lay-verify="required|number" autocomplete="off" class="layui-input" >
                             <input type="hidden" name="item['.$item_key.'][key_value]" value="'.$item_name.'"></td>';
                $str .= '</tr>';
//                var_dump($item_key_name);
        }
        //表身体结束
        $str .='</table>';
        exit($str);
    }
    //获取该分类下的规格
    public function specCats()
{
    if (Request::isAjax()){
        $id = Request::param('id');
        //因为我传过去的是一个1_2这样的数据,2是1的儿子
        $goodscatarr = explode('_',$id );
        $data['goodsCatId'] = $goodscatarr[0];
        $data['goodsCatIdTwo'] = $goodscatarr[1];
          //SpecCats 我的商品规格表
        $specList = SpecCats::where('goodsCatId',$data['goodsCatIdTwo'])->where('dataFlag',1)->field('catId,catName')->order('order','desc')->select();
        $this->view->assign('specList',$specList);
        //把一个页面返回回去
        return  view('spec');
    }
}

我的spec.html页面

{__NOLAYOUT__}//取消模板布局
<link rel="stylesheet" href="/static/layuimini/css/public.css" media="all">
<style>
    #spec_cat_item th ,#spec_cat_item{
        text-align: center;
    }
</style>
<body>
<div class="layui-form-item">
    <label class="layui-form-label">规格</label>
    <div class="layui-input-block">
        <table lay-filter="demo"   class="layui-table">
            <tbody>
            //展示规格项值添加的那一块
            {volist name="specList" id="vo"}
            <tr>
                <td style="width:100px">选择{$vo.catName}:</td>
                <td>
                    <div class="layui-input-inline" style="width: 100%">
                        <input type="text" id="specItem"  name="specItem"  autocomplete="off" class="layui-input" style=" width: 100px;display:inline-block">&nbsp;&nbsp;
                        <a href="javascript:;" style="color: #1E9FFF" onclick="addSpecItem(this,{$vo.catId})">添加</a>&nbsp;&nbsp;
                
                    </div>
                </td>
            </tr>
            {/volist}
            </tbody>
        </table>
        //这里用来添加商品的笛卡尔积后的数据
        <div id="spec_cat_item">

        </div>
    </div>
</div>
let specItem = {};
//添加规格项值
function addSpecItem(obj,specId) {
    let val = $(obj).siblings('input[name="specItem"]').val()
    //指出一个对象是否具有指定名称的属性。
    if (!specItem.hasOwnProperty(specId)){
        specItem[specId]=[]
    }
    //判断该数组中是否已存在该值
    if  (val.length == 0 ){
        layer.msg('规格值不能为空')
    }else if (specItem[specId].indexOf(val)==-1){
        specItem[specId].push(val)
        $.ajax({
            url:'/admin/goods/addSpecItem',
            data:{'item':val,specId},
            type:'get',
            success:function (e) {
                //吧值插入到页面中
                if  (e.code == 0){
                    layer.alert(e.msg)
                    return
                }
                let dom = "<div style='display: inline-block;' class='specAdd'>" +
                    "<button class='layui-btn'  spec_id='"+specId+"' data_id='"+e.id+"'  style='background-color: #d4d4d4'>"+val+"</button>" +
                    "<i onclick='specDelete(this,"+specId+",\""+val+"\")' class='layui-icon layui-icon-close-fill specDel'></i></div>"
                $(obj).siblings('#specItem').before(dom)
                //插入成功清空input里的值
                $(obj).siblings('#specItem').val('')
                //获取添加的规格值的id
                getSpecArr()
            }
        })
    }else{
        layer.msg('该规格值已存在')
    }
    // console.log(specItem)
}
//获取添加的规格值的id
function getSpecArr(){
    var spec_arr = {}
    $(".specAdd button").each(function () {
        let spec_id = $(this).attr('spec_id')
        let item_id = $(this).attr('data_id')
        if (!spec_arr.hasOwnProperty(spec_id)){
            spec_arr[spec_id]=[]
        }
        spec_arr[spec_id].push(item_id)
    })
    getSpecItem(spec_arr)
}
function getSpecItem(spec_arr){
    $.ajax({
        url:'/admin/goods/getSpecItem',
        data:{spec_arr},
        type:'post',
        success:function (e) {
        //插入到页面
            $("#spec_cat_item").html('')
            $("#spec_cat_item").append(e)
            //合并单元格
            merge()
        }
    })
}
//删除规格项值
function specDelete(obj,specId,val) {
    let key = specItem[specId].indexOf(val)
    specItem[specId].splice(key,1)
    $(obj).parent().remove();
    console.log(specItem)
    //获取添加的规格值的id重新加载一次
    getSpecArr()
}

就这些了。理解怎么做最重要, 站在巨人的肩膀上干什么都很舒服。

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议
phpcn_u238352020-05-07 00:02:451楼
js合并单元格就开始有点看不懂了