StockInScan.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <template>
  2. <van-nav-bar
  3. :title="title" left-arrow left-text="返回" fixed placeholder right-text="返回菜单" @click-left="goBack()"
  4. @click-right="goMenu"
  5. />
  6. <div class="content">
  7. <div class="scan-btn">
  8. <van-button block plain icon="orders-o" type="primary">
  9. 等待扫码数据
  10. </van-button>
  11. </div>
  12. <van-form :scroll-to-error="true">
  13. <van-field v-model="stockData.no" name="no" label="物料编号:" :readonly="true" />
  14. <van-field v-model="stockData.name" name="name" label="物料名称:" :readonly="true" />
  15. <van-field v-model="stockData.type" name="type" label="规格型号:" :readonly="true" />
  16. <van-field v-model="stockData.batchNo" name="batchNo" label="批号:" placeholder="点击输入批号" type="textarea" rows="1" autosize />
  17. <van-field v-model="stockData.num" name="num" label="生产数量:" placeholder="点击输入生产数量" />
  18. <van-field name="inventoryPackaged" label="是否包装">
  19. <template #input>
  20. <van-switch v-model="stockData.inventoryPackaged" size="20px" />
  21. </template>
  22. </van-field>
  23. <van-field v-model="stockData.carrierTypeName" name="carrierTypeName" label="托盘类型:" placeholder="点击选择托盘类型" is-link @click="showCarrierTypePicker = true" />
  24. <van-field
  25. v-model="stockData.transferName" is-link readonly name="transfer" label="中转区货位:"
  26. placeholder="点击选择中转区货位" @click="isShowTransfer = true"
  27. />
  28. <van-field
  29. v-model="stockData.idleName" is-link readonly name="warehouse" label="入库货位:" placeholder="点击选择入库货位"
  30. @click="isShowIdle = true"
  31. />
  32. <div style="margin: 16px">
  33. <van-button round block type="primary" @click="submit">
  34. 提交
  35. </van-button>
  36. </div>
  37. </van-form>
  38. </div>
  39. <div>
  40. <position-selector
  41. ref="transferPositionSelector" v-model:show="isShowTransfer" position-type="transfer"
  42. @confirm="onTransferPositionSelected"
  43. />
  44. <position-selector
  45. ref="idlePositionSelector" v-model:show="isShowIdle" position-type="idle" type="stockInPhoto"
  46. :is-default="true" @confirm="onIdlePositionSelected"
  47. />
  48. </div>
  49. <van-popup v-model:show="showCarrierTypePicker" destroy-on-close round position="bottom">
  50. <van-picker
  51. :model-value="carrierType"
  52. :columns="columns"
  53. @cancel="showCarrierTypePicker = false"
  54. @confirm="onConfirm"
  55. />
  56. </van-popup>
  57. </template>
  58. <script setup>
  59. import { ref, onMounted, onUnmounted } from 'vue';
  60. import { useRouter, useRoute } from 'vue-router';
  61. import PositionSelector from './PositionSelector.vue';
  62. import { processException } from '../common/Common.js';
  63. import { ajaxApiGet, ajaxApiPost } from '../common/utils.js';
  64. import { showSuccessToast, showFailToast, showConfirmDialog } from 'vant';
  65. import { showFullscreenLoading, hideFullscreenLoading } from '../common/loading.js';
  66. const route = useRoute();
  67. const router = useRouter();
  68. // WebSocket实例
  69. const ws = ref(null);
  70. // 状态
  71. const isShowTransfer = ref(false);
  72. const isShowIdle = ref(false);
  73. const transferPositionSelector = ref(null);
  74. const idlePositionSelector = ref(null);
  75. const stockData = ref({
  76. no: '',
  77. name: '',
  78. type: '',
  79. num: '',
  80. batchNo: '',
  81. transferId: '',
  82. transferName: '',
  83. transferNo: '',
  84. idleId: '',
  85. idleName: '',
  86. idleNo: '',
  87. inventoryPackaged: false,
  88. carrierType:'',
  89. carrierTypeName: '',
  90. });
  91. const warehouseId = ref('');
  92. const title = ref('');
  93. onMounted(() => {
  94. warehouseId.value = route.query.warehouseId || '';
  95. title.value = route.query.warehouseName ? '扫描入库 - ' + route.query.warehouseName : '扫描入库';
  96. getCarrierTypeList();
  97. // 建立WebSocket连接
  98. initWebSocket();
  99. });
  100. onUnmounted(() => {
  101. // 关闭WebSocket连接
  102. if (ws.value) {
  103. ws.value.close();
  104. }
  105. });
  106. // 初始化WebSocket连接
  107. const initWebSocket = () => {
  108. const wsUrl = 'ws://127.0.0.1:10023';
  109. ws.value = new WebSocket(wsUrl);
  110. ws.value.onopen = () => {
  111. console.log('WebSocket连接已建立');
  112. };
  113. ws.value.onmessage = event => {
  114. handleWebSocketMessage(event);
  115. };
  116. ws.value.onerror = error => {
  117. console.error('WebSocket错误:', error);
  118. };
  119. ws.value.onclose = () => {
  120. console.log('WebSocket连接已关闭');
  121. };
  122. };
  123. // 处理WebSocket消息
  124. const handleWebSocketMessage = event => {
  125. try {
  126. // 解析接收到的JSON数据
  127. const data = JSON.parse(event.data);
  128. console.log('接收到WebSocket数据:', data);
  129. // 将数据赋值到对应字段
  130. if (data.batchNumber) {
  131. stockData.value.batchNo = data.batchNumber;
  132. }
  133. if (data.count) {
  134. stockData.value.num = data.count.toString();
  135. }
  136. // 使用code调用getInfo方法
  137. if (data.code) {
  138. getInfo(data.code);
  139. }
  140. } catch (error) {
  141. console.error('解析WebSocket消息失败:', error);
  142. showFailToast('解析数据失败,请检查数据格式');
  143. }
  144. };
  145. const columns = ref([]);
  146. const showCarrierTypePicker = ref(false);
  147. const carrierType = ref([]);
  148. const onConfirm = ({ selectedValues, selectedOptions }) => {
  149. showCarrierTypePicker.value = false;
  150. stockData.value.carrierType = selectedValues[0];
  151. stockData.value.carrierTypeName = selectedOptions[0].text;
  152. };
  153. const goBack = () => {
  154. router.back();
  155. };
  156. const goMenu = () => {
  157. router.push('/app-menus');
  158. };
  159. // 处理中转区货位选择结果
  160. const onTransferPositionSelected = item => {
  161. stockData.value.transferId = item.id;
  162. stockData.value.transferNo = item.no;
  163. stockData.value.transferName = item.name;
  164. };
  165. // 处理入库货位选择结果
  166. const onIdlePositionSelected = item => {
  167. stockData.value.idleId = item.id;
  168. stockData.value.idleNo = item.no;
  169. stockData.value.idleName = item.name;
  170. };
  171. // 清除表单数据
  172. const clearFormData = () => {
  173. stockData.value = {
  174. id: '',
  175. no: '',
  176. name: '',
  177. type: '',
  178. num: '',
  179. batchNo: '',
  180. transferId: '',
  181. transferName: '',
  182. transferNo: '',
  183. idleId: '',
  184. idleName: '',
  185. idleNo: '',
  186. inventoryPackaged: false,
  187. carrierType:'',
  188. carrierTypeName:'',
  189. };
  190. carrierType.value = [];
  191. transferPositionSelector.value.clearSelected();
  192. idlePositionSelector.value.clearSelected();
  193. };
  194. // 提交入库
  195. const submit = () => {
  196. console.log(stockData.value);
  197. if (!stockData.value.no) {
  198. showFailToast({ duration: 4000, message: '请等待扫码数据并确认入库信息后再提交' });
  199. return;
  200. }
  201. if (!stockData.value.num) {
  202. showFailToast('请输入入库数量');
  203. return;
  204. }
  205. if (!stockData.value.transferId) {
  206. showFailToast('请选择中转区货位');
  207. return;
  208. }
  209. if (!stockData.value.idleId) {
  210. showFailToast('请选择入库货位');
  211. return;
  212. }
  213. if (!stockData.value.carrierType) {
  214. showFailToast('请选择托盘类型');
  215. return;
  216. }
  217. showConfirmDialog({
  218. title: '确认要入库吗?',
  219. message: '如果确认要入库,请点击【确认】按钮,否则点击【取消】按钮。',
  220. })
  221. .then(() => {
  222. submitStockIn();
  223. })
  224. .catch(() => {
  225. console.log('取消');
  226. });
  227. };
  228. // 获取入库物料详情
  229. const getInfo = no => {
  230. showFullscreenLoading();
  231. const url = `/api/InventoryResource/queryStockInInventory?start=0&length=1&filter=${no}`;
  232. ajaxApiGet(url).then(
  233. success => {
  234. const { errorCode, errorMessage, datas } = success;
  235. if (errorCode === 0) {
  236. if (datas && datas.length) {
  237. stockData.value = { ...stockData.value, ...datas[0] };
  238. showSuccessToast({ duration: 1000, message: '获取信息成功。' });
  239. }
  240. } else {
  241. showFailToast({ duration: 1000, message: errorMessage });
  242. }
  243. hideFullscreenLoading();
  244. },
  245. error => {
  246. hideFullscreenLoading();
  247. processException(error);
  248. },
  249. );
  250. };
  251. // 提交API
  252. const submitStockIn = () => {
  253. const url = '/api/stockInResource/scanGeneratorStockIn';
  254. const params = JSON.parse(JSON.stringify(stockData.value));
  255. delete params.workDate;
  256. ajaxApiPost(url, params).then(
  257. success => {
  258. if (success.errorCode === 0) {
  259. showSuccessToast('入库成功');
  260. clearFormData();
  261. } else {
  262. showFailToast(success.errorMessage);
  263. }
  264. },
  265. error => {
  266. processException(error);
  267. },
  268. );
  269. };
  270. const getCarrierTypeList = () => {
  271. const url = '/api/CarrierTypeResource/queryAllType';
  272. ajaxApiGet(url).then(
  273. success => {
  274. const { errorCode, errorMessage, datas, total } = success;
  275. if (errorCode === 0) {
  276. if (datas && datas.length) {
  277. columns.value = datas.map(item => ({ text: item.name, value: item.id }));
  278. } else {
  279. columns.value = [];
  280. }
  281. } else {
  282. showFailToast(errorMessage);
  283. }
  284. },
  285. error => {
  286. processException(error);
  287. },
  288. );
  289. };
  290. </script>
  291. <style scoped>
  292. .content {
  293. margin-top: 10px;
  294. }
  295. .scan-btn {
  296. margin: 0 10px;
  297. }
  298. .custom-picker {
  299. display: flex !important;
  300. flex-direction: column !important;
  301. height: 100% !important;
  302. }
  303. .picker-header {
  304. display: flex !important;
  305. justify-content: space-between !important;
  306. align-items: center !important;
  307. padding: 10px 16px !important;
  308. border-bottom: 1px solid #ebedf0 !important;
  309. }
  310. .picker-title {
  311. font-size: 16px !important;
  312. font-weight: 500 !important;
  313. }
  314. .picker-content {
  315. flex: 1 !important;
  316. overflow-y: auto !important;
  317. }
  318. .picker-footer {
  319. padding: 10px 16px !important;
  320. border-top: 1px solid #ebedf0 !important;
  321. display: flex !important;
  322. flex-direction: column !important;
  323. gap: 8px !important;
  324. }
  325. .loading-more {
  326. text-align: center !important;
  327. color: #969799 !important;
  328. padding: 10px 0 !important;
  329. }
  330. </style>