discount.js 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294
  1. define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function ($, undefined, Backend, Table, Form, Template) {
  2. // 代码更新说明:
  3. // 1. 添加了单规格商品的"折扣库存"列,多规格商品使用按钮方式设置
  4. // 2. 添加了折扣库存输入处理事件,并确保数据被正确保存
  5. // 3. 更新了updateDiscountData函数,确保折扣库存数据被正确提交
  6. var Controller = {
  7. index: function () {
  8. // 初始化表格参数配置
  9. Table.api.init({
  10. extend: {
  11. index_url: 'marketing/discount/index' + location.search,
  12. add_url: 'marketing/discount/add',
  13. edit_url: 'marketing/discount/edit',
  14. del_url: 'marketing/discount/del',
  15. multi_url: 'marketing/discount/multi',
  16. import_url: 'marketing/discount/import',
  17. table: 'shop_activity',
  18. }
  19. });
  20. var table = $("#table");
  21. // 初始化表格
  22. table.bootstrapTable({
  23. url: $.fn.bootstrapTable.defaults.extend.index_url,
  24. pk: 'id',
  25. sortName: 'id',
  26. fixedColumns: true,
  27. fixedRightNumber: 1,
  28. columns: [
  29. [
  30. {checkbox: true},
  31. {field: 'id', title: __('Id'), visible: false},
  32. {
  33. field: 'name',
  34. title: __('活动名称'),
  35. operate: 'LIKE',
  36. align: 'left',
  37. cellStyle: {css: {'max-width': '180px', 'overflow': 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap'}}
  38. },
  39. {
  40. field: 'image',
  41. title: __('活动主图'),
  42. operate: false,
  43. events: Table.api.events.image,
  44. formatter: Table.api.formatter.image,
  45. width: 80
  46. },
  47. {
  48. field: 'type',
  49. title: __('活动类型'),
  50. searchList: {"discount": "折扣活动"},
  51. formatter: Table.api.formatter.normal,
  52. width: 80
  53. },
  54. {
  55. field: 'goods_count',
  56. title: __('商品数量'),
  57. width: 80,
  58. align: 'center'
  59. },
  60. {
  61. field: 'start_time',
  62. title: __('开始时间'),
  63. operate: 'RANGE',
  64. addclass: 'datetimerange',
  65. formatter: Table.api.formatter.datetime,
  66. width: 150
  67. },
  68. {
  69. field: 'end_time',
  70. title: __('结束时间'),
  71. operate: 'RANGE',
  72. addclass: 'datetimerange',
  73. formatter: Table.api.formatter.datetime,
  74. width: 150
  75. },
  76. {
  77. field: 'activity_status',
  78. title: __('活动状态'),
  79. searchList: {
  80. 0: '未开始',
  81. 1: '进行中',
  82. 2: '已结束',
  83. 3: '手动停止'
  84. },
  85. formatter: function(value, row, index) {
  86. var statusMap = {
  87. 0: '<span class="label label-default">未开始</span>',
  88. 1: '<span class="label label-success">进行中</span>',
  89. 2: '<span class="label label-warning">已结束</span>',
  90. 3: '<span class="label label-danger">手动停止</span>'
  91. };
  92. return statusMap[value] || value;
  93. },
  94. width: 80,
  95. align: 'center'
  96. },
  97. {
  98. field: 'order_count',
  99. title: __('订单数'),
  100. width: 80,
  101. align: 'center'
  102. },
  103. {
  104. field: 'order_amount',
  105. title: __('成交金额'),
  106. width: 100,
  107. align: 'right',
  108. formatter: function(value, row, index) {
  109. return '¥' + value;
  110. }
  111. },
  112. {
  113. field: 'discount_amount',
  114. title: __('优惠金额'),
  115. width: 100,
  116. align: 'right',
  117. formatter: function(value, row, index) {
  118. return '¥' + value;
  119. }
  120. },
  121. {
  122. field: 'sale_quantity',
  123. title: __('销售数量'),
  124. width: 80,
  125. align: 'center'
  126. },
  127. {
  128. field: 'customer_count',
  129. title: __('成交人数'),
  130. width: 80,
  131. align: 'center'
  132. },
  133. {
  134. field: 'createtime',
  135. title: __('创建时间'),
  136. operate: 'RANGE',
  137. addclass: 'datetimerange',
  138. formatter: Table.api.formatter.datetime,
  139. visible: false
  140. },
  141. {
  142. field: 'updatetime',
  143. title: __('更新时间'),
  144. operate: 'RANGE',
  145. addclass: 'datetimerange',
  146. formatter: Table.api.formatter.datetime,
  147. visible: false
  148. },
  149. {
  150. field: 'operate',
  151. title: __('操作'),
  152. table: table,
  153. events: Table.api.events.operate,
  154. formatter: Table.api.formatter.operate,
  155. buttons: [
  156. {
  157. name: 'stop',
  158. text: __('停止活动'),
  159. icon: 'fa fa-stop',
  160. classname: 'btn btn-xs btn-warning btn-ajax',
  161. url: 'marketing/discount/stopActivity',
  162. confirm: '确认要停止此活动吗?停止后将无法恢复',
  163. visible: function(row) {
  164. return row.activity_status == 1; // 只有进行中的活动才显示停止按钮
  165. },
  166. success: function(data, ret) {
  167. Layer.msg(ret.msg);
  168. table.bootstrapTable('refresh');
  169. return false;
  170. },
  171. error: function(data, ret) {
  172. Layer.alert(ret.msg);
  173. return false;
  174. }
  175. }
  176. ]
  177. }
  178. ]
  179. ]
  180. });
  181. // 为表格绑定事件
  182. Table.api.bindevent(table);
  183. },
  184. recyclebin: function () {
  185. // 初始化表格参数配置
  186. Table.api.init({
  187. extend: {
  188. 'dragsort_url': ''
  189. }
  190. });
  191. var table = $("#table");
  192. // 初始化表格
  193. table.bootstrapTable({
  194. url: 'marketing/discount/recyclebin' + location.search,
  195. pk: 'id',
  196. sortName: 'id',
  197. columns: [
  198. [
  199. {checkbox: true},
  200. {field: 'id', title: __('Id')},
  201. {field: 'title', title: __('Title'), align: 'left'},
  202. {
  203. field: 'deletetime',
  204. title: __('Deletetime'),
  205. operate: 'RANGE',
  206. addclass: 'datetimerange',
  207. formatter: Table.api.formatter.datetime
  208. },
  209. {
  210. field: 'operate',
  211. width: '140px',
  212. title: __('Operate'),
  213. table: table,
  214. events: Table.api.events.operate,
  215. buttons: [
  216. {
  217. name: 'Restore',
  218. text: __('Restore'),
  219. classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
  220. icon: 'fa fa-rotate-left',
  221. url: 'marketing/discount/restore',
  222. refresh: true
  223. },
  224. {
  225. name: 'Destroy',
  226. text: __('Destroy'),
  227. classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
  228. icon: 'fa fa-times',
  229. url: 'marketing/discount/destroy',
  230. refresh: true
  231. }
  232. ],
  233. formatter: Table.api.formatter.operate
  234. }
  235. ]
  236. ]
  237. });
  238. // 为表格绑定事件
  239. Table.api.bindevent(table);
  240. },
  241. add: function () {
  242. Controller.api.bindevent();
  243. // 表单提交前处理
  244. $('#add-form').on('submit', function() {
  245. // 同时更新goods_info字段,包含商品的详细信息
  246. updateGoodsInfo();
  247. console.log('提交的商品信息:', $('#goods-info').val());
  248. return true;
  249. });
  250. // 选择商品功能
  251. $('#select-goods').on('click', function() {
  252. Fast.api.open('shop/goods/select', '选择商品', {
  253. area: ['95%', '90%'],
  254. callback: function(data) {
  255. if (data && data.length > 0) {
  256. var goodsIds = [];
  257. // 收集商品ID
  258. $.each(data, function(index, item) {
  259. goodsIds.push(item.id);
  260. });
  261. // 默认折扣值
  262. var defaultDiscount = 9;
  263. // 使用模板渲染表格
  264. var html = Template('goodsTableTpl', {
  265. list: data,
  266. discount: defaultDiscount
  267. });
  268. // 清空容器并显示表格
  269. $('#selected-goods-container').empty().append(html);
  270. // 初始化分页表格
  271. $('#selected-goods-table').bootstrapTable({
  272. data: data,
  273. pagination: true,
  274. pageSize: 5,
  275. pageList: [5, 10, 20],
  276. sortable: true,
  277. search: false,
  278. classes: 'table table-hover table-striped',
  279. onPostBody: function() {
  280. // 表格渲染完成后处理行属性
  281. $('#selected-goods-table tbody tr').each(function(index) {
  282. // 确保每行有data-id属性
  283. var rowData = data[index];
  284. if(rowData && rowData.id) {
  285. $(this).attr('data-id', rowData.id);
  286. console.log('设置行data-id属性:', rowData.id);
  287. }
  288. });
  289. },
  290. columns: [
  291. {
  292. field: 'goods',
  293. title: '商品',
  294. width: '30%',
  295. formatter: function(value, row) {
  296. return '<div class="goods-item">' +
  297. '<img src="' + Fast.api.cdnurl(row.image) + '" style="width:60px;height:60px;margin-right:10px;" class="pull-left img-thumbnail">' +
  298. '<div style="margin-left:70px;">' +
  299. '<h5 style="margin-top:0;font-weight:600;">' + row.title + '</h5>' +
  300. '<p class="text-muted">分类:' + (row.category ? row.category.name : '') + ' | 规格:' + (row.spec_type == 0 ? '单规格' : '多规格') + '</p>' +
  301. '</div>' +
  302. '</div>'
  303. }
  304. },
  305. {
  306. field: 'price',
  307. title: '价格',
  308. width: '10%',
  309. align: 'center',
  310. formatter: function(value, row) {
  311. return '<span class="text-danger">¥' + row.price + '</span>';
  312. }
  313. },
  314. {
  315. field: 'stocks',
  316. title: '现库存',
  317. width: '10%',
  318. align: 'center'
  319. },
  320. {
  321. field: 'discount_stocks',
  322. title: '折扣库存',
  323. width: '20%',
  324. align: 'center',
  325. formatter: function(value, row) {
  326. if(row.spec_type == 0) { // 只有单规格商品显示输入框
  327. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  328. console.log('渲染折扣库存输入框:', row.id, row.stocks);
  329. // 确保折扣库存默认等于现库存
  330. var stocks = parseInt(row.stocks);
  331. // 移除max属性,仅通过JS验证限制最大值
  332. return '<input type="number" class="form-control discount-stocks-input" value="' + stocks + '" min="0" data-id="' + row.id + '" data-max-stocks="' + stocks + '" style="width:100%;">';
  333. } else {
  334. return ''; // 多规格商品留空
  335. }
  336. }
  337. },
  338. {
  339. field: 'discount',
  340. title: '折扣',
  341. width: '15%',
  342. align: 'center',
  343. formatter: function(value, row) {
  344. if(row.spec_type == 1) {
  345. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  346. var hasSpec = discountData[row.id] && discountData[row.id].specs;
  347. var specBtnText = hasSpec ? '规格折扣设置 <span class="label label-success">已设置</span>' : '规格折扣设置';
  348. var html = '<div class="text-center">' +
  349. '<a href="javascript:;" class="btn btn-info btn-sm btn-spec-discount" data-id="' + row.id + '">' + specBtnText + '</a>' +
  350. '<div class="text-muted small" style="margin-top:8px;">请点击设置各规格折扣</div>';
  351. // 如果已设置规格折扣,显示汇总信息
  352. if (hasSpec && discountData[row.id].summary) {
  353. var summary = discountData[row.id].summary;
  354. var infoText = '已设置' + summary.participate_count + '个规格,平均' + summary.avg_discount + '折,共' + summary.total_stocks + '件';
  355. html += '<div class="spec-discount-info text-muted" style="margin-top:5px;">' + infoText + '</div>';
  356. }
  357. html += '</div>';
  358. return html;
  359. } else {
  360. return '<div class="input-group">' +
  361. '<input type="number" class="form-control discount-input" value="' + defaultDiscount + '" min="0.1" max="10" step="0.1" data-price="' + row.price + '" data-id="' + row.id + '">' +
  362. '<span class="input-group-addon">折</span>' +
  363. '</div>';
  364. }
  365. }
  366. },
  367. {
  368. field: 'discount_price',
  369. title: '折后价',
  370. width: '20%',
  371. align: 'center',
  372. formatter: function(value, row) {
  373. if(row.spec_type == 0) {
  374. return '<span class="discount-price text-success" id="discount-price-' + row.id + '">¥' + (row.price * defaultDiscount / 10).toFixed(2) + '</span>';
  375. } else {
  376. return '';
  377. }
  378. }
  379. },
  380. {
  381. field: 'operate',
  382. title: '操作',
  383. width: '15%',
  384. align: 'center',
  385. formatter: function(value, row) {
  386. return '<a href="javascript:;" class="btn btn-xs btn-danger remove-goods" data-id="' + row.id + '"><i class="fa fa-trash"></i> 删除</a>';
  387. },
  388. events: {
  389. 'click .remove-goods': function (e, value, row, index) {
  390. // 从表格中移除该行
  391. $('#selected-goods-table').bootstrapTable('remove', {
  392. field: 'id',
  393. values: [row.id]
  394. });
  395. // 更新隐藏字段
  396. var currentIds = $('#goods-ids').val().split(',');
  397. var newIds = [];
  398. $.each(currentIds, function(i, val) {
  399. if (val != row.id && val) {
  400. newIds.push(val);
  401. }
  402. });
  403. $('#goods-ids').val(newIds.join(','));
  404. // 移除折扣数据
  405. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  406. if(discountData[row.id]) {
  407. delete discountData[row.id];
  408. $('#discount-data').val(JSON.stringify(discountData));
  409. }
  410. // 如果表格为空,则移除整个表格
  411. if(newIds.length === 0) {
  412. $('#selected-goods-container').empty();
  413. }
  414. }
  415. }
  416. }
  417. ]
  418. });
  419. // 更新隐藏字段
  420. $('#goods-ids').val(goodsIds.join(','));
  421. // 初始化折扣数据
  422. var discountData = {};
  423. $.each(data, function(index, item) {
  424. if (item.spec_type == 0) { // 单规格商品
  425. // 确保折扣库存默认值为现有库存
  426. var currentStocks = parseInt(item.stocks) || 0;
  427. // 单规格商品需要获取其默认SKU ID
  428. var skuId = item.sku_id || 0; // 如果返回数据中有sku_id就使用,否则用0
  429. discountData[item.id] = {
  430. id: item.id,
  431. sku_id: skuId, // 添加SKU ID
  432. discount: defaultDiscount,
  433. discount_price: (item.price * defaultDiscount / 10).toFixed(2),
  434. stocks: currentStocks // 默认折扣库存等于商品库存
  435. };
  436. console.log('初始化单规格商品:', item.id, 'SKU ID:', skuId, '库存:', currentStocks);
  437. }
  438. });
  439. $('#discount-data').val(JSON.stringify(discountData));
  440. // 同步更新商品详情信息
  441. updateGoodsInfo();
  442. // 等待表格渲染完成后修复折扣库存输入框
  443. setTimeout(function() {
  444. $('.discount-stocks-input').each(function() {
  445. var $input = $(this);
  446. var goodsId = $input.data('id');
  447. var maxStocks = parseInt($input.data('max-stocks')) || 0;
  448. // 从折扣数据中获取正确的库存值
  449. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  450. var correctStocks = discountData[goodsId] && discountData[goodsId].stocks !== undefined ?
  451. discountData[goodsId].stocks : maxStocks;
  452. // 直接设置正确的库存值
  453. $input.val(correctStocks);
  454. console.log('修复折扣库存输入框值:', goodsId, '设置为:', correctStocks);
  455. });
  456. }, 500);
  457. // 绑定折扣库存输入事件
  458. $(document).on('input', '.discount-stocks-input', function() {
  459. var stocks = parseInt($(this).val()) || 0;
  460. var goodsId = $(this).data('id');
  461. var maxStocks = parseInt($(this).data('max-stocks')) || 0;
  462. console.log('折扣库存输入事件:', goodsId, '输入值:', stocks, '最大值:', maxStocks);
  463. // 限制输入范围,确保不超过现库存
  464. if(stocks < 0) stocks = 0;
  465. if(stocks > maxStocks) stocks = maxStocks;
  466. $(this).val(stocks);
  467. // 更新商品折扣数据
  468. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  469. if(discountData[goodsId]) {
  470. discountData[goodsId].stocks = stocks;
  471. } else {
  472. // 新建数据时需要包含SKU ID
  473. discountData[goodsId] = {
  474. id: goodsId,
  475. sku_id: 0, // 单规格商品默认SKU ID为0,实际应从商品数据获取
  476. stocks: stocks
  477. };
  478. }
  479. $('#discount-data').val(JSON.stringify(discountData));
  480. });
  481. // 绑定折扣输入事件
  482. $(document).on('input', '.discount-input', function() {
  483. var discount = parseFloat($(this).val()) || 0;
  484. if(discount < 0.1) discount = 0.1;
  485. if(discount > 10) discount = 10;
  486. var price = parseFloat($(this).data('price')) || 0;
  487. var goodsId = $(this).data('id');
  488. var discountPrice = (price * discount / 10).toFixed(2);
  489. $('#discount-price-' + goodsId).text('¥' + discountPrice);
  490. // 更新商品折扣数据
  491. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  492. // 保留折扣库存值和SKU ID,如果已存在
  493. var stocks = discountData[goodsId] && discountData[goodsId].stocks ? discountData[goodsId].stocks : null;
  494. var skuId = discountData[goodsId] && discountData[goodsId].sku_id ? discountData[goodsId].sku_id : 0;
  495. var goodsDiscount = {
  496. id: goodsId,
  497. sku_id: skuId, // 保留SKU ID
  498. discount: discount,
  499. discount_price: discountPrice
  500. };
  501. // 如果已有设置折扣库存,保留该值
  502. if (stocks !== null) {
  503. goodsDiscount.stocks = stocks;
  504. }
  505. // 存储折扣数据
  506. discountData[goodsId] = goodsDiscount;
  507. $('#discount-data').val(JSON.stringify(discountData));
  508. // 更新商品详情信息
  509. updateGoodsInfo();
  510. });
  511. // 处理规格设置按钮点击事件
  512. $(document).on('click', '.btn-spec-discount', function() {
  513. var goodsId = $(this).data('id');
  514. var $row = $(this).closest('tr');
  515. var hasDiscountSetting = $row.attr('data-has-discount-setting');
  516. var specData = '';
  517. console.log('点击规格折扣设置按钮:', goodsId, '当前设置状态:', hasDiscountSetting);
  518. // 如果已经有设置,则传递已有的数据
  519. if (hasDiscountSetting && $row.attr('data-discount-data')) {
  520. specData = encodeURIComponent($row.attr('data-discount-data'));
  521. console.log('已有折扣数据:', $row.attr('data-discount-data'));
  522. }
  523. Fast.api.open('marketing/discount/spec_discount?goods_id=' + goodsId + '&spec_data=' + specData, '规格折扣设置', {
  524. area: ['90%', '90%'],
  525. callback: function(data) {
  526. if (data && data.goodsId && data.specs) {
  527. console.log('规格折扣设置返回数据:', data);
  528. // 保存数据到行属性
  529. $row.attr('data-has-discount-setting', 'true');
  530. $row.attr('data-discount-data', JSON.stringify(data));
  531. // 调试确认设置成功
  532. console.log('设置后的data-discount-data属性:', $row.attr('data-discount-data'));
  533. // 更新按钮文字
  534. $row.find('.btn-spec-discount').html('规格折扣设置 <span class="label label-success">已设置</span>');
  535. // 更新状态和折扣显示
  536. $row.find('.discount-status').text('已设置');
  537. // 计算平均折扣和参与规格数量
  538. var totalDiscount = 0;
  539. var count = 0;
  540. var totalStocks = 0;
  541. $.each(data.specs, function(id, item) {
  542. if (item.participate) {
  543. totalDiscount += parseFloat(item.discount);
  544. totalStocks += parseInt(item.stocks);
  545. count++;
  546. }
  547. });
  548. var avgDiscount = count > 0 ? (totalDiscount / count).toFixed(1) : '-';
  549. // 添加汇总信息
  550. var summary = {
  551. participate_count: count,
  552. avg_discount: avgDiscount,
  553. total_stocks: totalStocks
  554. };
  555. // 更新显示
  556. var infoText = '已设置' + summary.participate_count + '个规格,平均' + summary.avg_discount + '折,共' + summary.total_stocks + '件';
  557. // 如果已有汇总信息区域则更新,否则创建
  558. var $infoArea = $row.find('.spec-discount-info');
  559. if ($infoArea.length > 0) {
  560. $infoArea.text(infoText);
  561. } else {
  562. $row.find('.btn-spec-discount').parent().append('<div class="spec-discount-info text-muted" style="margin-top:5px;">' + infoText + '</div>');
  563. }
  564. // 将汇总信息添加到数据中
  565. data.summary = summary;
  566. // 将折扣数据保存到隐藏字段
  567. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  568. discountData[goodsId] = data;
  569. $('#discount-data').val(JSON.stringify(discountData));
  570. // 更新商品详情信息
  571. updateGoodsInfo();
  572. console.log('多规格商品设置后的折扣数据:', $('#discount-data').val());
  573. }
  574. }
  575. });
  576. });
  577. }
  578. }
  579. });
  580. });
  581. },
  582. edit: function () {
  583. Controller.api.bindevent();
  584. // 表单提交前处理
  585. $('#add-form').on('submit', function() {
  586. // 同时更新goods_info字段,包含商品的详细信息
  587. updateGoodsInfo();
  588. console.log('提交的商品信息:', $('#goods-info').val());
  589. return true;
  590. });
  591. // 初始化已选择的商品
  592. var goodsIds = $('#goods-ids').val();
  593. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  594. if(goodsIds) {
  595. // 加载已选择的商品数据
  596. Fast.api.ajax({
  597. url: 'shop/goods/get_goods_by_ids',
  598. data: {ids: goodsIds}
  599. }, function(data) {
  600. if(data && data.length > 0) {
  601. // 使用模板渲染表格
  602. var html = Template('goodsTableTpl', {
  603. list: data
  604. });
  605. // 清空容器并显示表格
  606. $('#selected-goods-container').empty().append(html);
  607. // 初始化分页表格
  608. $('#selected-goods-table').bootstrapTable({
  609. data: data,
  610. pagination: true,
  611. pageSize: 5,
  612. pageList: [5, 10, 20],
  613. sortable: true,
  614. search: false,
  615. classes: 'table table-hover table-striped',
  616. columns: [
  617. {
  618. field: 'goods',
  619. title: '商品',
  620. width: '30%',
  621. formatter: function(value, row) {
  622. return '<div class="goods-item">' +
  623. '<img src="' + Fast.api.cdnurl(row.image) + '" style="width:60px;height:60px;margin-right:10px;" class="pull-left img-thumbnail">' +
  624. '<div style="margin-left:70px;">' +
  625. '<h5 style="margin-top:0;font-weight:600;">' + row.title + '</h5>' +
  626. '<p class="text-muted">分类:' + (row.category ? row.category.name : '') + ' | 规格:' + (row.spec_type == 0 ? '单规格' : '多规格') + '</p>' +
  627. '</div>' +
  628. '</div>'
  629. }
  630. },
  631. {
  632. field: 'price',
  633. title: '价格',
  634. width: '10%',
  635. align: 'center',
  636. formatter: function(value, row) {
  637. return '<span class="text-danger">¥' + row.price + '</span>';
  638. }
  639. },
  640. {
  641. field: 'stocks',
  642. title: '现库存',
  643. width: '10%',
  644. align: 'center'
  645. },
  646. {
  647. field: 'discount_stocks',
  648. title: '折扣库存',
  649. width: '10%',
  650. align: 'center',
  651. formatter: function(value, row) {
  652. if(row.spec_type == 0) { // 只有单规格商品显示输入框
  653. // 从折扣数据中获取库存值,如果没有则使用商品库存
  654. var currentStocks = discountData[row.id] && discountData[row.id].stocks !== undefined ?
  655. discountData[row.id].stocks : parseInt(row.stocks);
  656. return '<input type="number" class="form-control discount-stocks-input" value="' + currentStocks + '" min="0" data-id="' + row.id + '" data-max-stocks="' + parseInt(row.stocks) + '" style="width:100%;">';
  657. } else {
  658. return ''; // 多规格商品留空
  659. }
  660. }
  661. },
  662. {
  663. field: 'discount',
  664. title: '折扣',
  665. width: '15%',
  666. align: 'center',
  667. formatter: function(value, row) {
  668. if(row.spec_type == 1) {
  669. var hasSpec = discountData[row.id] && discountData[row.id].specs;
  670. var specBtnText = hasSpec ? '规格折扣设置 <span class="label label-success">已设置</span>' : '规格折扣设置';
  671. var html = '<div class="text-center">' +
  672. '<a href="javascript:;" class="btn btn-info btn-sm btn-spec-discount" data-id="' + row.id + '">' + specBtnText + '</a>' +
  673. '<div class="text-muted small" style="margin-top:8px;">请点击设置各规格折扣</div>';
  674. // 如果已设置规格折扣,显示汇总信息
  675. if (hasSpec && discountData[row.id].summary) {
  676. var summary = discountData[row.id].summary;
  677. var infoText = '已设置' + summary.participate_count + '个规格,平均' + summary.avg_discount + '折,共' + summary.total_stocks + '件';
  678. html += '<div class="spec-discount-info text-muted" style="margin-top:5px;">' + infoText + '</div>';
  679. }
  680. html += '</div>';
  681. return html;
  682. } else {
  683. var discount = (discountData[row.id] && discountData[row.id].discount) ? discountData[row.id].discount : 9;
  684. return '<div class="input-group">' +
  685. '<input type="number" class="form-control discount-input" value="' + discount + '" min="0.1" max="10" step="0.1" data-price="' + row.price + '" data-id="' + row.id + '">' +
  686. '<span class="input-group-addon">折</span>' +
  687. '</div>';
  688. }
  689. }
  690. },
  691. {
  692. field: 'discount_price',
  693. title: '折后价',
  694. width: '20%',
  695. align: 'center',
  696. formatter: function(value, row) {
  697. if(row.spec_type == 0) {
  698. var discount = (discountData[row.id] && discountData[row.id].discount) ? discountData[row.id].discount : 9;
  699. var discountPrice = (row.price * discount / 10).toFixed(2);
  700. return '<span class="discount-price text-success" id="discount-price-' + row.id + '">¥' + discountPrice + '</span>';
  701. } else {
  702. return '';
  703. }
  704. }
  705. },
  706. {
  707. field: 'operate',
  708. title: '操作',
  709. width: '15%',
  710. align: 'center',
  711. formatter: function(value, row) {
  712. return '<a href="javascript:;" class="btn btn-xs btn-danger remove-goods" data-id="' + row.id + '"><i class="fa fa-trash"></i> 删除</a>';
  713. },
  714. events: {
  715. 'click .remove-goods': function (e, value, row, index) {
  716. // 从表格中移除该行
  717. $('#selected-goods-table').bootstrapTable('remove', {
  718. field: 'id',
  719. values: [row.id]
  720. });
  721. // 更新隐藏字段
  722. var currentIds = $('#goods-ids').val().split(',');
  723. var newIds = [];
  724. $.each(currentIds, function(i, val) {
  725. if (val != row.id && val) {
  726. newIds.push(val);
  727. }
  728. });
  729. $('#goods-ids').val(newIds.join(','));
  730. // 移除折扣数据
  731. var discountData = $('#discount-data').val() ? JSON.parse($('#discount-data').val()) : {};
  732. if(discountData[row.id]) {
  733. delete discountData[row.id];
  734. $('#discount-data').val(JSON.stringify(discountData));
  735. }
  736. // 如果表格为空,则移除整个表格
  737. if(newIds.length === 0) {
  738. $('#selected-goods-container').empty();
  739. }
  740. }
  741. }
  742. }
  743. ]
  744. });
  745. }
  746. return false;
  747. });
  748. }
  749. // 选择商品功能和删除商品功能,与add方法相同
  750. Controller.add();
  751. },
  752. // 规格折扣设置页面
  753. spec_discount: function () {
  754. // 获取URL参数中的goods_id和spec_data
  755. var goodsId = Fast.api.query('goods_id');
  756. var specData = Fast.api.query('spec_data');
  757. // 设置批量折扣默认值,但不自动勾选
  758. $('#batch-participate').prop('checked', false);
  759. $('#batch-discount').val(9);
  760. // 更新批量价格信息
  761. Controller.api.updateBatchPriceInfo();
  762. // 如果有规格数据,则进行回显
  763. if (specData) {
  764. try {
  765. var data = JSON.parse(decodeURIComponent(specData));
  766. if (data && data.specs) {
  767. $.each(data.specs, function(id, item) {
  768. var $row = $('tr[data-id="' + id + '"]');
  769. if ($row.length > 0) {
  770. // 设置参与折扣
  771. if (item.participate) {
  772. // 勾选并确保输入框可编辑
  773. $row.find('.participate-checkbox').prop('checked', true);
  774. $row.find('.sku-stocks').prop('disabled', false);
  775. $row.find('.sku-discount').prop('disabled', false);
  776. // 设置折扣库存
  777. if (item.stocks !== undefined) {
  778. var maxStocks = parseInt($row.data('stocks')) || 0;
  779. var stocks = parseInt(item.stocks) || 0;
  780. // 确保回显的库存不超过现有库存
  781. if (stocks > maxStocks) {
  782. stocks = maxStocks;
  783. }
  784. $row.find('.sku-stocks').val(stocks);
  785. }
  786. // 设置折扣
  787. if (item.discount !== undefined) {
  788. $row.find('.sku-discount').val(item.discount);
  789. // 更新折后价显示
  790. var price = parseFloat($row.data('price')) || 0;
  791. var discount = parseFloat(item.discount) || 0;
  792. var discountPrice = (price * discount / 10).toFixed(2);
  793. $row.find('.discount-price').text('¥' + discountPrice);
  794. }
  795. } else {
  796. // 如果之前设置了不参与,则取消勾选
  797. $row.find('.participate-checkbox').prop('checked', false);
  798. $row.find('.sku-stocks').prop('disabled', true);
  799. $row.find('.sku-discount').prop('disabled', true);
  800. }
  801. }
  802. });
  803. // 检查已选规格数量,更新批量选择框状态
  804. var checkedCount = $('.participate-checkbox:checked').length;
  805. var totalCount = $('.participate-checkbox').length;
  806. if (checkedCount > 0 && checkedCount === totalCount) {
  807. // 如果所有规格都已选中,勾选批量选择框
  808. $('#batch-participate').prop('checked', true);
  809. } else if (checkedCount > 0) {
  810. // 部分选中,不勾选批量框但显示提示
  811. $('#batch-participate').prop('checked', false);
  812. } else {
  813. // 没有任何选中
  814. $('#batch-participate').prop('checked', false);
  815. }
  816. // 更新批量价格信息
  817. Controller.api.updateBatchPriceInfo();
  818. }
  819. } catch (e) {
  820. console.error('解析规格数据失败', e);
  821. }
  822. }
  823. // 参与折扣复选框事件,但勾选后可以直接编辑
  824. $('.participate-checkbox').off('change').on('change', function() {
  825. var checked = $(this).prop('checked');
  826. var $row = $(this).closest('tr');
  827. // 启用/禁用库存和折扣输入框
  828. var $stocksInput = $row.find('.sku-stocks');
  829. var $discountInput = $row.find('.sku-discount');
  830. $stocksInput.prop('disabled', !checked);
  831. $discountInput.prop('disabled', !checked);
  832. // 如果勾选,设置默认值
  833. if (checked) {
  834. // 获取库存默认值
  835. var stocks = $row.data('stocks') || 0;
  836. if (!$stocksInput.val()) {
  837. $stocksInput.val(stocks);
  838. } else if (parseInt($stocksInput.val()) > stocks) {
  839. // 如果当前值超过了最大库存,则调整为最大库存
  840. $stocksInput.val(stocks);
  841. }
  842. // 设置折扣默认值,如果未设置
  843. if (!$discountInput.val()) {
  844. $discountInput.val(9);
  845. }
  846. // 触发折扣输入框的input事件以更新折扣价格
  847. $discountInput.trigger('input');
  848. // 聚焦到折扣输入框方便用户直接修改
  849. setTimeout(function() {
  850. $discountInput.focus();
  851. }, 100);
  852. }
  853. });
  854. // 批量参与复选框事件
  855. $('#batch-participate').on('change', function() {
  856. var checked = $(this).prop('checked');
  857. // 同步所有规格的选中状态
  858. $('.participate-checkbox').prop('checked', checked).trigger('change');
  859. // 如果勾选了批量参与,则聚焦到批量折扣输入框
  860. if (checked) {
  861. setTimeout(function() {
  862. $('#batch-discount').focus();
  863. }, 100);
  864. }
  865. });
  866. // 批量折扣变化事件
  867. $('#batch-discount').on('input', function() {
  868. var discount = parseFloat($(this).val()) || 0;
  869. if (discount < 0.1) discount = 0.1;
  870. if (discount > 10) discount = 10;
  871. $(this).val(discount);
  872. // 更新批量价格信息
  873. Controller.api.updateBatchPriceInfo();
  874. });
  875. // 初始化批量价格信息
  876. Controller.api.updateBatchPriceInfo();
  877. // 应用批量设置按钮事件
  878. $('#apply-batch').on('click', function() {
  879. var discount = parseFloat($('#batch-discount').val()) || 9;
  880. // 如果没有选中规格,先勾选所有规格
  881. if ($('.participate-checkbox:checked').length === 0) {
  882. Layer.msg('请先勾选需要参与折扣的规格');
  883. return;
  884. }
  885. // 应用到所有已勾选的行
  886. $('.participate-checkbox:checked').each(function() {
  887. var $row = $(this).closest('tr');
  888. $row.find('.sku-discount').val(discount).trigger('input');
  889. });
  890. Layer.msg('批量设置折扣已应用');
  891. });
  892. // 折扣输入事件
  893. $('.sku-discount').off('input').on('input', function() {
  894. var discount = parseFloat($(this).val()) || 0;
  895. if(discount < 0.1) discount = 0.1;
  896. if(discount > 10) discount = 10;
  897. $(this).val(discount);
  898. var $row = $(this).closest('tr');
  899. var price = parseFloat($row.data('price')) || 0;
  900. var discountPrice = (price * discount / 10).toFixed(2);
  901. $row.find('.discount-price').text('¥' + discountPrice);
  902. });
  903. // 库存输入验证
  904. $('.sku-stocks').on('input', function() {
  905. var stocks = parseInt($(this).val()) || 0;
  906. var $row = $(this).closest('tr');
  907. var maxStocks = parseInt($row.data('stocks')) || 0;
  908. // 限制库存范围,不能超过现有库存
  909. if (stocks < 0) stocks = 0;
  910. if (stocks > maxStocks) stocks = maxStocks;
  911. $(this).val(stocks);
  912. });
  913. // 确认按钮事件
  914. $('.btn-confirm').on('click', function() {
  915. var specs = {};
  916. var hasParticipate = false;
  917. // 收集所有参与的规格数据
  918. $('.participate-checkbox:checked').each(function() {
  919. var $row = $(this).closest('tr');
  920. var id = $row.data('id');
  921. var price = parseFloat($row.data('price')) || 0;
  922. var discount = parseFloat($row.find('.sku-discount').val()) || 9;
  923. var stocks = parseInt($row.find('.sku-stocks').val()) || 0;
  924. var discountPrice = (price * discount / 10).toFixed(2);
  925. specs[id] = {
  926. id: id,
  927. participate: true,
  928. discount: discount,
  929. price: price,
  930. stocks: stocks,
  931. discount_price: discountPrice
  932. };
  933. hasParticipate = true;
  934. console.log('添加参与规格:', id, '折扣:', discount, '库存:', stocks);
  935. });
  936. // 验证是否有选择参与的规格
  937. if (!hasParticipate) {
  938. Layer.alert('请至少选择一个参与折扣的规格', {icon: 2});
  939. return;
  940. }
  941. // 计算平均折扣和参与规格数量
  942. var totalDiscount = 0;
  943. var count = 0;
  944. var totalStocks = 0;
  945. $.each(specs, function(id, item) {
  946. if (item.participate) {
  947. totalDiscount += parseFloat(item.discount);
  948. totalStocks += parseInt(item.stocks);
  949. count++;
  950. }
  951. });
  952. var avgDiscount = count > 0 ? (totalDiscount / count).toFixed(1) : '-';
  953. // 添加汇总信息
  954. var summary = {
  955. participate_count: count,
  956. avg_discount: avgDiscount,
  957. total_stocks: totalStocks
  958. };
  959. var returnData = {
  960. goodsId: goodsId,
  961. specs: specs,
  962. summary: summary
  963. };
  964. console.log('多规格确认返回数据:', returnData);
  965. // 返回数据到add.html页面
  966. Fast.api.close(returnData);
  967. });
  968. },
  969. api: {
  970. bindevent: function () {
  971. Form.api.bindevent($("form[role=form]"));
  972. // 处理活动类型选择
  973. $('select[name="row[type]"]').on('change', function() {
  974. var type = $(this).val();
  975. // 根据类型显示或隐藏内部类型选择
  976. if (type == 'discount') {
  977. // 折扣活动隐藏内部类型选择,默认为0
  978. $('#inner-type-group').hide();
  979. $('#c-inner-type').val(0);
  980. // 折扣活动隐藏商品参与方式,强制为指定商品
  981. $('.goods-join-type-group').hide();
  982. $('input[name="row[goods_join_type]"][value="2"]').prop('checked', true);
  983. } else {
  984. // 其他活动类型显示相关选项
  985. $('#inner-type-group').show();
  986. $('.goods-join-type-group').show();
  987. }
  988. }).trigger('change'); // 页面加载时立即触发一次
  989. // 活动名称字数限制处理
  990. var nameInput = $("#c-name");
  991. var countElement = $("#name-count");
  992. // 初始化显示
  993. countElement.text(nameInput.val().length + "/20");
  994. // 监听输入事件
  995. nameInput.on("input propertychange", function() {
  996. var text = $(this).val();
  997. var len = text.length;
  998. // 更新显示的字数
  999. countElement.text(len + "/20");
  1000. // 限制最多输入20个字符
  1001. if (len > 20) {
  1002. $(this).val(text.substring(0, 20));
  1003. countElement.text("20/20");
  1004. }
  1005. });
  1006. // 限购选项处理
  1007. $('input[name="row[purchase_limit_type]"]').on('change', function() {
  1008. var type = $(this).val();
  1009. // 禁用所有输入框
  1010. $('input[name="row[purchase_limit_total]"]').prop('disabled', true);
  1011. $('input[name="row[purchase_limit_daily]"]').prop('disabled', true);
  1012. // 根据选择启用对应输入框
  1013. if(type == '1') {
  1014. $('input[name="row[purchase_limit_total]"]').prop('disabled', false).focus();
  1015. } else if(type == '2') {
  1016. $('input[name="row[purchase_limit_daily]"]').prop('disabled', false).focus();
  1017. }
  1018. });
  1019. },
  1020. // 更新批量价格信息
  1021. updateBatchPriceInfo: function() {
  1022. var discount = parseFloat($('#batch-discount').val()) || 0;
  1023. var totalPrice = 0;
  1024. var totalCount = 0;
  1025. $('tr[data-price]').each(function() {
  1026. var price = parseFloat($(this).data('price')) || 0;
  1027. totalPrice += price;
  1028. totalCount++;
  1029. });
  1030. var avgPrice = totalCount > 0 ? totalPrice / totalCount : 0;
  1031. var discountPrice = (avgPrice * discount / 10).toFixed(2);
  1032. $('.batch-price-info').text('平均折后价:¥' + discountPrice);
  1033. }
  1034. }
  1035. };
  1036. // 更新商品详细信息到goods_info隐藏域
  1037. function updateGoodsInfo() {
  1038. // 获取表格中的所有商品数据
  1039. var goodsInfo = [];
  1040. var table = $('#selected-goods-table').bootstrapTable('getData');
  1041. var discountData = {};
  1042. // 处理单规格商品的折扣数据
  1043. $('.discount-input').each(function() {
  1044. var $input = $(this);
  1045. var goodsId = $input.data('id');
  1046. var discount = parseFloat($input.val()) || 0;
  1047. var price = parseFloat($input.data('price')) || 0;
  1048. var discountPrice = (price * discount / 10).toFixed(2);
  1049. var $tr = $input.closest('tr');
  1050. var $stocksInput = $tr.find('.discount-stocks-input');
  1051. var stocks = parseInt($stocksInput.val()) || 0;
  1052. discountData[goodsId] = {
  1053. id: goodsId,
  1054. discount: discount,
  1055. discount_price: discountPrice,
  1056. stocks: stocks
  1057. };
  1058. });
  1059. // 处理多规格商品的折扣数据
  1060. $('#selected-goods-table tbody tr').each(function() {
  1061. var $row = $(this);
  1062. var goodsId = $row.data('id');
  1063. var hasDiscountSetting = $row.attr('data-has-discount-setting');
  1064. var discountDataStr = $row.attr('data-discount-data');
  1065. console.log('检查多规格商品:', goodsId, '设置状态:', hasDiscountSetting, '数据:', discountDataStr);
  1066. if (goodsId && hasDiscountSetting === 'true' && discountDataStr) {
  1067. try {
  1068. // 如果是多规格商品且已设置规格折扣
  1069. var rowDiscountData = JSON.parse(discountDataStr);
  1070. discountData[goodsId] = rowDiscountData;
  1071. console.log('解析多规格数据成功:', goodsId, rowDiscountData);
  1072. } catch (e) {
  1073. console.error('解析多规格折扣数据失败:', e);
  1074. }
  1075. }
  1076. });
  1077. // 调试所有收集的折扣数据
  1078. console.log('所有折扣数据:', discountData);
  1079. // 更新折扣数据隐藏域
  1080. $('#discount-data').val(JSON.stringify(discountData));
  1081. if(table && table.length > 0) {
  1082. $.each(table, function(index, item) {
  1083. // 基础商品信息
  1084. var goodsData = {
  1085. goods_id: item.id,
  1086. title: item.title,
  1087. stock: item.stocks || 0,
  1088. spec_type: item.spec_type
  1089. };
  1090. // 添加折扣信息
  1091. if(discountData[item.id]) {
  1092. if(item.spec_type == 0) {
  1093. // 单规格商品只保留折扣相关字段
  1094. goodsData.sku_id = discountData[item.id].sku_id || 0; // 添加SKU ID
  1095. goodsData.discount = discountData[item.id].discount;
  1096. goodsData.discount_price = discountData[item.id].discount_price;
  1097. goodsData.discount_stocks = discountData[item.id].stocks;
  1098. } else if(item.spec_type == 1) {
  1099. // 多规格商品 - 检查是否有规格折扣设置
  1100. console.log('处理多规格商品:', item.id, discountData[item.id]);
  1101. var spec = [];
  1102. // 确保specs存在
  1103. if(discountData[item.id] && discountData[item.id].specs) {
  1104. var specData = discountData[item.id].specs;
  1105. // 提取已选中参与折扣的规格
  1106. $.each(specData, function(specId, specItem) {
  1107. console.log('规格项:', specId, specItem);
  1108. if(specItem && specItem.participate) {
  1109. spec.push({
  1110. sku_id: specId,
  1111. discount: specItem.discount,
  1112. discount_price: specItem.discount_price,
  1113. discount_stocks: specItem.stocks
  1114. });
  1115. }
  1116. });
  1117. console.log('最终生成的spec数组:', spec);
  1118. } else {
  1119. console.log('没有找到多规格商品的specs数据');
  1120. }
  1121. // 即使为空也保存spec数组
  1122. goodsData.spec = spec;
  1123. }
  1124. }
  1125. goodsInfo.push(goodsData);
  1126. });
  1127. }
  1128. // 调试最终结果
  1129. console.log('最终goods_info:', goodsInfo);
  1130. // 更新隐藏域
  1131. $('#goods-info').val(JSON.stringify(goodsInfo));
  1132. return goodsInfo;
  1133. }
  1134. return Controller;
  1135. });