2020-05-18 18:06:00
围观(10023)
开发一些商城会遇上这样的场景:
例如一件衣服, 可能有 红色 / 黄色.
红色和黄色衣服的尺码又有 S 和 M 还有 L.
使用 Layui 实现前端, 直接上代码:
<!DOCTYPE html> <html> <head> <title>不败君 www.bubaijun.com</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="layui/css/layui.css"> <script type="text/javascript" src="jquery-3.5.1.min.js"></script> <script type="text/javascript" src="layui/layui.js"></script> <style> layui-card-header { border-color: #EEEEEE; } .layui-form { margin-top: 30px; padding: 20px; background-color: #F9F9F9; } .layui-form-label { width: 100px; } .layui-input-block { margin-left: 130px; } .attr-key-item,#sku-table { background: #F9F9F9; padding: 20px; } .attr-key-item { padding-bottom: 0; } #sku-table table { width: 100%; } .attr-key-item .sku-title { margin-bottom: 10px } .attr-key-item .sku-title input{ display: inline; width: 260px; margin-right: 10px; } .attr-vals { width: 100%; display: block; } .btn-remove-attr-key,.btn-add-attr-val,.btn-remove-attr-val { color: #0071F2; } #images .layui-input-inline { width: auto; margin-bottom: 10px; } .static-bottom { background: white; bottom: 0px; padding-bottom: 40px; left: 0; right: 0; padding-top: 20px; border-top: 1px solid #cccccc; text-align: center; } .static-bottom .layui-input-block { margin-left: 0; } </style> </head> <body> <form class="layui-form" action="add" id="myForm" method="post" style=""> <div class="layui-row layui-col-space15"> <div class="layui-col-md12"> <div class="layui-card"> <div class="layui-card-header">销售信息</div> <div class="layui-card-body"> <div class="layui-form-item"> <label class="layui-form-label">商品规格</label> <div class="layui-input-inline"> <button type="button" class="layui-btn layui-btn-primary btn-add-attr-key"> 添加自定义商品规格 </button> </div> </div> <!-- 放多规格 --> <div id="sku-container"></div> <!-- SKU表 --> <div class="layui-form-item" id="sku-table"></div> </div> </div> </div> </div> <div class="layui-form-item static-bottom"> <div class="layui-input-block"> <button class="layui-btn" lay-submit="myForm" lay-filter="myForm">立即提交</button> </div> </div> </form> <script type="text/javascript"> // layui 公共配置 const layconfig = { base: 'layui/js/', version: true, debug: true, }; </script> <script type="text/javascript"> const init = function (options) { var _modules = options.modules || []; _modules = _modules.concat('layer', 'form', 'element'); layui.config(layconfig).use(_modules, function (opt) { var form = layui.form, laytpl = layui.laytpl, element = layui.element; // 其他事件 if (options.exec) { options.exec(layui); } form.render(); }); }; </script> <script type="text/javascript"> $(function () { var opt = { modules: ['table', 'form', 'element', 'laytpl', 'tree'], exec: function (layui) { // -- 商品规格开始 -- // 添加自定义 attr key var already_set_sku_vals = {}; // 已经设置的SKU值数据 var arr_sku = {}; // 已经设置的SKU值数据 var attr_key_id = ''; // 自定义 attr key id var attr_key_sort = 0; // 自定义 attr key sort var attr_val_id = ''; // 自定义 attr val id var attr_val_sort = 0; // 自定义 attr val sort $(document).on("click", ".btn-add-attr-key", function () { var tpl_attr_key_model = $("#tpl-attr-key-model").html(); attr_key_id = uuid(32, 16); attr_val_id = uuid(32, 16); attr_key_sort++; var data = {attr_key_id: attr_key_id, attr_val_id: attr_val_id, attr_key_sort: attr_key_sort}; layui.laytpl(tpl_attr_key_model).render(data, (html) => { $("#sku-container").append(html); // 添加到该按钮的前面 layui.form.render('checkbox'); initSkuTable(); }); }); // 删除自定义 attr key $(document).on("click", ".btn-remove-attr-key", function () { $(this).parent().parent().remove(); // 添加到该按钮的前面 initSkuTable(); }); // 监听 attr key 输入 $(document).on("change", ".attr-key", function () { var current_val = $(this).val(); // 判断标题是否已经重复了 var appear_time = 0; $(".attr-key").each(function () { var val = $(this).val(); if (current_val == val) { appear_time++; } }); if (appear_time > 1) { layer.msg('该规格名称已经存在!', {time: 1000}); $(this).val(""); return; } initSkuTable(); }); // 监听attr val 输入 $(document).on("change", ".attr-val", function () { var current_val = $(this).val(); // 判断值是否已经重复了 var appear_time = 0; $(".attr-val").each(function () { var val = $(this).val(); if (current_val == val) { appear_time++; } }); if (appear_time > 1) { layer.msg('该规格值已经存在!', {time: 1000}); $(this).val(""); return; } initSkuTable(); }); // 监听attr_val 输入 layui.form.on('checkbox(attr-val)', function (data) { initSkuTable(); }); // 绘制表格 function initSkuTable() { get_already_set_sku_vals(); // 获取已经设置的SKU值 var attrs = []; // 存放属性数组 var total_row = 1; // 总行数 var sku_table_dom = ""; // sku表格dom // 获取元素类型 $(".attr-key-item").each(function(index, item) { var attr_key = {}; attr_key.attr_key = $(item).find(".attr-key").val(); var attr_vals = []; $(item).find('.attr-val').each(function(index, item) { var val = $(item).val(); if (val == undefined || val == "") { return; } var attr_val = {}; // attr val对象 attr_val.attr_key_id = $(this).attr("attr_key_id"); attr_val.attr_val_id = $(this).attr("attr_val_id"); attr_val.attr_val = val; attr_vals.push(attr_val); }); if (attr_vals && attr_vals.length > 0) { total_row = total_row * attr_vals.length; attr_key.attr_vals = attr_vals; // sku值数组 attrs.push(attr_key); // 保存进数组中 } }); sku_table_dom = "<table class='layui-table skuTable'><thead><tr>"; // 创建表头 for(var i = 0 ; i < attrs.length ; i ++){ sku_table_dom += '<th>'+attrs[i].attr_key+'</th>'; } sku_table_dom += '<th>价格</th><th>库存</th>'; sku_table_dom += "</thead></tr>"; // 循环处理表体 for(var i = 0 ; i < total_row ; i ++){ //总共需要创建多少行 var curr_row_doms = ""; var row_count = 1; // 记录行数 var attr_key_ids = []; // 记录 attr key id var attr_val_ids = []; // 记录 attr val id var attr_key_arr = []; // 记录 attr key var attr_val_arr = []; // 记录 attr val for(var j = 0 ; j < attrs.length ; j ++) { var attr_vals = attrs[j].attr_vals; // attr val 数组 var attr_vals_len = attr_vals.length; // attr val 长度 row_count = (row_count * attr_vals_len); // 目前的生成的总行数 var an_inter_bank_num = (total_row / row_count); // 跨行数 var point = ((i / an_inter_bank_num) % attr_vals_len); attr_key_arr.push(attrs[j].attr_key); if (0 == (i % an_inter_bank_num)) { // 需要创建td curr_row_doms += '<td rowspan='+an_inter_bank_num+'>' + attr_vals[point].attr_val + '</td>'; attr_val_arr.push(attr_vals[point].attr_val); attr_val_ids.push(attr_vals[point].attr_val_id); attr_key_ids.push(attr_vals[point].attr_key_id); } else{ // 当前单元格为跨行 attr_val_arr.push(attr_vals[parseInt(point)].attr_val); attr_val_ids.push(attr_vals[parseInt(point)].attr_val_id); attr_key_ids.push(attr_vals[parseInt(point)].attr_key_id); } } var str_val_ids = attr_val_ids.toString() var already_set_sku_price = ""; // 已经设置的SKU价格 var already_set_sku_stock = ""; // 已经设置的SKU库存 if(already_set_sku_vals){ var curr_group_sku_val = already_set_sku_vals[str_val_ids];//当前这组SKU值 if(curr_group_sku_val){ already_set_sku_price = curr_group_sku_val.sku_price; already_set_sku_stock = curr_group_sku_val.sku_stock; } } sku_table_dom += '<tr attr_val_ids="'+attr_val_ids+'" attr_key_ids="'+attr_key_ids.toString()+'" attr_val_names="'+attr_val_arr.join(";")+'" propnames="'+attr_key_arr.join(";")+'" class="sku_table_tr">' + curr_row_doms + '<td><input type="number" step="0.01" min="0.01" name="sku_price" class="layui-input" value="'+already_set_sku_price+'"/></td>' + '<td><input type="number" step="0.01" min="0.01" name="sku_stock" class="layui-input" value="'+already_set_sku_stock+'"/></td>' + '</tr>'; } $("#sku-table").html(sku_table_dom); get_already_set_sku_vals(); layui.form.render("checkbox"); } // 单击添加 attr val $(document).on("click", ".btn-add-attr-val", function () { var tpl_attr_val_model = $("#tpl-attr-val-model").html(); attr_val_sort++; attr_val_id = uuid(32, 16); attr_key_id = $(this).attr('attr_key_id'); var data = {attr_key_id: attr_key_id, attr_val_id: attr_val_id, attr_val_sort: attr_val_sort}; layui.laytpl(tpl_attr_val_model).render(data, (html) => { $(this).parent().parent().parent().find(".attr-vals").append(html); // 添加到该按钮的前面 layui.form.render('checkbox'); }); initSkuTable(); }); // 单击删除 attr val $(document).on("click", ".btn-remove-attr-val", function () { $(this).parent().parent().remove(); // 添加到该按钮的前面 initSkuTable(); }); function get_already_set_sku_vals(){ already_set_sku_vals = {}; arr_sku = {}; //获取设置的SKU属性值 $("tr.sku_table_tr").each(function() { var sku_price = $(this).find("input[name=sku_price]").val(); // SKU价格 var sku_stock = $(this).find("input[name=sku_stock]").val(); // SKU库存 if (sku_price || sku_stock) { // 已经设置了全部或部分值 var attr_val_ids = $(this).attr("attr_val_ids"); // SKU值id集合 // var attr_val_names = $(this).attr("attr_val_names");//SKU值name集合 already_set_sku_vals[attr_val_ids] = { "sku_price" : sku_price, "sku_stock" : sku_stock, }; arr_sku[attr_val_ids] = { "sku_price" : sku_price, "sku_stock" : sku_stock, } } }); } layui.form.on('checkbox(unify-price)', function (data) { var unify_price = $("input[name=unify_price]").val(); if (data.elem.checked) { $("input[name=sku_price]").val(unify_price); } get_already_set_sku_vals(); }); layui.form.on('checkbox(unify-stock)', function (data) { var unify_stock = $("input[name=unify_stock]").val(); if (data.elem.checked) { $("input[name=sku_stock]").val(unify_stock); } }); function uuid(len, radix) { var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); var uuid = [], i; radix = radix || chars.length; if (len) { // Compact form for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; } else { // rfc4122, version 4 form var r; // rfc4122 requires these characters uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; uuid[14] = '4'; // Fill in random data. At i==19 set the high bits of clock sequence as // per rfc4122, sec. 4.1.5 for (i = 0; i < 36; i++) { if (!uuid[i]) { r = 0 | Math.random() * 16; uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; } } } return uuid.join(''); } // -- 商品规格结束 -- layui.form.on('submit(myForm)', function () { get_already_set_sku_vals(); var data = $('#myForm').serialize() + "&sku=" + JSON.stringify(arr_sku); console.log(data); $.ajax({ url: 'http://www.bubaijun.com/api/add_product_sku', method: 'POST', data: data, success: function(res){ console.log(res); } }); return false; }); } }; init(opt); }); </script> <!-- 自定义商品规格key --> <script type="text/html" id="tpl-attr-key-model"> <div class="layui-form-item attr-key-item"> <div class="sku-title"> <input type="text" name="attr_key[{{d.attr_key_id}}]" placeholder="规格名称,例如:颜色,尺寸" class="layui-input attr-key"><a class="btn-remove-attr-key" href="javascript:void(0);">删除</a> </div> <div class="attr-vals"> <div class="layui-inline"> <div class="layui-input-inline"> <input attr_val_id="{{d.attr_val_id}}" attr_key_id="{{d.attr_key_id}}" type="text" name="attr_val[{{d.attr_key_id}}][{{d.attr_val_id}}]" placeholder="例如:白色" lay-verify="required" class="attr-val layui-input"> </div> <div class="layui-form-mid layui-word-aux"> <a class="btn-remove-attr-val" href="javascript:void(0);">删除</a> </div> </div> </div> <div class="layui-inline"> <div class="layui-form-mid layui-word-aux"> <a class="btn-add-attr-val" href="javascript:void(0);" attr_key_id="{{d.attr_key_id}}">添加自定义项</a> </div> </div> </div> </script> <script type="text/html" id="tpl-attr-val-model"> <div class="layui-inline attr-vals-item"> <div class="layui-input-inline"> <input attr_val_id="{{d.attr_val_id}}" attr_key_id="{{d.attr_key_id}}" type="text" name="attr_val[{{d.attr_key_id}}][{{d.attr_val_id}}]" class="attr-val layui-input"> </div> <div class="layui-form-mid layui-word-aux"> <a class="btn-remove-attr-val" href="javascript:void(0);">删除</a> </div> </div> </script> </body> </html>
其实上面这段前端代码, 是博主从工作的项目中提取的, 但是和原项目区别还是很大的(原项目代码 2K+ 行封装了很多东西, 上面这段代码只有400多行, 被博主优化删减只有商品规格了).
接下来就是后端, 后端使用 Laravel 测试. 注意看 409 行代码:
layui.form.on('submit(myForm)', function () { get_already_set_sku_vals(); var data = $('#myForm').serialize() + "&sku=" + JSON.stringify(arr_sku); console.log(data); $.ajax({ url: 'http://www.bubaijun.com/api/add_product_sku', method: 'POST', data: data, success: function(res){ console.log(res); } }); return false; });
如果不需要异步提交, 把这段代码删除即可.
提交测试效果:
可以看到能打印出提交的数据, 接着就是将数据处理一下, 直接上代码(Laravel 控制器内):
public function index(Request $request) { $data = $request->all(); $arr_attr_key = $data['attr_key']; $arr_attr_val = $data['attr_val']; $map_attr_key = []; $lst_obj_attr = []; // attr组 (一个attr 组 含 id,title,一堆跟title相应的值) foreach ($arr_attr_key as $attr_key_id => $attr_key) { if (empty($arr_attr_val[$attr_key_id])) { continue; } $attr_vals = $arr_attr_val[$attr_key_id]; // attr_key $parent_sku_attrs = [ 'value' => $attr_key, ]; $map_attr_key[$attr_key_id] = $parent_sku_attrs; foreach ($attr_vals as $attr_val_id => $attr_val) { // attr_val $obj_attr_val = [ 'value' => $attr_val, ]; $obj_attr_val['attr_val_id'] = $attr_val_id; $lst_obj_attr[$attr_key_id][] = $obj_attr_val; } } $json_sku = json_decode($data['sku'], true); // 将多个attr整合SKU表数据 $rows = []; foreach ($lst_obj_attr as $option => $items) { if (count($rows) > 0) { // 2、将第一列作为模板 $clone = $rows; // 3、置空当前列表,因为只有第一列的数据,组合是不完整的 $rows = []; // 4、遍历当前列,追加到模板中,使模板中的组合变得完整 foreach ($items as $item) { $tmp = $clone; foreach ($tmp as $index => $value) { $value[$option] = $item; $tmp[$index] = $value; } // 5、将完整的组合拼回原列表中 $rows = array_merge($rows, $tmp); } } else { // 1、先计算出第一列 foreach ($items as $item) { $rows[][$option] = $item; } } } $lst_sku_data = $rows; $lst_product_sku = []; foreach ($lst_sku_data as $key => $sku) { $temp_data = []; $temp_data['product_id'] = '商品ID 可以是前端传过来的值 也可以是上方代码中增加保存商品然后获取商品主键ID'; $attrs_name = []; $ext_attr_name = ""; $sku_key = ""; foreach ($sku as $attr_key_id => $attr_val) { if (empty($attr_val)) { dd('请检查规格属性是否输入完整'); } $attrs_name[$map_attr_key[$attr_key_id]['value']] = $attr_val['value']; $ext_attr_name .= $attr_val['value'] . " "; $sku_key .= "{$attr_val['attr_val_id']},"; } $attrs_name = json_encode($attrs_name, JSON_UNESCAPED_UNICODE); $sku_key = substr($sku_key, 0, -1); // 前端传来的设置的指针 $sku = $json_sku[$sku_key]; if (empty($sku) || empty($sku['sku_stock']) || empty($sku['sku_price'])) { dd('规格缺少库存以及价格'); } $temp_data['attrs_name'] = $attrs_name; $temp_data['name'] = $ext_attr_name; $temp_data['stock'] = $sku['sku_stock']; $temp_data['price'] = $sku['sku_price']; // 这里可以转换一下金额 例如数据库存入单位为分的 $lst_product_sku[] = $temp_data; } dd($lst_product_sku); }
最后得到了这样的数组:
有了最终的这些数据, 就可以保存到数据库了.
本文地址 : bubaijun.com/page.php?id=182
版权声明 : 未经允许禁止转载!
上一篇文章: 使用PHP Laravel 框架开发淘客站点
下一篇文章: Laravel使用EasyWechat开发微信支付
@挑食的小草莓 那就要看你后端和前端的实际配合了, 假设后端数据如最后一张图那样存储, 后端利用 attrs_name 的索引就能拿到规格名称(颜色 尺码)再拿到对应的值返回给前端, 前端再进行处理就好了。
评论时间:2020-07-18 14:38:47