diff --git a/klp-ui/src/api/wms/rawMaterial.js b/klp-ui/src/api/wms/rawMaterial.js index fe8ef9ec..b8f5cad1 100644 --- a/klp-ui/src/api/wms/rawMaterial.js +++ b/klp-ui/src/api/wms/rawMaterial.js @@ -42,3 +42,11 @@ export function delRawMaterial(rawMaterialId) { method: 'delete' }) } + +export function listRawMaterialWithDemand(query) { + return request({ + url: '/wms/rawMaterial//listWithDemand', + method: 'get', + params: query + }) +} diff --git a/klp-ui/src/views/wms/purchasePlan/index.vue b/klp-ui/src/views/wms/purchasePlan/index.vue index 697ec532..85190229 100644 --- a/klp-ui/src/views/wms/purchasePlan/index.vue +++ b/klp-ui/src/views/wms/purchasePlan/index.vue @@ -235,14 +235,14 @@ title="新增采购计划" :visible.sync="addDrawerOpen" direction="btt" - size="90%" + size="98%" :with-header="true" :wrapperClosable="false" custom-class="purchase-drawer" append-to-body mask-closable="false" > - - + + + + + @@ -110,6 +114,12 @@ icon="el-icon-refresh" @click="handleStatusChange(scope.row, EPurchaseDetailStatus.REVIEW, '待审核')" >设为待审核 + { + // this.$modal.confirm('是否确认将采购计划明细编号为"' + row.detailId + '"的状态改为"' + label + '"?').then(() => { this.loading = true; updatePurchasePlanDetail({ detailId: row.detailId, status: status }).then(response => { this.$modal.msgSuccess("状态修改成功"); @@ -393,7 +403,7 @@ export default { }).finally(() => { this.loading = false; }); - }); + // }); }, /** 创建入库单按钮操作 */ handleCreateStockIn() { diff --git a/klp-ui/src/views/wms/purchasePlan/panels/transfer.vue b/klp-ui/src/views/wms/purchasePlan/panels/transfer.vue new file mode 100644 index 00000000..45d6a649 --- /dev/null +++ b/klp-ui/src/views/wms/purchasePlan/panels/transfer.vue @@ -0,0 +1,285 @@ + + + + + 采购单信息 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 添加》 + 《删除 + + + + 采购单明细 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ submitLoading ? '提交中...' : '确认' }} + + + + + + + \ No newline at end of file diff --git a/scaner/.gitignore b/scaner/.gitignore new file mode 100644 index 00000000..5ff6309b --- /dev/null +++ b/scaner/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/scaner/pom.xml b/scaner/pom.xml new file mode 100644 index 00000000..3c052700 --- /dev/null +++ b/scaner/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + org.example + jar + 1.0-SNAPSHOT + + + 8 + 8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + true + org.example.Main + + + + + + maven-assembly-plugin + 3.3.0 + + + + org.example.Main + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + + + + + com.klp + mv-code-reader-ctrl-wrapper + 1.0.0 + + + org.json + json + 20210307 + + + org.glassfish.tyrus + tyrus-server + 1.17 + + + org.glassfish.tyrus + tyrus-container-grizzly-server + 1.17 + + + org.glassfish.tyrus + tyrus-container-servlet + 2.1.1 + + + javax.websocket + javax.websocket-api + 1.1 + + + + \ No newline at end of file diff --git a/scaner/src/main/java/org/example/HttpRequestUtil.java b/scaner/src/main/java/org/example/HttpRequestUtil.java new file mode 100644 index 00000000..b0f93bc8 --- /dev/null +++ b/scaner/src/main/java/org/example/HttpRequestUtil.java @@ -0,0 +1,68 @@ +package org.example; + +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +public class HttpRequestUtil { + /** + * 向指定URL发送POST请求,发送JSON数据 + * @param urlStr 目标接口地址 + * @param jsonData 发送的JSON字符串 + * @return 响应内容 + * @throws Exception 网络异常 + */ + public static String postJson(String urlStr, String jsonData) throws Exception { + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + try (OutputStream os = conn.getOutputStream()) { + os.write(jsonData.getBytes("UTF-8")); + } + int code = conn.getResponseCode(); + if (code == 200) { + try (java.io.InputStream is = conn.getInputStream()) { + java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = is.read(buffer)) != -1) { + baos.write(buffer, 0, len); + } + return new String(baos.toByteArray(), "UTF-8"); + } + } else { + throw new RuntimeException("HTTP请求失败,状态码: " + code); + } + } + + /** + * 向指定URL发送GET请求 + * @param urlStr 目标接口地址 + * @return 响应内容 + * @throws Exception 网络异常 + */ + public static String get(String urlStr) throws Exception { + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + conn.setDoInput(true); + int code = conn.getResponseCode(); + if (code == 200) { + try (java.io.InputStream is = conn.getInputStream()) { + java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = is.read(buffer)) != -1) { + baos.write(buffer, 0, len); + } + return new String(baos.toByteArray(), "UTF-8"); + } + } else { + throw new RuntimeException("HTTP GET请求失败,状态码: " + code); + } + } +} \ No newline at end of file diff --git a/scaner/src/main/java/org/example/Main.java b/scaner/src/main/java/org/example/Main.java new file mode 100644 index 00000000..0abe65c5 --- /dev/null +++ b/scaner/src/main/java/org/example/Main.java @@ -0,0 +1,240 @@ +package org.example; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; + +// Main.java 顶部增加 +import org.glassfish.tyrus.server.Server; + +import MvCodeReaderCtrlWrapper.*; +import MvCodeReaderCtrlWrapper.MvCodeReaderCtrl.*; +import MvCodeReaderCtrlWrapper.MvCodeReaderCtrlDefine.*; +import MvCodeReaderCtrlWrapper.ParameterException.*; +import java.net.*; +import java.io.*; + +public class Main { + static Handle hHandle = null; + + // 保存设备列表 + private static ArrayList stCamListCache = new ArrayList<>(); + + // 获取设备列表JSON(推送给前端) + public static String getDeviceListJson() { + org.json.JSONObject obj = new org.json.JSONObject(); + obj.put("type", "deviceList"); + org.json.JSONArray arr = new org.json.JSONArray(); + for (int i = 0; i < stCamListCache.size(); i++) { + MV_CODEREADER_DEVICE_INFO camInfo = stCamListCache.get(i); +// System.out.println(camInfo.toString() + "设备相信信息"); + System.out.println(camInfo.stGigEInfo.chModelName + camInfo.stGigEInfo.chUserDefinedName + camInfo.stGigEInfo.nCurrentIp + camInfo.stGigEInfo.chSerialNumber + camInfo.stGigEInfo.chDeviceVersion + + camInfo.stGigEInfo.chManufacturerName + camInfo.stGigEInfo.chManufacturerSpecificInfo + camInfo.stGigEInfo.nIpCfgCurrent); + org.json.JSONObject dev = new org.json.JSONObject(); + dev.put("id", camInfo.stGigEInfo.chSerialNumber); // 序列号 + dev.put("name", camInfo.stGigEInfo.chUserDefinedName != null ? camInfo.stGigEInfo.chUserDefinedName : camInfo.stGigEInfo.chModelName); + dev.put("type", "scanner"); + dev.put("on", true); // 默认开 + arr.put(dev); + } + obj.put("devices", arr); + return obj.toString(); + } + + public static void saveDataToFile(byte[] dataToSave, int dataSize, String fileName) { + OutputStream os = null; + + try { + // Create saveImg directory + File tempFile = new File("saveImg"); + if (!tempFile.exists()) { + tempFile.mkdirs(); + } + + stCamListCache = MvCodeReaderCtrl.MV_CODEREADER_EnumDevices(); + os.write(dataToSave, 0, dataSize); + System.out.println("SaveImage succeed."); + } catch (IOException e) { + e.printStackTrace(); + } finally { + // Close file stream + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + // CallBack function + private static void printImgCBInfo(byte[] pdata, MV_CODEREADER_IMAGE_OUT_INFO_EX2 stOutInfo) { + if (null == stOutInfo) { + System.out.println("stOutInfo is null"); + return; + } + + System.out.print("/**CBpstOutInfo***************************************/\n"); + + + // 示例:扫码时调用POST接口,解析扫码内容并发送JSON + try { + // 假设扫码内容为第一个条码内容 + // String scanStr = null; + // if (stOutInfo != null && stOutInfo.pstCodeListEx != null && stOutInfo.pstCodeListEx.nCodeNum > 0) { + // scanStr = stOutInfo.pstCodeListEx.stBcrInfoEx.get(0).chCode; + // } + // if (scanStr != null && !scanStr.isEmpty()) { + // String json = ScanDataUtil.buildRequestJson( + // scanStr, + // "raw_material", // 物品类型 + // "扫码入库", // 备注 + // null, // 源库位ID + // 1 // 记录类型 + // ); + // System.out.println("json: " + json); + // // 发送两次请求:修改库存并且插入一条记录 + // // 插入记录 + // String postUrl1 = "http://localhost:8080/wms/stockIoDetail"; // TODO: 替换为实际接口 + // String postResponse = HttpRequestUtil.postJson(postUrl1, json); + // System.out.println("POST接口响应: " + postResponse); + // // 修改库存 + // String postUrl2 = "http://localhost:8080/wms/stockIo/scanInStock"; + // String postResponse2 = HttpRequestUtil.postJson(postUrl2, json); + // System.out.println("修改库存POST接口响应: " + postResponse2); + // } else { + // System.out.println("未获取到扫码内容,未发送POST请求"); + // } + } catch (Exception e) { + System.err.println("POST请求失败: " + e.getMessage()); + } + + // save buffer to file follow Image Type to Save + // saveDataToFile(pdata, stOutInfo.nFrameLen, "Image.jpg"); + // saveDataToFile(pdata, stOutInfo.nFrameLen, "Image.raw"); + + System.out.print(String.format("Get One Frame: nEventID[%d], nChannelID[%d], nWidth[%d], nHeight[%d], nFrameNum[%d], nTriggerIndex[%d], nFrameLen[%d], " + + " nCodeNumber[%d] \r\n", + stOutInfo.nEventID, stOutInfo.nChannelID, stOutInfo.nWidth, stOutInfo.nHeight, stOutInfo.nFrameNum, + stOutInfo.nTriggerIndex, stOutInfo.nFrameLen, stOutInfo.pstCodeListEx.nCodeNum)); + + System.out.print("Get One Code: bIsGetCode[" + stOutInfo.bIsGetCode + "]success\r\n"); + + System.out.print("Get GvspPixelType: MvCodeReaderGvspPixelType[" + stOutInfo.enPixelType + "]success\r\n"); + + // print code info + for (int a = 0; a < stOutInfo.pstCodeListEx.nCodeNum; a++) { + org.json.JSONObject obj = new org.json.JSONObject(); + obj.put("type", "scanMessage"); + // 解析码中的数据,用下划线分割,第一个为物料类型,第二个为物料id + String code = stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).chCode; + String[] parts = code.split("__"); + String itemType = parts[0]; + String itemId = parts[1]; + obj.put("itemType", itemType); + obj.put("itemId", itemId); + WsServer.broadcast(obj.toString()); + System.out.print(String.format("CodeInfo: TheCodeID[%d], CodeString[%s], nCodeLen[%d], nAngle[%d], nBarType[%d]," + + "sAlgoCost[%d], nIDRScore[%d], n1DIsGetQuality[%d]\r\n", + a, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).chCode, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).nLen, + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).nAngle, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).nBarType, + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).sAlgoCost, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).nIDRScore, + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).n1DIsGetQuality)); + + System.out.print(String.format("CodePointInfo: stCornerLeftTop.X[%d], stCornerLeftTop.Y[%d], stCornerRightTop.X[%d], stCornerRightTop.Y[%d]," + + "stCornerRightBottom.X[%d], stCornerRightBottom.Y[%d], stCornerLeftBottom.X[%d], stCornerLeftBottom.Y[%d]\r\n", + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerLeftTop.nX, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerLeftTop.nY, + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerRightTop.nX, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerRightTop.nY, + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerRightBottom.nX, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerRightBottom.nY, + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerLeftBottom.nX, stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).stCornerLeftBottom.nY)); + + System.out.print("Get CodeQuality: bIsGetQuality[" + stOutInfo.pstCodeListEx.stBcrInfoEx.get(a).bIsGetQuality + "]success\r\n"); + } + } + + private static void PrintDeviceInfo(MV_CODEREADER_DEVICE_INFO stCamInfo) { + if (stCamInfo.nTLayerType == MvCodeReaderCtrlDefine.MV_CODEREADER_GIGE_DEVICE) { + int nIp1 = ((stCamInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24) & 0xff; + int nIp2 = ((stCamInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16); + int nIp3 = ((stCamInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8); + int nIp4 = (stCamInfo.stGigEInfo.nCurrentIp & 0x000000ff); + + System.out.print("CurrentIp: " + nIp1 + "." + nIp2 + "." + nIp3 + "." + nIp4 + "\r\n"); + + System.out.print(String.format("GiGEInfo: UserDefinedName:[%s], chSerialNumber:[%s] \r\n\r\n", + stCamInfo.stGigEInfo.chUserDefinedName, stCamInfo.stGigEInfo.chSerialNumber)); + } + } + + + public static void main(String[] args) throws InterruptedException { + + System.out.print(" ***************Begin****************** \r\n"); + + String strVersion = MvCodeReaderCtrl.MV_CODEREADER_GetSDKVersion(); + System.out.print("Get version " + strVersion + "\r\n"); + + ArrayList stCamList = MvCodeReaderCtrl.MV_CODEREADER_EnumDevices(); + Main.stCamListCache = stCamList; + if (stCamList == null || stCamList.isEmpty()) { + System.out.print("Find No Device!\r\n"); + return; + } + + ArrayList handleList = new ArrayList<>(); + for (int i = 0; i < stCamList.size(); i++) { + MV_CODEREADER_DEVICE_INFO camInfo = stCamList.get(i); + PrintDeviceInfo(camInfo); + + Handle handle = null; + try { + handle = MvCodeReaderCtrl.MV_CODEREADER_CreateHandle(camInfo); + } catch (ParameterException e) { + throw new RuntimeException(e); + } + if (handle == null) { + System.out.print("Create handle failed for device " + i + "!\r\n"); + continue; + } + int nRet = MvCodeReaderCtrl.MV_CODEREADER_OpenDevice(handle); + if (nRet != 0) { + System.out.print("Open Device fail for device " + i + "! nRet[" + String.format("0x%x", nRet) + "]\r\n"); + continue; + } + nRet = MvCodeReaderCtrl.MV_CODEREADER_RegisterImageCallBackEx2(handle, new ImageCallBack() { + @Override + public int OnImageCallBack(byte[] pdata, MV_CODEREADER_IMAGE_OUT_INFO_EX2 stOutInfo) { + printImgCBInfo(pdata, stOutInfo); + return 0; + } + }); + if (nRet != 0) { + System.out.print("RegisterImageCallBackEx2 Failed for device " + i + "! nRet[" + String.format("0x%x", nRet) + "]\r\n"); + continue; + } + nRet = MvCodeReaderCtrl.MV_CODEREADER_StartGrabbing(handle); + if (nRet != 0) { + System.out.print("StartGrabbing Failed for device " + i + "! nRet[" + String.format("0x%x", nRet) + "]\r\n"); + continue; + } + handleList.add(handle); + System.out.print("Device " + i + " startGrabbing success!\r\n"); + } + + // 阻塞主线程直到退出 + System.out.println("所有扫码枪已开始监听"); + + // 启动 Socket 服务器 + Server server = new Server("localhost", 9000, "/", null, WsServer.class); + try { + server.start(); + System.out.println("WebSocket服务器已启动,端口9000"); + System.in.read(); // 阻塞主线程直到回车 + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + server.stop(); + } + } +} diff --git a/scaner/src/main/java/org/example/ScanDataUtil.java b/scaner/src/main/java/org/example/ScanDataUtil.java new file mode 100644 index 00000000..f90af837 --- /dev/null +++ b/scaner/src/main/java/org/example/ScanDataUtil.java @@ -0,0 +1,34 @@ +package org.example; + +import org.json.JSONObject; +import java.math.BigDecimal; + +public class ScanDataUtil { + /** + * 解析扫码内容并构造请求体 + * @param scanStr 扫码内容,格式:入库单id_仓库id_物料id_数量 + * @param itemType 物品类型 + * @param itemId 物品ID + * @param unit 单位 + * @param remark 备注(可选) + * @param fromWarehouseId 源库位ID(可选) + * @param recordType 记录类型(0/1) + * @return JSON字符串 + */ + public static String buildRequestJson(String scanStr, String itemType, String remark, Long fromWarehouseId, Integer recordType) { + String[] parts = scanStr.split("_"); + if (parts.length != 4) throw new IllegalArgumentException("扫码内容格式不正确,应为:入库单id_仓库id_物料id_数量"); + JSONObject obj = new JSONObject(); + obj.put("stockIoId", Long.valueOf(parts[0])); + obj.put("warehouseId", Long.valueOf(parts[1])); + obj.put("itemId", Long.valueOf(parts[2])); + obj.put("quantity", new BigDecimal(parts[3])); + obj.put("itemType", itemType); + obj.put("unit", 'g'); // 单位为null + obj.put("batchNo", 100); // 批次号为null + obj.put("remark", remark); + obj.put("fromWarehouseId", fromWarehouseId); + obj.put("recordType", recordType); + return obj.toString(); + } +} \ No newline at end of file diff --git a/scaner/src/main/java/org/example/WsServer.java b/scaner/src/main/java/org/example/WsServer.java new file mode 100644 index 00000000..607b7fbc --- /dev/null +++ b/scaner/src/main/java/org/example/WsServer.java @@ -0,0 +1,53 @@ +package org.example; + +import javax.websocket.OnClose; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +@javax.websocket.server.ServerEndpoint("/ws") +public class WsServer { + private static final Set sessions = new CopyOnWriteArraySet<>(); + + @javax.websocket.OnOpen + public void onOpen(javax.websocket.Session session) { + sessions.add(session); + System.out.println("WebSocket连接已建立: " + session.getId()); + // 推送扫码枪列表给前端 + String deviceListJson = org.example.Main.getDeviceListJson(); + try { + System.out.println("发送消息到前端"); + session.getBasicRemote().sendText(deviceListJson); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @javax.websocket.OnMessage + public void onMessage(String message, javax.websocket.Session session) throws IOException { + System.out.println("收到消息: " + message); + session.getBasicRemote().sendText("服务器已收到: " + message); + } + + @javax.websocket.OnClose + public void onClose(javax.websocket.Session session) { + sessions.remove(session); + System.out.println("WebSocket连接已关闭: " + session.getId()); + } + + public static void broadcast(String message) { + for (javax.websocket.Session session : sessions) { + if (session.isOpen()) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} \ No newline at end of file diff --git a/scaner/src/views/ScannerList.vue b/scaner/src/views/ScannerList.vue new file mode 100644 index 00000000..ea9714ee --- /dev/null +++ b/scaner/src/views/ScannerList.vue @@ -0,0 +1,80 @@ + + + + + + {{ item.name }} + {{ item.status }} + {{ item.type }} + + + + + + + + + + + + + + + + + + + \ No newline at end of file