batch-delivery.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  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. <div>
  112. <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;">
  113. <el-input size="small" :disabled="true" v-model="attachments[0].name" class="input-with-select">
  114. <el-button slot="append">选择文件</el-button>
  115. </el-input>
  116. </com-upload>
  117. <el-button @click="$navigate({r:'cloud-stock/order/download-delivery-template'})" type="text">下载批量发货模板</el-button>
  118. </div>
  119. </el-form-item>
  120. <div class="tips-text" style="margin-left: 150px;">注:最大支持1000条数据(仅支持csv格式的文件)</div>
  121. </el-form>
  122. <div slot="footer">
  123. <el-button size="small" @click="dialogFormVisible = false">取 消</el-button>
  124. <el-button size="small" type="primary" @click="handle('delivery',form)">提 交</el-button>
  125. </div>
  126. </el-dialog>
  127. <!-- 错误列表 -->
  128. <el-dialog title="错误原因" :visible.sync="dialogErrorVisible" :close-on-click-modal="false">
  129. <el-table :data="listError">
  130. <el-table-column prop="id" label="ID" width="100"></el-table-column>
  131. <el-table-column prop="order_no" label="订单编号"></el-table-column>
  132. <el-table-column prop="express_no" label="物流单号"></el-table-column>
  133. <el-table-column prop="content" label="错误原因"></el-table-column>
  134. </el-table>
  135. <div style="text-align: right;margin: 20px 0;">
  136. <el-pagination @current-change="paginationError" background layout="prev, pager, next" :page-count="pageCountError"></el-pagination>
  137. </div>
  138. </el-dialog>
  139. </div>
  140. <script>
  141. new Vue({
  142. el: '#app',
  143. data() {
  144. return {
  145. loading: false,
  146. loadingError: false,
  147. uploading: false,
  148. dialogFormVisible: false,
  149. dialogErrorVisible: false,
  150. list: [],
  151. listError: [],
  152. delivery_id: 0,
  153. upload_type: {upload_type:'files'},
  154. express_companys: [],
  155. delivery_status_arr: [
  156. {name: '处理中', status: 0},
  157. {name: '已完成', status: 1},
  158. ],
  159. operate_time: null,
  160. searchInfo: {
  161. delivery_status: null,
  162. operator_name: null,
  163. start_time: null,
  164. end_time: null,
  165. },
  166. page: 1,
  167. pageCount: 0,
  168. pageError: 1,
  169. pageCountError: 0,
  170. attachments: [{name: ''}],
  171. form: {
  172. express_id: null,
  173. express_name: null,
  174. customer_name: null,
  175. delivery_file: null,
  176. },
  177. FormRules: {
  178. delivery_file: [
  179. { required: true, message: '模板不能为空', trigger: 'blur' },
  180. ],
  181. express_name: [
  182. { required: true, message: '快递公司不能为空', trigger: 'blur' },
  183. ],
  184. },
  185. }
  186. },
  187. created() {
  188. this.getList();
  189. this.getOrderShipping();
  190. },
  191. computed: {
  192. show_clear_search_btn: function() {
  193. return !isEmpty(this.searchInfo.delivery_status) ||
  194. !isEmpty(this.searchInfo.operator_name) ||
  195. !isEmpty(this.searchInfo.start_time) ||
  196. !isEmpty(this.searchInfo.end_time);
  197. }
  198. },
  199. methods: {
  200. handleStart(files) {
  201. this.uploading = true;
  202. },
  203. handleSuccess(file) {
  204. if (file.response && file.response.data && file.response.data.code === 0) {
  205. const newItem = {
  206. url: file.response.data.data.url,
  207. name: file.response.data.data.name,
  208. };
  209. this.attachments.unshift(newItem);
  210. }
  211. },
  212. handleComplete(files) {
  213. this.form.delivery_file = this.attachments[0].url;
  214. this.uploading = false;
  215. },
  216. handleExpressChange(e){
  217. let index = this.express_companys.findIndex(v => v.id == e);
  218. this.form.express_id = this.express_companys[index].id;
  219. this.form.express_name = this.express_companys[index].name;
  220. },
  221. getOrderShipping(){
  222. request({
  223. params: {
  224. r: 'mall/order/shipping'
  225. },
  226. }).then(res => {
  227. if(res.data.code == 0){
  228. this.express_companys = res.data.data.express_list
  229. this.form.customer_name = res.data.data.jd_mch_code
  230. }
  231. })
  232. },
  233. // 弹框关闭事件回调
  234. dialogColse() {
  235. this.form = {
  236. express_id: null,
  237. express_name: null,
  238. delivery_file: null,
  239. };
  240. },
  241. search() {
  242. this.getList(this.searchInfo);
  243. },
  244. resetSearch() {
  245. this.searchInfo = {
  246. delivery_status: null,
  247. operator_name: null,
  248. start_time: null,
  249. end_time: null,
  250. };
  251. this.page = 1;
  252. this.getList();
  253. },
  254. addTimeChange(v){
  255. this.searchInfo.start_time = v?v[0]:'';
  256. this.searchInfo.end_time = v?v[1]:'';
  257. },
  258. pagination(currentPage) {
  259. let self = this;
  260. self.page = currentPage;
  261. self.getList();
  262. },
  263. paginationError(currentPage) {
  264. let self = this;
  265. self.pageError = currentPage;
  266. self.getListError();
  267. },
  268. handle(type, data = null) {
  269. let id = data ? parseInt(data.id) : '';
  270. switch (type) {
  271. case 'delivery':
  272. this.send(data, 'cloud-stock/order/batch-delivery',(resp) => {
  273. if (resp.code == 0) {
  274. this.dialogFormVisible = false;
  275. this.dialogColse();
  276. }
  277. });
  278. break;
  279. case 'error':
  280. this.delivery_id = data.id;
  281. this.dialogErrorVisible = true;
  282. this.listError = [];
  283. this.pageCountError = 0;
  284. this.getListError()
  285. break;
  286. case 'export':
  287. var param = {batch_delivery_id: data.id, flag: 'EXPORT'};
  288. this.send(param, 'cloud-stock/order/batch-delivery');
  289. break;
  290. }
  291. },
  292. send(params, url = '', callback) {
  293. let self = this;
  294. request({
  295. params: {
  296. r: url,
  297. },
  298. method: 'post',
  299. data: params
  300. }).then(e => {
  301. if (e.data.code == 0) {
  302. self.$message.success(e.data.msg);
  303. self.getList();
  304. } else {
  305. self.$message.error(e.data.msg);
  306. }
  307. if (callback) callback(e.data);
  308. }).catch(e => {
  309. console.log(e);
  310. });
  311. },
  312. getList($search = null) {
  313. this.loading = true;
  314. let basic_params = {
  315. r: 'cloud-stock/order/batch-delivery',
  316. page: this.page,
  317. };
  318. var params = Object.assign(basic_params, $search);
  319. this.$request({
  320. params: params,
  321. method: 'get',
  322. }).then(e => {
  323. this.loading = false;
  324. if (e.data.code == 0) {
  325. this.list = e.data.data.list;
  326. this.pageCount = e.data.data.page_count;
  327. }
  328. });
  329. },
  330. getListError() {
  331. this.loadingError = true;
  332. this.$request({
  333. params: {
  334. r: 'cloud-stock/order/batch-delivery-error',
  335. page: this.pageError,
  336. delivery_id: this.delivery_id,
  337. },
  338. method: 'get',
  339. }).then(e => {
  340. this.loading = false;
  341. if (e.data.code == 0) {
  342. this.listError = e.data.data.list;
  343. this.pageCountError = e.data.data.page_count;
  344. }
  345. });
  346. },
  347. }
  348. })
  349. </script>
  350. <style scoped>
  351. .nav {
  352. background: #ffffff;
  353. border-radius: 5px;
  354. padding: 26px 24px;
  355. margin-bottom: 10px;
  356. }
  357. .diy-index {
  358. display: flex;
  359. flex-direction: column;
  360. }
  361. .header_box {
  362. display: flex;
  363. flex-direction: column;
  364. padding: 25px;
  365. background: #fff;
  366. border-radius: 5px 5px 0 0;
  367. color: #808695;
  368. }
  369. .add_temp_right {
  370. width: 400px;
  371. }
  372. .operation {
  373. cursor: pointer;
  374. color: #409eff;
  375. }
  376. .operation:hover {
  377. color: #fb6638;
  378. }
  379. /* 公共样式 */
  380. .flex {
  381. display: -webkit-box;
  382. display: -webkit-flex;
  383. /* #ifndef APP-NVUE */
  384. display: flex;
  385. /* #endif */
  386. }
  387. .flex-x-center {
  388. display: -webkit-box;
  389. display: -webkit-flex;
  390. /* #ifndef APP-NVUE */
  391. display: flex;
  392. /* #endif */
  393. -webkit-box-pack: center;
  394. -webkit-justify-content: center;
  395. -ms-flex-pack: center;
  396. justify-content: center;
  397. }
  398. .flex-x-between {
  399. display: -webkit-box;
  400. display: -webkit-flex;
  401. /* #ifndef APP-NVUE */
  402. display: flex;
  403. /* #endif */
  404. -webkit-box-pack: space-between;
  405. -webkit-justify-content: space-between;
  406. -ms-flex-pack: space-between;
  407. justify-content: space-between;
  408. }
  409. .flex-y-center {
  410. display: -webkit-box;
  411. display: -webkit-flex;
  412. /* #ifndef APP-NVUE */
  413. display: flex;
  414. /* #endif */
  415. -webkit-box-align: center;
  416. -webkit-align-items: center;
  417. -ms-flex-align: center;
  418. -ms-grid-row-align: center;
  419. align-items: center;
  420. }
  421. /* 溢出隐藏 */
  422. .over1 {
  423. text-overflow: -o-ellipsis-lastline;
  424. overflow: hidden;
  425. text-overflow: ellipsis;
  426. display: -webkit-box;
  427. -webkit-line-clamp: 1;
  428. -webkit-box-orient: vertical;
  429. }
  430. .el-card {
  431. background: transparent;
  432. box-shadow: none !important;
  433. border: 0;
  434. }
  435. .el-card__body {
  436. padding: 0;
  437. }
  438. .el-table td,
  439. .el-table th {
  440. padding: 12px 0
  441. }
  442. </style>