skuTable.js 34 KB

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