From f24a21a41c908285bbcacc59e1356d0713cba151 Mon Sep 17 00:00:00 2001
From: geht <2947093423@qq.com>
Date: Mon, 20 Jul 2026 17:31:54 +0800
Subject: [PATCH] =?UTF-8?q?app=E7=AB=AF=E6=96=B0=E5=A2=9E=E5=8E=9F?=
=?UTF-8?q?=E6=9D=90=E6=96=99=E9=80=81=E6=A3=80=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
JeecgUniapp-master/.hbuilderx/launch.json | 16 +
.../docs/DT50扫描识别-广播参数与接入说明.md | 136 ++++++++
JeecgUniapp-master/env/.env.development | 6 +-
JeecgUniapp-master/pages.config.ts | 2 +-
JeecgUniapp-master/src/common/work.ts | 36 +-
.../HelpTipBubble/HelpTipBubble.vue | 91 +++++
.../src/components/components.d.ts | 1 +
JeecgUniapp-master/src/manifest.json | 6 +-
JeecgUniapp-master/src/pages.json | 41 ++-
JeecgUniapp-master/src/pages/index/index.vue | 20 +-
JeecgUniapp-master/src/pages/login/login.vue | 74 +++-
.../rawMaterialInspect/inspectResult.vue | 245 ++++++++++++++
.../rawMaterialInspect/rawMaterialInspect.vue | 289 ++++++++++++++++
.../src/pages/scanIdentify/scanIdentify.vue | 318 ++++++++++++++++++
.../src/service/xslmes/rawMaterialInspect.ts | 68 ++++
JeecgUniapp-master/src/static/logo/xslmes.png | Bin 0 -> 20618 bytes
JeecgUniapp-master/src/types/uni-pages.d.ts | 3 +
JeecgUniapp-master/src/utils/dt50Scan.ts | 291 ++++++++++++++++
.../jeecg-module-xslmes/doc/代码修改日志 | 10 +
...MesXslRawMaterialInspectAppController.java | 95 ++++++
...MesXslRawMaterialInspectRecordService.java | 12 +
...slRawMaterialInspectRecordServiceImpl.java | 65 ++++
.../vo/MesXslRawMaterialInspectAppScanVo.java | 30 ++
qhmes.code-workspace | 3 +-
24 files changed, 1806 insertions(+), 52 deletions(-)
create mode 100644 JeecgUniapp-master/.hbuilderx/launch.json
create mode 100644 JeecgUniapp-master/docs/DT50扫描识别-广播参数与接入说明.md
create mode 100644 JeecgUniapp-master/src/components/HelpTipBubble/HelpTipBubble.vue
create mode 100644 JeecgUniapp-master/src/pages/rawMaterialInspect/inspectResult.vue
create mode 100644 JeecgUniapp-master/src/pages/rawMaterialInspect/rawMaterialInspect.vue
create mode 100644 JeecgUniapp-master/src/pages/scanIdentify/scanIdentify.vue
create mode 100644 JeecgUniapp-master/src/service/xslmes/rawMaterialInspect.ts
create mode 100644 JeecgUniapp-master/src/static/logo/xslmes.png
create mode 100644 JeecgUniapp-master/src/utils/dt50Scan.ts
create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialInspectAppController.java
create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRawMaterialInspectAppScanVo.java
diff --git a/JeecgUniapp-master/.hbuilderx/launch.json b/JeecgUniapp-master/.hbuilderx/launch.json
new file mode 100644
index 00000000..5e2090f2
--- /dev/null
+++ b/JeecgUniapp-master/.hbuilderx/launch.json
@@ -0,0 +1,16 @@
+{
+ "version" : "1.0",
+ "configurations" : [
+ {
+ "playground" : "standard",
+ "type" : "uni-app:app-android"
+ },
+ {
+ "app-plus" :
+ {
+ "launchtype" : "local"
+ },
+ "type" : "uniCloud"
+ }
+ ]
+}
diff --git a/JeecgUniapp-master/docs/DT50扫描识别-广播参数与接入说明.md b/JeecgUniapp-master/docs/DT50扫描识别-广播参数与接入说明.md
new file mode 100644
index 00000000..3e6aeecc
--- /dev/null
+++ b/JeecgUniapp-master/docs/DT50扫描识别-广播参数与接入说明.md
@@ -0,0 +1,136 @@
+# DT50 扫描识别 —— 广播参数与接入说明
+
+> 面向 APP「协作」页「扫描识别」功能。
+> 设备:优博讯 UROVO **DT50**(Android 11)。
+> 参数来源:**设备实测 adb logcat 抓真实扫码** + **优博讯官方 ScanManager 文档** + **DCloud UniApp 官方示例** 三方交叉验证。
+> 记录日期:2026-07-20。
+
+---
+
+## 一、核心参数(最重要)
+
+| 项目 | 值 | 说明 |
+|---|---|---|
+| 输出方式 | **Intent / 广播** | 设备当前**已是此模式**,无需在设置页切换 |
+| 广播 Action | `android.intent.ACTION_DECODE_DATA` | 实测 + 官方一致 |
+| 条码文本(String) | `barcode_string` | 官方常量 `BARCODE_STRING_TAG`,**首选** |
+| 原始字节(byte[]) | `barcode` | 官方常量 `DECODE_DATA_TAG`,兜底用 |
+| 长度(int) | `length` | 官方常量 `BARCODE_LENGTH_TAG` |
+| 码制(int/String) | `barcodeType` | 官方常量 `BARCODE_TYPE_TAG`,实测读出 `Code128` |
+| 扫描键 keycode | `523` | KEYCODE_SCAN(机身侧键) |
+
+## 二、设备侧验证结论
+
+- **硬件**:成像头正常,成功解出 Code128 条码 ✅
+- **触发**:侧键 keycode = 523,按下发 `ACTION_KEYCODE_SCAN_PRESSED`
+- **广播**:成功解码时实测发出 `android.intent.ACTION_DECODE_DATA (has extras)` 并被接收 ✅
+- **模式**:当前即广播输出(非键盘输出),**直接 registerReceiver 即可**
+- **无需**:原生 `.aar` 插件 / 自定义基座 / USB 驱动 / 完整 AndroidStudio 套壳;标准基座即可
+
+相关系统组件(备查):
+- 扫描设置 App:`com.ubx.barcodescanner.tool`
+- 扫描服务:`com.ubx.uscanner`
+- DataWedge 式配置:`com.ubx.datawedge`
+
+## 三、UniApp 监听代码(标准基座,无需插件)
+
+```js
+let scanReceiver = null;
+
+/** 开始监听 DT50 扫描广播 */
+function startScanListener(onBarcode) {
+ const main = plus.android.runtimeMainActivity();
+ const IntentFilter = plus.android.importClass('android.content.IntentFilter');
+ const filter = new IntentFilter();
+ filter.addAction('android.intent.ACTION_DECODE_DATA'); // DT50 广播 Action
+
+ scanReceiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
+ onReceive: function (context, intent) {
+ plus.android.importClass(intent);
+ // 首选直接取字符串
+ let code = intent.getStringExtra('barcode_string');
+ // 兜底:部分场景用字节数组
+ if (!code) {
+ const bytes = intent.getByteArrayExtra('barcode');
+ if (bytes) code = plus.android.newObject('java.lang.String', bytes);
+ }
+ const type = intent.getStringExtra('barcodeType'); // 码制,可选
+ if (code) onBarcode(code, type);
+ }
+ });
+ main.registerReceiver(scanReceiver, filter);
+}
+
+/** 停止监听(务必在页面卸载时调用,避免重复注册 / 内存泄漏) */
+function stopScanListener() {
+ if (scanReceiver) {
+ plus.android.runtimeMainActivity().unregisterReceiver(scanReceiver);
+ scanReceiver = null;
+ }
+}
+```
+
+页面接入:
+
+```js
+onLoad() {
+ startScanListener((code, type) => {
+ this.barcode = code;
+ // TODO: 调 MES 后端接口 / 查料
+ });
+},
+onUnload() {
+ stopScanListener();
+}
+```
+
+> 注意:仅 App(plus 环境)下可用,H5/小程序无 `plus`,接入时需做平台判断(`#ifdef APP-PLUS`)。
+
+## 四、故障排查
+
+| 现象 | 原因 / 处理 |
+|---|---|
+| 收不到广播 | 确认扫描设置里输出为「Intent/广播」而非「键盘/输入法」;确认 Action 拼写 |
+| 扫描后文本框被输入内容 | 键盘输出模式也开了,去扫描设置关掉键盘输出 |
+| `barcode_string` 为空 | 用兜底的 `barcode`(byte[]) 转 String |
+| 一直解码失败(红光有但不出码) | 距离 15~20cm,红光完整压住条码,拿稳 1~2 秒;避免手机屏幕反光码 |
+
+## 五、参考
+
+- 优博讯官方 ScanManager 文档:https://en.urovo.com/developer/android/device/ScanManager.html
+- DCloud UniApp 监听优博讯扫码广播(含示例):https://ask.dcloud.net.cn/article/36468
+
+## 六、本仓库接入位置(2026-07-20)
+
+| 项 | 位置 |
+|---|---|
+| 菜单入口 | `src/common/work.ts` → 其他服务「合同」后「扫描识别」 |
+| 页面 | `src/pages/scanIdentify/scanIdentify.vue` |
+| 广播工具 | `src/utils/dt50Scan.ts` |
+| 行为 | 仅广播展示条码/码制/时间,不调后端、不弹输入法 |
+| 生命周期 | `onShow` 注册 / `onHide`+`onUnload` 注销;同码防抖 |
+
+## 七、键盘输出 vs 广播输出
+
+| | 键盘输出(Keyboard / 楔入) | 广播输出(Intent / Broadcast) |
+|---|---|---|
+| 原理 | 把条码当成「敲键盘」打进当前焦点输入框 | 系统发广播,App 用 `registerReceiver` 收 |
+| App 是否要抢焦点 | 要,通常还会弹出软键盘 | **不需要**输入框焦点 |
+| 适合场景 | 记事本、任意输入框快速验证 | 业务页稳定取码、拿码制等扩展字段 |
+| 本页策略 | 已关闭楔入,避免弹输入法 | **本页只走广播** |
+
+设备设置建议:扫描设置 → 关闭「键盘输出」,开启「Intent/广播输出」。
+本页进入时会主动 `hideKeyboard`,不自动聚焦任何输入框。
+
+## 附:参数是如何抓到的(可复现)
+
+```bash
+# adb 路径(本机 Android SDK)
+ADB="C:/Users/hp/AppData/Local/Android/Sdk/platform-tools/adb.exe"
+
+$ADB devices -l # 确认 DT50 已连(USB 调试)
+$ADB logcat -c # 清空日志缓冲
+$ADB shell "timeout 40 logcat -v threadtime '*:V'" > scan.log # 抓 40s
+# 抓取期间用 DT50 侧键扫几次条码,然后:
+grep -E "ACTION_DECODE_DATA|barcodeType|DECODER" scan.log
+```
diff --git a/JeecgUniapp-master/env/.env.development b/JeecgUniapp-master/env/.env.development
index 734633fc..1a35d730 100644
--- a/JeecgUniapp-master/env/.env.development
+++ b/JeecgUniapp-master/env/.env.development
@@ -2,7 +2,7 @@
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
-# 是否开启sourcemap
-VITE_SHOW_SOURCEMAP = true
+# 是否开启sourcemap(App 真机编译易 OOM,开发期先关闭)
+VITE_SHOW_SOURCEMAP = false
-VITE_SERVER_BASEURL = 'http://localhost:8080/jeecg-boot'
+VITE_SERVER_BASEURL = 'http://192.168.1.3:8888/jeecg-boot'
diff --git a/JeecgUniapp-master/pages.config.ts b/JeecgUniapp-master/pages.config.ts
index c109a168..0aff7102 100644
--- a/JeecgUniapp-master/pages.config.ts
+++ b/JeecgUniapp-master/pages.config.ts
@@ -36,7 +36,7 @@ export default defineUniPages({
iconPath: 'static/tabbar/tabbar-home-2.png',
selectedIconPath: 'static/tabbar/tabbar-home.png',
pagePath: 'pages/index/index',
- text: '协作',
+ text: '生产',
},
// {
// iconPath: 'static/tabbar/tabbar-workHome-2.png',
diff --git a/JeecgUniapp-master/src/common/work.ts b/JeecgUniapp-master/src/common/work.ts
index 4b37f483..645b2c0b 100644
--- a/JeecgUniapp-master/src/common/work.ts
+++ b/JeecgUniapp-master/src/common/work.ts
@@ -1,5 +1,5 @@
/**
- * 常用服务
+ * 基础服务(原常用服务)
* useful server
*/
@@ -108,36 +108,28 @@ export const us = {
}
/**
- * other server 其他服务
+ * 生产服务(原其他服务)
+ * 首页「更多」按钮仍按 type=other 追加,勿删空数组
*/
export const os = {
data: [
+ //update-begin---author:xsl ---date:20260720 for:【APP首页】生产服务菜单调整-----------
{
- title: '新闻中心',
- icon: icon_prefix + 'xinwen.png',
- description: '新闻中心',
- routeIndex: 'news',
+ title: '原材料送检',
+ icon: icon_prefix + 'renwu.png',
+ description: '扫码送检与录入检验结果',
useCount: 10000,
- },
- {
- title: '会议',
- icon: icon_prefix + 'huiyi.png',
- description: '会议',
- useCount: 10000,
- routeIndex: 'meeting',
+ routeIndex: 'rawMaterialInspect',
enabled: true,
},
{
- title: '任务中心',
- icon: icon_prefix + 'renwu.png',
- description: '任务中心',
- useCount: 10000,
- },
- {
- title: '合同',
- icon: icon_prefix + 'hetong.png',
- description: '合同',
+ title: '扫描识别',
+ icon: icon_prefix + 'wendang.png',
+ description: '手持机扫描头识别条码',
useCount: 10000,
+ routeIndex: 'scanIdentify',
+ enabled: true,
},
+ //update-end---author:xsl ---date:20260720 for:【APP首页】生产服务菜单调整-----------
],
}
diff --git a/JeecgUniapp-master/src/components/HelpTipBubble/HelpTipBubble.vue b/JeecgUniapp-master/src/components/HelpTipBubble/HelpTipBubble.vue
new file mode 100644
index 00000000..0db8515f
--- /dev/null
+++ b/JeecgUniapp-master/src/components/HelpTipBubble/HelpTipBubble.vue
@@ -0,0 +1,91 @@
+
+
+
+ ?
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
diff --git a/JeecgUniapp-master/src/components/components.d.ts b/JeecgUniapp-master/src/components/components.d.ts
index c2538692..718d9dcb 100644
--- a/JeecgUniapp-master/src/components/components.d.ts
+++ b/JeecgUniapp-master/src/components/components.d.ts
@@ -5,6 +5,7 @@ Breadcrumb: typeof import('./Breadcrumb/Breadcrumb.vue')['default']
CategorySelect: typeof import('./CategorySelect/CategorySelect.vue')['default']
DateTime: typeof import('./DateTime/DateTime.vue')['default']
Grid: typeof import('./Grid/Grid.vue')['default']
+HelpTipBubble: typeof import('./HelpTipBubble/HelpTipBubble.vue')['default']
ImgPreview: typeof import('./ImgPreview/ImgPreview.vue')['default']
LFile: typeof import('./LFile/LFile.vue')['default']
PageLayout: typeof import('./PageLayout/PageLayout.vue')['default']
diff --git a/JeecgUniapp-master/src/manifest.json b/JeecgUniapp-master/src/manifest.json
index 5f6b1883..244deba7 100644
--- a/JeecgUniapp-master/src/manifest.json
+++ b/JeecgUniapp-master/src/manifest.json
@@ -130,8 +130,8 @@
"sdkConfigs": {
"maps": {
"amap": {
- "key": "???",
- "securityJsCode": "???",
+ "key": "20854e7d231ee339bfa3b277c840070c",
+ "securityJsCode": "7a542edee4a82e56ed88fef8ef42b5a5",
"serviceHost": ""
}
}
@@ -142,4 +142,4 @@
"bundleName": "uniapp.demo.test"
}
}
-}
+}
\ No newline at end of file
diff --git a/JeecgUniapp-master/src/pages.json b/JeecgUniapp-master/src/pages.json
index 896f1b7e..af27f64f 100644
--- a/JeecgUniapp-master/src/pages.json
+++ b/JeecgUniapp-master/src/pages.json
@@ -33,7 +33,7 @@
"iconPath": "static/tabbar/tabbar-home-2.png",
"selectedIconPath": "static/tabbar/tabbar-home.png",
"pagePath": "pages/index/index",
- "text": "协作"
+ "text": "生产"
},
{
"iconPath": "static/tabbar/tabbar-user-2.png",
@@ -197,6 +197,45 @@
}
}
},
+ {
+ "path": "pages/rawMaterialInspect/inspectResult",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "录入检验结果",
+ "navigationStyle": "custom",
+ "disableScroll": true,
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
+ {
+ "path": "pages/rawMaterialInspect/rawMaterialInspect",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "原材料送检",
+ "navigationStyle": "custom",
+ "disableScroll": true,
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
+ {
+ "path": "pages/scanIdentify/scanIdentify",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "扫描识别",
+ "navigationStyle": "custom",
+ "disableScroll": true,
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
{
"path": "pages/user/people",
"type": "page",
diff --git a/JeecgUniapp-master/src/pages/index/index.vue b/JeecgUniapp-master/src/pages/index/index.vue
index 0766c509..5ac42e0f 100644
--- a/JeecgUniapp-master/src/pages/index/index.vue
+++ b/JeecgUniapp-master/src/pages/index/index.vue
@@ -28,22 +28,22 @@
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/JeecgUniapp-master/src/pages/login/login.vue b/JeecgUniapp-master/src/pages/login/login.vue
index 71a3a9ad..51c9d113 100644
--- a/JeecgUniapp-master/src/pages/login/login.vue
+++ b/JeecgUniapp-master/src/pages/login/login.vue
@@ -14,9 +14,12 @@
-
-
- {{ compTitle || 'JEECG BOOT' }}
+
+ 启航密炼
+ 智能制造MES手持端
+
+
+
@@ -69,7 +72,7 @@
-
+
{{ loading ? '登录...' : '登录' }}
@@ -80,6 +83,11 @@
+
+
@@ -361,20 +369,64 @@ if (isLocalConfig === false) {
.page-container {
padding: 0 20upx;
padding-top: 100upx;
+ padding-bottom: calc(24upx + env(safe-area-inset-bottom));
position: relative;
font-size: 15px;
color: var(--UI-FG-0);
- .logo {
- width: 200upx;
- height: 150px;
+ min-height: 100vh;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ .brand {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex-shrink: 0;
}
- .title {
- font-size: 58upx;
+ .brand-title {
+ font-size: 52upx;
+ font-weight: 700;
+ color: #1a1a1a;
+ letter-spacing: 6upx;
+ line-height: 1.3;
+ }
+ .brand-subtitle {
+ margin-top: 12upx;
+ font-size: 28upx;
+ color: #666;
+ letter-spacing: 2upx;
+ line-height: 1.4;
+ }
+ .form-wrap {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-height: 0;
+ }
+ .footer {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12upx;
+ padding-top: 24upx;
+ padding-bottom: 12upx;
+ }
+ .footer-logo {
+ width: 48upx;
+ height: 48upx;
+ border-radius: 8upx;
+ }
+ .footer-text {
+ font-size: 22upx;
+ color: #888;
+ letter-spacing: 1upx;
}
.enter-area {
- padding-top: 75px;
+ padding-top: 24px;
width: 87%;
- margin: 0 auto 60upx;
+ margin: 0 auto 40upx;
.box {
display: flex;
align-items: center;
diff --git a/JeecgUniapp-master/src/pages/rawMaterialInspect/inspectResult.vue b/JeecgUniapp-master/src/pages/rawMaterialInspect/inspectResult.vue
new file mode 100644
index 00000000..684cd1e4
--- /dev/null
+++ b/JeecgUniapp-master/src/pages/rawMaterialInspect/inspectResult.vue
@@ -0,0 +1,245 @@
+
+{
+ layout: 'default',
+ style: {
+ navigationBarTitleText: '录入检验结果',
+ navigationStyle: 'custom',
+ disableScroll: true,
+ 'app-plus': {
+ bounce: 'none',
+ },
+ },
+}
+
+
+
+
+
+
+
+
+
+
+ 条码{{ record.barcode || '-' }}
+ 批次{{ record.batchNo || '-' }}
+ 物料{{ record.materialName || '-' }}
+ 送检时间{{ record.inspectTime || '-' }}
+
+
+
+ 加载中…
+ 暂无检验明细
+
+ {{ idx + 1 }}. {{ line.inspectItemName || '检验项' }}
+ 容差:{{ formatRange(line) }}
+ 状态:{{ passText(line.passFlag) }}
+
+ 检验值
+ onValueInput(line, e)"
+ />
+
+
+ 检验值
+ {{ line.inspectValue ?? '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/JeecgUniapp-master/src/pages/rawMaterialInspect/rawMaterialInspect.vue b/JeecgUniapp-master/src/pages/rawMaterialInspect/rawMaterialInspect.vue
new file mode 100644
index 00000000..7dabb673
--- /dev/null
+++ b/JeecgUniapp-master/src/pages/rawMaterialInspect/rawMaterialInspect.vue
@@ -0,0 +1,289 @@
+
+{
+ layout: 'default',
+ style: {
+ navigationBarTitleText: '原材料送检',
+ navigationStyle: 'custom',
+ disableScroll: true,
+ 'app-plus': {
+ bounce: 'none',
+ },
+ },
+}
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+
+
+
+ 暂无待送检数据,请扫码或查询
+
+
+ {{ item.barcode }}
+ 移除
+
+ 批次{{ item.batchNo || '-' }}
+ 入场日期{{ formatDate(item.entryDate) }}
+ 物料名称{{ item.materialName || '-' }}
+ 供应商{{ item.supplierName || '-' }}
+ 厂家物料{{ item.manufacturerMaterialName || '-' }}
+ 保质期{{ item.shelfLife || '-' }}
+ 库区{{ item.warehouseArea || '-' }}
+ 卸货人{{ item.unloadOperator || '-' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/JeecgUniapp-master/src/pages/scanIdentify/scanIdentify.vue b/JeecgUniapp-master/src/pages/scanIdentify/scanIdentify.vue
new file mode 100644
index 00000000..3ee67a10
--- /dev/null
+++ b/JeecgUniapp-master/src/pages/scanIdentify/scanIdentify.vue
@@ -0,0 +1,318 @@
+
+{
+ layout: 'default',
+ style: {
+ navigationBarTitleText: '扫描识别',
+ navigationStyle: 'custom',
+ disableScroll: true,
+ 'app-plus': {
+ bounce: 'none',
+ },
+ },
+}
+
+
+
+
+
+
+ {{ isAppEnv ? '请按下侧键扫描条码' : '请在 DT50 手持机 App 中使用' }}
+
+ {{
+ isAppEnv
+ ? '本页使用广播接收扫码结果,不会自动弹出输入法。请在设备扫描设置中开启 Intent/广播输出。'
+ : 'H5 无法调用扫描头'
+ }}
+
+
+
+
+ 最近一次识别
+ {{ latestCode || '等待扫描…' }}
+
+ 来源:{{ latestSource }}
+ 码制:{{ latestType }}
+ 时间:{{ latestTime }}
+
+
+
+
+ 扫描记录
+ 清空
+
+
+ 暂无记录
+
+ {{ history.length - index }}
+
+ {{ item.code }}
+
+ {{ item.source }}
+ {{ item.type }}
+ {{ item.time }}
+
+
+
+
+
+
+ 调试日志
+ 清空
+
+
+ {{ line }}
+
+
+
+
+
+
+
+
diff --git a/JeecgUniapp-master/src/service/xslmes/rawMaterialInspect.ts b/JeecgUniapp-master/src/service/xslmes/rawMaterialInspect.ts
new file mode 100644
index 00000000..8bb7cbf3
--- /dev/null
+++ b/JeecgUniapp-master/src/service/xslmes/rawMaterialInspect.ts
@@ -0,0 +1,68 @@
+import { http } from '@/utils/http'
+
+const BASE = '/xslmes/app/rawMaterialInspect'
+
+/** 原材料卡片(展示字段) */
+export interface RawMaterialCard {
+ id: string
+ barcode?: string
+ batchNo?: string
+ entryDate?: string
+ materialName?: string
+ supplierName?: string
+ manufacturerMaterialName?: string
+ shelfLife?: string
+ warehouseArea?: string
+ unloadOperator?: string
+ testResult?: string
+}
+
+export interface InspectScanResult {
+ card: RawMaterialCard
+ /** sendInspect | enterResult | finished */
+ action: string
+ hasPendingInspect?: boolean
+ pendingRecordId?: string
+}
+
+export interface InspectRecordLine {
+ id: string
+ recordId?: string
+ inspectItemName?: string
+ allowMin?: number | null
+ includeMinFlag?: number
+ allowMax?: number | null
+ includeMaxFlag?: number
+ inspectValue?: number | string | null
+ passFlag?: string
+ sortNo?: number
+}
+
+export interface InspectRecord {
+ id: string
+ barcode?: string
+ batchNo?: string
+ materialName?: string
+ inspectStatus?: string
+ inspectTime?: string
+}
+
+/** 按条码查询卡片及送检状态 */
+export const queryByBarcode = (barcode: string) =>
+ http.get(`${BASE}/queryByBarcode`, { barcode })
+
+/** 按条码送检 */
+export const createByBarcode = (barcode: string) =>
+ http.post(`${BASE}/createByBarcode`, { barcode })
+
+/** 加载检验明细 */
+export const prepareResultEntry = (id: string) =>
+ http.post(`${BASE}/prepareResultEntry`, { id })
+
+/** 保存检验结果 */
+export const saveInspectResult = (id: string, lineList: Array<{ id: string; inspectValue: number | string }>) =>
+ http.post(`${BASE}/saveInspectResult`, { id, lineList })
+
+/** 查询送检记录主表 */
+export const queryRecordById = (id: string) =>
+ http.get(`${BASE}/queryRecordById`, { id })
diff --git a/JeecgUniapp-master/src/static/logo/xslmes.png b/JeecgUniapp-master/src/static/logo/xslmes.png
new file mode 100644
index 0000000000000000000000000000000000000000..fbcbade13e5fddd5f3c2583ffd25b3f4dd0ea2cb
GIT binary patch
literal 20618
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
B@x}lE
literal 0
HcmV?d00001
diff --git a/JeecgUniapp-master/src/types/uni-pages.d.ts b/JeecgUniapp-master/src/types/uni-pages.d.ts
index 34924c2b..e533b730 100644
--- a/JeecgUniapp-master/src/types/uni-pages.d.ts
+++ b/JeecgUniapp-master/src/types/uni-pages.d.ts
@@ -17,6 +17,9 @@ interface NavigateToOptions {
"/pages/login/loginOauth2" |
"/pages/message/message" |
"/pages/more/more" |
+ "/pages/rawMaterialInspect/inspectResult" |
+ "/pages/rawMaterialInspect/rawMaterialInspect" |
+ "/pages/scanIdentify/scanIdentify" |
"/pages/user/people" |
"/pages/workHome/workHome" |
"/pages-home/home/home" |
diff --git a/JeecgUniapp-master/src/utils/dt50Scan.ts b/JeecgUniapp-master/src/utils/dt50Scan.ts
new file mode 100644
index 00000000..dad25ebb
--- /dev/null
+++ b/JeecgUniapp-master/src/utils/dt50Scan.ts
@@ -0,0 +1,291 @@
+/**
+ * 优博讯 DT50 扫描头广播监听
+ * 参数见 docs/DT50扫描识别-广播参数与接入说明.md
+ *
+ * 说明:设备常同时开启「广播 + 键盘」双通道,需优先广播并合并去重,
+ * 否则键盘缓冲会把同一条码拼成两遍(如 6942…28286942…2828)。
+ */
+
+export type Dt50ScanCallback = (code: string, type?: string, source?: string) => void
+export type Dt50LogCallback = (msg: string) => void
+
+const SCAN_ACTIONS = ['android.intent.ACTION_DECODE_DATA']
+
+const CODE_KEYS = ['barcode_string', 'scannerdata', 'data', 'barcode']
+const TYPE_KEYS = ['codetype', 'barcodeType', 'codeType', 'type']
+
+/** 同码防抖窗口(广播+键盘几乎同时到达) */
+const DEBOUNCE_MS = 1200
+/** 广播成功后,忽略键盘楔入的时间窗 */
+const IGNORE_KEYBOARD_AFTER_BROADCAST_MS = 1500
+
+let scanReceiver: any = null
+let listening = false
+let registering = false
+let lastCode = ''
+let lastTime = 0
+let lastBroadcastTime = 0
+let broadcastConfirmed = false
+let activeCallback: Dt50ScanCallback | null = null
+let logCallback: Dt50LogCallback | null = null
+
+function log(msg: string) {
+ const line = `[dt50Scan] ${msg}`
+ console.log(line)
+ try {
+ logCallback && logCallback(line)
+ } catch (_e) {
+ // ignore
+ }
+}
+
+function hasPlusAndroid() {
+ return typeof plus !== 'undefined' && !!(plus as any).android
+}
+
+function toJsString(val: any): string {
+ if (val == null || val === undefined) return ''
+ if (typeof val === 'string') return val.trim()
+ try {
+ if (typeof val === 'object' && typeof val.toString === 'function') {
+ const s = val.toString()
+ if (s && s !== '[object Object]') return String(s).trim()
+ }
+ } catch (_e) {
+ // ignore
+ }
+ try {
+ return String(plus.android.invoke(val, 'toString') || '').trim()
+ } catch (_e) {
+ return String(val).trim()
+ }
+}
+
+function bytesToString(bytes: any): string {
+ if (!bytes) return ''
+ try {
+ const JString = plus.android.importClass('java.lang.String') as any
+ return toJsString(new JString(bytes, 'UTF-8'))
+ } catch (_e) {
+ try {
+ return toJsString(plus.android.newObject('java.lang.String', bytes))
+ } catch (_e2) {
+ return ''
+ }
+ }
+}
+
+/** 拆开「同一条码粘成两遍」的情况 */
+export function normalizeScanCode(raw: string): string {
+ const s = String(raw || '')
+ .replace(/[\r\n\t]+/g, '')
+ .trim()
+ if (!s) return ''
+ // 整串 = A+A
+ if (s.length >= 6 && s.length % 2 === 0) {
+ const half = s.length / 2
+ const a = s.slice(0, half)
+ const b = s.slice(half)
+ if (a && a === b) {
+ log(`检测到粘连双码,已拆半: ${s} -> ${a}`)
+ return a
+ }
+ }
+ return s
+}
+
+function dumpAllExtras(intent: any): string {
+ const found: string[] = []
+ const allKeys = Array.from(new Set([...CODE_KEYS, ...TYPE_KEYS, 'length']))
+ for (const key of allKeys) {
+ try {
+ const s = intent.getStringExtra(key)
+ if (s != null) found.push(`${key}(str)=${toJsString(s)}`)
+ } catch (_e) {
+ // ignore
+ }
+ try {
+ const bytes = intent.getByteArrayExtra(key)
+ if (bytes) found.push(`${key}(bytes)=${bytesToString(bytes)}`)
+ } catch (_e) {
+ // ignore
+ }
+ try {
+ const n = intent.getIntExtra(key, -999999)
+ if (n !== -999999) found.push(`${key}(int)=${n}`)
+ } catch (_e) {
+ // ignore
+ }
+ }
+ return found.length ? found.join(' | ') : '(无可用 extra)'
+}
+
+function parseBarcode(intent: any): string {
+ for (const key of CODE_KEYS) {
+ try {
+ const s = toJsString(intent.getStringExtra(key))
+ if (s) return normalizeScanCode(s)
+ } catch (_e) {
+ // ignore
+ }
+ }
+ try {
+ const bytes = intent.getByteArrayExtra('barcode')
+ const s = bytesToString(bytes)
+ if (s) return normalizeScanCode(s)
+ } catch (_e) {
+ // ignore
+ }
+ return ''
+}
+
+function parseType(intent: any): string {
+ for (const key of TYPE_KEYS) {
+ try {
+ const s = toJsString(intent.getStringExtra(key))
+ if (s) return s
+ } catch (_e) {
+ // ignore
+ }
+ }
+ return ''
+}
+
+function emitBarcode(code: string, type: string, source: string) {
+ const normalized = normalizeScanCode(code)
+ if (!normalized) return
+
+ const now = Date.now()
+
+ // 广播已出结果时,短时间内的键盘结果一律丢弃(避免粘连/重复)
+ if (source === 'keyboard') {
+ if (broadcastConfirmed && now - lastBroadcastTime < IGNORE_KEYBOARD_AFTER_BROADCAST_MS) {
+ log(`忽略键盘重复(广播优先) code=${normalized}`)
+ return
+ }
+ if (broadcastConfirmed) {
+ log(`忽略键盘(本页已确认广播可用) code=${normalized}`)
+ return
+ }
+ }
+
+ if (normalized === lastCode && now - lastTime < DEBOUNCE_MS) {
+ log(`防抖忽略重复码 source=${source} code=${normalized}`)
+ return
+ }
+
+ lastCode = normalized
+ lastTime = now
+ if (source === 'broadcast') {
+ lastBroadcastTime = now
+ broadcastConfirmed = true
+ }
+
+ log(`识别成功 source=${source} code=${normalized} type=${type || '-'}`)
+ const cb = activeCallback
+ if (!cb) {
+ log('警告:无页面回调,条码未展示')
+ return
+ }
+ setTimeout(() => {
+ try {
+ cb(normalized, type || undefined, source)
+ } catch (e) {
+ log(`回调异常: ${e}`)
+ }
+ }, 0)
+}
+
+function doReceive(_context: any, intent: any) {
+ log('>>> onReceive 触发(说明广播已进入 App)')
+ try {
+ plus.android.importClass(intent)
+ const action = toJsString(intent.getAction())
+ log(`action=${action}`)
+ log(`extras=${dumpAllExtras(intent)}`)
+ const code = parseBarcode(intent)
+ if (!code) {
+ log('广播已收到,但未能解析出条码文本')
+ return
+ }
+ emitBarcode(code, parseType(intent), 'broadcast')
+ } catch (e) {
+ log(`onReceive 异常: ${e}`)
+ }
+}
+
+function registerNow(onBarcode: Dt50ScanCallback) {
+ activeCallback = onBarcode
+ if (listening && scanReceiver) {
+ log('已在监听中,仅更新回调')
+ return
+ }
+ if (registering) {
+ log('正在注册中,跳过重复注册')
+ return
+ }
+ registering = true
+
+ const main = plus.android.runtimeMainActivity()
+ const IntentFilter = plus.android.importClass('android.content.IntentFilter') as any
+ const filter = new IntentFilter()
+ SCAN_ACTIONS.forEach((action) => filter.addAction(action))
+
+ scanReceiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
+ onReceive: doReceive,
+ })
+
+ try {
+ main.registerReceiver(scanReceiver, filter)
+ listening = true
+ log(`已注册广播监听 actions=${SCAN_ACTIONS.join(',')}`)
+ } catch (e) {
+ scanReceiver = null
+ listening = false
+ log(`registerReceiver 失败: ${e}`)
+ } finally {
+ registering = false
+ }
+}
+
+export function setDt50ScanLogger(cb: Dt50LogCallback | null) {
+ logCallback = cb
+}
+
+/** 本页是否已确认广播可用(用于关闭键盘楔入) */
+export function isDt50BroadcastConfirmed() {
+ return broadcastConfirmed
+}
+
+export function reportDt50ScanCode(code: string, source = 'keyboard') {
+ emitBarcode(String(code || ''), '', source)
+}
+
+export function startDt50ScanListener(onBarcode: Dt50ScanCallback) {
+ if (!hasPlusAndroid()) {
+ log('当前环境无 plus.android,无法监听扫描头广播')
+ return
+ }
+ registerNow(onBarcode)
+}
+
+export function stopDt50ScanListener() {
+ activeCallback = null
+ broadcastConfirmed = false
+ lastBroadcastTime = 0
+ if (!hasPlusAndroid() || !scanReceiver) {
+ listening = false
+ registering = false
+ scanReceiver = null
+ return
+ }
+ try {
+ plus.android.runtimeMainActivity().unregisterReceiver(scanReceiver)
+ log('已取消广播监听')
+ } catch (e) {
+ log(`unregisterReceiver 失败: ${e}`)
+ }
+ scanReceiver = null
+ listening = false
+ registering = false
+}
diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/doc/代码修改日志 b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/doc/代码修改日志
index 4576c58f..07b7171a 100644
--- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/doc/代码修改日志
+++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/doc/代码修改日志
@@ -1247,3 +1247,13 @@ jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/
jeecgboot-vue3/src/views/xslmes/mesXslDryingProcessSwitch/MesXslDryingProcessSwitchPage.vue
jeecgboot-vue3/src/views/xslmes/mesXslDryingProcessSwitch/MesXslDryingProcessSwitch.api.ts
-- author:jiangxh---date:20260708--for: 【MES】烘胶流程开启(总开关+二次确认,不校验烘胶房日期) ---
+
+-- author:xsl---date:20260720--for: APPԭͼ졿ֳֶ˰ѯ/ͼ/¼ӿҳ ---
+jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRawMaterialInspectAppScanVo.java
+jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialInspectAppController.java
+jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRawMaterialInspectRecordService.java
+jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRawMaterialInspectRecordServiceImpl.java
+JeecgUniapp-master/src/service/xslmes/rawMaterialInspect.ts
+JeecgUniapp-master/src/pages/rawMaterialInspect/rawMaterialInspect.vue
+JeecgUniapp-master/src/pages/rawMaterialInspect/inspectResult.vue
+JeecgUniapp-master/src/common/work.ts
diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialInspectAppController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialInspectAppController.java
new file mode 100644
index 00000000..4aa22056
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialInspectAppController.java
@@ -0,0 +1,95 @@
+package org.jeecg.modules.xslmes.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInspectRecord;
+import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInspectRecordLine;
+import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInspectRecordService;
+import org.jeecg.modules.xslmes.vo.MesXslRawMaterialInspectAppScanVo;
+import org.jeecg.modules.xslmes.vo.MesXslRawMaterialInspectRecordPage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * APP 原材料送检接口(手持端)
+ * 判重规则与 PC 一致:存在 inspect_status=0 的送检记录视为已送检。
+ */
+@Tag(name = "APP-原材料送检")
+@RestController
+@RequestMapping("/xslmes/app/rawMaterialInspect")
+public class MesXslRawMaterialInspectAppController {
+
+ @Autowired private IMesXslRawMaterialInspectRecordService inspectRecordService;
+
+ //update-begin---author:xsl ---date:20260720 for:【APP原材料送检】扫码查询-----------
+ @AutoLog(value = "APP原材料送检-按条码查询")
+ @Operation(summary = "按条码查询卡片及送检状态")
+ @GetMapping("/queryByBarcode")
+ public Result queryByBarcode(
+ @RequestParam(name = "barcode") String barcode) {
+ if (StringUtils.isBlank(barcode)) {
+ return Result.error("条码不能为空");
+ }
+ return Result.OK(inspectRecordService.queryByBarcodeForApp(barcode.trim()));
+ }
+ //update-end---author:xsl ---date:20260720 for:【APP原材料送检】扫码查询-----------
+
+ //update-begin---author:xsl ---date:20260720 for:【APP原材料送检】按条码送检-----------
+ @AutoLog(value = "APP原材料送检-按条码送检")
+ @Operation(summary = "按条码送检")
+ @PostMapping("/createByBarcode")
+ public Result createByBarcode(@RequestBody Map body) {
+ String barcode = body.get("barcode") == null ? "" : body.get("barcode").toString();
+ if (StringUtils.isBlank(barcode)) {
+ return Result.error("条码不能为空");
+ }
+ MesXslRawMaterialInspectRecord record = inspectRecordService.createByBarcode(barcode.trim());
+ return Result.OK("送检成功", record);
+ }
+ //update-end---author:xsl ---date:20260720 for:【APP原材料送检】按条码送检-----------
+
+ //update-begin---author:xsl ---date:20260720 for:【APP原材料送检】录入检验结果-----------
+ @AutoLog(value = "APP原材料送检-加载检验明细")
+ @Operation(summary = "录入检验结果前加载明细")
+ @PostMapping("/prepareResultEntry")
+ public Result> prepareResultEntry(
+ @RequestBody Map body) {
+ String id = body.get("id") == null ? "" : body.get("id").toString();
+ if (StringUtils.isBlank(id)) {
+ return Result.error("id不能为空");
+ }
+ return Result.OK(inspectRecordService.prepareResultEntryLines(id.trim()));
+ }
+
+ @AutoLog(value = "APP原材料送检-保存检验结果")
+ @Operation(summary = "保存检验结果")
+ @PostMapping("/saveInspectResult")
+ public Result saveInspectResult(@RequestBody MesXslRawMaterialInspectRecordPage page) {
+ if (StringUtils.isBlank(page.getId())) {
+ return Result.error("id不能为空");
+ }
+ inspectRecordService.saveInspectResult(page.getId(), page.getLineList());
+ return Result.OK("保存成功");
+ }
+
+ @Operation(summary = "按送检记录ID查询主表")
+ @GetMapping("/queryRecordById")
+ public Result queryRecordById(@RequestParam(name = "id") String id) {
+ MesXslRawMaterialInspectRecord db = inspectRecordService.getById(id);
+ if (db == null) {
+ return Result.error("未找到对应数据");
+ }
+ return Result.OK(db);
+ }
+ //update-end---author:xsl ---date:20260720 for:【APP原材料送检】录入检验结果-----------
+}
diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRawMaterialInspectRecordService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRawMaterialInspectRecordService.java
index fb16ed7b..ee91714c 100644
--- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRawMaterialInspectRecordService.java
+++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRawMaterialInspectRecordService.java
@@ -23,4 +23,16 @@ public interface IMesXslRawMaterialInspectRecordService
Integer includeMinFlag,
BigDecimal allowMax,
Integer includeMaxFlag);
+
+ //update-begin---author:xsl ---date:20260720 for:【APP原材料送检】按条码查询/送检-----------
+ /**
+ * APP:按条码查询卡片及送检状态(是否已有待检送检记录)
+ */
+ org.jeecg.modules.xslmes.vo.MesXslRawMaterialInspectAppScanVo queryByBarcodeForApp(String barcode);
+
+ /**
+ * APP:按条码发起送检
+ */
+ MesXslRawMaterialInspectRecord createByBarcode(String barcode);
+ //update-end---author:xsl ---date:20260720 for:【APP原材料送检】按条码查询/送检-----------
}
diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRawMaterialInspectRecordServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRawMaterialInspectRecordServiceImpl.java
index d1db6d68..76f243ec 100644
--- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRawMaterialInspectRecordServiceImpl.java
+++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRawMaterialInspectRecordServiceImpl.java
@@ -324,4 +324,69 @@ public class MesXslRawMaterialInspectRecordServiceImpl
private String normalize(String value) {
return StringUtils.trimToEmpty(value);
}
+
+ //update-begin---author:xsl ---date:20260720 for:【APP原材料送检】按条码查询/送检-----------
+ @Override
+ public org.jeecg.modules.xslmes.vo.MesXslRawMaterialInspectAppScanVo queryByBarcodeForApp(String barcode) {
+ String code = normalize(barcode);
+ if (StringUtils.isBlank(code)) {
+ throw new JeecgBootException("条码不能为空");
+ }
+ MesXslRawMaterialCard card =
+ rawMaterialCardService
+ .lambdaQuery()
+ .eq(MesXslRawMaterialCard::getBarcode, code)
+ .last("LIMIT 1")
+ .one();
+ if (card == null) {
+ throw new JeecgBootException("未找到条码对应的原材料卡片");
+ }
+ MesXslRawMaterialInspectRecord pending =
+ this.lambdaQuery()
+ .eq(MesXslRawMaterialInspectRecord::getRawMaterialCardId, card.getId())
+ .eq(MesXslRawMaterialInspectRecord::getInspectStatus, STATUS_PENDING)
+ .orderByDesc(MesXslRawMaterialInspectRecord::getInspectTime)
+ .last("LIMIT 1")
+ .one();
+
+ org.jeecg.modules.xslmes.vo.MesXslRawMaterialInspectAppScanVo vo =
+ new org.jeecg.modules.xslmes.vo.MesXslRawMaterialInspectAppScanVo();
+ vo.setCard(card);
+ if (pending != null) {
+ // 已有待检送检记录 → 视为已送检,进入录入检验结果
+ vo.setHasPendingInspect(true);
+ vo.setPendingRecordId(pending.getId());
+ vo.setAction("enterResult");
+ return vo;
+ }
+ vo.setHasPendingInspect(false);
+ String cardTestResult = normalize(card.getTestResult());
+ if (StringUtils.isBlank(cardTestResult) || CARD_TEST_RESULT_UNTESTED.equals(cardTestResult)) {
+ vo.setAction("sendInspect");
+ } else {
+ // 已合格/不合格且无待检记录
+ vo.setAction("finished");
+ }
+ return vo;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public MesXslRawMaterialInspectRecord createByBarcode(String barcode) {
+ String code = normalize(barcode);
+ if (StringUtils.isBlank(code)) {
+ throw new JeecgBootException("条码不能为空");
+ }
+ MesXslRawMaterialCard card =
+ rawMaterialCardService
+ .lambdaQuery()
+ .eq(MesXslRawMaterialCard::getBarcode, code)
+ .last("LIMIT 1")
+ .one();
+ if (card == null) {
+ throw new JeecgBootException("未找到条码对应的原材料卡片");
+ }
+ return createByRawMaterialCardId(card.getId());
+ }
+ //update-end---author:xsl ---date:20260720 for:【APP原材料送检】按条码查询/送检-----------
}
diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRawMaterialInspectAppScanVo.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRawMaterialInspectAppScanVo.java
new file mode 100644
index 00000000..dc9f4110
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslRawMaterialInspectAppScanVo.java
@@ -0,0 +1,30 @@
+package org.jeecg.modules.xslmes.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.Serializable;
+import lombok.Data;
+import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCard;
+
+/**
+ * APP 原材料送检扫码查询结果
+ */
+@Data
+@Schema(description = "APP原材料送检扫码结果")
+public class MesXslRawMaterialInspectAppScanVo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "原材料卡片")
+ private MesXslRawMaterialCard card;
+
+ /**
+ * sendInspect=可送检;enterResult=已送检待录入;finished=已完成检验
+ */
+ @Schema(description = "下一步动作:sendInspect/enterResult/finished")
+ private String action;
+
+ @Schema(description = "是否存在待检送检记录")
+ private Boolean hasPendingInspect;
+
+ @Schema(description = "待检送检记录ID(action=enterResult 时有值)")
+ private String pendingRecordId;
+}
diff --git a/qhmes.code-workspace b/qhmes.code-workspace
index ab2f0bda..1b450c5b 100644
--- a/qhmes.code-workspace
+++ b/qhmes.code-workspace
@@ -17,7 +17,8 @@
"name": "打印调节器"
},
{
- "path": "JeecgUniapp-master"
+ "path": "JeecgUniapp-master",
+ "name": "APP端"
}
],
"settings": {