在做商品添加的时候,最常见的多规格产品数据。如何来实现呢?
分享一个我自己学习做出来的小栗子(tp5.1&layui)。如图:
代码写的比较啰嗦,但是清楚逻辑就可以。
思路:
但选择分类时,去生成对应的规格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"> <a href="javascript:;" style="color: #1E9FFF" onclick="addSpecItem(this,{$vo.catId})">添加</a> </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() }
就这些了。理解怎么做最重要, 站在巨人的肩膀上干什么都很舒服。