index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. <template>
  2. <view class="container">
  3. <view class="example">
  4. <!-- 基础表单校验 -->
  5. <uni-forms ref="valiForm" label-align="right" :rules="rules" :modelValue="valiFormData">
  6. <uni-forms-item label="单号" :required="true" name="orderNum">
  7. <uni-easyinput
  8. v-model="valiFormData.orderNum"
  9. placeholder="请输入单号"
  10. suffixIcon="scan"
  11. :focus="focusType"
  12. @iconClick="scan('orderNum')"
  13. @input="orderChange"
  14. />
  15. </uni-forms-item>
  16. <uni-forms-item label="批次号" name="batch_number">
  17. <uni-data-select v-model="valiFormData.batch_number" :localdata="batchOptions" placeholder="请选择批次号"></uni-data-select>
  18. </uni-forms-item>
  19. <view v-if="orderInfo.order_id">
  20. <view v-if="!orderInfo.express_sn">
  21. <uni-forms-item label="物流公司" name="express_company_id">
  22. <uni-data-select v-model="valiFormData.express_company_id" :localdata="companyOptions" placeholder="请选择物流公司"></uni-data-select>
  23. </uni-forms-item>
  24. <uni-forms-item label="物流单号" name="express_no">
  25. <uni-easyinput v-model="valiFormData.express_no" placeholder="请输入物流单号" suffixIcon="scan" @iconClick="scan('express_no')" />
  26. </uni-forms-item>
  27. <uni-forms-item label="追踪单号" name="express_tracking_number">
  28. <uni-easyinput
  29. v-model="valiFormData.express_tracking_number"
  30. placeholder="请输入追踪单号"
  31. suffixIcon="scan"
  32. @iconClick="scan('express_tracking_number')"
  33. />
  34. </uni-forms-item>
  35. </view>
  36. </view>
  37. </uni-forms>
  38. <view class="button-group">
  39. <button type="info" @click="reset">重置</button>
  40. <button type="primary" @click="onsubmit" :loading="loading">
  41. <uni-icons v-if="!loading" type="checkmarkempty" size="18" color="white"></uni-icons>
  42. 提交
  43. </button>
  44. </view>
  45. </view>
  46. <view class="history">
  47. <view class="item" v-for="(item, i) in historyList.slice(0, 5)" :key="i">
  48. <text class="code" :style="{ color: item.status ? 'green' : '#666' }">
  49. {{ item.orderNum }}
  50. <text v-if="item.batch_text">批次号: {{ item.batch_text }}</text>
  51. <text v-if="item.space">仓位编码: {{ item.space }}</text>
  52. {{ item.type }}
  53. </text>
  54. <uni-icons v-if="item.status" type="checkmarkempty" class="status" size="16" color="green"></uni-icons>
  55. <text class="status fail" v-else>F</text>
  56. <text style="margin-left: 10rpx; font-weight: 300">
  57. {{ '\r\n' + item.createTime }}
  58. </text>
  59. </view>
  60. </view>
  61. <uni-popup ref="message" type="message">
  62. <uni-popup-message :type="messageType" :message="messageText" :duration="2000"></uni-popup-message>
  63. </uni-popup>
  64. <uni-popup ref="printerDialog" type="dialog" :is-mask-click="false">
  65. <view style="width: 90%; margin: 0 auto; min-height: 250px; background-color: #fff; border-radius: 5px">
  66. <view class="" style="font-size: 20px; border-bottom: 1px solid #e1e1e1; padding: 15px 10px">
  67. <view>
  68. <view style="margin-bottom: 20px">
  69. <text>打印尾程面单</text>
  70. </view>
  71. <view>
  72. <view v-if="printerList">
  73. <view>
  74. <uni-data-checkbox
  75. multiple
  76. v-model="sendToPeinter"
  77. :localdata="[
  78. {
  79. text: '发送到标签打印机打印',
  80. value: 1
  81. }
  82. ]"
  83. @change="sendToPeinterFun"
  84. >
  85. >
  86. </uni-data-checkbox>
  87. <uni-data-checkbox :disabled="sendToPeinter.length === 0" v-model="selectPrinter" :localdata="printers"></uni-data-checkbox>
  88. </view>
  89. </view>
  90. </view>
  91. </view>
  92. <view style="text-align: center; position: absolute; bottom: 10px; width: 90%; display: flex; margin: 0 auto; left: 0; right: 0">
  93. <button @click="close" style="width: 35%">关闭</button>
  94. <button
  95. @click="printConfirm"
  96. :hover-stay-time="500"
  97. :loading="subLoading"
  98. :disabled="sendToPeinter.length === 0 || subLoading"
  99. class="my-bt-bg"
  100. style="width: 35%"
  101. >
  102. 打印
  103. </button>
  104. </view>
  105. </view>
  106. </view>
  107. </uni-popup>
  108. </view>
  109. </template>
  110. <script setup lang="ts">
  111. import permision from '@/common/permission.js';
  112. import { ref, reactive, nextTick, computed } from 'vue';
  113. import { onShow, onHide, onNavigationBarButtonTap } from '@dcloudio/uni-app';
  114. import { outStockScanURL, printWaybillLabelURL, getBindParamsURL, getPrinterListURL, orderInfoURL, companyListURL } from '@/utils/api.js';
  115. const message = ref();
  116. const valiForm = ref();
  117. const printerDialog = ref();
  118. const token = ref(null);
  119. const user = ref(null);
  120. const loading = ref(false);
  121. const messageType = ref('');
  122. const messageText = ref('');
  123. const batchOptions = ref([] as any);
  124. const companyOptions = ref([] as any);
  125. const orderInfo = ref({} as any);
  126. const printerList = ref({} as any);
  127. const historyList = ref([] as any);
  128. const printers = ref([] as any);
  129. const sendToPeinter = ref([1]);
  130. const selectPrinter = ref(0);
  131. const subLoading = ref(false);
  132. const focusType = ref(true);
  133. const result = ref();
  134. // 校验表单数据
  135. const valiFormData = reactive({
  136. orderNum: '',
  137. batch_number: '',
  138. typing: true,
  139. express_company_id: '',
  140. express_no: '',
  141. express_tracking_number: ''
  142. });
  143. const rules = computed(() => {
  144. return {
  145. orderNum: {
  146. rules: [
  147. {
  148. required: false,
  149. errorMessage: '单号不能为空'
  150. }
  151. ]
  152. }
  153. };
  154. });
  155. onShow(() => {
  156. focusType.value = true;
  157. loading.value = false;
  158. token.value = uni.getStorageSync('token');
  159. user.value = uni.getStorageSync('user');
  160. getBindParams();
  161. getCompanyList();
  162. getPrinterList();
  163. });
  164. onHide(() => {
  165. focusType.value = false;
  166. loading.value = false;
  167. });
  168. onNavigationBarButtonTap((event: any) => {
  169. if (event.index === 0) {
  170. uni.navigateTo({
  171. url: '/pages/outbound/instockLog'
  172. });
  173. }
  174. });
  175. const getBindParams = () => {
  176. uni.request({
  177. url: getBindParamsURL,
  178. method: 'GET',
  179. header: {
  180. batoken: token.value
  181. },
  182. success: (res: any) => {
  183. if (res.data.code === 1) {
  184. batchOptions.value = res.data.data.batch_number.map((item) => {
  185. return {
  186. text: item.name,
  187. value: item.id
  188. };
  189. });
  190. }
  191. },
  192. fail(e) {
  193. console.log('fail--', e);
  194. }
  195. });
  196. };
  197. const getCompanyList = () => {
  198. uni.request({
  199. url: companyListURL,
  200. method: 'GET',
  201. header: {
  202. batoken: token.value
  203. },
  204. data: {
  205. limit: 100,
  206. order: 'id,desc'
  207. },
  208. success: (res: any) => {
  209. if (res.data.code === 1) {
  210. companyOptions.value = res.data.data.list.map((item) => {
  211. return {
  212. text: item.name,
  213. value: item.id
  214. };
  215. });
  216. }
  217. },
  218. fail(e) {
  219. console.log('fail--', e);
  220. }
  221. });
  222. };
  223. const getPrinterList = () => {
  224. uni.request({
  225. url: getPrinterListURL,
  226. method: 'GET',
  227. header: {
  228. batoken: token.value
  229. },
  230. success: (res: any) => {
  231. if (res.data.code === 1) {
  232. printerList.value = res.data.data.printers;
  233. printers.value = Object.values(printerList.value).map((item: any) => {
  234. return {
  235. text: item.name,
  236. value: item.value
  237. };
  238. });
  239. }
  240. },
  241. fail(e) {
  242. console.log('fail--', e);
  243. }
  244. });
  245. };
  246. let st2;
  247. const orderChange = (res) => {
  248. valiFormData.orderNum = res;
  249. st2 && clearTimeout(st2);
  250. st2 = setTimeout(() => {
  251. getOrderInfoURL();
  252. clearTimeout(st2);
  253. }, 800);
  254. };
  255. const getOrderInfoURL = () => {
  256. if (valiFormData.orderNum.length === 0) {
  257. return;
  258. }
  259. uni.request({
  260. url: orderInfoURL,
  261. method: 'GET',
  262. header: {
  263. batoken: token.value
  264. },
  265. data: {
  266. order_no: valiFormData.orderNum
  267. },
  268. success: (res: any) => {
  269. if (res.data.code === 1) {
  270. orderInfo.value = res.data.data;
  271. // printerList.value = res.data.data.printers;
  272. // printers.value = Object.values(printerList.value).map((item: any) => {
  273. // return {
  274. // text: item.name,
  275. // value: item.value
  276. // };
  277. // });
  278. } else {
  279. orderInfo.value = {};
  280. messageType.value = 'error';
  281. messageText.value = res.data.msg;
  282. message.value.open();
  283. }
  284. },
  285. fail(e) {
  286. console.log('fail--', e);
  287. }
  288. });
  289. };
  290. const batchText = (batch_number) => {
  291. return batchOptions.value.find((item: any) => item.value === batch_number)?.text;
  292. };
  293. const reset = () => {
  294. loading.value = false;
  295. focusType.value = false;
  296. valiFormData.orderNum = '';
  297. valiFormData.batch_number = '';
  298. valiFormData.typing = true;
  299. nextTick(() => {
  300. focusType.value = true;
  301. });
  302. };
  303. const scan = async (key: string) => {
  304. // #ifdef APP-PLUS
  305. let status = await checkPermission();
  306. if (status !== 1) {
  307. return;
  308. }
  309. // #endif
  310. uni.scanCode({
  311. success: (res: any) => {
  312. result.value = res.result;
  313. valiFormData[key] = res.result;
  314. orderChange(res.result);
  315. // this.onsubmit();
  316. },
  317. fail: (err) => {
  318. // 需要注意的是小程序扫码不需要申请相机权限
  319. }
  320. });
  321. };
  322. // #ifdef APP-PLUS
  323. const checkPermission = async () => {
  324. let status = permision.isIOS ? await permision.requestIOS('camera') : await permision.requestAndroid('android.permission.CAMERA');
  325. if (status === null || status === 1) {
  326. status = 1;
  327. } else {
  328. uni.showModal({
  329. content: 'Camera permission required',
  330. confirmText: 'Setting',
  331. success: function (res) {
  332. if (res.confirm) {
  333. permision.gotoAppSetting();
  334. }
  335. }
  336. });
  337. }
  338. return status;
  339. };
  340. // #endif
  341. let st;
  342. const warehouseScan = async () => {
  343. st && clearTimeout(st);
  344. scanOutstock();
  345. };
  346. const scanOutstock = () => {
  347. console.log(valiFormData.orderNum, valiFormData.batch_number);
  348. if (valiFormData.orderNum.length === 0 && valiFormData.batch_number.length === 0) {
  349. messageType.value = 'error';
  350. messageText.value = '单号或批次号至少填一个';
  351. message.value.open();
  352. return;
  353. }
  354. loading.value = true;
  355. uni.request({
  356. url: outStockScanURL,
  357. method: 'POST',
  358. header: {
  359. batoken: token.value
  360. },
  361. data: {
  362. order_no: valiFormData.orderNum,
  363. batch_number: valiFormData.batch_number,
  364. express_company_id: valiFormData.express_company_id,
  365. express_no: valiFormData.express_no,
  366. express_tracking_number: valiFormData.express_tracking_number
  367. },
  368. success: (res: any) => {
  369. loading.value = false;
  370. if (res.data.code == 1) {
  371. messageType.value = 'success';
  372. messageText.value = res.data.msg;
  373. message.value.open();
  374. const historyItem = {
  375. orderNum: valiFormData.orderNum,
  376. batch_text: batchText(valiFormData.batch_number),
  377. createTime: new Date(),
  378. type: '出库',
  379. status: true
  380. };
  381. historyList.value.unshift(historyItem);
  382. historyList.value.length > 10 && (historyList.value.length = 10);
  383. uni.setStorageSync('outboundHistory', historyList.value);
  384. getHistory();
  385. if (res.data.data.labels && res.data.data.labels.length > 0) {
  386. console.log('有打印面单');
  387. // selectPrinter.value = sendToPeinter.value.length > 0 ? printers.value[0].value : 0;
  388. // printerDialog.value.open();
  389. } else {
  390. st = setTimeout(() => {
  391. reset();
  392. st && clearTimeout(st);
  393. }, 700);
  394. }
  395. } else {
  396. messageType.value = 'error';
  397. messageText.value = res.data.msg;
  398. message.value.open();
  399. const historyItem = {
  400. orderNum: valiFormData.orderNum,
  401. batch_text: batchText(valiFormData.batch_number),
  402. createTime: new Date(),
  403. type: '出库',
  404. status: false
  405. };
  406. historyList.value.unshift(historyItem);
  407. historyList.value.length > 10 && (historyList.value.length = 10);
  408. uni.setStorageSync('outboundHistory', historyList.value);
  409. getHistory();
  410. st = setTimeout(() => {
  411. reset();
  412. st && clearTimeout(st);
  413. }, 700);
  414. }
  415. }
  416. });
  417. };
  418. const close = () => {
  419. printerDialog.value.close();
  420. st = setTimeout(() => {
  421. reset();
  422. st && clearTimeout(st);
  423. }, 700);
  424. };
  425. const sendToPeinterFun = (res: any) => {
  426. if (!res.detail.value.length > 0) {
  427. selectPrinter.value = 0;
  428. } else {
  429. selectPrinter.value = printers.value[0].value;
  430. }
  431. };
  432. const printConfirm = () => {
  433. subLoading.value = true;
  434. uni.request({
  435. url: printWaybillLabelURL,
  436. method: 'POST',
  437. header: {
  438. batoken: token.value
  439. },
  440. data: {
  441. order_no: valiFormData.orderNum,
  442. printer_code: selectPrinter
  443. },
  444. success: (res) => {
  445. close();
  446. subLoading.value = false;
  447. console.log('打印成功', res);
  448. messageType.value = 'success';
  449. messageText.value = '打印成功';
  450. message.value.open();
  451. }
  452. });
  453. };
  454. const onsubmit = () => {
  455. valiForm.value
  456. .validate()
  457. .then((res) => {
  458. warehouseScan();
  459. })
  460. .catch((err) => {
  461. console.log('err', err);
  462. });
  463. };
  464. const getHistory = () => {
  465. historyList.value = uni.getStorageSync('outboundHistory');
  466. };
  467. </script>
  468. <style lang="scss">
  469. .example {
  470. padding: 15px;
  471. padding-top: 30px;
  472. background-color: #fff;
  473. }
  474. .checkbox-cum {
  475. margin-bottom: 20rpx;
  476. font-size: 14rpx;
  477. }
  478. .sub-choice {
  479. margin-bottom: 20rpx;
  480. margin-left: 20rpx;
  481. font-size: 14rpx;
  482. }
  483. .button-group {
  484. margin-top: 15px;
  485. display: flex;
  486. flex-direction: row;
  487. justify-content: space-around;
  488. button {
  489. display: flex;
  490. align-items: center;
  491. justify-content: center;
  492. height: 35px;
  493. width: 50%;
  494. margin-left: 10px;
  495. font-size: 16rpx;
  496. }
  497. .uni-icons {
  498. margin-right: 10px;
  499. }
  500. }
  501. .weight-right {
  502. padding-right: 10rpx;
  503. font-size: 14rpx;
  504. }
  505. .history {
  506. display: flex;
  507. width: 100%;
  508. flex-direction: column;
  509. justify-items: start;
  510. .title {
  511. padding: 20rpx;
  512. font-size: 24rpx;
  513. font-weight: 600;
  514. }
  515. .code {
  516. font-weight: 600;
  517. }
  518. .item {
  519. padding: 20rpx;
  520. font-size: 20rpx;
  521. color: #666;
  522. .status {
  523. padding-left: 20rpx;
  524. }
  525. .fail {
  526. font-weight: 600;
  527. color: #f00;
  528. }
  529. }
  530. }
  531. </style>
  532. <style scoped>
  533. .upload-container {
  534. padding: 16px;
  535. }
  536. .preview {
  537. display: flex;
  538. flex-wrap: wrap;
  539. }
  540. .image-container {
  541. position: relative;
  542. margin-right: 10px;
  543. margin-bottom: 10px;
  544. }
  545. .preview-image {
  546. width: 130rpx;
  547. height: 130rpx;
  548. }
  549. .delete-icon {
  550. position: absolute;
  551. top: 0;
  552. right: 0;
  553. cursor: pointer;
  554. }
  555. .choose-image-container {
  556. width: 130rpx;
  557. /* 与图片大小一致 */
  558. height: 130rpx;
  559. /* 与图片大小一致 */
  560. display: flex;
  561. align-items: center;
  562. justify-content: center;
  563. background-color: #fff;
  564. /* 背景颜色为白色 */
  565. border: 1px dashed #cccccc;
  566. /* 虚线边框 */
  567. border-radius: 5px;
  568. /* 圆角 */
  569. cursor: pointer;
  570. }
  571. .preview-modal {
  572. position: fixed;
  573. top: 0;
  574. left: 0;
  575. right: 0;
  576. bottom: 0;
  577. background-color: rgba(0, 0, 0, 0.8);
  578. /* 半透明背景 */
  579. display: flex;
  580. align-items: center;
  581. justify-content: center;
  582. z-index: 999;
  583. }
  584. .preview-large {
  585. max-width: 90%;
  586. /* 最大宽度 */
  587. max-height: 90%;
  588. /* 最大高度 */
  589. }
  590. .progress-bar {
  591. position: absolute;
  592. bottom: 0;
  593. left: 0;
  594. width: 100%;
  595. }
  596. </style>