order.js 28 KB

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