diff --git a/README.md b/README.md index 965b038..1b67a28 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# 星元智灵 +# WOER 回收箱 -星元智灵是基于智投终端流程改造的竖屏 Android 投放终端应用。项目保留串口投口控制、称重回包、本地备份和服务器上报框架,并集成本地 SeetaFace6 人脸识别与人员录入流程。 +WOER 回收箱是基于智投终端流程改造的竖屏 Android 投放终端应用。项目保留串口投口控制、称重回包、本地备份和服务器上报框架,并集成本地 SeetaFace6 人脸识别与人员录入流程。 本项目为沃尔核材专用 APP,定位为安卓上位机应用。 ## 主要功能 @@ -76,11 +76,11 @@ path: /dev/ttyS4 baudRate: 9600 ``` -如果首页提示串口故障,请检查串口占用情况以及下位机连接。旧智投 APP 与星元智灵同时运行时,可能会竞争 `/dev/ttyS4`。 +如果首页提示串口故障,请检查串口占用情况以及下位机连接。旧智投 APP 与 WOER 回收箱同时运行时,可能会竞争 `/dev/ttyS4`。 ## 接口说明 -当前机台号、人员、生产批次接口仍为占位实现。接口未接入时页面允许手动填写机台号,不会注入测试人员。后续接入正式接口时,需要替换: +当前机台号、人员、生产批次接口仍为测试数据实现,内置 `WAL-A01`、`WAL-A02`、`WAL-B01`、`WAL-B02` 四台机台供流程测试。后续接入正式接口时,需要替换: - `app/src/main/java/com/zhitou/terminal/ZhitouBridge.java` 中的 `fetchWorkContext` - 前端 `app/src/main/assets/www/app.js` 中的接口解析和兜底逻辑 @@ -98,7 +98,7 @@ baudRate: 9600 当前应用版本: ```text -versionName: 1.2.12-wal-review-fix -versionCode: 101 +versionName: 1.2.22-ui-spacing +versionCode: 111 package: com.xingyuan.zhiling ``` diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5fbe58a..1ec61cb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:versionCode="111" + android:versionName="1.2.22-ui-spacing"> ' + '
' + - '
' + + '
' + '

' + escapeHtml(t("appName")) + '

' + @@ -1477,7 +1478,8 @@ } function isFacePreviewActive() { - return state.screen === "home" || Boolean( + // 工号弹窗显示时先隐藏原生 SurfaceView,否则摄像头层会盖住 WebView 中央弹窗。 + return (state.screen === "home" && !(state.employeeLogin && state.employeeLogin.open)) || Boolean( state.screen === "settings" && state.activeTab === "face" && state.faceEnrollment && @@ -1540,7 +1542,7 @@ '

' + '
' + '
' + - '

星元智灵启动自检

' + + '

WOER 回收箱启动自检

' + '

' + escapeHtml(bootDesc) + "

" + @@ -1596,7 +1598,7 @@ escapeHtml("请正对摄像头进行人脸识别") + "" + '

' + - escapeHtml("人脸识别失败可输入工号进行登陆投放") + + escapeHtml("人脸识别失败可选择工号进行登陆投放") + "

" + '
正在连接 USB 摄像头画面…
' + (faceFeedback ? '
' + escapeHtml(faceFeedback) + '
' : '') + @@ -1613,6 +1615,10 @@ "
" + renderEmployeeLoginDialog() + "
" + + '
' + + '沃尔核材·数字化生产' + + '智能回收终端' + + "
" + "
" ); } @@ -1627,12 +1633,11 @@ } function renderHomeActions(reason) { - var blocked = Boolean(reason); + var serialBlocked = !state.deviceStatus.serialOnline; return ( '
' + '" + "
" @@ -1640,28 +1645,52 @@ } function renderEmployeeLoginDialog() { - var login = state.employeeLogin || { open: false, value: "", message: "" }; + var login = state.employeeLogin || { open: false, employeeNo: "", message: "" }; + var people = employeeLoginPeople(); + var serialBlocked = !state.deviceStatus.serialOnline; if (!login.open) return ""; + var choices = people.length + ? people.map(function (person) { + var active = String(login.employeeNo || "") === person.employeeNo; + return '"; + }).join("") + : '
暂无人员,请先到“设置 → 人脸”新增人员。
'; return ( '" ); } + function employeeLoginPeople() { + var seen = {}; + return (Array.isArray(state.facePeople) && state.facePeople.length ? state.facePeople : state.faceDirectory || []) + .map(function (person) { + return { + employeeNo: String(person && person.employeeNo || "").trim(), + name: String(person && person.name || "").trim() + }; + }) + .filter(function (person) { + if (!person.employeeNo || seen[person.employeeNo]) return false; + seen[person.employeeNo] = true; + return true; + }) + .sort(function (first, second) { + return first.employeeNo.localeCompare(second.employeeNo); + }); + } + function homeBlockReason() { if (state.deviceStatus.maintenance || state.deviceStatus.deviceDisabled) return t("maintenance"); if (!state.deviceStatus.collectionDoorClosed) return "收运门未关闭"; @@ -2121,7 +2150,7 @@ '

