本文章基于jade 的基础上,完善部分功能。多谢jade提供sku方案
- 多个规格名,只显示2个
- 添加规则值,保留已有的信息
- 保存数据库后,json数据会自动排序 (2020-09-21 新增)
Form 扩展 – 商品Sku
1.安装:
composer require jadekun/sku
php artisan vendor:publish --provider="JadeKun\Sku\SkuServiceProvider"
2. 路由规则
给上传图片使用
// sku 上传图片
$router->post('upload_file', 'GoodsController@uploadFile'); // 方法
// 上传图片public function uploadFile(Request $request){if($request->hasFile('file')) {$file = $request->file('file');// 文件是否上传成功if ($file->isValid()) {// 获取文件相关信息$originalName = $file->getClientOriginalName(); // 文件原名$ext = $file->getClientOriginalExtension(); // 扩展名$realPath = $file->getRealPath(); //临时文件的绝对路径$type = $file->getClientMimeType(); // image/jpeg// 上传文件$filename = uniqid() . '.' . $ext;$data['pic_path'] = '/skus/'.$filename;// 使用我们新建的uploads本地存储空间(目录) uploads是配置文件的名称$bool = Storage::disk(config('admin.upload.disk'))->put('/'.$data['pic_path'], file_get_contents($realPath));//判断是否创建成功if (!$bool){return $this->responseError('添加图片失败', $this->status_blackvirus_insert_img_error);}return ['url'=> '/upload'.$data['pic_path']];}return [];}}
3. 数据库字段设置
数据类型: json
4. 使用方法
$form->sku('good_sku','商品SKU');
5. 原始数据展示
{
"type": "many",
"attrs": {"尺寸": ["L"], "颜色": ["红色", "黑色", "白色"]},
"sku": [{"pic": "/upload/skus/5f62fbdbc87f1.png", "price": "30", "stock": "1000", "尺寸": "L", "颜色": "红色"}, {"pic": "/upload/skus/5f62fbde2f99c.png", "price": "30", "stock": "1000", "尺寸": "L", "颜色": "黑色"}, {"pic": "/upload/skus/5f62fbe08bc28.png", "price": "30", "stock": "1000", "尺寸": "L", "颜色": "白色"}]
}
修改处理
(不足之处请指正 !!! )
1. 超过2条规则名,只会展示2条
# 文件位置 /public/vendor/jadekun/sku/sku.js···// 大概145行if(index < attr_keys_len - 1) {// 加载数据时,超过2条规则名时,只展示2条规则名tbody.find('tr').eq(0).find('td:eq(2) .Js_add_attr_name').trigger('click');}
···
2. 新增规则名,会清空已填写的信息
···// 大概在183行if (JSON.stringify(_this.attrs) !== JSON.stringify(attr)) {_this.attrs = attr;// 1. 获取历史数据,渲染数据,let old_val = _this.warp.find('.Js_sku_input').val();old_val = JSON.parse(old_val);_this.SKUForm(old_val.sku, 1)}// 2. SKUForm方法 生成具体的SKU配置表单type=0 第一次加载渲染SKU.prototype.SKUForm = function (default_sku, type=0) {···// 大概在223行// 根据计算的笛卡尔积渲染tbodylet tbody_html = '';cartesianProductOf.forEach(function (sku_item) {tbody_html += '<tr>';sku_item.forEach(function (attr_val, index) {let attr_name = attr_names[index];tbody_html += '<td data-field="' + attr_name + '">' + attr_val + '</td>';});// 3. 添加新的规则值时,保留历史信息if(default_sku && type == 1) {let back_img = ' ';let sku_price = _this.commonPrice;let sku_stock = _this.commonStock;default_sku.forEach(function(item_sku, index) {let skus = Object.values(item_sku);if (sku_item.every(val => skus.includes(val))) {back_img = item_sku.pic;sku_price = item_sku.price;sku_stock = item_sku.stock;}})tbody_html += '<td data-field="pic"><input value="'+ back_img +'" type="hidden" class="form-control">'+'<span class="Js_sku_upload" style="background-image: url('+ back_img +') "> </span> '+'<span class="Js_sku_del_pic">清空</span></td>';tbody_html += '<td data-field="price"><input value="' + sku_price + '" type="text" class="form-control"></td>';tbody_html += '<td data-field="stock"><input value="' + sku_stock + '" type="text" class="form-control"></td>';} else {tbody_html += '<td data-field="pic"><input value="" type="hidden" class="form-control"><span class="Js_sku_upload"> </span><span class="Js_sku_del_pic">清空</span></td>';tbody_html += '<td data-field="price"><input value="' + _this.commonPrice + '" type="text" class="form-control"></td>';tbody_html += '<td data-field="stock"><input value="' + _this.commonStock + '" type="text" class="form-control"></td>';}tbody_html += '</tr>'});···}
···
3. json数据保存数据库后,会自动排序,导致数据展示排版无法按照要求
原始数据:
"attrs": {"颜色": ["红色", "黑色", "白色"],"尺寸": ["L"]}
保存后:
"attrs": {"尺寸": ["L"], "颜色": ["红色", "黑色", "白色"]}
解决思路:在生成json数据之前,给‘颜色’,‘尺寸’ 前增加排序数字
# 大概在167行, 注释排序
···
let nums = 0; // 1. 增加json排序数字
trs.each(function () {let tr = $(this);let attr_name = tr.find('td:eq(0) input').val(); let attr_val = []; if (attr_name) {tr.find('td:eq(1) input').each(function () {let ipt_val = $(this).val();if (ipt_val) {attr_val.push(ipt_val)}});}// 2. 更改:json数据保存到数据库自动排序if (attr_val.length) {attr[ nums + '·' + attr_name] = attr_val;nums += 1;}
});
···
# 也需要更改展示数据,不要把增加的排序数字展示出来,有2处需要修改
# 方案:将字符串转成数组,取对应值
# 大概在128行
···
attr_keys.forEach(function (attr_key, index) {// 规格名// 更改:json自动排序,去除排序字段let attrsArr = attr_key.split('·');let tr = tbody.find('tr').eq(index);tr.find('td:eq(0) input').val(attrsArr[1]);
···
# 大概在206行
···attr_names.forEach(function (attr_name) {// 更改:json自动排序,去除排序字段let attrsArr = attr_name.split('·');thead_html += '<th>' + attrsArr[1] + '</th>'
});
···
sku.js 全部代码
(function () {// 上传地址const UploadHost = '/admin/upload_file';function SKU(warp) {this.warp = $(warp);this.attrs = {};this.commonStock = 0; // 统一库存this.commonPrice = 0; // 统一价格this.init();}SKU.prototype.init = function () {let _this = this;// 选择sku的类型(单规格/多规格)_this.warp.find('.sku_attr_select .btn').click(function () {let _dom = $(this);if (!_dom.hasClass('btn-success')) {_dom.addClass('btn-success').removeClass('btn-default').siblings().removeClass('btn-success').addClass('btn-default');if (_dom.hasClass('Js_single_btn')) {// 点击了单规格// 隐藏多规格编辑DOM_this.warp.find('.sku_attr_key_val,.sku_edit_warp').hide();} else if (_dom.hasClass('Js_many_btn')) {// 点击了多规格// 显示多规格编辑DOM_this.warp.find('.sku_attr_key_val,.sku_edit_warp').show();}}_this.processSku()});// 绑定属性值添加事件_this.warp.find('.sku_attr_key_val').on('click', '.Js_add_attr_val', function () {let html = '<div class="sku_attr_val_item">' +'<div class="sku_attr_val_input">' +'<input type="text" class="form-control">' +'</div>' +'<span class="btn btn-danger Js_remove_attr_val"><i class="glyphicon glyphicon-remove"></i></span>' +'</div>';$(this).before(html);});// 绑定属性值移除事件_this.warp.find('.sku_attr_key_val').on('click', '.Js_remove_attr_val', function () {$(this).parent('.sku_attr_val_item').remove();_this.getSkuAttr();});// 绑定添加属性名事件_this.warp.find('.Js_add_attr_name').click(function () {let html = '<tr>' +'<td><input type="text" class="form-control"></td>' +'<td>' +'<div class="sku_attr_val_warp">' +'<div class="sku_attr_val_item">' +'<div class="sku_attr_val_input">' +'<input type="text" class="form-control">' +'</div>' +'<span class="btn btn-danger Js_remove_attr_val"><i class="glyphicon glyphicon-remove"></i></span>' +'</div>' +'<div class="sku_attr_val_item Js_add_attr_val" style="padding-left:10px">' +'<span class="btn btn-success"><i class="glyphicon glyphicon-plus"></i></span>' +'</div>' +'</div>' +'</td>' +'<td>' +'<span class="btn btn-danger Js_remove_attr_name">移除</span>' +'</td>' +'</tr>';_this.warp.find('.sku_attr_key_val tbody').append(html)});// 绑定移除属性名事件_this.warp.find('.sku_attr_key_val').on('click', '.Js_remove_attr_name', function () {// console.log('移除属性名');$(this).parents('tr').remove();_this.getSkuAttr()});// 绑定input变化事件_this.warp.find('.sku_attr_key_val tbody').on('change', 'input', _this.getSkuAttr.bind(_this));_this.warp.find('.sku_edit_warp tbody').on('keyup', 'input', _this.processSku.bind(_this));// 统一价格_this.warp.find('.sku_edit_warp thead').on('keyup', 'input.Js_price', function () {_this.commonPrice = $(this).val();_this.warp.find('.sku_edit_warp tbody td[data-field="price"] input').val(_this.commonPrice);_this.processSku()});// 统一库存_this.warp.find('.sku_edit_warp thead').on('keyup', 'input.Js_stock', function () {_this.commonStock = $(this).val();_this.warp.find('.sku_edit_warp tbody td[data-field="stock"] input').val(_this.commonStock);_this.processSku()});// SKU图片上传_this.warp.find('.sku_edit_warp tbody').on('click', '.Js_sku_upload', function() {_this.upload($(this))});// 清空SKU图片_this.warp.find('.sku_edit_warp tbody').on('click','.Js_sku_del_pic', function() {let td = $(this).parent();td.find('input').val('');td.find('.Js_sku_upload').css('background-image','none');_this.processSku()});let old_val = _this.warp.find('.Js_sku_input').val();if (old_val) {// 根据值生成DOMold_val = JSON.parse(old_val);if (old_val.type === 'many') {// 多规格_this.warp.find('.sku_attr_select .Js_many_btn').trigger('click');// 处理规格名let attr_names = old_val.attrs;let tbody = _this.warp.find('.sku_attr_key_val tbody');let attr_keys = Object.keys(attr_names);let attr_keys_len = attr_keys.length;attr_keys.forEach(function (attr_key, index) {// 规格名// 更改:json自动排序,去除排序字段let attrsArr = attr_key.split('·');let tr = tbody.find('tr').eq(index);tr.find('td:eq(0) input').val(attrsArr[1]);// 规格值let attr_val_td = tr.find('td:eq(1)');let attr_vals = attr_names[attr_key];let attr_vals_len = attr_vals.length;attr_vals.forEach(function (attr_val, index_2) {attr_val_td.find('input').eq(index_2).val(attr_val);if (index_2 < attr_vals_len - 1) {attr_val_td.find('.Js_add_attr_val').trigger('click');}});// 接着处理下一行if(index < attr_keys_len - 1) {// 加载数据时,超过2条规则名时,只展示2条规则名tbody.find('tr').eq(0).find('td:eq(2) .Js_add_attr_name').trigger('click');}});// 生成具体的SKU配置表单_this.attrs = old_val.attrs;_this.SKUForm(old_val.sku);}} else {_this.processSku()}};// 获取SKU属性SKU.prototype.getSkuAttr = function () {let attr = {}; // 所有属性let _this = this;let trs = _this.warp.find('.sku_attr_key_val tbody tr');let nums = 0;trs.each(function () {let tr = $(this);let attr_name = tr.find('td:eq(0) input').val(); // 属性名let attr_val = []; // 属性值if (attr_name) {// 获取对应的属性值tr.find('td:eq(1) input').each(function () {let ipt_val = $(this).val();if (ipt_val) {attr_val.push(ipt_val)}});}// 更改:json数据保存到数据库自动排序if (attr_val.length) {attr[ nums + '·' + attr_name] = attr_val;nums += 1;}});if (JSON.stringify(_this.attrs) !== JSON.stringify(attr)) {_this.attrs = attr;let old_val = _this.warp.find('.Js_sku_input').val();old_val = JSON.parse(old_val);_this.SKUForm(old_val.sku, 1)}};// 生成具体的SKU配置表单type=0 第一次加载渲染SKU.prototype.SKUForm = function (default_sku, type=0) {let _this = this;let attr_names = Object.keys(_this.attrs);if (attr_names.length === 0) {_this.warp.find('.sku_edit_warp tbody').html(' ');_this.warp.find('.sku_edit_warp thead').html(' ');} else {// 渲染表头let thead_html = '<tr>';attr_names.forEach(function (attr_name) {// 更改:json自动排序,去除排序字段let attrsArr = attr_name.split('·');thead_html += '<th>' + attrsArr[1] + '</th>'});thead_html += '<th style="width: 100px">图片</th>';thead_html += '<th style="width: 100px">价格 <input value="' + _this.commonPrice + '" type="text" style="width: 50px" class="Js_price"></th>';thead_html += '<th style="width: 100px">库存 <input value="' + _this.commonStock + '" type="text" style="width: 50px" class="Js_stock"></th>';thead_html += '</tr>';_this.warp.find('.sku_edit_warp thead').html(thead_html);// 求笛卡尔积let cartesianProductOf = (function () {return Array.prototype.reduce.call(arguments, function (a, b) {var ret = [];a.forEach(function (a) {b.forEach(function (b) {ret.push(a.concat([b]));});});return ret;}, [[]]);})(...Object.values(_this.attrs));// 根据计算的笛卡尔积渲染tbodylet tbody_html = '';cartesianProductOf.forEach(function (sku_item) {tbody_html += '<tr>';sku_item.forEach(function (attr_val, index) {let attr_name = attr_names[index];tbody_html += '<td data-field="' + attr_name + '">' + attr_val + '</td>';});// 添加新的规则值时,保留历史信息if(default_sku && type == 1) {let back_img = ' ';let sku_price = _this.commonPrice;let sku_stock = _this.commonStock;default_sku.forEach(function(item_sku, index) {let skus = Object.values(item_sku);if (sku_item.every(val => skus.includes(val))) {back_img = item_sku.pic;sku_price = item_sku.price;sku_stock = item_sku.stock;}})tbody_html += '<td data-field="pic"><input value="'+ back_img +'" type="hidden" class="form-control">'+'<span class="Js_sku_upload" style="background-image: url('+ back_img +') "> </span> '+'<span class="Js_sku_del_pic">清空</span></td>';tbody_html += '<td data-field="price"><input value="' + sku_price + '" type="text" class="form-control"></td>';tbody_html += '<td data-field="stock"><input value="' + sku_stock + '" type="text" class="form-control"></td>';} else {tbody_html += '<td data-field="pic"><input value="" type="hidden" class="form-control"><span class="Js_sku_upload"> </span><span class="Js_sku_del_pic">清空</span></td>';tbody_html += '<td data-field="price"><input value="' + _this.commonPrice + '" type="text" class="form-control"></td>';tbody_html += '<td data-field="stock"><input value="' + _this.commonStock + '" type="text" class="form-control"></td>';}tbody_html += '</tr>'});_this.warp.find('.sku_edit_warp tbody').html(tbody_html);if(default_sku && type == 0) {// 填充数据default_sku.forEach(function(item_sku, index) {let tr = _this.warp.find('.sku_edit_warp tbody tr').eq(index);Object.keys(item_sku).forEach(function(field) {let input = tr.find('td[data-field="'+field+'"] input');if(input.length) {input.val(item_sku[field]);let sku_upload = tr.find('td[data-field="'+field+'"] .Js_sku_upload');if(sku_upload.length) {sku_upload.css('background-image','url('+item_sku[field]+')');}}})});}}_this.processSku()};// 处理最终SKU数据,并写入inputSKU.prototype.processSku = function () {let _this = this;let sku_json = {};sku_json.type = _this.warp.find('.sku_attr_select .btn.btn-success').attr('data-type');if (sku_json.type === 'many') {// 多规格sku_json.attrs = _this.attrs;let sku = [];_this.warp.find('.sku_edit_warp tbody tr').each(function () {let tr = $(this);let item_sku = {};tr.find('td[data-field]').each(function () {let td = $(this);let field = td.attr('data-field');let input = td.find('input');if (input.length) {item_sku[field] = input.val();} else {item_sku[field] = td.text();}});sku.push(item_sku);});sku_json.sku = sku;}_this.warp.find('.Js_sku_input').val(JSON.stringify(sku_json));};// 图片上传SKU.prototype.upload = function(obj) {let _this = this;// 创建input[type="file"]元素let file_input = document.createElement('input');file_input.setAttribute('type','file');file_input.setAttribute('accept','image/x-png,image/jpeg');// 模拟点击 选择文件file_input.click();file_input.onchange = function() {let file = file_input.files[0]; //获取上传的文件名let formData = new FormData();formData.append('file', file);formData.append('_token', LA.token);console.log(formData);// 使用ajax上传文件$.ajax({type: "POST",url: UploadHost,data: formData,contentType: false, //告诉jQuery不要去设置Content-Type请求头headers: {Accept: "application/json"},processData: false, //告诉jQuery不要去处理发送的数据success: function (res) {obj.css('background-image','url('+res.url+')');obj.parent().find('input').val(res.url);_this.processSku()}})}};window.JadeKunSKU = SKU;
})();