index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. <template>
  2. <div>
  3. <el-row :gutter="20">
  4. <el-col :span="8">
  5. <div class="type-title">载具入库</div>
  6. <ScanCodeInput
  7. v-model="boxSearch"
  8. :clearable="true"
  9. :showSuffix="true"
  10. placeholder="请扫描或输入料箱编号"
  11. @keyup.enter="enterBox"
  12. :disabled="outStatus || addStatus"
  13. />
  14. <el-scrollbar class="boxes-container base-container">
  15. <div
  16. v-for="(item, index) in boxesList"
  17. :key="index"
  18. :class="{ 'box-selected': index === currentBoxIndex }"
  19. class="box-item"
  20. @click="clickBox(item, index)"
  21. >
  22. <div class="box-header">
  23. <div class="box-name">{{ item.vehicleNo }}</div>
  24. <div class="box-name">
  25. {{ item.locationNo ? item.locationNo : "-" }}
  26. </div>
  27. </div>
  28. <div
  29. v-for="(material, index) in item.list"
  30. :key="index"
  31. class="box-info"
  32. >
  33. <div>{{ material.materialName }}</div>
  34. <div>{{ material.num }}</div>
  35. </div>
  36. </div>
  37. </el-scrollbar>
  38. <el-button
  39. class="sureBtn"
  40. type="primary"
  41. :disabled="outStatus || addStatus || Machinery"
  42. @click="sureToOut"
  43. >出库
  44. </el-button>
  45. </el-col>
  46. <el-col :span="8">
  47. <div class="type-title">盒内原有物料</div>
  48. <el-scrollbar class="outing-container">
  49. <div style="height: 300px" v-if="Machinery">
  50. <MachineryLoading />
  51. </div>
  52. <div v-if="!outStatus">
  53. <div class="addbox" v-if="addSuccessStatus">
  54. 扫码的盒子编码:{{ addBoxInfo.code }}
  55. </div>
  56. <ScanCodeInput
  57. v-if="addStatus && addSuccessStatus == false"
  58. v-model="addBoxSearch"
  59. :clearable="true"
  60. :showSuffix="true"
  61. placeholder="请扫描或输入料箱编号"
  62. @keyup.enter="addenterBox"
  63. />
  64. <el-button
  65. class="sureBtn"
  66. :type="addStatus ? 'info' : 'primary'"
  67. @click="changeAddStatus"
  68. :disabled="Machinery"
  69. style="margin-bottom: 20px"
  70. >{{ addStatus ? "取消" : "入库新载具" }}
  71. </el-button>
  72. </div>
  73. <div
  74. v-for="(material, index) in outingMaterials"
  75. :key="index"
  76. class="outing-box"
  77. >
  78. <div class="outing-box-header">
  79. <div class="material-title">
  80. {{ material.materialName }} | {{ material.materialNo }}
  81. </div>
  82. <!-- <div
  83. @click=""
  84. class="material-title deleteSty"
  85. >
  86. 删除
  87. </div> -->
  88. <i-ep-delete
  89. class="delete-btn"
  90. color="#ff4d4f"
  91. size="20px"
  92. @click="deleteIndexOld(index)"
  93. />
  94. </div>
  95. <div class="material-info">
  96. <div>批次号/序列号</div>
  97. <el-tooltip :content="material.batchCode" placement="top">
  98. <div class="batchCode">{{ material.batchCode }}</div>
  99. </el-tooltip>
  100. </div>
  101. <div class="material-info">
  102. <div>位置</div>
  103. <div>{{ material.locationNo }}</div>
  104. </div>
  105. <div class="material-info">
  106. <div>量值</div>
  107. <div>{{ material.num }}</div>
  108. </div>
  109. </div>
  110. </el-scrollbar>
  111. <el-button
  112. class="sureBtn"
  113. type="info"
  114. @click="backToStorage"
  115. :disabled="Machinery || !boxStatus"
  116. >入库
  117. </el-button>
  118. </el-col>
  119. <el-col :span="8">
  120. <div class="type-title">请扫码物料</div>
  121. <ScanCodeInput
  122. v-model="scanCodeInput"
  123. placeholder="请扫描或输入物料编码"
  124. @keyup.enter="handleScanCodeInput"
  125. />
  126. <el-scrollbar class="base-container">
  127. <div class="list-container">
  128. <div
  129. v-for="(item, index) in materialList"
  130. :key="index"
  131. class="list-box"
  132. >
  133. <div class="list-box-header">
  134. <div>
  135. <div>
  136. <div class="name">{{ item.materialName }}</div>
  137. <div class="spec">{{ item.spec }}</div>
  138. </div>
  139. <div class="bottom">
  140. <NumberInput v-model="item.num" />
  141. <span class="unit">{{ item.unitDictLabel }}</span>
  142. </div>
  143. </div>
  144. <!-- <div
  145. @click="deleteIndexScanAdd(index)"
  146. class="list-box-delete deleteSty"
  147. >
  148. 删除
  149. </div> -->
  150. <i-ep-delete
  151. class="delete-btn"
  152. color="#ff4d4f"
  153. size="20px"
  154. @click="deleteIndexScanAdd(index)"
  155. />
  156. </div>
  157. </div>
  158. </div>
  159. </el-scrollbar>
  160. <el-button
  161. class="sureBtn"
  162. type="primary"
  163. @click="sureToAdd"
  164. :disabled="Machinery || materialList.length < 1 || !boxStatus"
  165. >确认添加
  166. </el-button>
  167. </el-col>
  168. </el-row>
  169. </div>
  170. </template>
  171. <script lang="ts" setup>
  172. //料箱
  173. import {
  174. getDestinationList,
  175. getMaterialInfoByLabel,
  176. } from "@/api/process/materialFlow";
  177. import { getVehicleInfo } from "@/api/prepare";
  178. import {
  179. backToStorageAPI,
  180. getStorageBoxesList,
  181. toOutBox,
  182. inBoxAPI,
  183. } from "@/api/storage/in";
  184. import { getStorageOrderInfo } from "@/api/storage/out";
  185. import { useCommonStoreHook } from "@/store";
  186. const commonS = useCommonStoreHook();
  187. const boxSearch = ref("");
  188. const deleteIndexOld = (index: any) => {
  189. outingMaterials.value.splice(index, 1);
  190. ElMessage.success("操作成功!");
  191. };
  192. const deleteIndexScanAdd = (index: any) => {
  193. materialList.value.splice(index, 1);
  194. ElMessage.success("操作成功!");
  195. };
  196. //硬件操作期间的状态:false 表示未有操作 true表示硬件在操作中
  197. const Machinery = ref(false);
  198. //盒内原有物料data
  199. const boxesList = ref<any[]>([]);
  200. //新加载具的开起状态
  201. const addStatus = ref(false);
  202. //新加载具的成功状态
  203. const addSuccessStatus = ref(false);
  204. //新加载具的内容
  205. const addBoxInfo = ref();
  206. //库中选中的item
  207. const currentBox = ref<any>({});
  208. //绑定当前盒子的状态
  209. const boxStatus = ref(false);
  210. const currentBoxIndex = ref(-1);
  211. //库中选中的状态
  212. const outStatus = ref(false);
  213. //新扫时候的input值
  214. const addBoxSearch = ref("");
  215. const resetData = () => {
  216. addSuccessStatus.value = false;
  217. addBoxInfo.value = null;
  218. addBoxSearch.value = "";
  219. outingBox.value = {};
  220. outingRawBoxList = [];
  221. outingMaterials.value = [];
  222. materialList.value = [];
  223. currentBox.value = {};
  224. currentBoxIndex.value = -1;
  225. boxSearch.value = "";
  226. addStatus.value = false;
  227. outStatus.value = false;
  228. boxStatus.value = false;
  229. };
  230. const changeAddStatus = () => {
  231. if (addStatus.value == true) {
  232. resetData();
  233. } else {
  234. addStatus.value = !addStatus.value;
  235. }
  236. };
  237. //扫盒子
  238. const addenterBox = () => {
  239. addBoxSearch.value = addBoxSearch.value.trim();
  240. getVehicleInfo(addBoxSearch.value ? addBoxSearch.value : {}).then((res) => {
  241. addBoxInfo.value = res.data;
  242. addSuccessStatus.value = true;
  243. outingBox.value.vehicleNo = addBoxInfo.value.code;
  244. outingBox.value.vehicleId = addBoxInfo.value.id;
  245. outingBox.value.houseNo = "1";
  246. ElMessage.success("扫码成功!");
  247. boxStatus.value = true;
  248. });
  249. };
  250. //出库
  251. const enterBox = () => {
  252. boxSearch.value = boxSearch.value.trim();
  253. getStorageBoxesList(
  254. boxSearch.value ? { vehicleCode: boxSearch.value } : {}
  255. ).then((res) => {
  256. boxesList.value = res.data;
  257. commonS.orderInId = res.data.orderId;
  258. });
  259. };
  260. const clickBox = (box: any, index: number) => {
  261. currentBox.value = box;
  262. currentBoxIndex.value = index;
  263. };
  264. //出库操作
  265. const sureToOut = async () => {
  266. let res = await toOutBox({
  267. vehicleNo: currentBox.value.vehicleNo,
  268. houseNo: currentBox.value.houseNo,
  269. });
  270. ElMessage.success("出库成功,等待料箱到达捡料位置");
  271. commonS.orderInId = res.data.orderId;
  272. commonS.changeBoxType = 1;
  273. Machinery.value = true;
  274. checkHasOuting();
  275. outStatus.value = true;
  276. };
  277. //定时器任务
  278. let outInterval: number = -1;
  279. const checkHasOuting = () => {
  280. // 点击出库之后,需要在其他地方(机器)确实已经到达检料位置,才能进行捡料
  281. // 接口需要传入出库outBox接口返回的orderId转为id传上去, 如果有这个orderId就开始轮询
  282. // 如果没有数据则停止轮询。数据返回的state是1处理中2启用3处理异常4已完成, 当为3或者4的时候停止轮询,并且在派发后清空oderid。
  283. if (commonS.orderInId != -1) {
  284. outInterval = setInterval(() => {
  285. getStorageOrderInfo({ id: commonS.orderInId })
  286. .then((res: any) => {
  287. if (res.data) {
  288. if (res.data.state === 3 || res.data.state === 4) {
  289. clearInterval(outInterval);
  290. commonS.orderInId = -1;
  291. if (commonS.changeBoxType === 1) {
  292. outingBox.value = currentBox.value;
  293. if (outingBox.value.list.length > 0) {
  294. outingMaterials.value = outingBox.value.list;
  295. outingRawBoxList = JSON.parse(
  296. JSON.stringify(outingBox.value.list)
  297. );
  298. } else {
  299. outingRawBoxList = [];
  300. outingMaterials.value = [];
  301. }
  302. getStorageBoxesList({}).then((res: any) => {
  303. boxesList.value = res.data;
  304. });
  305. currentBoxIndex.value = -1;
  306. ElMessage.success("料箱已到达");
  307. Machinery.value = false;
  308. boxStatus.value = true;
  309. } else {
  310. resetData();
  311. getStorageBoxesList({}).then((res) => {
  312. boxesList.value = res.data;
  313. });
  314. ElMessage.success("料箱入库成功");
  315. Machinery.value = false;
  316. }
  317. } else {
  318. outStatus.value = false;
  319. ElMessage.error(res.msg);
  320. }
  321. } else {
  322. clearInterval(outInterval);
  323. }
  324. })
  325. .catch(() => {
  326. clearInterval(outInterval);
  327. commonS.orderInId = -1;
  328. Machinery.value = false;
  329. });
  330. }, 5000);
  331. }
  332. };
  333. let outingRawBoxList: any[] = []; //确认添加的时候会对比捡料位和扫描的数量,多次点击会导致捡料位的数字一直改变,所以用一个变量来保存原始的捡料位数据
  334. const outingBox = ref<any>({});
  335. const outingMaterials = ref<any[]>([]);
  336. const backToStorage = async () => {
  337. const { data } = await backToStorageAPI({
  338. vehicleCode: outingBox.value.vehicleNo,
  339. houseNo: outingBox.value.houseNo,
  340. detailsList: outingMaterials.value,
  341. });
  342. outingBox.value = {};
  343. outingMaterials.value = [];
  344. ElMessage.success("返库操作成功,等待料箱到达");
  345. commonS.changeBoxType = 2;
  346. commonS.orderInId = data.orderId;
  347. Machinery.value = true;
  348. checkHasOuting();
  349. };
  350. // 物料
  351. const scanCodeInput = ref("");
  352. //扫码后的array
  353. const materialList = ref<any>([]);
  354. // 扫码料码
  355. const handleScanCodeInput = () => {
  356. getMaterialInfoByLabel(scanCodeInput.value).then((res) => {
  357. //seq是序列号唯一的
  358. if (res.data.codeType == "SEQ") {
  359. res.data.seqNo = scanCodeInput.value;
  360. } else {
  361. res.data.seqNo = "";
  362. }
  363. materialList.value.push({ ...res.data });
  364. scanCodeInput.value = "";
  365. ElMessage.success("扫码成功!");
  366. });
  367. };
  368. //确认添加:只是页面静态添加 不做接口发送
  369. const sureToAdd = () => {
  370. for (let i = 0; i < materialList.value.length; i++) {
  371. let status = false;
  372. for (let b = 0; b < outingMaterials.value.length; b++) {
  373. if (materialList.value[i].seqNo != "") {
  374. outingMaterials.value.push({
  375. ...materialList.value[i],
  376. materialNo: materialList.value[i].materialCode,
  377. unit: materialList.value[i].unitDictValue,
  378. });
  379. status = true;
  380. } else {
  381. if (
  382. materialList.value[i].materialCode ==
  383. outingMaterials.value[b].materialNo &&
  384. materialList.value[i].materialName ==
  385. outingMaterials.value[b].materialName &&
  386. materialList.value[i].batchCode == outingMaterials.value[b].batchCode
  387. ) {
  388. outingMaterials.value[b].num =
  389. outingMaterials.value[b].num + materialList.value[i].num;
  390. status = true;
  391. }
  392. }
  393. }
  394. if (status == false) {
  395. outingMaterials.value.push({
  396. ...materialList.value[i],
  397. materialNo: materialList.value[i].materialCode,
  398. unit: materialList.value[i].unitDictValue,
  399. });
  400. }
  401. }
  402. materialList.value = [];
  403. ElMessage.success("添加成功!");
  404. };
  405. // 流转终点
  406. const destinationList = ref([]);
  407. onMounted(() => {
  408. getDestinationList(1).then((res) => {
  409. destinationList.value = res.data;
  410. });
  411. getStorageBoxesList({}).then((res) => {
  412. boxesList.value = res.data;
  413. });
  414. checkHasOuting();
  415. });
  416. </script>
  417. <style lang="scss" scoped>
  418. .type-title {
  419. font-weight: 500;
  420. font-size: 30px;
  421. color: rgba(0, 0, 0, 0.9);
  422. text-align: left;
  423. margin-bottom: 15px;
  424. }
  425. .base-container {
  426. width: 100%;
  427. margin-top: 15px;
  428. height: calc(100vh - 420px);
  429. }
  430. .boxes-container {
  431. .box-item {
  432. background: #fff;
  433. border-radius: 16px;
  434. margin-bottom: 15px;
  435. padding: 10px 8px;
  436. }
  437. .box-selected {
  438. border: 2px solid #0a59f7;
  439. }
  440. .box-header {
  441. display: flex;
  442. justify-content: space-between;
  443. }
  444. .box-name {
  445. font-size: 20px;
  446. line-height: 40px;
  447. }
  448. .box-info {
  449. display: flex;
  450. justify-content: space-between;
  451. align-items: center;
  452. font-weight: bolder;
  453. font-size: 24px;
  454. }
  455. }
  456. .deleteSty {
  457. color: #0a59f7;
  458. }
  459. .outing-container {
  460. height: calc(100vh - 354px);
  461. .outing-box {
  462. background: #fff;
  463. border-radius: 16px;
  464. margin-bottom: 15px;
  465. padding: 10px 8px;
  466. }
  467. .outing-box-header {
  468. display: flex;
  469. justify-content: space-between;
  470. }
  471. .material-title {
  472. font-weight: bolder;
  473. font-size: 24px;
  474. text-align: left;
  475. }
  476. .material-info {
  477. font-size: 20px;
  478. display: flex;
  479. justify-content: space-between;
  480. align-items: center;
  481. }
  482. .batchCode {
  483. max-width: 60%;
  484. flex-shrink: 0;
  485. overflow: hidden;
  486. text-overflow: ellipsis;
  487. }
  488. }
  489. .list-container {
  490. width: 100%;
  491. display: grid;
  492. /*行间距*/
  493. grid-row-gap: 24px;
  494. /*列间距*/
  495. //grid-column-gap: 24px;
  496. grid-template-columns: 1fr;
  497. .list-box {
  498. height: 210px;
  499. background: #fff;
  500. border-radius: 16px 16px 16px 16px;
  501. display: flex;
  502. flex-direction: column;
  503. justify-content: space-between;
  504. align-items: start;
  505. padding: 30px 30px;
  506. .list-box-header {
  507. width: 100%;
  508. display: flex;
  509. justify-content: space-between;
  510. }
  511. .list-box-delete {
  512. text-align: right;
  513. font-weight: bolder;
  514. font-size: 24px;
  515. }
  516. .name {
  517. font-weight: 500;
  518. font-size: 24px;
  519. color: rgba(0, 0, 0, 0.9);
  520. text-align: left;
  521. }
  522. .spec {
  523. font-size: 20px;
  524. color: rgba(0, 0, 0, 0.6);
  525. text-align: left;
  526. }
  527. .bottom {
  528. display: flex;
  529. justify-content: start;
  530. align-items: end;
  531. }
  532. .unit {
  533. font-weight: 500;
  534. font-size: 24px;
  535. color: rgba(0, 0, 0, 0.6);
  536. text-align: left;
  537. margin-left: 5px;
  538. }
  539. }
  540. }
  541. .destination {
  542. height: calc(100vh - 350px);
  543. margin-top: 15px;
  544. .end-box {
  545. height: 80px;
  546. background: #ffffff;
  547. border-radius: 40px;
  548. display: flex;
  549. justify-content: center;
  550. align-items: center;
  551. font-weight: 500;
  552. font-size: 24px;
  553. color: rgba(0, 0, 0, 0.9);
  554. display: flex;
  555. justify-content: space-evenly;
  556. align-items: center;
  557. }
  558. .end-box:not(:last-child) {
  559. margin-bottom: 15px;
  560. }
  561. .selected {
  562. border-radius: 40px;
  563. border: 2px solid rgba(10, 89, 247, 1);
  564. }
  565. .name {
  566. font-weight: 500;
  567. font-size: 24px;
  568. color: rgba(0, 0, 0, 0.9);
  569. }
  570. }
  571. .sureBtn {
  572. height: 80px;
  573. font-size: 24px;
  574. //background: #0a59f7;
  575. border-radius: 76px 76px 76px 76px;
  576. width: 100%;
  577. margin-top: 10px;
  578. }
  579. .addbox {
  580. margin-bottom: 10px;
  581. border-radius: 16px;
  582. background-color: white;
  583. text-align: center;
  584. padding: 20px 0;
  585. font-size: $f24;
  586. }
  587. </style>