' + escapeHtml(t("deviceParams")) + '

' + field(t("deviceNo"), "config.device.deviceNo", state.config.device.deviceNo || "") + field(t("terminalImei"), "config.device.terminalId", state.config.device.terminalId) + - field(t("serialPath"), "config.device.serialPath", state.config.device.serialPath) + + serialPathField() + "
" + renderMaintenanceSettings() + renderDeviceDisabledSettings() + @@ -2139,6 +2168,71 @@ ); } + function serialPathField() { + var current = String(state.config.device.serialPath || "/dev/ttyS4"); + var serialState = state.serialPorts || { loaded: false, loading: false, items: [], message: "" }; + var items = Array.isArray(serialState.items) ? serialState.items.slice() : []; + var hasCurrent = items.some(function (item) { return item.path === current; }); + if (!hasCurrent) { + items.unshift({ path: current, readable: false, writable: false, configuredOnly: true }); + } + if (!serialState.loaded && !serialState.loading) { + serialState.loading = true; + state.serialPorts = serialState; + window.setTimeout(function () { refreshSerialPorts(false); }, 0); + } + var options = items.map(function (item) { + var permission = item.configuredOnly + ? "当前配置,未检测到" + : item.readable && item.writable ? "可读写" : "需获取权限"; + return '"; + }).join(""); + var hint = serialState.loading + ? "正在扫描安卓设备串口…" + : serialState.message || "选择串口后,请点击“重新启动自检”验证连接。"; + return ( + '
" + + '
" + + '
' + + '

' + escapeHtml(hint) + "

" + ); + } + + function refreshSerialPorts(notify) { + var bridge = nativeBridge(); + var serialState = state.serialPorts || { loaded: false, loading: false, items: [], message: "" }; + serialState.loading = true; + state.serialPorts = serialState; + try { + if (!bridge || typeof bridge.listSerialPorts !== "function") { + throw new Error("当前版本未加载串口扫描模块"); + } + var result = JSON.parse(String(bridge.listSerialPorts("{}") || "{}")); + if (!result.ok) throw new Error(result.error || "串口扫描失败"); + var seen = {}; + serialState.items = (Array.isArray(result.ports) ? result.ports : []).filter(function (item) { + var path = String(item && item.path || ""); + if (!/^\/dev\/[A-Za-z0-9._/-]+$/.test(path) || seen[path]) return false; + seen[path] = true; + item.path = path; + return true; + }); + serialState.message = serialState.items.length + ? "已发现 " + serialState.items.length + " 个串口设备。" + : "未发现串口设备,已保留当前配置。"; + if (notify) showToast(serialState.message); + } catch (error) { + serialState.items = []; + serialState.message = "串口扫描失败:" + (error.message || error); + if (notify) showToast(serialState.message); + } + serialState.loaded = true; + serialState.loading = false; + state.serialPorts = serialState; + render(); + } + function renderMaintenanceSettings() { return ( '

' + escapeHtml(t("maintenanceMode")) + "

" + @@ -2754,45 +2848,45 @@ } function openEmployeeLogin() { - if (homeBlockReason()) { - showToast(homeBlockReason()); + if (!state.deviceStatus.serialOnline) { + showToast("串口异常,无法选择工号"); return; } - state.employeeLogin = { open: true, value: "", message: "" }; + state.employeeLogin = { open: true, employeeNo: "", message: "" }; render(); } function closeEmployeeLogin() { - state.employeeLogin = { open: false, value: "", message: "" }; + state.employeeLogin = { open: false, employeeNo: "", message: "" }; render(); } - function appendEmployeeDigit(digit) { - var login = state.employeeLogin; - if (!login || !login.open || login.value.length >= 20) return; - login.value += String(digit); - login.message = ""; - render(); - } - - function backspaceEmployeeDigit() { - if (!state.employeeLogin || !state.employeeLogin.open) return; - state.employeeLogin.value = state.employeeLogin.value.slice(0, -1); - state.employeeLogin.message = ""; - render(); - } - - function clearEmployeeDigits() { - if (!state.employeeLogin || !state.employeeLogin.open) return; - state.employeeLogin.value = ""; + function selectEmployeeForLogin(target) { + if (!state.deviceStatus.serialOnline || !state.employeeLogin || !state.employeeLogin.open) return; + var employeeNo = String(target.getAttribute("data-employee-no") || "").trim(); + if (!employeeNo || !employeeLoginPeople().some(function (person) { return person.employeeNo === employeeNo; })) return; + state.employeeLogin.employeeNo = employeeNo; state.employeeLogin.message = ""; render(); } function submitEmployeeLogin() { - var value = String((state.employeeLogin && state.employeeLogin.value) || "").trim(); - if (!/^\d{1,20}$/.test(value)) { - state.employeeLogin.message = "请输入正确的工号"; + var reason; + if (!state.deviceStatus.serialOnline) { + state.employeeLogin.message = "串口异常,无法使用工号登录"; + render(); + return; + } + var value = String((state.employeeLogin && state.employeeLogin.employeeNo) || "").trim(); + if (!value || !employeeLoginPeople().some(function (person) { return person.employeeNo === value; })) { + state.employeeLogin.message = "请选择人员"; + render(); + return; + } + reason = homeBlockReason(); + if (reason) { + // 鉴权前设备状态可能发生变化;保持弹窗状态一致并直接展示阻断原因。 + state.employeeLogin.message = reason; render(); return; } @@ -3065,7 +3159,7 @@ if (state.screen !== "home" && state.screen !== "auth") return; if (state.screen === "auth" && priority <= Number(state.auth.priority || 0)) return; if (!identityRaw) { - showToast(source === "face" ? "等待人脸识别结果" : source === "employee" ? "请输入工号" : "请刷 IC/ID 卡"); + showToast(source === "face" ? "等待人脸识别结果" : source === "employee" ? "请选择工号" : "请刷 IC/ID 卡"); return; } var identity = identityRaw; @@ -3080,7 +3174,7 @@ status: "reading", priority: priority, token: Date.now() + "-" + Math.random(), - message: source === "face" ? "已识别人脸,正在核对本地人脸库。" : source === "card" ? "已读取 IC/ID 卡,正在向门口终端机鉴权。" : "正在向门口终端机校验工号。" + message: source === "face" ? "已识别人脸,正在核对本地人脸库。" : source === "card" ? "已读取 IC/ID 卡,正在向门口终端机鉴权。" : "请稍候,正在建立用户会话。" }; requestToken = state.auth.token; render(); @@ -3614,7 +3708,7 @@ payload: { deviceNo: String(state.config.device.deviceNo || ""), deviceUniqueId: String(state.config.device.terminalId || state.lowerImei || ""), - deviceName: "星元智灵回收箱", + deviceName: "WOER 回收箱", deliveryTime: nowText(Date.now()), createdAt: Date.now(), employeeName: work.employeeName || work.person || "", @@ -3905,7 +3999,7 @@ timestamp: record.createdAt, deviceNo: String(state.config.device.deviceNo || ""), deviceUniqueId: String(state.config.device.terminalId || state.lowerImei || ""), - deviceName: "星元智灵回收箱", + deviceName: "WOER 回收箱", machineNo: work.machineNo || record.payload.machineNo || "", person: work.employeeName || work.person || record.payload.employeeName || record.payload.person || "", employeeNo: work.employeeNo || record.payload.employeeNo || "", @@ -4045,7 +4139,7 @@ startHeartbeat: "启动心跳 1001", reportMemberVerify: "发送会员校验 4301", reportDelivery: "发送投递记录 6011", - reportXingyuanDelivery: "发送星元智灵投放结果", + reportXingyuanDelivery: "发送 WOER 回收箱投放结果", reportDeliveryResult: "发送投放结果", fetchWorkContext: "获取投放信息", exportLogs: "导出日志", @@ -4445,7 +4539,7 @@ state.portSettingsPort = 0; state.delivery = null; state.auth = { status: "idle", message: "" }; - state.employeeLogin = { open: false, value: "", message: "" }; + state.employeeLogin = { open: false, employeeNo: "", message: "" }; render(); } @@ -4921,6 +5015,13 @@ } function bindEvents() { + // 终端为固定业务界面:禁止所有页面的长按菜单、文字选择和图片拖拽。 + ["contextmenu", "selectstart", "dragstart"].forEach(function (eventName) { + document.addEventListener(eventName, function (event) { + event.preventDefault(); + }, true); + }); + window.addEventListener("resize", function () { syncFacePreviewLayout(isFacePreviewActive()); }); @@ -4940,9 +5041,7 @@ if (action === "open-work-info") openWorkInfo(false); if (action === "open-employee-login") openEmployeeLogin(); if (action === "close-employee-login") closeEmployeeLogin(); - if (action === "employee-key") appendEmployeeDigit(target.getAttribute("data-key")); - if (action === "employee-backspace") backspaceEmployeeDigit(); - if (action === "employee-clear") clearEmployeeDigits(); + if (action === "select-employee") selectEmployeeForLogin(target); if (action === "submit-employee-login") submitEmployeeLogin(); if (action === "refresh-work-info") fetchWorkContext(); if (action === "submit-work-info") submitWorkInfo(); @@ -4979,6 +5078,7 @@ if (action === "check-lower-version") checkLowerVersion(); if (action === "get-lower-imei") getLowerImei(); if (action === "get-lower-info") getLowerInfo(); + if (action === "refresh-serial-ports") refreshSerialPorts(true); if (action === "retry-cache") retryCache(); if (action === "cleanup-cache") { cleanupCache(true); @@ -5045,8 +5145,6 @@ if (state.screen === "adminLogin" && /^[0-9]$/.test(event.key)) appendAdminDigit(event.key); if (state.screen === "adminLogin" && event.key === "Backspace") backspaceAdminDigit(); if (state.screen === "adminLogin" && event.key === "Enter") submitAdmin(); - if (state.screen === "home" && state.employeeLogin && state.employeeLogin.open && /^[0-9]$/.test(event.key)) appendEmployeeDigit(event.key); - if (state.screen === "home" && state.employeeLogin && state.employeeLogin.open && event.key === "Backspace") backspaceEmployeeDigit(); if (state.screen === "home" && state.employeeLogin && state.employeeLogin.open && event.key === "Enter") submitEmployeeLogin(); if (state.screen === "workInfo" && event.key === "Enter") submitWorkInfo(); if (state.screen === "delivery" && event.key === "Escape") closeDoor(true); diff --git a/app/src/main/assets/www/assets/woer-logo-mark.png b/app/src/main/assets/www/assets/woer-logo-mark.png new file mode 100644 index 0000000..b3522d4 Binary files /dev/null and b/app/src/main/assets/www/assets/woer-logo-mark.png differ diff --git a/app/src/main/assets/www/index.html b/app/src/main/assets/www/index.html index c478abe..798746d 100644 --- a/app/src/main/assets/www/index.html +++ b/app/src/main/assets/www/index.html @@ -4,7 +4,7 @@ - 星元智灵 + WOER 回收箱 diff --git a/app/src/main/assets/www/styles.css b/app/src/main/assets/www/styles.css index 436c643..191133c 100644 --- a/app/src/main/assets/www/styles.css +++ b/app/src/main/assets/www/styles.css @@ -15,6 +15,14 @@ * { box-sizing: border-box; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; +} + +img { + -webkit-user-drag: none; + user-drag: none; } html, @@ -78,19 +86,30 @@ select:disabled { .brand { display: flex; align-items: center; - gap: 12px; + gap: 0; min-width: 0; } +.brand-mark + div { + margin-left: 20px; +} + .brand-mark { width: 42px; height: 42px; display: grid; place-items: center; + padding: 2px; + overflow: hidden; border-radius: 8px; background: #fff; - color: var(--deep); - font-weight: 800; +} + +.brand-mark img { + width: 100%; + height: 100%; + object-fit: contain; + pointer-events: none; } .brand-title { @@ -279,7 +298,9 @@ select:disabled { .home-main { flex: 1; - display: block; + display: flex; + flex-direction: column; + min-height: 0; padding: 22px; } @@ -347,7 +368,8 @@ select:disabled { .home-lower { position: relative; - min-height: calc(100vh - 118px); + flex: 1; + min-height: calc(100vh - 182px); display: grid; grid-template-rows: 1fr auto; align-items: center; @@ -357,6 +379,33 @@ select:disabled { border-radius: 8px; } +.home-brand-footer { + width: 100%; + flex: 0 0 auto; + min-height: 64px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + padding: 0 38px 20px; + border-top: 1px solid var(--line); + background: rgba(255, 255, 255, 0.72); + color: #94a098; + font-size: 12px; + letter-spacing: 0.04em; + line-height: 1.4; +} + +.home-brand-footer-company, +.home-brand-footer-terminal { + white-space: nowrap; +} + +.home-brand-footer-terminal { + margin-left: auto; + text-align: right; +} + .home-prompt { text-align: center; } @@ -621,6 +670,7 @@ select:disabled { flex-wrap: wrap; justify-content: center; gap: 12px; + margin-top: 22px; } .home-login-btn { @@ -708,6 +758,11 @@ select:disabled { .work-picker-backdrop { position: fixed; + /* Android 7 的旧版 WebView 不支持 inset,必须保留四边定位作为兼容写法。 */ + top: 0; + right: 0; + bottom: 0; + left: 0; inset: 0; z-index: 50; display: grid; @@ -716,6 +771,16 @@ select:disabled { background: rgba(10, 28, 30, 0.38); } +.employee-login-backdrop { + width: 100vw; + height: 100vh; + height: 100dvh; + display: flex; + align-items: center; + justify-content: center; + z-index: 100; +} + .work-picker-dialog { width: min(92vw, 620px); max-height: min(72vh, 560px); @@ -729,28 +794,47 @@ select:disabled { .employee-login-dialog { width: min(92vw, 520px); + max-height: calc(100dvh - 48px); + margin: auto; } -.employee-number-display { - min-height: 64px; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 14px; - padding: 10px 16px; +.employee-choice-list { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 12px 0 18px; +} + +.employee-choice { + min-height: 76px; + padding: 12px 14px; border: 2px solid var(--line); border-radius: 8px; background: #f8fbfa; - font-size: 28px; - font-weight: 900; - letter-spacing: 2px; + color: var(--ink); + display: grid; + gap: 5px; + text-align: left; } -.employee-keypad { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 10px; - margin: 12px 0 18px; +.employee-choice span { + font-size: 20px; + font-weight: 900; +} + +.employee-choice strong { + color: var(--muted); + font-size: 15px; +} + +.employee-choice.selected { + border-color: var(--teal); + background: #e5f4ef; +} + +.employee-choice:disabled { + background: #f1f3f2; + color: #98a2a3; } .work-picker-head { @@ -1087,6 +1171,25 @@ select:disabled { color: var(--ink); } +.serial-path-field { + grid-column: 1 / -1; +} + +.serial-path-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: stretch; +} + +.serial-path-row .btn { + min-width: 104px; +} + +.serial-path-hint { + margin: 0; +} + .readonly-value { width: 100%; min-height: 44px; @@ -1623,6 +1726,12 @@ select:disabled { padding: 22px 16px 76px; } + .home-brand-footer { + min-height: 64px; + padding: 0 16px 20px; + font-size: 12px; + } + .home-camera-preview { width: calc(100% + 32px); max-width: none; @@ -1752,10 +1861,15 @@ select:disabled { } .home-lower { - min-height: calc(100dvh - 92px); + min-height: calc(100vh - 156px); + min-height: calc(100dvh - 156px); padding: 22px 14px 76px; } + .home-brand-footer { + gap: 12px; + } + .prompt-title { font-size: clamp(26px, 7vw, 38px); } diff --git a/app/src/main/java/com/zhitou/terminal/MainActivity.java b/app/src/main/java/com/zhitou/terminal/MainActivity.java index 9d8f074..7fb28c5 100644 --- a/app/src/main/java/com/zhitou/terminal/MainActivity.java +++ b/app/src/main/java/com/zhitou/terminal/MainActivity.java @@ -116,6 +116,16 @@ public class MainActivity extends Activity implements settings.setLoadWithOverviewMode(true); settings.setUseWideViewPort(true); + // 终端所有业务页面均禁止长按:拦截 WebView 的文本选择、上下文菜单和触觉反馈。 + webView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + return true; + } + }); + webView.setLongClickable(false); + webView.setHapticFeedbackEnabled(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // 页面只通过受控的原生桥通讯,不允许 file:// 页面任意读取文件或跨域访问。 settings.setAllowFileAccessFromFileURLs(false); diff --git a/app/src/main/java/com/zhitou/terminal/ZhitouBridge.java b/app/src/main/java/com/zhitou/terminal/ZhitouBridge.java index 710f50f..4123f77 100644 --- a/app/src/main/java/com/zhitou/terminal/ZhitouBridge.java +++ b/app/src/main/java/com/zhitou/terminal/ZhitouBridge.java @@ -10,6 +10,10 @@ import java.io.FileReader; import java.io.FileWriter; import java.net.InetSocketAddress; import java.net.Socket; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.Locale; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -206,6 +210,56 @@ final class ZhitouBridge { } } + /** 扫描常见 Android 串口设备节点,供设置页选择,不在扫描阶段占用串口。 */ + @JavascriptInterface + public String listSerialPorts(String json) { + JSONObject result = new JSONObject(); + JSONArray ports = new JSONArray(); + try { + File[] devices = new File("/dev").listFiles(); + if (devices == null) { + throw new IllegalStateException("无法读取 /dev 目录"); + } + List candidates = new ArrayList(); + for (File device : devices) { + String name = device == null ? "" : device.getName(); + if (isSerialDeviceName(name)) { + candidates.add(device); + } + } + Collections.sort(candidates, new Comparator() { + @Override + public int compare(File first, File second) { + return first.getAbsolutePath().compareToIgnoreCase(second.getAbsolutePath()); + } + }); + for (File device : candidates) { + JSONObject item = new JSONObject(); + item.put("path", device.getAbsolutePath()); + item.put("readable", device.canRead()); + item.put("writable", device.canWrite()); + ports.put(item); + } + result.put("ok", true); + result.put("ports", ports); + result.put("count", ports.length()); + } catch (Exception error) { + try { + result.put("ok", false); + result.put("ports", ports); + result.put("count", 0); + result.put("error", error.getMessage() == null ? "串口扫描失败" : error.getMessage()); + } catch (Exception ignored) { + } + } + return result.toString(); + } + + private boolean isSerialDeviceName(String name) { + return name != null && name.matches( + "^(ttyS|ttyUSB|ttyACM|ttyAMA|ttyFIQ|ttyXRUSB|ttyHS|ttyMT|ttymxc|ttysWK|ttyRK|ttyAP)[0-9]+$"); + } + @JavascriptInterface public String getLowerVersion(String json) { try { @@ -386,8 +440,27 @@ final class ZhitouBridge { @JavascriptInterface public String fetchWorkContext(String json) { - // 正式机台接口未接入前返回空数组,前端仍允许手动填写,不能注入测试人员或批次。 - return new JSONArray().toString(); + JSONArray machines = new JSONArray(); + try { + machines.put(new JSONObject() + .put("machineNo", "WAL-A01") + .put("persons", new JSONArray().put("张伟").put("李娜")) + .put("batches", new JSONArray().put("20260723-A01").put("20260723-A02"))); + machines.put(new JSONObject() + .put("machineNo", "WAL-A02") + .put("persons", new JSONArray().put("王强").put("赵敏")) + .put("batches", new JSONArray().put("20260723-B01").put("20260723-B02"))); + machines.put(new JSONObject() + .put("machineNo", "WAL-B01") + .put("persons", new JSONArray().put("陈晨").put("刘洋")) + .put("batches", new JSONArray().put("20260723-C01").put("20260723-C02"))); + machines.put(new JSONObject() + .put("machineNo", "WAL-B02") + .put("persons", new JSONArray().put("周杰").put("孙悦")) + .put("batches", new JSONArray().put("20260723-D01").put("20260723-D02"))); + } catch (Exception ignored) { + } + return machines.toString(); } /** 人脸平台接口尚未接入;明确返回失败,避免把测试人员写入真实人脸库。 */ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 37eb6f1..74308c3 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 93147f7..b057398 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 7f260f7..e262555 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index e5ecd7f..066409b 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index e016d1b..cf0d862 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b541425..240619b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - 星元智灵 + WOER 回收箱 diff --git a/build.sh b/build.sh index 8ae1658..3c14883 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,6 @@ #!/bin/bash # ============================================================ -# 星元智灵 APK 手动编译脚本 (无 Gradle) +# WOER 回收箱 APK 手动编译脚本 (无 Gradle) # 使用 Android SDK 命令行工具: aapt / javac / d8 / zipalign / apksigner # ============================================================ set -e @@ -34,7 +34,7 @@ CLASSES="$BUILD/classes" DEX="$BUILD/dex" echo "==========================================" -echo " 星元智灵 APK 编译" +echo " WOER 回收箱 APK 编译" echo "==========================================" echo "SDK: $SDK" echo "BuildTools: 34.0.0" diff --git a/tests/browser/harness.html b/tests/browser/harness.html index 3e2a576..9f41660 100644 --- a/tests/browser/harness.html +++ b/tests/browser/harness.html @@ -3,7 +3,7 @@ - 星元智灵异常场景测试 + WOER 回收箱异常场景测试 @@ -15,6 +15,7 @@ var scenario = params.get("scenario") || "pass"; var baselineDelay = Number(params.get("baselineDelay") || 30); var weightQueryCounts = {}; + var connectionCheckCount = 0; if (params.get("clear") === "1") localStorage.clear(); window.__nativeCalls = []; @@ -42,7 +43,9 @@ }, requestConnectionCheck: function (json) { var request = record("requestConnectionCheck", json); - var serialOnline = scenario !== "serial-fail" && scenario !== "all-fail"; + connectionCheckCount += 1; + var serialOnline = scenario !== "serial-fail" && scenario !== "all-fail" && + !(scenario === "serial-recover" && connectionCheckCount < 3); var networkOnline = scenario !== "network-fail" && scenario !== "all-fail"; emitConnection({ requestId: request.requestId, @@ -78,10 +81,67 @@ openThrowDoor: function (json) { record("openThrowDoor", json); return scenario !== "open-fail"; }, closeThrowDoor: function (json) { record("closeThrowDoor", json); return scenario !== "close-fail"; }, reportXingyuanDelivery: function (json) { record("reportXingyuanDelivery", json); return true; }, - saveLocalBackup: function (json) { record("saveLocalBackup", json); return "/tmp/test-backup.json"; } + saveLocalBackup: function (json) { record("saveLocalBackup", json); return "/tmp/test-backup.json"; }, + setFaceRecognitionActive: function (json) { record("setFaceRecognitionActive", json); }, + setFacePreviewLayout: function (json) { record("setFacePreviewLayout", json); }, + prepareFaceEnrollment: function (json) { record("prepareFaceEnrollment", json); return true; }, + getFaceRecognitionStatus: function () { + return JSON.stringify({ ok: true, status: "ready", message: "SeetaFace6 识别引擎已就绪", enrolledCount: 1 }); + }, + enrollCurrentFace: function (json) { + record("enrollCurrentFace", json); + return JSON.stringify({ ok: true, message: "人脸录入成功" }); + }, + setFaceMatchThreshold: function (json) { + var request = record("setFaceMatchThreshold", json); + return JSON.stringify({ ok: true, threshold: Number(request.threshold || 0.75) }); + }, + listFacePeople: function () { + var people = scenario === "no-people" ? [] : [ + { faceId: "FACE-1000", employeeNo: "1000", name: "测试员" } + ]; + return JSON.stringify({ ok: true, people: people, count: people.length }); + }, + verifyMember: function (json) { + var request = record("verifyMember", json); + if (scenario === "auth-fail") return JSON.stringify({ ok: false, error: "测试鉴权失败" }); + return JSON.stringify({ + ok: true, + memberCode: request.idContent || "1000", + employeeNo: request.idContent || "1000", + memberId: "MEMBER-TEST", + name: "测试员" + }); + }, + listSerialPorts: function () { + return JSON.stringify({ + ok: true, + ports: [ + { path: "/dev/ttyS0", readable: true, writable: true }, + { path: "/dev/ttysWK0", readable: true, writable: true }, + { path: "/dev/ttyUSB0", readable: true, writable: true } + ] + }); + } }; })(); - + +