order.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
  2. if (Config.shop && Config.shop.printtype == 'clodop') {
  3. require([Config.shop.clodop_ip_port + '/CLodopfuncs.js']);
  4. }
  5. var Controller = {
  6. // OrderEnum 辅助方法
  7. orderEnum: {
  8. // 获取订单状态常量
  9. getStatusConstants: function() {
  10. return Controller.api.parseConfigJson('orderStatusConstants') || {};
  11. },
  12. // 获取显示类型状态映射
  13. getShowTypeStatusMap: function() {
  14. return Controller.api.parseConfigJson('showTypeStatusMap') || {};
  15. },
  16. // 根据状态码获取状态文本
  17. getStatusText: function(statusCode) {
  18. var statusList = Controller.api.parseConfigJson('orderStatusList') || {};
  19. return statusList[statusCode] || '未知状态';
  20. },
  21. // 根据显示类型获取对应的状态列表
  22. getStatusByShowType: function(showType) {
  23. var statusMap = this.getShowTypeStatusMap();
  24. return statusMap[showType] || [];
  25. },
  26. // 检查订单状态是否属于某个显示类型
  27. isStatusInShowType: function(statusCode, showType) {
  28. var statusList = this.getStatusByShowType(showType);
  29. return statusList.indexOf(parseInt(statusCode)) !== -1;
  30. }
  31. },
  32. index: function () {
  33. // 初始化表格参数配置
  34. Table.api.init({
  35. extend: {
  36. index_url: 'shop/order/index' + location.search,
  37. // add_url: 'shop/order/add',
  38. // edit_url: 'shop/order/edit',
  39. del_url: 'shop/order/del',
  40. multi_url: 'shop/order/multi',
  41. import_url: 'shop/order/import',
  42. table: 'shop_order',
  43. }
  44. });
  45. var table = $("#table");
  46. //当双击单元格时
  47. table.on('dbl-click-row.bs.table', function (e, row, element, field) {
  48. $(".btn-detail", element).trigger("click");
  49. });
  50. // 初始化表格
  51. table.bootstrapTable({
  52. url: $.fn.bootstrapTable.defaults.extend.index_url,
  53. pk: 'id',
  54. sortName: 'id',
  55. fixedColumns: true,
  56. fixedRightNumber: 1,
  57. columns: [
  58. [{
  59. checkbox: true
  60. },
  61. {
  62. field: 'id',
  63. title: __('Id')
  64. },
  65. {
  66. field: 'order_sn',
  67. title: __('Order_sn'),
  68. operate: 'LIKE'
  69. },
  70. {
  71. field: 'user_id',
  72. title: __('User_id'),
  73. operate: 'LIKE',
  74. visible: false, // 默认不显示,但可以用于搜索
  75. },
  76. {
  77. field: 'user.username',
  78. title: __('User'),
  79. operate: 'LIKE',
  80. formatter: function (value, row, index) {
  81. // 显示用户头像和用户名
  82. var avatar = row.user && row.user.avatar ? row.user.avatar : '/assets/img/avatar.png';
  83. var username = row.user && row.user.username ? row.user.username : '游客';
  84. var userId = row.user_id || '';
  85. // 处理头像URL
  86. var avatarUrl = avatar;
  87. if (avatar && !avatar.startsWith('http') && !avatar.startsWith('//')) {
  88. avatarUrl = Fast.api.cdnurl ? Fast.api.cdnurl(avatar) : avatar;
  89. }
  90. return '<div style="display:flex;align-items:center;">' +
  91. '<img src="' + avatarUrl + '" style="width:40px;height:40px;border-radius:50%;margin-right:10px;" />' +
  92. '<div>' +
  93. '<div style="color:#337ab7;font-weight:bold;">' + username + '</div>' +
  94. '<div style="color:#999;font-size:12px;">ID: ' + userId + '</div>' +
  95. '</div>' +
  96. '</div>';
  97. }
  98. },
  99. {
  100. field: 'order_address.consignee',
  101. title: __('Consignee'),
  102. operate: 'LIKE',
  103. formatter: function (value, row, index) {
  104. return row.order_address ? row.order_address.consignee : '';
  105. }
  106. },
  107. {
  108. field: 'order_address.mobile',
  109. title: __('Mobile'),
  110. operate: 'LIKE',
  111. formatter: function (value, row, index) {
  112. return row.order_address ? row.order_address.mobile : '';
  113. }
  114. },
  115. {
  116. field: 'order_address.address',
  117. title: __('Address'),
  118. operate: 'LIKE',
  119. visible: false, // 可以用于搜索但默认不显示
  120. formatter: function (value, row, index) {
  121. if (!row.order_address) return '';
  122. var address = '';
  123. if (row.order_address.province_name) address += row.order_address.province_name;
  124. if (row.order_address.city_name) address += row.order_address.city_name;
  125. if (row.order_address.district_name) address += row.order_address.district_name;
  126. if (row.order_address.address) address += row.order_address.address;
  127. return address;
  128. }
  129. },
  130. {
  131. field: 'amount',
  132. title: __('Amount'),
  133. operate: 'BETWEEN'
  134. },
  135. {
  136. field: 'goods_price',
  137. title: __('Goods_price'),
  138. operate: 'BETWEEN'
  139. },
  140. {
  141. field: 'discount_fee',
  142. title: __('Discount_fee'),
  143. operate: 'BETWEEN'
  144. },
  145. {
  146. field: 'express_fee',
  147. title: __('Express_fee'),
  148. operate: 'BETWEEN'
  149. },
  150. {
  151. field: 'order_amount',
  152. title: __('Order_amount'),
  153. operate: 'BETWEEN'
  154. },
  155. {
  156. field: 'pay_amount',
  157. title: __('Pay_amount'),
  158. operate: 'BETWEEN'
  159. },
  160. {
  161. field: 'pay_type',
  162. title: __('Pay_type'),
  163. searchList: Controller.api.parseConfigJson('payTypeList'),
  164. operate: 'LIKE',
  165. formatter: function (value, row) {
  166. return row.pay_type_text || value;
  167. }
  168. },
  169. {
  170. field: 'pay_mode',
  171. title: __('Pay_mode'),
  172. searchList: Controller.api.parseConfigJson('payModeList'),
  173. operate: 'LIKE',
  174. formatter: function (value, row) {
  175. return row.pay_mode_text || value;
  176. }
  177. },
  178. {
  179. field: 'express_name',
  180. title: __('Express_name'),
  181. operate: 'LIKE'
  182. },
  183. {
  184. field: 'express_no',
  185. title: __('Express_no'),
  186. operate: 'LIKE'
  187. },
  188. {
  189. field: 'order_status',
  190. title: __('Order_status'),
  191. searchList: Controller.api.parseConfigJson('orderStatusList'),
  192. formatter: function (value, row) {
  193. return row.order_status_text || value;
  194. }
  195. },
  196. {
  197. field: 'memo',
  198. title: __('Memo'),
  199. operate: 'LIKE'
  200. },
  201. {
  202. field: 'status',
  203. title: __('Status'),
  204. searchList: Controller.api.parseConfigJson('statusList'),
  205. formatter: Table.api.formatter.status
  206. },
  207. {
  208. field: 'createtime',
  209. title: __('Createtime'),
  210. operate: 'RANGE',
  211. addclass: 'datetimerange',
  212. autocomplete: false,
  213. formatter: Table.api.formatter.datetime
  214. },
  215. {
  216. field: 'updatetime',
  217. title: __('Updatetime'),
  218. operate: 'RANGE',
  219. addclass: 'datetimerange',
  220. autocomplete: false,
  221. formatter: Table.api.formatter.datetime
  222. },
  223. {
  224. field: 'expire_time',
  225. title: __('Expire_time'),
  226. operate: 'RANGE',
  227. addclass: 'datetimerange',
  228. autocomplete: false,
  229. formatter: Table.api.formatter.datetime
  230. },
  231. {
  232. field: 'pay_time',
  233. title: __('Pay_time'),
  234. operate: 'RANGE',
  235. addclass: 'datetimerange',
  236. autocomplete: false,
  237. formatter: Table.api.formatter.datetime
  238. },
  239. {
  240. field: 'refund_time',
  241. title: __('Refund_time'),
  242. operate: 'RANGE',
  243. addclass: 'datetimerange',
  244. autocomplete: false,
  245. formatter: Table.api.formatter.datetime
  246. },
  247. {
  248. field: 'shipping_time',
  249. title: __('Shipping_time'),
  250. operate: 'RANGE',
  251. addclass: 'datetimerange',
  252. autocomplete: false,
  253. formatter: Table.api.formatter.datetime
  254. },
  255. {
  256. field: 'receive_time',
  257. title: __('Receive_time'),
  258. operate: 'RANGE',
  259. addclass: 'datetimerange',
  260. autocomplete: false,
  261. formatter: Table.api.formatter.datetime
  262. },
  263. {
  264. field: 'cancel_time',
  265. title: __('Cancel_time'),
  266. operate: 'RANGE',
  267. addclass: 'datetimerange',
  268. autocomplete: false,
  269. formatter: Table.api.formatter.datetime
  270. },
  271. {
  272. field: 'operate',
  273. title: __('Operate'),
  274. table: table,
  275. events: Table.api.events.operate,
  276. formatter: Table.api.formatter.operate,
  277. clickToSelect: false,
  278. buttons: [{
  279. name: '取消电子面单',
  280. title: __('取消电子面单'),
  281. classname: 'btn btn-xs btn-success btn-ajax',
  282. text: '取消电子面单',
  283. icon: 'fa fa-sticky-note-o',
  284. extend: 'data-area=\'["90%","90%"]\'',
  285. url: 'shop/order/cancel_electronics/oe_id/{oe_id}',
  286. hidden: function (row) {
  287. return !row.print_template;
  288. },
  289. success: function () {
  290. table.bootstrapTable('refresh', {});
  291. }
  292. }, {
  293. name: '订单详情',
  294. title: __('订单详情'),
  295. classname: 'btn btn-xs btn-primary btn-dialog btn-detail',
  296. text: '订单详情',
  297. icon: 'fa fa-sticky-note-o',
  298. extend: 'data-area=\'["90%","90%"]\'',
  299. url: 'shop/order/detail'
  300. }]
  301. }
  302. ]
  303. ]
  304. });
  305. var options = table.bootstrapTable('getOptions');
  306. var queryParams = options.queryParams;
  307. // 绑定TAB事件
  308. $('.panel-heading a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
  309. var value = $(this).data("value");
  310. options = table.bootstrapTable('getOptions');
  311. options.pageNumber = 1;
  312. options.queryParams = function (params) {
  313. if (value) {
  314. params.filter = JSON.stringify(value);
  315. let op = {}
  316. for (let i in value) {
  317. op[i] = '=';
  318. }
  319. params.op = JSON.stringify(op);
  320. }
  321. params = queryParams.call(this, params);
  322. return params;
  323. };
  324. table.bootstrapTable('refresh', {});
  325. return false;
  326. });
  327. // 批量打印电子面单
  328. $(document).on('click', '.btn-print-multiple-electronic', function () {
  329. let ids = Table.api.selectedids(table);
  330. if (!ids.length) {
  331. Toastr.error('请选择需打印的订单');
  332. return;
  333. }
  334. Layer.index = 0;
  335. Layer.open({
  336. id: 'electronic',
  337. title: '请选择电子面单模板',
  338. content: Template("electronictpl", {ids: ids}),
  339. zIndex: 8,
  340. btn: ["开始打印", "取消"],
  341. success: function (layero, index) {
  342. Form.events.selectpage(layero);
  343. },
  344. yes: function (index, layero) {
  345. let electronics_id = $('#electronics_id', layero).val();
  346. if (!electronics_id) {
  347. Toastr.error('请选择电子面单模板');
  348. return;
  349. }
  350. Fast.api.ajax(
  351. {
  352. url: 'shop/order/prints',
  353. data: {
  354. ids: ids.join('_'),
  355. electronics_id: electronics_id
  356. }
  357. }, function (data) {
  358. let html = '';
  359. data.forEach(item => {
  360. //模板,获取成功
  361. if (item.Success) {
  362. html += '<div style="page-break-after: always">';
  363. html += item.PrintTemplate;
  364. html += '</div>';
  365. }
  366. });
  367. html && Controller.api.print('电子面单打印', html);
  368. return false;
  369. }
  370. );
  371. }
  372. });
  373. });
  374. // 批量打印发货单
  375. $(document).on('click', '.btn-print-multiple-invoice', function () {
  376. let order_ids = Table.api.selectedids(table);
  377. if (!order_ids.length) {
  378. Toastr.error('请选择需打印的订单');
  379. return;
  380. }
  381. Fast.api.ajax({
  382. url: 'shop/order/orderList',
  383. data: {
  384. ids: order_ids
  385. }
  386. }, function (data) {
  387. var html = Template("invoicetpl", data);
  388. Controller.api.print('发货单打印', html);
  389. return false;
  390. })
  391. });
  392. // 为表格绑定事件
  393. Table.api.bindevent(table);
  394. },
  395. recyclebin: function () {
  396. // 初始化表格参数配置
  397. Table.api.init({
  398. extend: {
  399. 'dragsort_url': ''
  400. }
  401. });
  402. var table = $("#table");
  403. // 初始化表格
  404. table.bootstrapTable({
  405. url: 'shop/order/recyclebin' + location.search,
  406. pk: 'id',
  407. sortName: 'id',
  408. columns: [
  409. [{
  410. checkbox: true
  411. },
  412. {
  413. field: 'id',
  414. title: __('Id')
  415. },
  416. {
  417. field: 'order_sn',
  418. title: __('Order_sn'),
  419. operate: 'LIKE'
  420. },
  421. {
  422. field: 'deletetime',
  423. title: __('Deletetime'),
  424. operate: 'RANGE',
  425. addclass: 'datetimerange',
  426. formatter: Table.api.formatter.datetime
  427. },
  428. {
  429. field: 'operate',
  430. width: '130px',
  431. title: __('Operate'),
  432. table: table,
  433. events: Table.api.events.operate,
  434. buttons: [{
  435. name: 'Restore',
  436. text: __('Restore'),
  437. classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
  438. icon: 'fa fa-rotate-left',
  439. url: 'shop/order/restore',
  440. refresh: true
  441. },
  442. {
  443. name: 'Destroy',
  444. text: __('Destroy'),
  445. classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
  446. icon: 'fa fa-times',
  447. url: 'shop/order/destroy',
  448. refresh: true
  449. }
  450. ],
  451. formatter: Table.api.formatter.operate
  452. }
  453. ]
  454. ]
  455. });
  456. // 为表格绑定事件
  457. Table.api.bindevent(table);
  458. },
  459. add: function () {
  460. Controller.api.bindevent();
  461. },
  462. edit: function () {
  463. Controller.api.bindevent();
  464. },
  465. detail: function () {
  466. $(document).on("click", ".btn-refresh", function () {
  467. setTimeout(function () {
  468. location.reload();
  469. }, 1000);
  470. });
  471. //取消,支付
  472. $(document).on('click', '.btn-status-click', function () {
  473. let order = $(this).data('order');
  474. let order_id = $('#order_id').val();
  475. $.post('shop/order/edit_status', {
  476. order_id,
  477. ...order
  478. }, function (res) {
  479. if (res.code == 1) {
  480. window.location.reload();
  481. parent.Toastr.success(res.msg);
  482. } else {
  483. parent.Toastr.error(res.msg);
  484. }
  485. })
  486. });
  487. //发货
  488. $(document).on('click', '.btn-deliver', function () {
  489. let expressname = $(this).data('expressname');
  490. let expressno = $(this).data('expressno');
  491. let html = Template('expresstpl', {expressname, expressno});
  492. let type = $(this).data('type');
  493. layer.open({
  494. type: 1,
  495. skin: 'layui-layer-demo', //样式类名
  496. title: type == 0 ? '发货' : '修改快递信息',
  497. anim: 2,
  498. area: ['400px', '300px'],
  499. shadeClose: 1, //开启遮罩关闭
  500. content: html,
  501. btn: ['确认', '取消'],
  502. zIndex: 10,
  503. yes: function (index) {
  504. let expressname = ($('#expressname').val()).trim();
  505. let expressno = ($('#expressno').val()).trim();
  506. if (!expressname) {
  507. Toastr.error('请输入快递名称');
  508. return;
  509. }
  510. if (!expressno) {
  511. Toastr.error('请输入快递单号');
  512. return;
  513. }
  514. let order_id = $('#order_id').val();
  515. $.post('shop/order/deliver', {
  516. order_id,
  517. expressname,
  518. expressno,
  519. type
  520. }, function (res) {
  521. if (res.code == 1) {
  522. window.location.reload();
  523. Toastr.success(res.msg);
  524. Layer.close(index);
  525. } else {
  526. Toastr.error(res.msg);
  527. }
  528. })
  529. },
  530. success: function (layero, index) {
  531. Form.api.bindevent($('.shipper'));
  532. }
  533. });
  534. });
  535. //编辑备注
  536. $('#app').delegate('.btn-edit,.btn-cancel,.btn-save', 'click', function () {
  537. let status = $(this).data('status');
  538. switch (status) {
  539. case 'edit':
  540. $(this).nextAll('a').removeClass('hide');
  541. $(this).parent().prev().children('div').addClass('hide');
  542. $(this).parent().prev().children('input').removeClass('hide');
  543. $(this).addClass('hide');
  544. break;
  545. case 'cancel':
  546. $(this).prev().removeClass('hide');
  547. $(this).next().addClass('hide');
  548. $(this).parent().prev().children('div').removeClass('hide');
  549. $(this).parent().prev().children('input').addClass('hide');
  550. $(this).addClass('hide');
  551. break;
  552. case 'save':
  553. let val = $(this).parent().prev().children('input').val();
  554. let field = $(this).data('field');
  555. let id = $(this).data('id');
  556. $.post('shop/order/edit_info', {
  557. id: id,
  558. field: field,
  559. value: val
  560. }, function (res) {
  561. if (res.code == 1) {
  562. window.location.reload();
  563. parent.Toastr.success(res.msg);
  564. } else {
  565. parent.Toastr.error(res.msg);
  566. }
  567. })
  568. break;
  569. }
  570. });
  571. function moneyPrompt(value) {
  572. return new Promise((resolve, reject) => {
  573. layer.prompt({
  574. title: '确认退款金额',
  575. formType: 0,
  576. value: value
  577. }, function (pass, index) {
  578. layer.close(index);
  579. resolve(pass)
  580. });
  581. })
  582. }
  583. function reasonPrompt() {
  584. return new Promise((resolve, reject) => {
  585. layer.prompt({
  586. title: '拒绝原因',
  587. formType: 2
  588. }, function (pass, index) {
  589. layer.close(index);
  590. resolve(pass)
  591. });
  592. })
  593. }
  594. Form.api.bindevent($('.electronics'));
  595. // 打印快递电子面单
  596. $(document).on('click', '.btn-print-electronic', function () {
  597. let order_id = $(this).data('id');
  598. let electronics_id = $('#electronics_id').val();
  599. if (!electronics_id) {
  600. Toastr.error('请选择电子面单模板');
  601. return;
  602. }
  603. Fast.api.ajax('shop/order/electronics?order_id=' + order_id + '&electronics_id=' + electronics_id, function (res) {
  604. Controller.api.print('电子面单打印', res.PrintTemplate);
  605. return false;
  606. });
  607. });
  608. // 打印发货单
  609. $(document).on('click', '.btn-print-invoice', function () {
  610. let order_ids = $(this).data("id");
  611. Fast.api.ajax({
  612. url: 'shop/order/orderList',
  613. data: {
  614. ids: order_ids
  615. }
  616. }, function (data) {
  617. var html = Template("invoicetpl", data);
  618. Controller.api.print('发货单打印', html);
  619. return false;
  620. })
  621. });
  622. //查询物流
  623. $(document).on('click', '.btn-logistics', function () {
  624. parent.layer.open({
  625. type: 2,
  626. shade: false,
  627. zIndex: 9999999999,
  628. title: '物流信息',
  629. area: ['90%', '90%'],
  630. content: 'https://www.kuaidi100.com/all/htky.shtml?mscomnu=' + $(this).data('expressno')
  631. })
  632. })
  633. },
  634. //批量打印
  635. prints: function () {
  636. let that = this;
  637. let ids = Config.order_ids;
  638. Controller.api.bindevent();
  639. },
  640. api: {
  641. // 解析Config中的JSON字符串的辅助函数
  642. parseConfigJson: function(configKey, defaultValue) {
  643. var configValue = Config[configKey] || defaultValue || {};
  644. // 如果是字符串,尝试解析JSON
  645. if (typeof configValue === 'string') {
  646. try {
  647. return JSON.parse(configValue);
  648. } catch (e) {
  649. return defaultValue || {};
  650. }
  651. }
  652. return configValue;
  653. },
  654. bindevent: function () {
  655. Form.api.bindevent($("form[role=form]"));
  656. },
  657. print: function (title, html) {
  658. if (Config.shop.printtype == 'system') {
  659. require(['../addons/shop/js/print-html-element.min'], function (PHE) {
  660. PHE.printHtml(html, {
  661. stylesheets: [],
  662. styles: '@page {margin: 5px;}'
  663. });
  664. });
  665. } else {
  666. if (typeof LODOP == 'undefined') {
  667. Toastr.error('请检查C-Lodop打印组件是否安装并启动');
  668. return;
  669. }
  670. LODOP.PRINT_INIT(title); //初始化
  671. LODOP.ADD_PRINT_HTM(0, 0, "100%", "100%", html);
  672. if (Config.shop.clodop_print_view == '1') {
  673. LODOP.PREVIEW();
  674. } else if (Config.shop.clodop_print_view == '2') {
  675. LODOP.PRINT_DESIGN();
  676. } else {
  677. LODOP.PRINT();
  678. }
  679. }
  680. return;
  681. }
  682. }
  683. };
  684. return Controller;
  685. });