skuTable.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. /*
  2. * Name: skuTable
  3. * Author: cshaptx4869
  4. * Project: https://github.com/cshaptx4869/skuTable
  5. */
  6. layui.define(['jquery', 'form', 'upload', 'layer', 'sortable'], function (exports) {
  7. "use strict";
  8. var $ = layui.jquery,
  9. form = layui.form,
  10. upload = layui.upload,
  11. layer = layui.layer,
  12. sortable = layui.sortable,
  13. MOD_NAME = 'skuTable';
  14. //工具类
  15. class Util {
  16. static config = {
  17. shade: [0.02, '#000'],
  18. time: 2000
  19. };
  20. static msg = {
  21. // 成功消息
  22. success: function (msg, callback = null) {
  23. return layer.msg(msg, {
  24. icon: 1,
  25. shade: Util.config.shade,
  26. scrollbar: false,
  27. time: Util.config.time,
  28. shadeClose: true
  29. }, callback);
  30. },
  31. // 失败消息
  32. error: function (msg, callback = null) {
  33. return layer.msg(msg, {
  34. icon: 2,
  35. shade: Util.config.shade,
  36. scrollbar: false,
  37. time: Util.config.time,
  38. shadeClose: true
  39. }, callback);
  40. },
  41. // 警告消息框
  42. alert: function (msg, callback = null) {
  43. return layer.alert(msg, {end: callback, scrollbar: false});
  44. },
  45. // 对话框
  46. confirm: function (msg, ok, no) {
  47. var index = layer.confirm(msg, {title: '操作确认', btn: ['确认', '取消']}, function () {
  48. typeof ok === 'function' && ok.call(this);
  49. }, function () {
  50. typeof no === 'function' && no.call(this);
  51. Util.msg.close(index);
  52. });
  53. return index;
  54. },
  55. // 消息提示
  56. tips: function (msg, callback = null) {
  57. return layer.msg(msg, {
  58. time: Util.config.time,
  59. shade: Util.config.shade,
  60. end: callback,
  61. shadeClose: true
  62. });
  63. },
  64. // 加载中提示
  65. loading: function (msg, callback = null) {
  66. return msg ? layer.msg(msg, {
  67. icon: 16,
  68. scrollbar: false,
  69. shade: Util.config.shade,
  70. time: 0,
  71. end: callback
  72. }) : layer.load(2, {time: 0, scrollbar: false, shade: Util.config.shade, end: callback});
  73. },
  74. // 输入框
  75. prompt: function (option, callback = null) {
  76. return layer.prompt(option, callback);
  77. },
  78. // 关闭消息框
  79. close: function (index) {
  80. return layer.close(index);
  81. }
  82. };
  83. static request = {
  84. post: function (option, ok, no, ex) {
  85. return Util.request.ajax('post', option, ok, no, ex);
  86. },
  87. get: function (option, ok, no, ex) {
  88. return Util.request.ajax('get', option, ok, no, ex);
  89. },
  90. ajax: function (type, option, ok, no, ex) {
  91. type = type || 'get';
  92. option.url = option.url || '';
  93. option.data = option.data || {};
  94. option.statusName = option.statusName || 'code';
  95. option.statusCode = option.statusCode || 200;
  96. ok = ok || function (res) {
  97. };
  98. no = no || function (res) {
  99. var msg = res.msg == undefined ? '返回数据格式有误' : res.msg;
  100. Util.msg.error(msg);
  101. return false;
  102. };
  103. ex = ex || function (res) {
  104. };
  105. if (option.url == '') {
  106. Util.msg.error('请求地址不能为空');
  107. return false;
  108. }
  109. //var index = Util.msg.loading('加载中');
  110. $.ajax({
  111. url: option.url,
  112. type: type,
  113. contentType: "application/x-www-form-urlencoded; charset=UTF-8",
  114. dataType: "json",
  115. data: option.data,
  116. timeout: 60000,
  117. success: function (res) {
  118. //Util.msg.close(index);
  119. /*
  120. if (res[option.statusName] == option.statusCode) {
  121. return ok(res);
  122. } else {
  123. return no(res);
  124. }*/
  125. return ok(res);
  126. },
  127. error: function (xhr, textstatus, thrown) {
  128. Util.msg.error('Status:' + xhr.status + ',' + xhr.statusText + ',请稍后再试!', function () {
  129. ex(xhr);
  130. });
  131. return false;
  132. }
  133. });
  134. }
  135. };
  136. static tool = {
  137. uuid: function uuid(randomLength = 8) {
  138. return Number(Math.random().toString().substr(2, randomLength) + Date.now()).toString(36)
  139. }
  140. }
  141. }
  142. class SkuTable {
  143. options = {
  144. isAttributeValue: 1, //规格类型 0统一规格 1多规格
  145. isAttributeElemId: 'fairy-is-attribute', //规格类型容器id
  146. specTableElemId: 'fairy-spec-table', //规格表容器id
  147. skuTableElemId: 'fairy-sku-table', //SKU表容器id
  148. rowspan: false, //是否开启SKU行合并,
  149. sortable: false, //规格拖拽排序
  150. skuIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDY3IDc5LjE1Nzc0NywgMjAxNS8wMy8zMC0yMzo0MDo0MiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjczN0RFNzU1MTk1RTExRTlBMEQ5OEEwMEM5NDNFOEE4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjczN0RFNzU2MTk1RTExRTlBMEQ5OEEwMEM5NDNFOEE4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzM3REU3NTMxOTVFMTFFOUEwRDk4QTAwQzk0M0U4QTgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzM3REU3NTQxOTVFMTFFOUEwRDk4QTAwQzk0M0U4QTgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5NHmJUAAAA+0lEQVR42pySPwsBYRzH7zk3KIP34CVIKSOrELLJdpuymyzew90kIwMZvACDsCldWZTFn5WQpPN5rlPXlXJ39en7/J57fn+fR9i2rYT5NNM0B2gC3n/6qHBQDMOwZNYg4LOQ3vcQld40/w6lC13Xbd/eHElC3G1JqL4DFWSNprz7BMpAFJ6YkW+jThaosuxAD/rY6R9lCmeq8IAmtKBA1A1OW9YjtIS9QvPYRZkcXo43EzqjF/mDQ5an7ALShTFk4eQOsgFTWeoNKl4nt68J0oYc1LHLbmtDp1IyLgPe4QCuMkIsyAWSuYbs5HD29DML8OTkHR9F2Ef+EWAAdwmkvBAtw94AAAAASUVORK5CYII=',
  151. uploadUrl: '',
  152. requestSuccessCode: 1, //请求成功返回状态码值
  153. specDataDelete: false, //开启规格删除
  154. productId: '', //商品id 配合specDataUrl和skuDataUrl使用
  155. specData: [], //规格数据
  156. specDataUrl: '', //优先级大于specData
  157. skuData: {}, //SKU数据
  158. skuDataUrl: '', //优先级大于skuDataUrl
  159. skuNameType: 0,
  160. skuNameDelimiter: '-',
  161. //统一规格配置项
  162. singleSkuTableConfig: {
  163. thead: [
  164. {title: '销售价(元)', icon: 'layui-icon-cols'},
  165. {title: '市场价(元)', icon: 'layui-icon-cols'},
  166. {title: '成本价(元)', icon: 'layui-icon-cols'},
  167. {title: '库存', icon: 'layui-icon-cols'},
  168. {title: '状态', icon: ''},
  169. ],
  170. tbody: [
  171. {type: 'input', field: 'price', value: '', verify: 'required|number', reqtext: '销售价不能为空'},
  172. {type: 'input', field: 'market_price', value: '0', verify: 'required|number', reqtext: '市场价不能为空'},
  173. {type: 'input', field: 'cost_price', value: '0', verify: 'required|number', reqtext: '成本价不能为空'},
  174. {type: 'input', field: 'stock', value: '0', verify: 'required|number', reqtext: '库存不能为空'},
  175. {type: 'select', field: 'status', option: [{key: '启用', value: '1'}, {key: '禁用', value: '0'}], verify: 'required', reqtext: '状态不能为空'},
  176. ]
  177. },
  178. //多规格配置项
  179. multipleSkuTableConfig: {
  180. thead: [
  181. {title: '图片', icon: ''},
  182. {title: '销售价(元)', icon: 'layui-icon-cols'},
  183. {title: '市场价(元)', icon: 'layui-icon-cols'},
  184. {title: '成本价(元)', icon: 'layui-icon-cols'},
  185. {title: '库存', icon: 'layui-icon-cols'},
  186. {title: '状态', icon: ''},
  187. ],
  188. tbody: [
  189. {type: 'image', field: 'picture', value: '', verify: '', reqtext: ''},
  190. {type: 'input', field: 'price', value: '', verify: 'required|number', reqtext: '销售价不能为空'},
  191. {type: 'input', field: 'market_price', value: '0', verify: 'required|number', reqtext: '市场价不能为空'},
  192. {type: 'input', field: 'cost_price', value: '0', verify: 'required|number', reqtext: '成本价不能为空'},
  193. {type: 'input', field: 'stock', value: '0', verify: 'required|number', reqtext: '库存不能为空'},
  194. {
  195. type: 'select',
  196. field: 'status',
  197. option: [{key: '启用', value: '1'}, {key: '禁用', value: '0'}],
  198. verify: '',
  199. reqtext: ''
  200. },
  201. ]
  202. }
  203. };
  204. constructor(options) {
  205. this.options = $.extend(this.options, options);
  206. if (this.options.skuDataUrl && this.options.productId) {
  207. Util.request.get({
  208. url: this.options.skuDataUrl,
  209. data: {
  210. product_id: this.options.productId
  211. },
  212. statusCode: this.options.requestSuccessCode
  213. }, (res) => {
  214. if (!res.data) {
  215. res.data = [];
  216. }
  217. this.options.skuData = res.data;
  218. this.css();
  219. this.render();
  220. this.listen();
  221. });
  222. } else {
  223. this.css();
  224. this.render();
  225. this.listen();
  226. }
  227. }
  228. css() {
  229. $('head').append(`<style>
  230. ${this.options.sortable ? `#${this.options.specTableElemId} tbody tr {cursor: move;transition:unset;-webkit-transition:unset;}` : ''}
  231. #${this.options.specTableElemId} tbody tr td:first-child > i.layui-icon-delete {
  232. margin-left:3px;
  233. }
  234. #${this.options.specTableElemId} tbody tr td:last-child > i.layui-icon-delete {
  235. margin-right:15px;
  236. margin-left:-7px;
  237. vertical-align: top;
  238. }
  239. #${this.options.specTableElemId} tbody tr td div.fairy-spec-value-create,
  240. #${this.options.specTableElemId} tfoot tr td div.fairy-spec-create {
  241. display: inline-block;
  242. color: #1E9FFF;
  243. vertical-align: middle;
  244. padding: 4px 6px;
  245. }
  246. #${this.options.specTableElemId} tfoot tr td div.layui-form-checkbox {
  247. margin-top: 0;
  248. }
  249. #${this.options.specTableElemId} tfoot tr td div.layui-form-checkbox > span{
  250. color: #1E9FFF;
  251. }
  252. #${this.options.skuTableElemId} tbody tr td > img.fairy-sku-img{
  253. width: 16px;
  254. height: 16px;
  255. padding: 6px;
  256. border: 1px solid #eceef1;
  257. vertical-align: middle;
  258. }
  259. #${this.options.specTableElemId} tbody tr td > i.layui-icon-delete,
  260. #${this.options.specTableElemId} tbody tr td div.fairy-spec-value-create,
  261. #${this.options.specTableElemId} tfoot tr td div.fairy-spec-create,
  262. #${this.options.skuTableElemId} thead tr th > i.layui-icon,
  263. #${this.options.skuTableElemId} tbody tr td > img.fairy-sku-img {
  264. cursor: pointer;
  265. }
  266. </style>`
  267. );
  268. }
  269. listen() {
  270. var that = this;
  271. /**
  272. * 监听规格类型选择
  273. */
  274. form.on('radio(fairy-is-attribute)', function (data) {
  275. that.options.isAttributeValue = data.value;
  276. that.render();
  277. });
  278. /**
  279. * 监听所选规格值的变化
  280. */
  281. form.on('checkbox(fairy-spec-filter)', function (data) {
  282. var specData = [];
  283. $.each($(`#${that.options.specTableElemId} tbody tr`), function () {
  284. var child = [];
  285. $.each($(this).find('input[type=checkbox]'), function () {
  286. child.push({id: $(this).val(), title: $(this).attr('title'), checked: $(this).is(':checked') ? '1' : '0'});
  287. });
  288. var specItem = {
  289. id: $(this).find('td').eq(0).data('spec-id'),
  290. title: $(this).find('td').eq(0).text(),
  291. child: child
  292. };
  293. specData.push(specItem);
  294. });
  295. that.options.specData = specData;
  296. //that.options.skuData = $.extend(that.options.skuData, that.getFormSkuData());
  297. that.resetRender(that.options.skuTableElemId);
  298. that.renderMultipleSkuTable();
  299. });
  300. /**
  301. * 监听批量赋值
  302. */
  303. $(document).on('click', `#${this.options.skuTableElemId} thead tr th i`, function () {
  304. var thisI = this;
  305. Util.msg.prompt({title: $(thisI).parent().text().trim() + '批量赋值'}, function (value, index, elem) {
  306. $.each($(`#${that.options.skuTableElemId} tbody tr`), function () {
  307. var index = that.options.rowspan ?
  308. $(thisI).parent().index() - ($(`#${that.options.skuTableElemId} thead th.fairy-spec-name`).length - $(this).children('td.fairy-spec-value').length) :
  309. $(thisI).parent().index();
  310. $(this).find('td').eq(index).children('input').val(value);
  311. });
  312. Util.msg.close(index);
  313. });
  314. });
  315. /**
  316. * 监听添加规格
  317. */
  318. $(document).on('click', `#${this.options.specTableElemId} .fairy-spec-create`, function () {
  319. layer.prompt({title: '规格'}, function (value, index, elem) {
  320. var specTitleArr = [];
  321. $.each(that.options.specData, function (k, v) {
  322. specTitleArr.push(v.title)
  323. })
  324. if (specTitleArr.includes(value)) {
  325. Util.msg.error('规格名已存在');
  326. } else {
  327. that.options.specData.push({id: Util.tool.uuid(), title: value, child: []});
  328. that.resetRender(that.options.specTableElemId);
  329. that.renderSpecTable();
  330. }
  331. Util.msg.close(index);
  332. });
  333. });
  334. /**
  335. * 监听添加规格值
  336. */
  337. $(document).on('click', `#${this.options.specTableElemId} .fairy-spec-value-create`, function () {
  338. var specId = $(this).parent('td').prev().data('spec-id');
  339. layer.prompt({title: '规格值'}, function (value, index, elem) {
  340. that.options.specData.forEach(function (v, i) {
  341. if (v.id == specId) {
  342. v.child.push({id: Util.tool.uuid(), title: value, checked: '0'});
  343. }
  344. });
  345. that.resetRender(that.options.specTableElemId);
  346. that.renderSpecTable();
  347. Util.msg.close(index);
  348. });
  349. });
  350. /**
  351. * 监听删除规格/规格值
  352. */
  353. $(document).on('click', `#${this.options.specTableElemId} i.layui-icon-delete`, function () {
  354. if (typeof $(this).attr('data-spec-index') !== "undefined") {
  355. that.options.specData.splice($(this).data('spec-index'), 1);
  356. that.resetRender([that.options.specTableElemId, that.options.skuTableElemId]);
  357. that.renderSpecTable();
  358. that.renderMultipleSkuTable();
  359. } else if (typeof $(this).attr('data-spec-value-index') !== "undefined") {
  360. var [i, ii] = $(this).data('spec-value-index').split('-');
  361. that.options.specData[i].child.splice(ii, 1);
  362. that.resetRender([that.options.specTableElemId, that.options.skuTableElemId]);
  363. that.renderSpecTable();
  364. that.renderMultipleSkuTable();
  365. }
  366. });
  367. /**
  368. * 监听规格表是否开启删除
  369. */
  370. form.on('checkbox(fairy-spec-delete-filter)', function (data) {
  371. that.options.specDataDelete = data.elem.checked;
  372. if (data.elem.checked) {
  373. $(`#${that.options.specTableElemId} tbody tr i.layui-icon-delete`).removeClass('layui-hide');
  374. } else {
  375. $(`#${that.options.specTableElemId} tbody tr i.layui-icon-delete`).addClass('layui-hide')
  376. }
  377. });
  378. /**
  379. * 图片移入放大/移出恢复
  380. */
  381. var imgLayerIndex = null;
  382. $(document).on('mouseenter', '.fairy-sku-img', function () {
  383. imgLayerIndex = layer.tips('<img src="' + $(this).attr('src') + '" style="max-width:200px;" alt=""/>', this, {
  384. tips: [2, 'rgba(41,41,41,.5)'],
  385. time: 0
  386. });
  387. }).on('mouseleave', '.fairy-sku-img', function () {
  388. layer.close(imgLayerIndex);
  389. })
  390. }
  391. /**
  392. * 渲染
  393. */
  394. render() {
  395. this.resetRender();
  396. this.renderIsAttribute(this.options.isAttributeValue);
  397. if (this.options.isAttributeValue == '2') {
  398. if (this.options.specDataUrl && this.options.productId) {
  399. Util.request.get({
  400. url: this.options.specDataUrl,
  401. productId: this.options.productId,
  402. statusCode: this.options.requestSuccessCode
  403. }, (res) => {
  404. if (!res.data) {
  405. res.data = [];
  406. }
  407. this.options.specData = res.data;
  408. this.renderSpecTable();
  409. this.renderMultipleSkuTable();
  410. });
  411. } else {
  412. this.renderSpecTable();
  413. this.renderMultipleSkuTable();
  414. }
  415. } else {
  416. this.renderSingleSkuTable();
  417. }
  418. }
  419. /**
  420. * 重新渲染
  421. * @param targets
  422. */
  423. resetRender(targets) {
  424. if (typeof targets === 'string') {
  425. $(`#${targets}`).parents('.dever_sku').replaceWith(`<div id="${targets}"></div>`);
  426. } else if ($.isArray(targets) && targets.length) {
  427. targets.forEach((item) => {
  428. $(`#${item}`).parents('.dever_sku').replaceWith(`<div id="${item}"></div>`);
  429. })
  430. } else {
  431. $(`#${this.options.isAttributeElemId}`).parents('.dever_sku').replaceWith(`<div id="${this.options.isAttributeElemId}"></div>`);
  432. $(`#${this.options.specTableElemId}`).parents('.dever_sku').replaceWith(`<div id="${this.options.specTableElemId}"></div>`);
  433. $(`#${this.options.skuTableElemId}`).parents('.dever_sku').replaceWith(`<div id="${this.options.skuTableElemId}"></div>`);
  434. }
  435. }
  436. /**
  437. * 渲染规格类型
  438. * @param checkedValue
  439. */
  440. renderIsAttribute(checkedValue) {
  441. var html = '';
  442. html += `<input type="radio" name="update_spec_type" title="单规格" value="1" lay-filter="fairy-is-attribute" ${checkedValue == '1' ? 'checked' : ''}>`;
  443. html += `<input type="radio" name="update_spec_type" title="多规格" value="2" lay-filter="fairy-is-attribute" ${checkedValue == '2' ? 'checked' : ''}>`;
  444. this.renderFormItem('规格类型', html, this.options.isAttributeElemId);
  445. }
  446. renderSingleSkuTable() {
  447. var that = this,
  448. table = `<table class="layui-table" id="${this.options.skuTableElemId}">`;
  449. table += '<thead>';
  450. table += '<tr>';
  451. this.options.singleSkuTableConfig.thead.forEach((item) => {
  452. table += `<th>${item.title}</th>`;
  453. });
  454. table += '</tr>';
  455. table += '</thead>';
  456. table += '<tbody>';
  457. table += '<tr>';
  458. that.options.singleSkuTableConfig.tbody.forEach(function (item) {
  459. switch (item.type) {
  460. case "select":
  461. table += '<td>';
  462. table += `<select style="width: 100%;" name="skus[-1][${item.field}]" lay-verify="${item.verify}" lay-reqtext="${item.reqtext}">`;
  463. item.option.forEach(function (o) {
  464. table += `<option value="${o.id}" ${that.options.skuData && that.options.skuData[-1] && that.options.skuData[-1][item.field] && that.options.skuData[item.field] == o.id ? 'selected' : ''}>${o.name}</option>`;
  465. });
  466. table += '</select>';
  467. table += '</td>';
  468. break;
  469. case "input":
  470. default:
  471. table += '<td>';
  472. table += `<input type="text" style="width: 100%;" name="skus[-1][${item.field}]" value="${that.options.skuData && that.options.skuData[-1] && that.options.skuData[-1][item.field] ? that.options.skuData[-1][item.field] : item.value}" class="layui-input" lay-verify="${item.verify}" lay-reqtext="${item.reqtext}">`;
  473. table += '</td>';
  474. break;
  475. }
  476. });
  477. table += '</tr>';
  478. table += '<tbody>';
  479. table += '</table>';
  480. this.renderFormItem('SKU', table, this.options.skuTableElemId);
  481. }
  482. /**
  483. * 渲染规格表
  484. */
  485. renderSpecTable() {
  486. var that = this,
  487. table = `<input type="hidden" name="spec" id="spec" value=""/><table class="layui-table" id="${this.options.specTableElemId}"><thead><tr><th>规格名</th><th>规格值</th></tr></thead><colgroup><col width="140"></colgroup><tbody>`;
  488. if (this.options.specData.length > 0) {
  489. $.each(this.options.specData, function (index, item) {
  490. table += that.options.sortable ? `<tr data-id="${item.id}">` : '<tr>';
  491. table += `<td data-spec-id="${item.id}">${item.title}<i class="layui-icon layui-icon-delete layui-anim layui-anim-scale ${that.options.specDataDelete ? '' : 'layui-hide'}" data-spec-index="${index}"></i></td>`;
  492. table += '<td>';
  493. if (item.child) {
  494. $.each(item.child, function (key, value) {
  495. table += `<input type="checkbox" title="${value.title}" lay-filter="fairy-spec-filter" value="${value.id}" ${value.checked == '1' ? 'checked' : ''} /><i class="layui-icon layui-icon-delete layui-anim layui-anim-scale ${that.options.specDataDelete ? '' : 'layui-hide'}" data-spec-value-index="${index}-${key}"></i> `;
  496. });
  497. }
  498. table += '<div class="fairy-spec-value-create"><i class="layui-icon layui-icon-addition"></i>规格值</div>'
  499. table += '</td>';
  500. table += '</tr>';
  501. });
  502. }
  503. table += '</tbody>';
  504. table += '<tfoot><tr><td colspan="2">';
  505. table += `<input type="checkbox" title="开启删除" lay-skin="primary" lay-filter="fairy-spec-delete-filter" ${that.options.specDataDelete ? 'checked' : ''}/>`;
  506. table += `<div class="fairy-spec-create"><i class="layui-icon layui-icon-addition"></i>规格</div>`;
  507. table += '</td></tr></tfoot>';
  508. table += '</table>';
  509. this.renderFormItem('商品规格', table, this.options.specTableElemId);
  510. if (this.options.specData.length > 0) {
  511. $('#spec').val(JSON.stringify(this.options.specData));
  512. }
  513. if (this.options.sortable) {
  514. /**
  515. * 拖拽
  516. */
  517. var sortableObj = sortable.create($(`#${this.options.specTableElemId} tbody`)[0], {
  518. animation: 1000,
  519. onEnd: (evt) => {
  520. //获取拖动后的排序
  521. var sortArr = sortableObj.toArray(),
  522. sortSpecData = [];
  523. this.options.specData.forEach((item) => {
  524. sortSpecData[sortArr.indexOf(String(item.id))] = item;
  525. });
  526. this.options.specData = sortSpecData;
  527. this.resetRender(that.options.skuTableElemId);
  528. this.renderMultipleSkuTable();
  529. },
  530. });
  531. }
  532. }
  533. /**
  534. * 渲染sku表
  535. */
  536. renderMultipleSkuTable() {
  537. var that = this, table = `<table class="layui-table" id="${this.options.skuTableElemId}">`;
  538. if ($(`#${this.options.specTableElemId} tbody input[type=checkbox]:checked`).length) {
  539. var prependThead = [], prependTbody = [];
  540. if (this.options.specData.length > 0) {
  541. $.each(this.options.specData, function (index, item) {
  542. var isShow = item.child.some(function (value, index, array) {
  543. return value.checked == '1' ? true : false;
  544. });
  545. if (isShow) {
  546. prependThead.push(item.title);
  547. var prependTbodyItem = [];
  548. if (item.child) {
  549. $.each(item.child, function (key, value) {
  550. if (value.checked == '1') {
  551. prependTbodyItem.push({id: value.id, title: value.title});
  552. }
  553. });
  554. }
  555. prependTbody.push(prependTbodyItem);
  556. }
  557. });
  558. $('#spec').val(JSON.stringify(that.options.specData));
  559. }
  560. table += '<colgroup>' + '<col width="70">'.repeat(prependThead.length + 1) + '</colgroup>';
  561. table += '<thead>';
  562. if (prependThead.length > 0) {
  563. var theadTr = '<tr>';
  564. theadTr += prependThead.map(function (t, i, a) {
  565. return '<th class="fairy-spec-name">' + t + '</th>';
  566. }).join('');
  567. this.options.multipleSkuTableConfig.thead.forEach(function (item) {
  568. theadTr += '<th>' + item.title + (item.icon ? ' <i class="layui-icon ' + item.icon + '"></i>' : '') + '</th>';
  569. });
  570. theadTr += '</tr>';
  571. table += theadTr;
  572. }
  573. table += '</thead>';
  574. if (this.options.rowspan) {
  575. var skuRowspanArr = [];
  576. prependTbody.forEach(function (v, i, a) {
  577. var num = 1, index = i;
  578. while (index < a.length - 1) {
  579. num *= a[index + 1].length;
  580. index++;
  581. }
  582. skuRowspanArr.push(num);
  583. });
  584. }
  585. var prependTbodyTrs = [];
  586. prependTbody.reduce(function (prev, cur, index, array) {
  587. var tmp = [];
  588. if (cur.length > 0) {
  589. prev.forEach(function (a) {
  590. cur.forEach(function (b) {
  591. tmp.push({id: a.id + that.options.skuNameDelimiter + b.id, title: a.title + that.options.skuNameDelimiter + b.title});
  592. })
  593. });
  594. } else {
  595. prev.forEach(function (a) {
  596. tmp.push({id: a.id, title: a.title});
  597. });
  598. }
  599. return tmp;
  600. }).forEach(function (item, index, array) {
  601. var tr = '<tr>';
  602. tr += item.title.split(that.options.skuNameDelimiter).map(function (t, i, a) {
  603. if (that.options.rowspan) {
  604. if (index % skuRowspanArr[i] === 0 && skuRowspanArr[i] > 1) {
  605. return '<td class="fairy-spec-value" rowspan="' + skuRowspanArr[i] + '">' + t + '</td>';
  606. } else if (skuRowspanArr[i] === 1) {
  607. return '<td class="fairy-spec-value">' + t + '</td>';
  608. } else {
  609. return '';
  610. }
  611. } else {
  612. return '<td>' + t + '</td>';
  613. }
  614. }).join('');
  615. that.options.multipleSkuTableConfig.tbody.forEach(function (c) {
  616. switch (c.type) {
  617. case "image":
  618. var skuValue = that.getSkuData(that.options.skuData, item, c);
  619. tr += '<td><input type="hidden" name="' + that.makeSkuName(item, c) + '" value="' + (skuValue ? skuValue : c.value) + '" lay-verify="' + c.verify + '" lay-reqtext="' + c.reqtext + '"><img class="fairy-sku-img" src="' + (skuValue ? skuValue : that.options.skuIcon) + '" alt="' + c.field + '图片"></td>';
  620. break;
  621. case "select":
  622. var skuValue = that.getSkuData(that.options.skuData, item, c);
  623. tr += '<td><select style="width: 100%;" name="' + that.makeSkuName(item, c) + '" lay-verify="' + c.verify + '" lay-reqtext="' + c.reqtext + '">';
  624. c.option.forEach(function (o) {
  625. tr += '<option value="' + o.id + '" ' + (skuValue && skuValue == o.id ? 'selected' : '') + '>' + o.name + '</option>';
  626. });
  627. tr += '</select></td>';
  628. break;
  629. case "input":
  630. default:
  631. var skuValue = that.getSkuData(that.options.skuData, item, c);
  632. tr += '<td><input style="width: 100%;" type="text" name="' + that.makeSkuName(item, c) + '" value="' + (skuValue ? skuValue : c.value) + '" class="layui-input" lay-verify="' + c.verify + '" lay-reqtext="' + c.reqtext + '"></td>';
  633. break;
  634. }
  635. });
  636. tr += '</tr>';
  637. tr && prependTbodyTrs.push(tr);
  638. });
  639. table += '<tbody>';
  640. if (prependTbodyTrs.length > 0) {
  641. table += prependTbodyTrs.join('');
  642. }
  643. table += '</tbody>';
  644. } else {
  645. table += '<thead></thead><tbody></tbody><tfoot><tr><td>请先选择规格值</td></tr></tfoot>';
  646. }
  647. table += '</table>';
  648. this.renderFormItem('SKU', table, this.options.skuTableElemId);
  649. //上传
  650. if (this.options.uploadUrl) {
  651. upload.render({
  652. elem: '.fairy-sku-img',
  653. url: this.options.uploadUrl,
  654. exts: 'png|jpg|ico|jpeg|gif',
  655. accept: 'images',
  656. acceptMime: 'image/*',
  657. multiple: false,
  658. xhr:xhrOnProgress,
  659. done: function (res) {
  660. if (res.status === that.options.requestSuccessCode) {
  661. var url = res.url;
  662. $(this.item).attr('src', url).prev().val(url);
  663. //Util.msg.success(res.msg);
  664. } else {
  665. var msg = res.msg == undefined ? '返回数据格式有误' : res.msg;
  666. Util.msg.error(msg);
  667. }
  668. return false;
  669. }
  670. });
  671. }
  672. }
  673. /**
  674. * 渲染表单项
  675. * @param label 标题
  676. * @param content 内容
  677. * @param target id
  678. * @param isRequired
  679. */
  680. renderFormItem(label, content, target, isRequired = true) {
  681. var html = '';
  682. html += '<div class="layui-form-item dever_sku">';
  683. html += `<label class="layui-form-label ${isRequired ? 'required' : ''}">${label.length ? label : ''}</label>`;
  684. html += '<div class="layui-input-block">';
  685. html += content;
  686. html += '</div>';
  687. html += '</div>';
  688. $(`#${target}`).replaceWith(html);
  689. form.render();
  690. }
  691. makeSkuName(sku, conf) {
  692. return 'skus[' + (this.options.skuNameType === 0 ? sku.id : sku.title) + '][' + conf.field + ']';
  693. }
  694. getSkuData(data, sku, conf) {
  695. if (data[(this.options.skuNameType === 0 ? sku.id : sku.title)]) {
  696. return data[(this.options.skuNameType === 0 ? sku.id : sku.title)][conf.field];
  697. } else {
  698. return false;
  699. }
  700. }
  701. getSpecData() {
  702. return this.options.specData;
  703. }
  704. getFormFilter() {
  705. var fariyForm = $('form.layui-form');
  706. if (!fariyForm.attr('lay-filter')) {
  707. fariyForm.attr('lay-filter', 'fairy-form-filter');
  708. }
  709. return fariyForm.attr('lay-filter');
  710. }
  711. getFormSkuData() {
  712. var skuData = {};
  713. console.info(this.getFormFilter(), form.val(this.getFormFilter()));
  714. $.each(form.val(this.getFormFilter()), function (key, value) {
  715. if (key.startsWith('skus')) {
  716. skuData[key] = value;
  717. }
  718. });
  719. return skuData;
  720. }
  721. }
  722. exports(MOD_NAME, {
  723. render: function (options) {
  724. return new SkuTable(options);
  725. }
  726. })
  727. });