batch-delivery.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. <?php
  2. use common\helpers\ComponentHelper;
  3. ComponentHelper::loadComponentView('com-export-dialog');
  4. ?>
  5. <div id="app" class="diy-index" v-cloak ref="contain" v-loading="loading">
  6. <div class="nav">批量发货</div>
  7. <div class="header_box" style="flex: 1;">
  8. <el-alert
  9. style="padding: 16px;"
  10. title="批量发货目前仅支持快递订单,暂不支持自提订单和同城配送方式的订单。提高操作发货的成功率:"
  11. type="error">
  12. 特别注意:<br>
  13. ① 按模板要求填写发货信息<br>
  14. ② 当快递单号为纯数字时,请在数字前添加一个英文单引号,如运单号为:3123120000000000000,填写时写成:'3123120000000000000 ;<b style="color:deeppink;">注意事项:订单编号前面的"单引号是英文的,不是中文的",如果使用中文引号,将导致系统上无法查看到该订单的物流更新信息</b><br>
  15. ③ 导出失败订单 操作完成后,请在左侧的【下载】菜单进行下载查看。<br>
  16. </el-alert>
  17. <div ref="crad-header" class="add_temp_box flex flex-x-between flex-y-center" style="margin: 20px 0;">
  18. <div class="add_temp_left flex flex-y-center">
  19. <el-button type="primary" size="small" @click="dialogFormVisible=true">批量发货</el-button>
  20. </div>
  21. </div>
  22. <!-- 搜索开始 -->
  23. <el-form ref="searchInfo" :model="searchInfo" size="small">
  24. <el-row :gutter="24">
  25. <el-col :span="7">
  26. <el-form-item label="时间">
  27. <el-date-picker
  28. v-model="operate_time"
  29. type="datetimerange"
  30. range-separator="至"
  31. start-placeholder="开始日期"
  32. end-placeholder="结束日期"
  33. value-format="yyyy-MM-dd HH:mm:ss"
  34. @change="addTimeChange"
  35. :default-time="['00:00:00', '23:59:59']"
  36. >
  37. </el-date-picker>
  38. </el-form-item>
  39. </el-col>
  40. <el-col :span="5">
  41. <el-form-item label="状态">
  42. <el-select v-model="searchInfo.delivery_status" placeholder="请选择状态">
  43. <el-option v-for="item in delivery_status_arr" :label="item.name" :value="item.status" :key="item.status"></el-option>
  44. </el-select>
  45. </el-form-item>
  46. </el-col>
  47. <el-col :span="5">
  48. <el-form-item label="操作人">
  49. <el-input style="width: auto;" v-model="searchInfo.operator_name" placeholder="请输入操作人" clearable></el-input>
  50. </el-form-item>
  51. </el-col>
  52. <el-col :span="4">
  53. <el-button @click="search" size="small" type="primary">筛选</el-button>
  54. <el-button type="danger" size="small" @click="resetSearch" v-if="show_clear_search_btn">清除条件</el-button>
  55. </el-col>
  56. </el-row>
  57. </el-form>
  58. <!-- 搜索结束 -->
  59. <div style="flex: 1;margin-top:10px">
  60. <el-table :data="list">
  61. <el-table-column prop="id" label="ID" width="100"></el-table-column>
  62. <el-table-column prop="operator_name" label="操作人"></el-table-column>
  63. <el-table-column prop="delivery_num" label="导入订单数"></el-table-column>
  64. <el-table-column prop="success_delivery_num" label="导入成功"></el-table-column>
  65. <el-table-column label="导入失败">
  66. <template slot-scope="scope">
  67. {{ scope.row.delivery_num - scope.row.success_delivery_num }}
  68. </template>
  69. </el-table-column>
  70. <el-table-column prop="delivery_status" label="状态">
  71. <template slot-scope="scope">
  72. <el-tag v-if="scope.row.delivery_status == 0" type="danger">处理中</el-tag>
  73. <el-tag v-else type="success">已完成</el-tag>
  74. </template>
  75. </el-table-column>
  76. <el-table-column prop="created_at" label="操作时间">
  77. <template slot-scope="scope">
  78. <div>{{ scope.row.created_at | dateTimeFormat('Y-m-d H:i')}}</div>
  79. </template>
  80. </el-table-column>
  81. <el-table-column label="操作">
  82. <template slot-scope="scope">
  83. <div v-if="scope.row.delivery_status == 0"><span>批量发货正在处理</span></div>
  84. <div v-else>
  85. <span v-if="scope.row.delivery_num != scope.row.success_delivery_num">
  86. <el-button type="text" @click="handle('error',scope.row)">查看失败原因</el-button>
  87. <span style="color: #DCDFE6">|</span>
  88. <el-button type="text" @click="handle('export',scope.row)">导出失败订单</el-button>
  89. </span>
  90. <span v-else>导入成功</span>
  91. </div>
  92. </template>
  93. </el-table-column>
  94. </el-table>
  95. </div>
  96. <div style="text-align: right;margin: 20px 0;">
  97. <el-pagination @current-change="pagination" background layout="prev, pager, next" :page-count="pageCount"></el-pagination>
  98. </div>
  99. </div>
  100. <el-dialog @close="dialogColse" title="批量发货" :visible.sync="dialogFormVisible" :close-on-click-modal="false">
  101. <el-form :model="form" label-width="150px" :rules="FormRules" ref="form">
  102. <el-form-item label="选择快递公司" prop="express">
  103. <el-select size="small" v-model="form.express_id" filterable placeholder="请选择" @change="handleExpressChange">
  104. <el-option v-for="company in express_companys" :key="company.id" :label="company.name" :value="company.id"></el-option>
  105. </el-select>
  106. </el-form-item>
  107. <el-form-item label="商家编码" prop="customer_name" v-if="form.express_name === '京东物流' || form.express_name === '京东快运'">
  108. <el-input style="width: 40%;" placeholder="请输入商家编码" size="small" v-model="form.customer_name" autocomplete="off"></el-input>
  109. </el-form-item>
  110. <el-form-item label="发货方式">
  111. <el-radio-group v-model="form.isByGoods">
  112. <el-radio :label="0">根据订单号发货</el-radio>
  113. <el-radio :label="1">根据订单号及商品名称发货</el-radio>
  114. </el-radio-group>
  115. </el-form-item>
  116. <el-form-item label="导入模板">
  117. <div>
  118. <com-upload class="flex flex-y-center" v-loading="uploading" :disabled="uploading" :fields="upload_type" @start="handleStart" @success="handleSuccess" @complete="handleComplete" :max="1" accept=".csv" style="width: 40%;float: left;margin-right: 20px;">
  119. <el-input size="small" :disabled="true" v-model="attachments[0].name" class="input-with-select">
  120. <el-button slot="append">选择文件</el-button>
  121. </el-input>
  122. </com-upload>
  123. <el-button @click="$navigate({r: 'mall/order/download-delivery-template', isByGoods: form.isByGoods})" type="text">下载批量发货模板</el-button>
  124. </div>
  125. </el-form-item>
  126. <div class="tips-text" style="margin-left: 150px;">注:最大支持1000条数据(仅支持csv格式的文件)</div>
  127. </el-form>
  128. <div slot="footer">
  129. <el-button size="small" @click="dialogFormVisible = false">取 消</el-button>
  130. <el-button size="small" type="primary" @click="handle('delivery',form)">提 交</el-button>
  131. </div>
  132. </el-dialog>
  133. <!-- 错误列表 -->
  134. <el-dialog title="错误原因" :visible.sync="dialogErrorVisible" :close-on-click-modal="false">
  135. <el-table :data="listError">
  136. <el-table-column prop="id" label="ID" width="100"></el-table-column>
  137. <el-table-column prop="order_no" label="订单编号"></el-table-column>
  138. <el-table-column prop="express_no" label="物流单号"></el-table-column>
  139. <el-table-column prop="content" label="错误原因"></el-table-column>
  140. </el-table>
  141. <div style="text-align: right;margin: 20px 0;">
  142. <el-pagination @current-change="paginationError" background layout="prev, pager, next" :page-count="pageCountError"></el-pagination>
  143. </div>
  144. </el-dialog>
  145. </div>
  146. <script>
  147. new Vue({
  148. el: '#app',
  149. data() {
  150. return {
  151. loading: false,
  152. loadingError: false,
  153. uploading: false,
  154. dialogFormVisible: false,
  155. dialogErrorVisible: false,
  156. list: [],
  157. listError: [],
  158. delivery_id: 0,
  159. upload_type: {upload_type:'files'},
  160. express_companys: [],
  161. delivery_status_arr: [
  162. {name: '处理中', status: 0},
  163. {name: '已完成', status: 1},
  164. ],
  165. operate_time: null,
  166. searchInfo: {
  167. delivery_status: null,
  168. operator_name: null,
  169. start_time: null,
  170. end_time: null,
  171. },
  172. page: 1,
  173. pageCount: 0,
  174. pageError: 1,
  175. pageCountError: 0,
  176. attachments: [{name: ''}],
  177. form: {
  178. express_id: null,
  179. express_name: null,
  180. customer_name: null,
  181. delivery_file: null,
  182. isByGoods: 0, // 是否根据订单的商品名称和数量发货
  183. },
  184. FormRules: {
  185. delivery_file: [
  186. { required: true, message: '模板不能为空', trigger: 'blur' },
  187. ],
  188. express_name: [
  189. { required: true, message: '快递公司不能为空', trigger: 'blur' },
  190. ],
  191. }
  192. }
  193. },
  194. created() {
  195. this.getList();
  196. this.getOrderShipping();
  197. },
  198. computed: {
  199. show_clear_search_btn: function() {
  200. return !isEmpty(this.searchInfo.delivery_status) ||
  201. !isEmpty(this.searchInfo.operator_name) ||
  202. !isEmpty(this.searchInfo.start_time) ||
  203. !isEmpty(this.searchInfo.end_time);
  204. }
  205. },
  206. methods: {
  207. handleStart(files) {
  208. this.uploading = true;
  209. },
  210. handleSuccess(file) {
  211. if (file.response && file.response.data && file.response.data.code === 0) {
  212. const newItem = {
  213. url: file.response.data.data.url,
  214. name: file.response.data.data.name,
  215. };
  216. this.attachments.unshift(newItem);
  217. }
  218. },
  219. handleComplete(files) {
  220. this.form.delivery_file = this.attachments[0].url;
  221. this.uploading = false;
  222. },
  223. handleExpressChange(e){
  224. let index = this.express_companys.findIndex(v => v.id == e);
  225. this.form.express_id = this.express_companys[index].id;
  226. this.form.express_name = this.express_companys[index].name;
  227. },
  228. getOrderShipping(){
  229. request({
  230. params: {
  231. r: 'mall/order/shipping'
  232. },
  233. }).then(res => {
  234. if(res.data.code == 0){
  235. this.express_companys = res.data.data.express_list
  236. this.form.customer_name = res.data.data.jd_mch_code
  237. }
  238. })
  239. },
  240. // 弹框关闭事件回调
  241. dialogColse() {
  242. this.form = {
  243. express_id: null,
  244. express_name: null,
  245. delivery_file: null,
  246. };
  247. },
  248. search() {
  249. this.getList(this.searchInfo);
  250. },
  251. resetSearch() {
  252. this.searchInfo = {
  253. delivery_status: null,
  254. operator_name: null,
  255. start_time: null,
  256. end_time: null,
  257. };
  258. this.page = 1;
  259. this.getList();
  260. },
  261. addTimeChange(v){
  262. this.searchInfo.start_time = v?v[0]:'';
  263. this.searchInfo.end_time = v?v[1]:'';
  264. },
  265. pagination(currentPage) {
  266. let self = this;
  267. self.page = currentPage;
  268. self.getList();
  269. },
  270. paginationError(currentPage) {
  271. let self = this;
  272. self.pageError = currentPage;
  273. self.getListError();
  274. },
  275. handle(type, data = null) {
  276. let id = data ? parseInt(data.id) : '';
  277. switch (type) {
  278. case 'delivery':
  279. this.send(data, 'mall/order/batch-delivery',(resp) => {
  280. if (resp.code == 0) {
  281. this.dialogFormVisible = false;
  282. this.dialogColse();
  283. }
  284. });
  285. break;
  286. case 'error':
  287. this.delivery_id = data.id;
  288. this.dialogErrorVisible = true;
  289. this.listError = [];
  290. this.pageCountError = 0;
  291. this.getListError()
  292. break;
  293. case 'export':
  294. var param = {batch_delivery_id: data.id, flag: 'EXPORT'};
  295. this.send(param, 'mall/order/batch-delivery');
  296. break;
  297. }
  298. },
  299. send(params, url = '', callback) {
  300. let self = this;
  301. request({
  302. params: {
  303. r: url,
  304. },
  305. method: 'post',
  306. data: params
  307. }).then(e => {
  308. if (e.data.code == 0) {
  309. self.$message.success(e.data.msg);
  310. self.getList();
  311. } else {
  312. self.$message.error(e.data.msg);
  313. }
  314. if (callback) callback(e.data);
  315. }).catch(e => {
  316. console.log(e);
  317. });
  318. },
  319. getList($search = null) {
  320. this.loading = true;
  321. let basic_params = {
  322. r: 'mall/order/batch-delivery',
  323. page: this.page,
  324. };
  325. var params = Object.assign(basic_params, $search);
  326. this.$request({
  327. params: params,
  328. method: 'get',
  329. }).then(e => {
  330. this.loading = false;
  331. if (e.data.code == 0) {
  332. this.list = e.data.data.list;
  333. this.pageCount = e.data.data.page_count;
  334. }
  335. });
  336. },
  337. getListError() {
  338. this.loadingError = true;
  339. this.$request({
  340. params: {
  341. r: 'mall/order/batch-delivery-error',
  342. page: this.pageError,
  343. delivery_id: this.delivery_id,
  344. },
  345. method: 'get',
  346. }).then(e => {
  347. this.loading = false;
  348. if (e.data.code == 0) {
  349. this.listError = e.data.data.list;
  350. this.pageCountError = e.data.data.page_count;
  351. }
  352. });
  353. },
  354. }
  355. })
  356. </script>
  357. <style scoped>
  358. @media screen and (max-width: 1500px) {
  359. #app {
  360. min-width: 1270px;
  361. }
  362. }
  363. .nav {
  364. background: #ffffff;
  365. border-radius: 5px;
  366. padding: 26px 24px;
  367. margin-bottom: 10px;
  368. }
  369. .diy-index {
  370. display: flex;
  371. flex-direction: column;
  372. }
  373. .header_box {
  374. display: flex;
  375. flex-direction: column;
  376. padding: 25px;
  377. background: #fff;
  378. border-radius: 5px 5px 0 0;
  379. color: #808695;
  380. }
  381. .add_temp_right {
  382. width: 400px;
  383. }
  384. .operation {
  385. cursor: pointer;
  386. color: #409eff;
  387. }
  388. .operation:hover {
  389. color: #fb6638;
  390. }
  391. /* 公共样式 */
  392. .flex {
  393. display: -webkit-box;
  394. display: -webkit-flex;
  395. /* #ifndef APP-NVUE */
  396. display: flex;
  397. /* #endif */
  398. }
  399. .flex-x-center {
  400. display: -webkit-box;
  401. display: -webkit-flex;
  402. /* #ifndef APP-NVUE */
  403. display: flex;
  404. /* #endif */
  405. -webkit-box-pack: center;
  406. -webkit-justify-content: center;
  407. -ms-flex-pack: center;
  408. justify-content: center;
  409. }
  410. .flex-x-between {
  411. display: -webkit-box;
  412. display: -webkit-flex;
  413. /* #ifndef APP-NVUE */
  414. display: flex;
  415. /* #endif */
  416. -webkit-box-pack: space-between;
  417. -webkit-justify-content: space-between;
  418. -ms-flex-pack: space-between;
  419. justify-content: space-between;
  420. }
  421. .flex-y-center {
  422. display: -webkit-box;
  423. display: -webkit-flex;
  424. /* #ifndef APP-NVUE */
  425. display: flex;
  426. /* #endif */
  427. -webkit-box-align: center;
  428. -webkit-align-items: center;
  429. -ms-flex-align: center;
  430. -ms-grid-row-align: center;
  431. align-items: center;
  432. }
  433. /* 溢出隐藏 */
  434. .over1 {
  435. text-overflow: -o-ellipsis-lastline;
  436. overflow: hidden;
  437. text-overflow: ellipsis;
  438. display: -webkit-box;
  439. -webkit-line-clamp: 1;
  440. -webkit-box-orient: vertical;
  441. }
  442. .el-card {
  443. background: transparent;
  444. box-shadow: none !important;
  445. border: 0;
  446. }
  447. .el-card__body {
  448. padding: 0;
  449. }
  450. .el-table td,
  451. .el-table th {
  452. padding: 12px 0
  453. }
  454. </style>