+
+
+
未检测到 App 接口
+
+
+ 设备信息:
+ 应用版本: Unknown
+ 环境检测: Browser
+
+
+
+
(支持 PDF 导出、网络打印机、A4 适配)
+
+
+
(适用于蓝牙/USB 小票机,传输原始 JSON)
+
+
+
+
+
+
测试签收单
+
+
单号: SN20260315888
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
物料: 工业级调度终端 x 5
+
时间:
+
+ [ 此处为二维码/条码预览 ]
+
+
+
+
+
+
+
+
+
diff --git a/StandAPP/app/src/main/java/com/stand/standapp/AndroidInterface.kt b/StandAPP/app/src/main/java/com/stand/standapp/AndroidInterface.kt
new file mode 100644
index 0000000..8494e62
--- /dev/null
+++ b/StandAPP/app/src/main/java/com/stand/standapp/AndroidInterface.kt
@@ -0,0 +1,92 @@
+package com.stand.standapp
+
+import android.content.Context
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.webkit.JavascriptInterface
+import android.widget.Toast
+import android.print.PrintAttributes
+import android.print.PrintManager
+import com.stand.standapp.R
+
+class AndroidInterface(private val activity: MainActivity) {
+
+ /**
+ * JS 调用:标准系统打印 (PDF/网络打印机/AirPrint)
+ * 适用于打印精美的 A4 单据或页面
+ */
+ @JavascriptInterface
+ fun printPage() {
+ Handler(Looper.getMainLooper()).post {
+ try {
+ val printManager = activity.getSystemService(Context.PRINT_SERVICE) as PrintManager
+ // 获取 WebView 的打印适配器
+ val webView = activity.getAgentWeb()?.webCreator?.getWebView()
+ if (webView == null) {
+ Toast.makeText(activity, "WebView 未就绪", Toast.LENGTH_SHORT).show()
+ return@post
+ }
+
+ val printAdapter = webView.createPrintDocumentAdapter("Document")
+ val jobName = activity.getString(R.string.app_name) + " Print Job"
+
+ printManager.print(jobName, printAdapter, PrintAttributes.Builder().build())
+ } catch (e: Exception) {
+ Log.e("Print", "系统打印失败", e)
+ Toast.makeText(activity, "打印服务启动失败", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+
+ /**
+ * JS 调用:原始数据打印
+ * 适用于热敏小票打印机 (蓝牙、USB、网络)
+ * @param data 可以是 JSON 字符串,包含打印的指令或文本
+ */
+ @JavascriptInterface
+ fun printRawData(data: String) {
+ Log.d("Print", "收到 H5 原始打印数据: $data")
+ Handler(Looper.getMainLooper()).post {
+ // 这里可以弹出一个选择框让用户选择打印机,或者根据配置自动连接
+ // 常见的打印流程:1. 解析数据 -> 2. 转换成 ESC/POS 指令 -> 3. 发送到通道
+ Toast.makeText(activity, "H5指令打印: $data", Toast.LENGTH_LONG).show()
+
+ // 预留不同通道的入口
+ // connectAndPrint(data)
+ }
+ }
+
+ /**
+ * JS 调用:在线升级 (手动触发检查)
+ */
+ @JavascriptInterface
+ fun startUpdate(apkUrl: String) {
+ Handler(Looper.getMainLooper()).post {
+ UpdateManager(activity).checkUpdate()
+ }
+ }
+
+ /**
+ * JS 调用:退出应用
+ */
+ @JavascriptInterface
+ fun exitApp() {
+ Handler(Looper.getMainLooper()).post {
+ activity.showExitDialog()
+ }
+ }
+
+ /**
+ * 获取版本号给 H5 判断是否需要升级
+ */
+ @JavascriptInterface
+ fun getVersionName(): String {
+ return try {
+ val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
+ packageInfo.versionName ?: "1.0.0"
+ } catch (e: Exception) {
+ "1.0.0"
+ }
+ }
+}
diff --git a/StandAPP/app/src/main/java/com/stand/standapp/AppConfig.kt b/StandAPP/app/src/main/java/com/stand/standapp/AppConfig.kt
new file mode 100644
index 0000000..de1777d
--- /dev/null
+++ b/StandAPP/app/src/main/java/com/stand/standapp/AppConfig.kt
@@ -0,0 +1,51 @@
+package com.stand.standapp
+
+import android.content.Context
+import android.content.SharedPreferences
+
+object AppConfig {
+ private const val PREF_NAME = "app_settings"
+ private const val KEY_SERVER_URL = "server_url"
+ private const val KEY_REMEMBER_LOGIN = "remember_login"
+ private const val KEY_USERNAME = "saved_username"
+ private const val KEY_PASSWORD = "saved_password"
+
+ // 默认服务器地址
+ const val DEFAULT_SERVER_URL = "http://152.32.186.199:38080"
+
+ private fun getPrefs(context: Context): SharedPreferences {
+ return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
+ }
+
+ fun getServerUrl(context: Context): String {
+ return getPrefs(context).getString(KEY_SERVER_URL, DEFAULT_SERVER_URL) ?: DEFAULT_SERVER_URL
+ }
+
+ fun setServerUrl(context: Context, url: String) {
+ getPrefs(context).edit().putString(KEY_SERVER_URL, url).apply()
+ }
+
+ fun isRememberLogin(context: Context): Boolean {
+ return getPrefs(context).getBoolean(KEY_REMEMBER_LOGIN, false)
+ }
+
+ fun setRememberLogin(context: Context, remember: Boolean) {
+ getPrefs(context).edit().putBoolean(KEY_REMEMBER_LOGIN, remember).apply()
+ }
+
+ fun getSavedUsername(context: Context): String {
+ return getPrefs(context).getString(KEY_USERNAME, "") ?: ""
+ }
+
+ fun setSavedUsername(context: Context, username: String) {
+ getPrefs(context).edit().putString(KEY_USERNAME, username).apply()
+ }
+
+ fun getSavedPassword(context: Context): String {
+ return getPrefs(context).getString(KEY_PASSWORD, "") ?: ""
+ }
+
+ fun setSavedPassword(context: Context, password: String) {
+ getPrefs(context).edit().putString(KEY_PASSWORD, password).apply()
+ }
+}
diff --git a/StandAPP/app/src/main/java/com/stand/standapp/LoginActivity.kt b/StandAPP/app/src/main/java/com/stand/standapp/LoginActivity.kt
new file mode 100644
index 0000000..9041191
--- /dev/null
+++ b/StandAPP/app/src/main/java/com/stand/standapp/LoginActivity.kt
@@ -0,0 +1,103 @@
+package com.stand.standapp
+
+import android.content.Intent
+import android.os.Bundle
+import android.widget.*
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+
+class LoginActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_login)
+
+ val etUsername = findViewById