Browse Source

初始提交

cjb 2 weeks ago
commit
164473485d
100 changed files with 16043 additions and 0 deletions
  1. 1 0
      .gitattributes
  2. 11 0
      .gitignore
  3. 10 0
      AppScope/app.json5
  4. 8 0
      AppScope/resources/base/element/string.json
  5. BIN
      AppScope/resources/base/media/app_icon.png
  6. 49 0
      build-profile.json5
  7. 6 0
      entry/.gitignore
  8. 31 0
      entry/build-profile.json5
  9. 6 0
      entry/hvigorfile.ts
  10. 18 0
      entry/obfuscation-rules.txt
  11. 53 0
      entry/oh-package-lock.json5
  12. 16 0
      entry/oh-package.json5
  13. 109 0
      entry/src/main/ets/common/component/AddSubtractComp.ets
  14. 67 0
      entry/src/main/ets/common/component/TimeAndTitle.ets
  15. 101 0
      entry/src/main/ets/common/constants/CommonConstants.ets
  16. 371 0
      entry/src/main/ets/common/util/Mqtt.ets
  17. 114 0
      entry/src/main/ets/common/util/PerferencesUtil.ets
  18. 113 0
      entry/src/main/ets/common/util/PreviewManager.ets
  19. 55 0
      entry/src/main/ets/common/util/TimeUtil.ets
  20. 217 0
      entry/src/main/ets/common/util/UploadUtil.ets
  21. 129 0
      entry/src/main/ets/common/util/UsbDevice.ets
  22. 72 0
      entry/src/main/ets/common/util/request/ProcessRequest.ets
  23. 108 0
      entry/src/main/ets/common/util/request/helps.ets
  24. 74 0
      entry/src/main/ets/entryability/EntryAbility.ets
  25. 141 0
      entry/src/main/ets/model/ElectricScrewdriverPresetModel.ets
  26. 120 0
      entry/src/main/ets/model/ElectricSolderingIronPresetModel.ets
  27. 1034 0
      entry/src/main/ets/pages/CameraView.ets
  28. 1290 0
      entry/src/main/ets/pages/DeviceControllerPage.ets
  29. 1530 0
      entry/src/main/ets/pages/ProcessIndex.ets
  30. 1067 0
      entry/src/main/ets/view/AuxiliaryOperationView.ets
  31. 265 0
      entry/src/main/ets/view/BarcodeAssociationDialog.ets
  32. 219 0
      entry/src/main/ets/view/CompleteReceiveDialog.ets
  33. 66 0
      entry/src/main/ets/view/ConfirmDialog.ets
  34. 322 0
      entry/src/main/ets/view/DefectRecordDialog.ets
  35. 274 0
      entry/src/main/ets/view/DeviceInspectionDialog.ets
  36. 341 0
      entry/src/main/ets/view/InAndOutBoundDialog.ets
  37. 215 0
      entry/src/main/ets/view/JoinPersonNameDialog.ets
  38. 444 0
      entry/src/main/ets/view/LittleMaterialRequestDialog.ets
  39. 518 0
      entry/src/main/ets/view/LoginInfoDialog.ets
  40. 332 0
      entry/src/main/ets/view/ModifyMaterialNumDialog.ets
  41. 125 0
      entry/src/main/ets/view/PictureDrawingDialog.ets
  42. 328 0
      entry/src/main/ets/view/ReportDefectNumDialog.ets
  43. 422 0
      entry/src/main/ets/view/ReportPieceworkDialog.ets
  44. 481 0
      entry/src/main/ets/view/ReportTimeBasedDialog.ets
  45. 91 0
      entry/src/main/ets/view/ReportWorkHourRateDialog.ets
  46. 332 0
      entry/src/main/ets/view/ReportWorkNumDialog.ets
  47. 232 0
      entry/src/main/ets/view/SelectWorkOrderDialog.ets
  48. 169 0
      entry/src/main/ets/view/SerialNoMaterialDialog.ets
  49. 231 0
      entry/src/main/ets/view/SpecialInspectionDialog.ets
  50. 431 0
      entry/src/main/ets/view/SwitchProductDialog.ets
  51. 353 0
      entry/src/main/ets/view/SwitchStationDialog.ets
  52. 156 0
      entry/src/main/ets/view/SwitchUserDialog.ets
  53. 127 0
      entry/src/main/ets/view/WorkInstructionsDialog.ets
  54. 226 0
      entry/src/main/ets/view/process/DeviceCheckView.ets
  55. 284 0
      entry/src/main/ets/view/process/MaterialCollectView.ets
  56. 829 0
      entry/src/main/ets/view/process/MultiMediaCollect.ets
  57. 270 0
      entry/src/main/ets/view/process/SelfInspectView.ets
  58. 8 0
      entry/src/main/ets/viewmodel/ConfirmDialogParam.ets
  59. 10 0
      entry/src/main/ets/viewmodel/DictInfo.ets
  60. 8 0
      entry/src/main/ets/viewmodel/DictValue.ets
  61. 27 0
      entry/src/main/ets/viewmodel/DrawingInfo.ets
  62. 24 0
      entry/src/main/ets/viewmodel/MaterialInfo.ets
  63. 18 0
      entry/src/main/ets/viewmodel/MessageInfo.ets
  64. 33 0
      entry/src/main/ets/viewmodel/OperationItem.ets
  65. 11 0
      entry/src/main/ets/viewmodel/PageInfo.ets
  66. 111 0
      entry/src/main/ets/viewmodel/RequestParamModel.ets
  67. 112 0
      entry/src/main/ets/viewmodel/UserInfo.ets
  68. 23 0
      entry/src/main/ets/viewmodel/VehicleInfo.ets
  69. 74 0
      entry/src/main/ets/viewmodel/WorkOrderInfo.ets
  70. 8 0
      entry/src/main/ets/viewmodel/WorkOrderPage.ets
  71. 13 0
      entry/src/main/ets/viewmodel/WorkOrderSeq.ets
  72. 11 0
      entry/src/main/ets/viewmodel/device/AntiWristStrap.ets
  73. 9 0
      entry/src/main/ets/viewmodel/device/CardReader.ets
  74. 31 0
      entry/src/main/ets/viewmodel/device/ElectricScrewdriver.ets
  75. 25 0
      entry/src/main/ets/viewmodel/device/ElectricScrewdriverPreset.ets
  76. 9 0
      entry/src/main/ets/viewmodel/device/ElectricSolderingIron.ets
  77. 11 0
      entry/src/main/ets/viewmodel/device/ElectricSolderingIronPreset.ets
  78. 7 0
      entry/src/main/ets/viewmodel/device/Lighting.ets
  79. 11 0
      entry/src/main/ets/viewmodel/device/Robot.ets
  80. 9 0
      entry/src/main/ets/viewmodel/device/TempHumiditySensor.ets
  81. 15 0
      entry/src/main/ets/viewmodel/device/ThreeColourLight.ets
  82. 7 0
      entry/src/main/ets/viewmodel/device/WeldFumeExtractor.ets
  83. 6 0
      entry/src/main/ets/viewmodel/mqtt/MqttCmdData.ets
  84. 9 0
      entry/src/main/ets/viewmodel/mqtt/MqttDataItem.ets
  85. 6 0
      entry/src/main/ets/viewmodel/mqtt/MqttUploadData.ets
  86. 9 0
      entry/src/main/ets/viewmodel/process/BindTaskSeq.ets
  87. 19 0
      entry/src/main/ets/viewmodel/process/OperationComponent.ets
  88. 41 0
      entry/src/main/ets/viewmodel/process/OperationInfo.ets
  89. 11 0
      entry/src/main/ets/viewmodel/process/ProcessBaseBug.ets
  90. 13 0
      entry/src/main/ets/viewmodel/process/ProcessCallMaterial.ets
  91. 37 0
      entry/src/main/ets/viewmodel/process/ProcessCheck.ets
  92. 21 0
      entry/src/main/ets/viewmodel/process/ProcessDefectRecord.ets
  93. 23 0
      entry/src/main/ets/viewmodel/process/ProcessDeviceDailyCheck.ets
  94. 53 0
      entry/src/main/ets/viewmodel/process/ProcessInfo.ets
  95. 85 0
      entry/src/main/ets/viewmodel/process/ProcessMaterial.ets
  96. 11 0
      entry/src/main/ets/viewmodel/process/ProcessReportTimeBased.ets
  97. 13 0
      entry/src/main/ets/viewmodel/process/ReporterInfo.ets
  98. 12 0
      entry/src/main/ets/viewmodel/process/TaskSeqInfo.ets
  99. 45 0
      entry/src/main/module.json5
  100. 0 0
      entry/src/main/resources/base/element/color.json

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+.preview/*

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test

+ 10 - 0
AppScope/app.json5

@@ -0,0 +1,10 @@
+{
+  "app": {
+    "bundleName": "com.hj.hj_openharmony_process",
+    "vendor": "example",
+    "versionCode": 1000000,
+    "versionName": "1.0.0",
+    "icon": "$media:app_icon",
+    "label": "$string:app_name"
+  }
+}

+ 8 - 0
AppScope/resources/base/element/string.json

@@ -0,0 +1,8 @@
+{
+  "string": [
+    {
+      "name": "app_name",
+      "value": "HJ_openHarmony_process"
+    }
+  ]
+}

BIN
AppScope/resources/base/media/app_icon.png


+ 49 - 0
build-profile.json5

@@ -0,0 +1,49 @@
+{
+  "app": {
+    "signingConfigs": [
+      {
+        "name": "default",
+        "material": {
+          "certpath": "C:/Users/hjzx2/.ohos/config/openharmony/default_HJ_openHarmony_process_fsSWWuqE3hB6JtlQ0qIVKU_RvbEwxi_gTtSv4f1zvOo=.cer",
+          "storePassword": "0000001B0B263AB22C9FFA7BEF335E866ACF26F55131F6224F9C8FAAA94E01B840AC6FF7C0DD96AB12C0AA",
+          "keyAlias": "debugKey",
+          "keyPassword": "0000001BC4AB29C44B474EB29827AEE217A8CF9E27822FBBF51573DE3E902614575ED23003EEBE727CC140",
+          "profile": "C:/Users/hjzx2/.ohos/config/openharmony/default_HJ_openHarmony_process_fsSWWuqE3hB6JtlQ0qIVKU_RvbEwxi_gTtSv4f1zvOo=.p7b",
+          "signAlg": "SHA256withECDSA",
+          "storeFile": "C:/Users/hjzx2/.ohos/config/openharmony/default_HJ_openHarmony_process_fsSWWuqE3hB6JtlQ0qIVKU_RvbEwxi_gTtSv4f1zvOo=.p12"
+        }
+      }
+    ],
+    "products": [
+      {
+        "name": "default",
+        "signingConfig": "default",
+        "compileSdkVersion": 10,
+        "compatibleSdkVersion": 10,
+        "runtimeOS": "OpenHarmony",
+      }
+    ],
+    "buildModeSet": [
+      {
+        "name": "debug",
+      },
+      {
+        "name": "release"
+      }
+    ]
+  },
+  "modules": [
+    {
+      "name": "entry",
+      "srcPath": "./entry",
+      "targets": [
+        {
+          "name": "default",
+          "applyToProducts": [
+            "default"
+          ]
+        }
+      ]
+    }
+  ]
+}

+ 6 - 0
entry/.gitignore

@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test

+ 31 - 0
entry/build-profile.json5

@@ -0,0 +1,31 @@
+{
+  "apiType": "stageMode",
+  "buildOption": {
+    "arkOptions": {
+      // "apPath": "./modules.ap"  /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
+    }
+  },
+  "buildOptionSet": [
+    {
+      "name": "release",
+      "arkOptions": {
+        "obfuscation": {
+          "ruleOptions": {
+            "enable": true,
+            "files": [
+              "./obfuscation-rules.txt"
+            ]
+          }
+        }
+      }
+    },
+  ],
+  "targets": [
+    {
+      "name": "default"
+    },
+    {
+      "name": "ohosTest",
+    }
+  ]
+}

+ 6 - 0
entry/hvigorfile.ts

@@ -0,0 +1,6 @@
+import { hapTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+    system: hapTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */
+    plugins:[]         /* Custom plugin to extend the functionality of Hvigor. */
+}

+ 18 - 0
entry/obfuscation-rules.txt

@@ -0,0 +1,18 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+#   https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope

+ 53 - 0
entry/oh-package-lock.json5

@@ -0,0 +1,53 @@
+{
+  "lockfileVersion": 2,
+  "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
+  "specifiers": {
+    "@ohos/axios@2.2.1-rc.0": "@ohos/axios@2.2.1-rc.0",
+    "@ohos/commons-fileupload@^1.0.0": "@ohos/commons-fileupload@1.0.2",
+    "mime-db@1.52.0": "mime-db@1.52.0",
+    "pinyin-pro@^3.18.3": "pinyin-pro@3.26.0",
+    "@ohos/mqtt@2.0.6": "@ohos/mqtt@2.0.6",
+    "@ohos/hypium@1.0.13": "@ohos/hypium@1.0.13"
+  },
+  "packages": {
+    "@ohos/axios@2.2.1-rc.0": {
+      "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/axios/-/axios-2.2.1-rc.0.har",
+      "integrity": "sha512-owJh8oOd1i56vZGU7opjz8EwYVsiHtuFYCwTyYCLZYLyeCdn9+laWjDpNrCSDZktySyYCu9qmK9867XQ+8QVFg==",
+      "registryType": "ohpm"
+    },
+    "@ohos/commons-fileupload@1.0.2": {
+      "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/commons-fileupload/-/commons-fileupload-1.0.2.har",
+      "integrity": "sha512-fkFytjuRiH6hrXMCsnHJz0mkglQP43RfrIyisnEsBeZtR9ulFjA3AwvoIeWH0vaVhD0bl76PudX7LS4d5muHiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "registryType": "ohpm"
+    },
+    "mime-db@1.52.0": {
+      "resolved": "https://ohpm.openharmony.cn/ohpm/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "registryType": "ohpm",
+      "shasum": "bbabcdc02859f4987301c856e3387ce5ec43bf70"
+    },
+    "pinyin-pro@3.26.0": {
+      "resolved": "https://ohpm.openharmony.cn/ohpm/pinyin-pro/-/pinyin-pro-3.26.0.tgz",
+      "integrity": "sha512-HcBZZb0pvm0/JkPhZHWA5Hqp2cWHXrrW/WrV+OtaYYM+kf35ffvZppIUuGmyuQ7gDr1JDJKMkbEE+GN0wfMoGg==",
+      "registryType": "ohpm",
+      "shasum": "9e3b8a9f848263b81552d56e9319f520f7709ad6"
+    },
+    "@ohos/mqtt@2.0.6": {
+      "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/mqtt/-/mqtt-2.0.6.har",
+      "integrity": "sha512-2uZKhjM2JWo/eqAPjKIHK3zEa6J8Zp2qz89soY2i34podtcPm23DvP3VLI1HNa+0Tc8Y04RwSz3Gmedqe/OWsw==",
+      "dependencies": {
+        "libmqttasync.so": "file:./src/main/cpp/types/libmqttasync"
+      },
+      "registryType": "ohpm"
+    },
+    "@ohos/hypium@1.0.13": {
+      "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.13.tgz",
+      "integrity": "sha512-d0+XvDeAYk5Vgl6JQ8Q1G+NPmTyJI8qgZ1PwPfcUbx/dfyKVAAv9lz1XtVNhYypyWEKqAzu8zMAC9GuHo2Y53Q==",
+      "registryType": "ohpm",
+      "shasum": "88d8dda420097efb98d770bf59616faef4682f06"
+    }
+  }
+}

+ 16 - 0
entry/oh-package.json5

@@ -0,0 +1,16 @@
+{
+  "name": "entry",
+  "version": "1.0.0",
+  "description": "Please describe the basic information.",
+  "main": "",
+  "author": "",
+  "license": "",
+  "dependencies": {
+    "@ohos/hypium": "1.0.13",
+    "@ohos/axios": "2.2.1-rc.0",
+    "@ohos/commons-fileupload": "^1.0.0",
+    "pinyin-pro": "^3.18.3",
+    "@ohos/mqtt": "2.0.6"
+  }
+}
+

+ 109 - 0
entry/src/main/ets/common/component/AddSubtractComp.ets

@@ -0,0 +1,109 @@
+import ElectricScrewdriver from '../../viewmodel/device/ElectricScrewdriver'
+
+@Component
+export struct AddSubtractComp {
+  // 输入值
+  @State inputNum: number = 0.00
+  // 输入值所在的对象用于修改(不存在则不传)
+  @Link obj: ElectricScrewdriver
+  // 需要修改的对象的属性名
+  editPropertyName: string=''
+  // 输入值上限、下限
+  upperLimit: number | undefined = undefined
+  lowerLimit: number | undefined = undefined
+  // 点击加、减事件调用的方法,及输入修改调用的方法
+  addFunction: Function | undefined = undefined
+  subFunction: Function | undefined = undefined
+  editFunction: Function | undefined = undefined
+
+
+  build() {
+    Row() {
+      Row() {
+        Image($r('app.media.general_subtract'))
+          .fillColor($r('app.color.FFFFFF'))
+          .width($r('app.float.virtualSize_48'))
+          .height($r('app.float.virtualSize_48'))
+      }
+      .width('25%')
+      .height('100%')
+      .justifyContent(FlexAlign.Center)
+      .onClick(()=>{
+        if (this.lowerLimit != undefined && this.inputNum <= this.lowerLimit) {
+          return
+        }
+        this.inputNum --
+        if (this.obj) {
+          // 设置属性
+          Reflect.set(this.obj, this.editPropertyName, this.inputNum);
+        }
+        if (this.subFunction) {
+          this.subFunction()
+        }
+      })
+      Row() {
+        TextInput({text: this.inputNum.toString()})
+          .fontSize($r('app.float.fontSize_38'))
+          .fontWeight(FontWeight.Lighter)
+          .fontColor($r('app.color.FFFFFF'))
+          .height('100%')
+          .width('100%')
+          .type(InputType.Normal)
+          .textAlign(TextAlign.Center)
+          .onChange((value: string)=>{
+            if (!value || value.length === 0) {
+              this.inputNum = 0.00
+              return
+            }
+
+            let exchangeNumber: number = Number.parseFloat(value)
+            // 校验数据有效性
+            if (this.lowerLimit != undefined && exchangeNumber < this.lowerLimit) {
+              this.inputNum = this.lowerLimit
+            } else if (this.upperLimit != undefined && exchangeNumber > this.upperLimit) {
+              this.inputNum = this.upperLimit
+            } else {
+              this.inputNum = exchangeNumber
+            }
+            if (this.obj) {
+              // 设置属性
+              Reflect.set(this.obj, this.editPropertyName, this.inputNum);
+            }
+            if (this.editFunction) {
+              this.editFunction()
+            }
+          })
+      }
+      .width('50%')
+      .height('100%')
+      .borderColor($r('app.color.10FFFFFF'))
+
+      Row() {
+        Image($r('app.media.general_add'))
+          .fillColor($r('app.color.FFFFFF'))
+          .width($r('app.float.virtualSize_48'))
+          .height($r('app.float.virtualSize_48'))
+      }
+      .width('25%')
+      .height('100%')
+      .justifyContent(FlexAlign.Center)
+      .onClick(()=>{
+        if (this.upperLimit != undefined && this.inputNum >= this.upperLimit) {
+          return
+        }
+        this.inputNum ++
+        if (this.obj) {
+          // 设置属性
+          Reflect.set(this.obj, this.editPropertyName, this.inputNum);
+        }
+        if (this.addFunction) {
+          this.addFunction()
+        }
+      })
+    }
+    .width('100%')
+    .height('100%')
+    .backgroundColor($r('app.color.20FFFFFF'))
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 67 - 0
entry/src/main/ets/common/component/TimeAndTitle.ets

@@ -0,0 +1,67 @@
+import TimeUtil from "../util/TimeUtil"
+@Component
+export struct TimeAndTitle {
+  @State currentTime: string = TimeUtil.getHourAndMinute(); // 响应式时间数据
+  @State timePeriod: string = TimeUtil.getTimePeriod();
+  @State dateAndWeek: string = TimeUtil.getDateAndWeek();
+  private timerID: number | undefined; // 定时器ID
+  aboutToAppear() {
+    this.timerID = setInterval(() => {
+      this.updateAllTime(); // 每秒更新
+    }, 1000);
+  }
+  aboutToDisappear() {
+    if (this.timerID) {
+      clearInterval(this.timerID);
+    }
+  }
+  private updateAllTime() {
+    this.currentTime = TimeUtil.getHourAndMinute();
+    this.timePeriod = TimeUtil.getTimePeriod();
+    this.dateAndWeek = TimeUtil.getDateAndWeek();
+  }
+
+  build() {
+    Row() {
+      Row(){
+        Text(this.dateAndWeek)
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+          .fontWeight(FontWeight.Lighter)
+        Text(' ' + this.timePeriod)
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+          .fontWeight(FontWeight.Lighter)
+        Text(' ' + this.currentTime)
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+          .fontWeight(FontWeight.Lighter)
+      }
+      Row() {
+        Image($r('app.media.state_ai'))
+          .width($r('app.float.virtualSize_24'))
+          .height($r('app.float.virtualSize_24'))
+          .fillColor($r('app.color.FFFFFF'))
+        Image($r('app.media.state_ipc'))
+          .width($r('app.float.virtualSize_24'))
+          .height($r('app.float.virtualSize_24'))
+          .fillColor($r('app.color.FFFFFF'))
+        Image($r('app.media.state_voice'))
+          .width($r('app.float.virtualSize_24'))
+          .height($r('app.float.virtualSize_24'))
+          .fillColor($r('app.color.FFFFFF'))
+        Image($r('app.media.state_microphone'))
+          .width($r('app.float.virtualSize_24'))
+          .height($r('app.float.virtualSize_24'))
+          .fillColor($r('app.color.FFFFFF'))
+      }
+      .width('10%')
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .alignItems(VerticalAlign.Bottom)
+      .margin({ right: 1 })
+      .height('100%')
+    }
+    .justifyContent(FlexAlign.SpaceEvenly)
+    .alignItems(VerticalAlign.Bottom)
+  }
+}

+ 101 - 0
entry/src/main/ets/common/constants/CommonConstants.ets

@@ -0,0 +1,101 @@
+import DictValue from '../../viewmodel/DictValue'
+import HashMap from '@ohos.util.HashMap'
+
+export default class CommonConstants {
+  // perferences数据库名称
+  static readonly PREFERENCE_INSTANCE_NAME: string = 'myStore';
+  static readonly RDB_NAME: string = 'Presentation.db'
+  static readonly PRE_NAME: string = 'MyPreferences'
+  static AUTH_TOKEN: string = ''
+  // 附件地址前缀
+  static FILE_URL_PREFIX: string = 'http://192.168.1.3:9000'
+  // 图片地址前缀
+  static PICTURE_URL_PREFIX: string = 'http://192.168.1.3:20001'
+
+  // 当前登录用户相关信息
+  // static USER_ID?: number
+  // static USER_NAME: string = ''
+  // static USER_AVATAR: string = ''
+  // static STATION_NANE: string = ''
+  // static DEPARTMENT_NANE: string = ''
+  // static PRODUCT_NANE: string = ''
+  // static STATION_ID: number
+  // static STATION_CODE: string = ''
+  // static STATION_IP: string = ''
+  static stationDictValue: string = ''
+
+  static MATERIAL_BOX_ID: string = ''
+  // 是否是退出登陆
+  static LOGIN_OUT: boolean = false
+  static readonly STORAGE_TYPE: string[] = ['电子元器件', '电路板', '结构件', '辅助材料']
+  static readonly ipPrefix: string = '192.168.1.'
+  static mqttSubscribeTopic1: string = 'station11/data/devices';
+  static readonly mqttSubscribeTopic2: string = 'station11/data/devices';
+  static readonly mqttSubscribeTopic3: string = 'station100/data/devices';
+  static readonly mqttSubscribePublish: string = 'station11/cmd/devices';
+  // 默认随机ip
+  static readonly STATION_DEFAULT_IP: string = '192.168.1.' + Math.floor(Math.random() * 90 + 10)
+
+  // 电烙铁允许最大、最小温度
+  static readonly ironTempMin: number = 150
+  static readonly ironTempMax: number = 450
+
+  static OPERATION_COMPONENT_TYPE: Map<string, string> = new Map<string, string>([
+    ['wuliaocaiji', '1'],
+    ['jiluxiang', '2'],
+    ['duomeiticaiji', '3'],
+    ['ESOP', '4'],
+    ['dianjian', '5'],
+    ['shebeiqueren', '6'],
+    ['jingu', '7'],
+    // ['tiaoshipipei', '8'],
+    ['mingpaibangding', '9'],
+    ['operationExcel', '10'],
+    ['zidongceshi', '11'],
+    ['shebeiqueren', '12'],
+  ]);
+
+  static DICT_DATA: HashMap<string, DictValue[]> = new HashMap<string, DictValue[]>()
+
+  static readonly attrMap: Map<string, string> = new Map([
+    ['LedRed', 'SSD'],
+    ['LedOrange', 'SSD'],
+    ['LedGreen', 'SSD'],
+    ['Buzzer', 'SSD'],
+    ['ExhaustFan', 'HYPYJ'],
+    ['SolderingCurrTemp', 'WKDLT'],
+    ['SolderingSetTemp', 'WKDLT'],
+
+    ['TightenTorqueMax', 'DDLSD'],
+    ['TightenResult', 'DDLSD'],
+    ['TightenTorqueTarget', 'DDLSD'],
+    ['TorqueHoldTime', 'DDLSD'],
+    ['TighteRotationDirection', 'DDLSD'],
+    ['TorqueUpperLimit', 'DDLSD'],
+    ['TorqueLowerLimit', 'DDLSD'],
+    ['SlipDetectionTurns', 'DDLSD'],
+    ['FloatDetectionTurns', 'DDLSD'],
+    ['EnableFloatSlipDetection', 'DDLSD'],
+    ['ScrewWarning', 'DDLSD'],
+
+    ['RingWearNormal', 'FJDJC'],
+    ['RingWearStandby', 'FJDJC'],
+    ['RingWearFault', 'FJDJC'],
+    ['RfidCardNum1', 'DKQ'],
+    ['RfidCardNum2', 'DKQ'],
+    ['lighting', 'ZM'],
+    ['Temperature', 'WSDCGQ'],
+    ['Humidity', 'WSDCGQ'],
+  ]);
+}
+
+export enum DeviceType {
+  ThreeColourLight = 'SSD',
+  WeldFumeExtractor = 'HYPYJ',
+  ElectricSolderingIron = 'WKDLT',
+  ElectricScrewdriver = 'DDLSD',
+  AntiWristStrap = 'FJDJC',
+  CardReader = 'DKQ',
+  Lighting = 'ZM',
+  TempHumiditySensor = 'WSDCGQ',
+}

+ 371 - 0
entry/src/main/ets/common/util/Mqtt.ets

@@ -0,0 +1,371 @@
+import { MqttAsync, MqttClient, MqttClientOptions, MqttConnectOptions,MqttQos, MqttMessage, MqttPublishOptions } from '@ohos/mqtt';
+import CommonConstants, { DeviceType } from '../constants/CommonConstants';
+import AntiWristStrap from '../../viewmodel/device/AntiWristStrap';
+import CardReader from '../../viewmodel/device/CardReader';
+import ElectricScrewdriver from '../../viewmodel/device/ElectricScrewdriver';
+import ElectricSolderingIron from '../../viewmodel/device/ElectricSolderingIron';
+import Lighting from '../../viewmodel/device/Lighting';
+import TempHumiditySensor from '../../viewmodel/device/TempHumiditySensor';
+import ThreeColourLight from '../../viewmodel/device/ThreeColourLight';
+import WeldFumeExtractor from '../../viewmodel/device/WeldFumeExtractor';
+
+const TAG = 'hhtest';
+
+type MessageCallback = (topic: string, payload: string) => void;
+
+
+
+interface TagValuePair {
+  tag: string;
+  value: number;
+}
+
+class MqttManager {
+  // static readonly MQTT_SERVICE = '192.168.1.10'
+  private static instance: MqttManager;
+  private client: MqttClient | null = null;
+  private callbacks: Map<string, MessageCallback[]> = new Map();
+
+  private constructor() {}
+
+  public static getInstance(): MqttManager {
+    if (!MqttManager.instance) {
+      MqttManager.instance = new MqttManager();
+    }
+    return MqttManager.instance;
+  }
+
+  public init(options: MqttClientOptions): void {
+    try {
+      this.client = MqttAsync.createMqtt(options);
+      this.registerCallbacks();
+      console.info(TAG, 'MQTT client initialized');
+    } catch (err) {
+      console.error(TAG, `Initialization failed: ${JSON.stringify(err)}`);
+    }
+  }
+
+  private registerCallbacks(): void {
+    if (!this.client) return;
+
+    this.client.messageArrived((err, message) => {
+      if (err) {
+        console.error(TAG, `Message error: ${err.message}`);
+        return;
+      }
+      this.handleMessage(message);
+    });
+
+    this.client.connectLost((err) => {
+      if (err) {
+        console.error(TAG, `Connection lost: ${err.message}`);
+      }
+      this.reconnect();
+    });
+  }
+
+  // 修改后的 handleMessage 方法片段
+  private async handleMessage(message: MqttMessage): Promise<void> {
+    const topic = message.topic;
+    const payload = message.payload.toString();
+    const topicCallbacks = this.callbacks.get(topic) || [];
+    topicCallbacks.forEach(cb => cb(topic, payload));
+    try {
+      const valueJson: MQTTReceiveData = JSON.parse(payload);
+      //订阅主题一的接收数据
+      if (CommonConstants.mqttSubscribeTopic1 == topic) {
+        let device0: ThreeColourLight = {
+          LedRed: 0,
+          LedOrange: 0,
+          LedGreen: 0,
+          Buzzer: 0,
+          OpenStatus: 0
+        }
+        let device1: WeldFumeExtractor = { ExhaustFan: 0, OnlineStatus: 0 }
+        let device2: ElectricSolderingIron = { SolderingCurrTemp: 0, OnlineStatus: 0 }
+        let device3: ElectricScrewdriver = {
+          TighteRotationDirection: 0,
+          TorqueHoldTime: 0,
+          TightenTorqueTarget: 0,
+          TorqueUpperLimit: 0,
+          TorqueLowerLimit: 0,
+          EnableFloatSlipDetection: 0,
+          FloatDetectionTurns: 0,
+          OnlineStatus: 0
+        }
+        let device4: AntiWristStrap = { RingWearNormal: 0, RingWearStandby: 0, RingWearFault: 0, OnlineStatus: 0 }
+        let device5: CardReader = { RfidCardNum1: '', RfidCardNum2: '', OnlineStatus: 0 }
+        let device6: Lighting = { lighting: 0, OnlineStatus: 0 }
+        let device7: TempHumiditySensor = { Temperature: 0, Humidity: 0, OnlineStatus: 0 }
+        for (const element of valueJson.d) {
+          if (CommonConstants.attrMap.has(element.tag)) {
+            let deviceType: string = CommonConstants.attrMap.get(element.tag) ?? '';
+            switch (deviceType) {
+              case DeviceType.ThreeColourLight:
+                if (element.tag! === 'LedRed') {
+                  device0.LedRed = element.value
+                } else if (element.tag! === 'LedOrange') {
+                  device0.LedOrange = element.value
+                } else if (element.tag! === 'LedGreen') {
+                  device0.LedGreen = element.value
+                } else if (element.tag! === 'Buzzer') {
+                  device0.Buzzer = element.value
+                }
+                if (device0.LedRed! === 1 || device0.LedOrange! === 1
+                  || device0.LedGreen! === 1 || device0.Buzzer! === 1) {
+                  device0.OpenStatus = 1
+                }
+                device0.OnlineStatus = 1
+                break;
+              case DeviceType.WeldFumeExtractor:
+                if (element.tag! === 'ExhaustFan') {
+                  device1.ExhaustFan = element.value
+                }
+                device1.OnlineStatus = 1
+                break;
+              case DeviceType.ElectricSolderingIron:
+                if (element.tag! === 'SolderingCurrTemp') {
+                  device2.SolderingCurrTemp = element.value
+                }
+                device2.OnlineStatus = 1
+                break;
+              case DeviceType.ElectricScrewdriver:
+                if (element.tag! === 'TightenTorqueTarget') {
+                  device3.TightenTorqueTarget = element.value
+                } else if (element.tag! === 'TorqueHoldTime') {
+                  device3.TorqueHoldTime = element.value
+                }
+                device3.OnlineStatus = 1
+                break;
+              case DeviceType.AntiWristStrap:
+                if (element.tag! === 'RingWearNormal') {
+                  device4.RingWearNormal = element.value
+                } else if (element.tag! === 'RingWearStandby') {
+                  device4.RingWearStandby = element.value
+                } else if (element.tag! === 'RingWearFault') {
+                  device4.RingWearFault = element.value
+                }
+                device4.OnlineStatus = 1
+                break;
+              case DeviceType.CardReader:
+                if (element.tag! === 'RfidCardNum1') {
+                  let value = element.value?.toString(16)
+                  device5.RfidCardNum1 = value === '0' ? '' : value
+                } else if (element.tag! === 'RfidCardNum2') {
+                  let value = element.value?.toString(16)
+                  device5.RfidCardNum2 = value === '0' ? '' : value
+                }
+                device5.OnlineStatus = 1
+                break;
+              case DeviceType.Lighting:
+                if (element.tag! === 'lighting') {
+                  device6.lighting = element.value
+                }
+                device6.OnlineStatus = 1
+                break;
+              case DeviceType.TempHumiditySensor:
+                if (element.tag! === 'Temperature') {
+                  device7.Temperature = element.value
+                } else if (element.tag! === 'Humidity') {
+                  device7.Humidity = element.value
+                }
+                device7.OnlineStatus = 1
+                break;
+            }
+          }
+          AppStorage.setOrCreate<ThreeColourLight>('ThreeColourLight', device0);
+          AppStorage.setOrCreate<WeldFumeExtractor>('WeldFumeExtractor', device1);
+          AppStorage.setOrCreate<ElectricSolderingIron>('ElectricSolderingIron', device2);
+          AppStorage.setOrCreate<ElectricScrewdriver>('ElectricScrewdriver', device3);
+          AppStorage.setOrCreate<AntiWristStrap>('AntiWristStrap', device4);
+          AppStorage.setOrCreate<CardReader>('CardReader', device5);
+          AppStorage.setOrCreate<Lighting>('Lighting', device6)
+          AppStorage.setOrCreate<TempHumiditySensor>('TempHumiditySensor', device7)
+        }
+      } else if (CommonConstants.mqttSubscribeTopic2 == topic) {
+        //订阅主题二的接收数据
+        let device0: TempHumiditySensor = { Temperature: 0, Humidity: 0, OnlineStatus: 0 }
+        for (const element of valueJson.d) {
+          if (CommonConstants.attrMap.has(element.tag)) {
+            let deviceType: string = CommonConstants.attrMap.get(element.tag) ?? '';
+            switch (deviceType) {
+              case DeviceType.TempHumiditySensor:
+                if (element.tag! === 'Temperature') {
+                  device0.Temperature = element.value
+                } else if (element.tag! === 'Humidity') {
+                  device0.Humidity = element.value
+                }
+                device0.OnlineStatus = 1
+                break;
+            }
+          }
+        }
+        AppStorage.setOrCreate<TempHumiditySensor>('TempHumiditySensor', device0);
+      }
+      //订阅主题二的接收数据
+      else if(CommonConstants.mqttSubscribeTopic3 == topic){
+        const station1Set = valueJson?.d?.find(item => item.tag === 'Station1Set')?.value;
+        const station1Weight = decodeWeight(valueJson?.d?.find(item => item.tag === 'Station1Weight')?.value);
+        const rfidStringIn = decodeRfidString(
+          valueJson?.d?.find(item => item.tag === 'Barcode1Data1')?.value as number,
+          valueJson?.d?.find(item => item.tag === 'Barcode1Data2')?.value as number,
+          valueJson?.d?.find(item => item.tag === 'Barcode1Data3')?.value as number,
+          valueJson?.d?.find(item => item.tag === 'Barcode1Data4')?.value as number
+        );
+        AppStorage.SetOrCreate<number>('drawerPositionStatus', station1Set);
+        AppStorage.SetOrCreate<number>('materialBoxWeight', station1Weight);
+        AppStorage.SetOrCreate<string>('materialBoxInRfid', rfidStringIn);
+      }
+    } catch (e) {
+      console.error("MQTT消息处理异常:", e);
+    }
+  }
+
+  public async connect(options: MqttConnectOptions): Promise<boolean> {
+    if (!this.client) return false;
+
+    try {
+      const res = await this.client.connect(options);
+      if (res.code === 0) {
+        console.info(TAG, 'Connected to broker');
+        return true;
+      }
+      console.error(TAG, `Connect failed: ${res.message}`);
+      return false;
+    } catch (err) {
+      console.error(TAG, `Connect error: ${err.message}`);
+      return false;
+    }
+  }
+
+  public async subscribe(topic: string, callback?: MessageCallback): Promise<void> {
+    if (!this.client) return;
+    try {
+      const res = await this.client.subscribe({ topic, qos: 1 });
+      if (res.code === 0) {
+        console.info(TAG, `Subscribed to ${topic}`);
+        if (callback) {
+          this.addCallback(topic, callback);
+        }
+      }
+    } catch (err) {
+      console.error(TAG, `Subscribe error: ${err.message}`);
+    }
+  }
+
+  private addCallback(topic: string, callback: MessageCallback): void {
+    const callbacks = this.callbacks.get(topic) || [];
+    callbacks.push(callback);
+    this.callbacks.set(topic, callbacks);
+  }
+
+  private reconnect(): void {
+    setTimeout(() => {
+      this.client?.reconnect().then(success => {
+        if (success) {
+          console.info(TAG, 'Reconnected successfully');
+        }
+      });
+    }, 5000);
+  }
+
+  public async disconnect(): Promise<void> {
+    if (!this.client) return;
+
+    try {
+      await this.client.disconnect();
+      console.info(TAG, 'Disconnected');
+    } catch (err) {
+      console.error(TAG, `Disconnect error: ${err.message}`);
+    }
+  }
+  public async publish(
+    topic: string,
+    payload: string | object,
+    qos: MqttQos = 1,
+    retained: boolean = false
+  ): Promise<void> {
+    if (!this.client) {
+      console.error(TAG, 'MQTT客户端未初始化');
+      return;
+    }
+
+    try {
+      // 统一处理 payload 类型
+      const payloadStr = typeof payload === 'string' ? payload : JSON.stringify(payload);
+
+      const options: MqttPublishOptions  = {
+        topic: topic,
+        payload: payloadStr,
+        qos: qos,
+        retained: retained
+      };
+
+      const res = await this.client.publish(options);
+      if (res.code === 0) {
+        console.info(TAG, `消息发布成功: ${topic}`);
+      } else {
+        console.error(TAG, `发布失败: ${res.message}`);
+      }
+    } catch (err) {
+      console.error(TAG, `发布异常: ${JSON.stringify(err)}`);
+    }
+  }
+}
+
+
+export default MqttManager.getInstance();
+
+
+export interface MQTTPublishData {
+  w: TagValuePair[];
+}
+
+export interface MQTTReceiveData {
+  d: TagValuePair[];
+}
+
+
+function decodeRegister(regValue: number | undefined): string {
+  if (regValue === undefined || regValue === 0) return '';
+  //if (regValue === undefined) return ''; // 处理undefined
+
+  // 确保是16位无符号整数
+  const value = regValue & 0xFFFF;
+
+  // 提取高8位和低8位
+  const highByte = (value >> 8) & 0xFF;
+  const lowByte = value & 0xFF;
+
+  // 转换为ASCII字符
+  return String.fromCharCode(highByte) + String.fromCharCode(lowByte);
+}
+
+// 完整RFID解码函数
+function decodeRfidString(
+  rfidData1?: number,
+  rfidData2?: number,
+  rfidData3?: number,
+  rfidData4?: number
+): string {
+  return [
+    decodeRegister(rfidData1),
+    decodeRegister(rfidData2),
+    decodeRegister(rfidData3),
+    decodeRegister(rfidData4)
+  ].join('');
+}
+
+function decodeWeight(regValue: number | undefined): number {
+  if (regValue === undefined) return 0; // 处理undefined
+
+  // 确保是16位无符号整数
+  const value = regValue & 0xFFFF;
+
+  // 提取高8位和低8位
+  const highByte = (value >> 8) & 0xFF;
+  const lowByte = value & 0xFF;
+
+  // 转换为ASCII字符
+  return highByte+lowByte/100
+}

+ 114 - 0
entry/src/main/ets/common/util/PerferencesUtil.ets

@@ -0,0 +1,114 @@
+import preferences from '@ohos.data.preferences';
+import common from '@ohos.app.ability.common';
+
+// 定义支持的数据类型
+type ValueType = number | string | boolean | number[] | string[] | boolean[];
+
+class PreferencesUtil {
+  // 缓存多个 Preferences 实例
+  private prefMap: Map<string, preferences.Preferences> = new Map();
+
+  /**
+  * 加载 Preferences 文件
+  * @param context 应用上下文
+  * @param name 文件名(如 'userConfig')
+  */
+  async loadPreference(context: common.Context, name: string): Promise<void> {
+    try {
+      const pref = await preferences.getPreferences(context, name);
+      this.prefMap.set(name, pref);
+      console.log('hhtest', `[PreferencesUtil] 加载文件 "${name}" 成功`);
+    } catch (err) {
+      console.error('hhtest', `[PreferencesUtil] 加载失败: ${JSON.stringify(err)}`);
+    }
+  }
+
+  /**
+  * 写入数据(自动刷盘)
+  * @param fileName 文件名
+  * @param key 键名
+  * @param value 值(支持6种类型)
+  */
+  async put(fileName: string, key: string, value: ValueType): Promise<void> {
+    const pref = this.prefMap.get(fileName);
+    if (!pref) {
+      console.warn('hhtest', `[PreferencesUtil] 文件未加载: ${fileName}`);
+      return;
+    }
+
+    try {
+      await pref.put(key, value);
+      await pref.flush(); // 立即持久化
+      console.log('hhtest', `[PreferencesUtil] 保存 ${fileName}.${key} = ${value}`);
+    } catch (err) {
+      console.error('hhtest', `[PreferencesUtil] 写入失败: ${JSON.stringify(err)}`);
+    }
+  }
+
+  /**
+  * 读取数据(带默认值)
+  * @param fileName 文件名
+  * @param key 键名
+  * @param defaultValue 默认值
+  * @returns 类型安全的返回值
+  */
+  async get<T extends ValueType>(
+    fileName: string,
+    key: string,
+    defaultValue: T
+  ): Promise<T> {
+    const pref = this.prefMap.get(fileName);
+    if (!pref) {
+      console.warn('hhtest', `[PreferencesUtil] 文件未加载: ${fileName}`);
+      return defaultValue;
+    }
+
+    try {
+      const value = await pref.get(key, defaultValue);
+      console.log('hhtest', `[PreferencesUtil] 读取 ${fileName}.${key} = ${value}`);
+      return value as T; // 泛型类型断言
+    } catch (err) {
+      console.error('hhtest', `[PreferencesUtil] 读取失败: ${JSON.stringify(err)}`);
+      return defaultValue;
+    }
+  }
+
+  /**
+  * 删除指定键值
+  * @param fileName 文件名
+  * @param key 键名
+  */
+  async delete(fileName: string, key: string): Promise<void> {
+    const pref = this.prefMap.get(fileName);
+    if (!pref) return;
+
+    try {
+      await pref.delete(key);
+      await pref.flush();
+      console.log('hhtest', `[PreferencesUtil] 删除键值: ${fileName}.${key}`);
+    } catch (err) {
+      console.error('hhtest', `[PreferencesUtil] 删除失败: ${JSON.stringify(err)}`);
+    }
+  }
+
+  /**
+  * 清空文件数据
+  * @param fileName 文件名
+  */
+  async clear(fileName: string): Promise<void> {
+    const pref = this.prefMap.get(fileName);
+    if (!pref) return;
+
+    try {
+      await pref.clear();
+      await pref.flush();
+      console.log('hhtest', `[PreferencesUtil] 已清空文件: ${fileName}`);
+    } catch (err) {
+      console.error('hhtest', `[PreferencesUtil] 清空失败: ${JSON.stringify(err)}`);
+    }
+  }
+}
+
+// 导出单例(全局使用)
+const preferencesUtil = new PreferencesUtil();
+export default preferencesUtil;

+ 113 - 0
entry/src/main/ets/common/util/PreviewManager.ets

@@ -0,0 +1,113 @@
+import image from '@ohos.multimedia.image';
+const TAG = "sony_camera info"
+export class PreviewManager {
+  // 上下文对象
+  private context: Context;
+  // 当前活跃的显示缓冲区
+  private activeBuffer: PixelMap | null = null;
+  // 下一个准备显示的缓冲区
+  private nextBuffer: PixelMap | null = null;
+  // 处理状态标志,防止重复处理
+  private isProcessing: boolean = false;
+  // 缓冲区交换锁,防止并发交换
+  private bufferLock: boolean = false;
+
+  constructor(context: Context) {
+    this.context = context;
+  }
+  //处理指定索引的帧
+  async processFrame(index: number): Promise<boolean> {
+    if (this.isProcessing)
+    {
+      console.info(TAG,"isprocessing")
+      return false;
+    }
+
+    this.isProcessing = true;
+    try {
+      const filePath = `${this.context.filesDir}/live${index}.jpg`;
+
+      // // 1. 文件检查
+      // try {
+      //   await fs.access(filePath); // 仅检查文件是否存在
+      // } catch (error) {
+      //   console.warn("文件访问失败:", error);
+      //   return false;
+      // }
+      // 2. 创建图像源
+      const imageSource = image.createImageSource(filePath);
+      if (!imageSource) {
+        console.warn(TAG,"创建ImageSource失败");
+        return false;
+      }
+      try {
+        // 3. 创建PixelMap
+        const pixelMap = await imageSource.createPixelMap({
+          // 添加降级选项
+          desiredSize: { width: 720, height: 1280 }, // 限制分辨率
+          desiredPixelFormat: 3 // RGB_565节省内存
+        });
+        if (!pixelMap) {
+          console.warn(TAG,"创建PixelMap失败");
+          return false;
+        }
+        // 5. 安全交换缓冲区
+        this.nextBuffer = pixelMap;
+        this.swapBuffers();
+        return true;
+      } finally {
+        imageSource.release();
+      }
+    } catch (error) {
+      console.error(TAG,`处理预览帧失败`, error);
+      return false;
+    } finally {
+      this.isProcessing = false;
+    }
+  }
+  //交换活跃缓冲区和下一个缓冲区
+  private swapBuffers(): void {
+    if (this.bufferLock) return;
+    this.bufferLock = true;
+    try {
+      const oldBuffer = this.activeBuffer;
+      this.activeBuffer = this.nextBuffer;
+      this.nextBuffer = null;
+      if (oldBuffer) {
+        oldBuffer.release();
+      }
+    } finally {
+      this.bufferLock = false;
+    }
+  }
+  //获取当前活跃缓冲区
+  async getActiveBuffer(): Promise<PixelMap | null> {
+    return this.activeBuffer;
+  }
+  //检查是否准备好显示
+  isReady(): boolean {
+    return !this.isProcessing && this.activeBuffer !== null;
+  }
+  //检查是否有新帧等待处理
+  hasNewFrame(): boolean {
+    return this.nextBuffer !== null;
+  }
+
+  // getErrorCount():number{
+  //   return this.errorCount
+  // }
+  // resetErrorCount(){
+  //   this.errorCount = 0
+  // }
+  //释放资源
+  release(): void {
+    if (this.activeBuffer) {
+      this.activeBuffer.release();
+      this.activeBuffer = null;
+    }
+    if (this.nextBuffer) {
+      this.nextBuffer.release();
+      this.nextBuffer = null;
+    }
+  }
+}

+ 55 - 0
entry/src/main/ets/common/util/TimeUtil.ets

@@ -0,0 +1,55 @@
+class TimeUtil {
+
+  getCurrentDate(): string {
+    let dateTime = new Date()
+    let month: string = JSON.stringify(dateTime.getMonth() + 1)
+    let time = dateTime.getFullYear() + '-' + (dateTime.getMonth() + 1).toString().padStart(2, '0')
+      + '-' + (dateTime.getDate().toString().padStart(2, '0'))
+    return time
+  }
+
+  getCurrentTime(): string {
+    let dateTime = new Date()
+    let time = dateTime.getFullYear() + '-' + (dateTime.getMonth() + 1).toString().padStart(2, '0')
+      + '-' + (dateTime.getDate().toString().padStart(2, '0')) + ' ' +
+      (dateTime.getHours().toString().length == 1 ? '0' + dateTime.getHours() : dateTime.getHours()) + ':'
+      + (dateTime.getMinutes().toString().length == 1 ? '0' + dateTime.getMinutes() : dateTime.getMinutes()) + ':'
+      + (dateTime.getSeconds().toString().length == 1 ? '0'+ dateTime.getSeconds(): dateTime.getSeconds())
+    return time
+  }
+
+  getHourAndMinute(): string {
+    let dateTime = new Date()
+    let time = (dateTime.getHours().toString().length == 1 ? '0' + dateTime.getHours() : dateTime.getHours()) + ':'
+      + (dateTime.getMinutes().toString().length == 1 ? '0' + dateTime.getMinutes() : dateTime.getMinutes())
+    return time
+  }
+
+  getDateAndWeek(): string {
+    let dateTime = new Date()
+    let weekDay = ["星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
+    let time = ((dateTime.getMonth() + 1) < 10 ? ' ' + (dateTime.getMonth() + 1) : (dateTime.getMonth() + 1))
+      + '月' + (dateTime.getDate() < 10 ? dateTime.getDate().toString() + '日' + '  ' : dateTime.getDate().toString() + '日' + ' ')+
+    weekDay[dateTime.getDay()]
+    return time
+  }
+
+  //上午、下午、晚上
+  getTimePeriod(): string {
+    let currentTime = new Date();
+    let currentHour = currentTime.getHours();
+    let period: string;
+    if (currentHour < 12) {
+      period = "上午";
+    } else if (currentHour < 18) {
+      period = "下午";
+    } else {
+      period = "晚上";
+    };
+    return period;
+  }
+}
+
+let timeUtil = new TimeUtil();
+
+export default timeUtil as TimeUtil;

+ 217 - 0
entry/src/main/ets/common/util/UploadUtil.ets

@@ -0,0 +1,217 @@
+import { BusinessError } from '@ohos.base'
+import http from '@ohos.net.http'
+import { FormData, File, FileUpload } from "@ohos/commons-fileupload"
+import fs from '@ohos.file.fs';
+import ProcessRequest from '../util/request/ProcessRequest';
+import web_webview from '@ohos.web.webview';
+import { AxiosResponse } from '@ohos/axios';
+
+const TAG = "openHarmonyBridge upload"
+
+
+// 从webview那边穿过来的 用于上传完成后调接口的值
+interface BridgeParams {
+  token?: string,
+  operationMediaId?: string,
+  processId?: string,
+  seqNo?: string,
+  methodName?: string,
+  apiUrlPath?: string //上传完成后调的接口 不包含baseurl
+  method?: string
+  stationIP?: string
+  messageKey?: string //web端会进行window.addEventListener('messageKey' 然后把url整体传回去, 为了后续定义传回去对象的json {fullUrl: ''}
+}
+
+// 上传文件后,返回一个图片的url,然后再调用其他接口上传url和一些值, 如果有其他接口需要key,在这里添加,然后在下面赋值就行了
+interface APIParamsModel extends BridgeParams {
+  filePath?: string, //工序的多媒体采集需要用到的key
+  file?: string,
+  fileName?: string,
+}
+
+interface ResultModel {
+  code?: string
+  data?: FileDataModel
+  msg?: string
+}
+
+interface FileDataModel {
+  fileUrl?: string
+}
+
+
+class UploadUtil {
+  //private baseUrl: string = JGRequestBaseUrl
+  //private fileUrl: string = "http://10.88.11.201:9000" //上传完成后可以查看的
+  private fileUrl: string = "http://192.168.1.3:20001" //江山
+  private uploadPath: string = "/api/v1/base/upload"
+  controller: web_webview.WebviewController | null = null
+  ports: web_webview.WebMessagePort[] = [];
+
+  private constructor() {
+  }
+
+  public uploadParams: BridgeParams = {}
+  // public token: string = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOnsiaWQiOjEwMDAwLCJ1c2VyTmFtZSI6ImFkbWluIiwiZGVwdElkIjoxLCJzdGF0aW9uSWQiOjAsImF2YXRhciI6Ii9qZ2ZpbGUvLzIwMjQvNS8yNy8yNzQzMjkzOTU4Mzg4NTIuanBnIiwib3JnSWQiOjF9LCJkZXZpY2UiOiJiYWNrIiwiZWZmIjoxNzI0MTIyNTQwMTQwLCJyblN0ciI6IkRkakc3akFSNEtkZ1h5VWV5emhROFVib1ZlOTJUVVo4In0.yVzW8YkjiqLf9T4oUsJUPUifqPFANWX78pov8aGRsFQ"
+  private static instance: UploadUtil; //单例实例
+
+  public static getInstance(): UploadUtil {
+    if (!UploadUtil.instance) {
+      UploadUtil.instance = new UploadUtil();
+    }
+    return UploadUtil.instance;
+  }
+
+  public configUploadParam(p: string) {
+    let model: BridgeParams = JSON.parse(p)
+    this.uploadParams = model
+  }
+
+  startUploadBase64(uri: string, callBack: () => void) {
+    try {
+      let base64Test =  fs.readTextSync(uri)
+      console.log(TAG, "baes64",base64Test)
+
+      let base64Data: APIParamsModel = {
+        file: base64Test,
+        fileName: 'test.jpg',
+      }
+      ProcessRequest.request({
+        method:  "post",
+        url: "/api/v1/base/uploadBase64",
+        data: base64Data
+      }).then( async (res: AxiosResponse) => {
+
+        let result: FileDataModel = JSON.parse(JSON.stringify(res))
+
+
+        // 上传完成后调其他接口
+        if (result.fileUrl) {
+          let data: APIParamsModel = {
+            filePath: result.fileUrl,
+            operationMediaId: this.uploadParams?.operationMediaId ,
+            processId: this.uploadParams?.processId ,
+            seqNo: this.uploadParams?.seqNo ,
+          }
+
+          await ProcessRequest.request({
+            method: this.uploadParams?.method ,
+            url: this.uploadParams.apiUrlPath,
+            data: data
+          })
+        }
+
+        // 这个是 给webview发送post messages 通知的
+        if (this.uploadParams.messageKey) {
+          uploadInstance.ports = uploadInstance.controller!.createWebMessagePorts();
+          uploadInstance.controller!.postMessage(JSON.stringify({
+            fullUrl: this.fileUrl + result.fileUrl ?? "",
+            messageKey: this.uploadParams.messageKey ?? ""
+          }), [uploadInstance.ports[0]], '*');
+        }
+
+        callBack()
+      }).catch(() => {
+        callBack()
+      })
+
+
+    }
+    catch (e) {
+      callBack()
+      console.log(TAG, 'mk-logger', 'photoPlugin-str', JSON.stringify(e))
+    }
+  }
+
+  startUpload(uri: string, callBack: () => void) {
+
+
+    //   开始上传
+    try {
+      // 2.2 根据uri同步打开文件
+      const file = fs.openSync(uri)
+      // 2.3 同步获取文件的详细信息
+      const stat = fs.statSync(file.fd)
+      // 2.4 创建缓冲区存储读取的文件流
+      const buffer = new ArrayBuffer(stat.size)
+      // 2.5 开始同步读取文件流到缓冲区
+      fs.readSync(file.fd, buffer)
+      // 2.6 关闭文件流
+      fs.closeSync(file)
+
+      // 3. 转成base64编码的字符串
+      // const helper = new util.Base64Helper()
+      // const str = helper.encodeToStringSync(new Uint8Array(buffer))
+      // console.log('mk-logger', 'photoPlugin-str', buffer.byteLength, JSON.stringify(buffer))
+
+      let toUpFile = new File()
+      toUpFile.fileName = "test.jpg"
+      toUpFile.fileData = buffer
+
+      let fileUpload = new FileUpload({
+        //baseUrl: this.baseUrl,
+        readTimeout: 60000,
+        connectTimeout: 60000,
+      })
+
+      const formData = new FormData();
+      // formData.append("key", 1); //可以添加基本类型的值,目前支持string、number
+      // formData.append("fileName", "test.jpg"); //也可以添加File类型的对象
+      formData.append('file', toUpFile)
+      console.log(TAG,"================toUpFile", toUpFile)
+      console.log(TAG,"================formData", formData)
+      fileUpload.post(this.uploadPath, formData, {
+        header: {
+          'Content-Type': 'multipart/form-data',
+          // "Authorization": this.token
+          'Authorization': this.uploadParams?.token ?? "",
+        },
+      }).then(async (res: http.HttpResponse) => {
+
+        let result: ResultModel = JSON.parse(res.result as string)
+        console.log(TAG, "ddddddd success", JSON.stringify(result?.data ?? ""));
+
+
+        // 上传完成后调其他接口
+        if (this.uploadParams.apiUrlPath) {
+          let data: APIParamsModel = {
+            filePath: result.data?.fileUrl ?? "",
+            operationMediaId: this.uploadParams?.operationMediaId ?? "",
+            processId: this.uploadParams?.processId ?? "",
+            seqNo: this.uploadParams?.seqNo ?? "",
+          }
+
+          await ProcessRequest.request({
+            method: this.uploadParams?.method ?? "post",
+            url: this.uploadParams?.apiUrlPath,
+            data: data
+          })
+        }
+
+        // 这个是 给webview发送post messages 通知的
+        if (this.uploadParams.messageKey) {
+          uploadInstance.ports = uploadInstance.controller!.createWebMessagePorts();
+          uploadInstance.controller!.postMessage(JSON.stringify({
+            fullUrl: this.fileUrl + result.data?.fileUrl ?? "",
+            messageKey: this.uploadParams.messageKey ?? ""
+          }), [uploadInstance.ports[0]], '*');
+        }
+
+        callBack()
+
+        //上传完成后浏览器可以产看的图片地址  http://10.88.11.201:9000
+      }).catch((err: BusinessError) => {
+        callBack()
+        console.error(TAG, "ddddddd    fail", JSON.stringify(err));
+      })
+    }
+    catch (e) {
+      callBack()
+      console.log(TAG, 'mk-logger', 'photoPlugin-str', JSON.stringify(e))
+    }
+  }
+}
+
+let uploadInstance = UploadUtil.getInstance();
+
+export default uploadInstance;

+ 129 - 0
entry/src/main/ets/common/util/UsbDevice.ets

@@ -0,0 +1,129 @@
+import usbManager from '@ohos.usbManager';
+import { BusinessError } from '@ohos.base';
+
+// 定义常用USB设备ID常量数组
+export const USBDeviceIds: Array<UsbDeviceInfo> = [
+  {
+    DeviceName: "Sony_Camera",
+    VendorId: 0x054c,
+    ProductId: 0x0caa
+  },
+  {
+    DeviceName: "Scanning_Gun",
+    VendorId: 0x23d0,
+    ProductId: 0x0c80
+  }
+];
+
+// USB设备信息接口
+interface UsbDeviceInfo {
+  DeviceName: string;
+  VendorId: number;
+  ProductId: number;
+}
+
+export
+class USBDeviceManager {
+  private connectedDevices: Array<usbManager.USBDevice> = [];
+  private pollTimer: number | null = null;
+  private isPollingPaused: boolean = false;
+
+  // 启动轮询(带暂停控制)
+  public startPolling(interval: number, callback: (devices: usbManager.USBDevice[]) => void): void {
+    this.stopPolling(); // 先停止已有轮询
+    this.pollTimer = setInterval(async () => {
+      if (this.isPollingPaused) return; // 暂停时跳过检测
+      try {
+        this.connectedDevices = await this.refreshDeviceList();
+        callback(this.connectedDevices);
+      } catch (error) {
+        console.error('轮询失败:', (error as BusinessError).message);
+      }
+    }, interval)  as number; // 鸿蒙中setInterval返回类型需转换
+  }
+
+  // 暂停轮询
+  public pausePolling(): void {
+    this.isPollingPaused = true;
+  }
+
+  // 恢复轮询
+  public resumePolling(): void {
+    this.isPollingPaused = false;
+  }
+
+  // 停止轮询(释放资源)
+  public stopPolling(): void {
+    if (this.pollTimer !== null) {
+      clearInterval(this.pollTimer);
+      this.pollTimer = null;
+    }
+    this.isPollingPaused = false;
+  }
+
+  /* 手动刷新设备列表(需主动调用)
+  * @returns 返回刷新后的设备列表
+  */
+  public async refreshDeviceList(): Promise<Array<usbManager.USBDevice>> {
+    try {
+      this.connectedDevices = usbManager.getDevices();
+      console.log('[USB] 当前连接的设备:', this.connectedDevices);
+      return this.connectedDevices;
+    } catch (error) {
+      console.error('[USB] 获取USB设备失败:', (error as BusinessError).message);
+      this.connectedDevices = [];
+      return [];
+    }
+  }
+
+  /* 检查是否有USB设备连接
+  * @param forceRefresh 是否强制刷新设备列表
+  */
+  public async hasConnectedDevices(forceRefresh: boolean = false): Promise<boolean> {
+    if (forceRefresh || this.connectedDevices.length === 0) {
+      await this.refreshDeviceList();
+    }
+    return this.connectedDevices.length > 0;
+  }
+
+  /* 检查特定设备是否连接
+  * @param vendorId 厂商ID
+  * @param productId 产品ID
+  * @param forceRefresh 是否强制刷新
+  */
+  public async isDeviceConnectedById(
+    vendorId: number,
+    productId: number,
+    forceRefresh: boolean = false
+  ): Promise<boolean> {
+    if (forceRefresh) {
+      await this.refreshDeviceList();
+    }
+    return this.connectedDevices.some(device =>
+    device.vendorId === vendorId && device.productId === productId
+    );
+  }
+
+  /* 检查预定义的设备是否连接
+  * @param deviceName 设备名称(需在USBDeviceIds中定义)
+  * @param forceRefresh 是否强制刷新
+  */
+  public async isDeviceConnectedByName(
+    deviceName: string,
+    forceRefresh: boolean = false
+  ): Promise<boolean> {
+    const deviceInfo = USBDeviceIds.find(item => item.DeviceName === deviceName);
+    if (!deviceInfo) {
+      console.warn(`[USB] 设备 ${deviceName} 不在预定义列表中`);
+      return false;
+    }
+    return this.isDeviceConnectedById(deviceInfo.VendorId, deviceInfo.ProductId, forceRefresh);
+  }
+
+  /*
+  * 获取当前缓存的设备列表(不主动刷新)
+  */
+  public getCachedDevices(): Array<usbManager.USBDevice> {
+    return [...this.connectedDevices];
+  }
+}

+ 72 - 0
entry/src/main/ets/common/util/request/ProcessRequest.ets

@@ -0,0 +1,72 @@
+import axios, {
+  AxiosError,
+  AxiosResponse,
+  AxiosRequestHeaders,
+  AxiosRequestConfig,
+  CreateAxiosDefaults,
+  InternalAxiosRequestConfig
+} from '@ohos/axios';
+import CommonConstants from '../../constants/CommonConstants';
+import { printError, printRequest, printResponse, handleRes } from './Helps';
+
+
+// jiaxiaoqiang:这里要改
+//const baseUrl = "http://192.168.1.3:30010"//huajing
+// const baseUrl = "http://192.168.1.246:30010" // huhao本地
+//const baseUrl = "http://192.168.1.3:20010"//huajing
+const baseUrl = "http://192.168.1.3:20001" // huhao本地
+const DEBUG = true //
+
+// 创建实例
+const ProcessRequest = axios.create(
+
+  {
+    baseURL: baseUrl,
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+    timeout: 60 * 1000,
+  }
+)
+
+// 添加请求拦截器
+ProcessRequest.interceptors.request.use((config: InternalAxiosRequestConfig) => {
+
+  // 以后登录之后可以在这里传
+  config.headers.Authorization = CommonConstants.AUTH_TOKEN
+  printRequest(config)
+  return config;
+}, (error: AxiosError) => {
+  // 对请求错误做些什么
+  printError(error)
+  return Promise.reject(error);
+});
+
+
+// 添加响应拦截器
+ProcessRequest.interceptors.response.use((response: AxiosResponse) => {
+  // 对响应数据做点什么
+  printResponse(response)
+  let res = handleRes(response)
+  let success = res[0] as boolean
+  let msg = res[1] as string
+  console.debug("handleRes的返回结果 ", success, msg)
+
+  if (success) {
+    return response.data.data;
+  }
+  else {
+    return Promise.reject<string>(msg)
+  }
+
+}, (error: AxiosError) => {
+  // 对响应错误做点什么
+  printError(error)
+  return Promise.reject(error);
+});
+
+export default ProcessRequest;
+
+
+
+

+ 108 - 0
entry/src/main/ets/common/util/request/helps.ets

@@ -0,0 +1,108 @@
+import { InternalAxiosRequestConfig, AxiosResponse, AxiosError } from "@ohos/axios"
+import promptAction from '@ohos.promptAction'
+
+// 是否在控制台打印某项log
+const pConfig = false
+const pHeaders = false
+const pUrl = true
+const pParams = true
+const pBody = true
+const pResponse = true
+const pData = true
+const pError = true
+
+// @CustomDialog
+// struct CustomDialogExample {
+//   controller: CustomDialogController
+//   build() {
+//     Column() {
+//       Text('我是内容')
+//         .fontSize(20)
+//         .margin({ top: 10, bottom: 10 })
+//     }
+//   }
+// }
+
+const printRequest = (config: InternalAxiosRequestConfig) => {
+  if (pConfig) {
+    console.debug("printRequest config", JSON.stringify(config))
+  }
+
+  if (pHeaders) {
+    console.debug("printRequest headers", JSON.stringify(config.headers))
+  }
+
+  if (pUrl) {
+    console.debug("printRequest url", `Method:${config.method} ${config.baseURL}${config.url}`)
+  }
+
+  if (pBody) {
+    console.debug("printRequest 请求参数data", JSON.stringify(config.data))
+  }
+
+  if (pParams) {
+    console.debug("printRequest 请求参数params", JSON.stringify(config.params))
+  }
+
+}
+
+const printResponse = (response: AxiosResponse) => {
+  if (pResponse) {
+    console.debug('printResponse response: ', JSON.stringify(response))
+  }
+  if (pData) {
+    console.debug("printResponse data", JSON.stringify(response.data))
+  }
+}
+
+const printError = (error: AxiosError) => {
+  if (pError) {
+    console.debug("printError error", error.message)
+  }
+  promptAction.showToast({
+    message: error.message ?? "请求出错",
+    duration: 1500,
+    bottom: 100
+  })
+}
+
+
+// 处理返回数据
+const handleRes = (response: AxiosResponse): [boolean, string] => {
+  let isSuccess = true
+  let msg = ""
+
+  if (response.status === 200) { //判断返回状态是否为200
+    if (String(response.data?.code) === "200") { //判断数据的code是否为200,有可能是500的服务器错误
+      isSuccess = true
+      msg = `请求数据成功`
+    }
+    else {
+      isSuccess = false
+      // msg = `${response.data?.code}: ${response.data?.msg ?? "服务器错误"}`
+      msg = `${response.data?.msg ?? "服务器错误"}`
+    }
+  }
+  else {
+    isSuccess = false
+    msg = `状态码非200 status: ${response.status}}`
+  }
+
+  if (!isSuccess) {
+    promptAction.showToast({
+      message: msg,
+      duration: 2000,
+      bottom: 100
+    })
+
+    // const dialogController: CustomDialogController = new CustomDialogController({
+    //   builder: CustomDialogExample({}),
+    // })
+    // dialogController.open()
+  }
+
+  return [isSuccess, msg]
+}
+
+
+export { printRequest, printResponse, printError, handleRes }

+ 74 - 0
entry/src/main/ets/entryability/EntryAbility.ets

@@ -0,0 +1,74 @@
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
+import hilog from '@ohos.hilog';
+import UIAbility from '@ohos.app.ability.UIAbility';
+import Want from '@ohos.app.ability.Want';
+import window from '@ohos.window';
+import ElectricScrewdriverPresetModel from '../model/ElectricScrewdriverPresetModel';
+import ElectricSolderingIronPresetModel from '../model/ElectricSolderingIronPresetModel';
+import preferencesUtil from '../common/util/PerferencesUtil';
+import CommonConstants from '../common/constants/CommonConstants';
+
+export default class EntryAbility extends UIAbility {
+  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
+    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
+    ElectricScrewdriverPresetModel.initTaskDB(this.context)
+    ElectricSolderingIronPresetModel.initTaskDB(this.context)
+    await preferencesUtil.loadPreference(this.context, CommonConstants.PREFERENCE_INSTANCE_NAME);
+  }
+
+  onDestroy(): void {
+    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
+  }
+
+  onWindowStageCreate(windowStage: window.WindowStage) {
+    // Main window is created, set main page for this ability
+    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
+    // 重新打开需要清除preferences中保存的数据
+    preferencesUtil.clear(CommonConstants.PREFERENCE_INSTANCE_NAME)
+    // 1.获取应用主窗口。
+    let windowClass: window.Window
+    windowStage.getMainWindow((err, data) => {
+
+      if (err.code) {
+        console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
+        return;
+      }
+      windowClass = data;
+      console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));
+      // 2.实现沉浸式效果:设置导航栏、状态栏不显示。
+      let names: [] = [];
+      windowClass.setWindowSystemBarEnable(names, (err) => {
+        if (err.code) {
+          console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err));
+          return;
+        }
+        console.info('Succeeded in setting the system bar to be visible.');
+      });
+
+      windowClass.setWindowLayoutFullScreen(true)
+    })
+
+    windowStage.loadContent('pages/ProcessIndex', (err) => {
+      if (err.code) {
+        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+        return;
+      }
+      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
+    });
+  }
+
+  onWindowStageDestroy(): void {
+    // Main window is destroyed, release UI related resources
+    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
+  }
+
+  onForeground(): void {
+    // Ability has brought to foreground
+    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
+  }
+
+  onBackground(): void {
+    // Ability has back to background
+    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
+  }
+}

+ 141 - 0
entry/src/main/ets/model/ElectricScrewdriverPresetModel.ets

@@ -0,0 +1,141 @@
+import relationalStore from '@ohos.data.relationalStore';
+import CommonConstants from '../common/constants/CommonConstants';
+import ElectricScrewdriverPreset from '../viewmodel/device/ElectricScrewdriverPreset';
+
+/*
+ * 电动螺丝刀预设数据库model
+ * */
+class ElectricScrewdriverPresetModel {
+
+  private rdbStore: relationalStore.RdbStore |undefined = undefined
+  private tableName: string = 'ElectricScrewdriverPreset'
+
+  /**
+   * 初始化表
+   */
+  async initTaskDB(context:Context){
+    // 1.rdb配置
+    const config = {
+      name: CommonConstants.RDB_NAME,
+      securityLevel: relationalStore.SecurityLevel.S1
+    }as relationalStore.StoreConfig
+    try {
+      // 获取数据库存储对象
+      this.rdbStore = await relationalStore.getRdbStore(context, config);
+    } catch (err) {
+      console.info('hhtest', `getRdbStore err ${JSON.stringify(err)}`);
+    }
+    // 2.初始化SQL语句
+    const sql = `CREATE TABLE IF NOT EXISTS ElectricScrewdriverPreset(
+                  ID INTEGER PRIMARY KEY AUTOINCREMENT,
+                  SET_NAME VARCHAR NOT NULL,
+                  ROTATION_DIRECTION INTEGER NULL,
+                  HOLD_TIME INTEGER NULL,
+                  TARGET_TORQUE INTEGER NULL,
+                  MAX_TORQUE_DEVIATION INTEGER NULL,
+                  MIN_TORQUE_DEVIATION INTEGER NULL,
+                  DETECTION_STATUS INTEGER NULL,
+                  FLOAT_HEIGHT_TURN INTEGER NULL,
+                  THREAD_STRIP_TURN INTEGER NULL
+                 )`
+    // 3.获取rdb
+    relationalStore.getRdbStore(context, config, (err, rdbStore) => {
+      if(err){
+        console.log('hhtest',  '获取rdbStore失败!')
+        return
+      }
+      // 执行Sql
+      rdbStore.executeSql(sql).then(()=>{
+        console.log('hhtest',  '创建电动螺丝刀预设表成功!')
+      }).catch((err: object)=>{
+        console.log('hhtest',  '创建失败')
+      })
+      // 保存rdbStore
+      this.rdbStore = rdbStore
+    })
+  }
+
+  /**
+   * 添加电动螺丝刀预设
+   * @param preset 预设信息
+   * @returns 预设id
+   */
+  async addPreset(preset: ElectricScrewdriverPreset): Promise<number | undefined> {
+    return this.rdbStore?.insert(this.tableName, {SET_NAME: preset.setName,
+      ROTATION_DIRECTION: preset.TighteRotationDirection, HOLD_TIME: preset.TorqueHoldTime,
+      TARGET_TORQUE: preset.TightenTorqueTarget, MAX_TORQUE_DEVIATION: preset.TorqueUpperLimit,
+      MIN_TORQUE_DEVIATION: preset.TorqueLowerLimit, DETECTION_STATUS: preset.EnableFloatSlipDetection,
+      FLOAT_HEIGHT_TURN: preset.FloatDetectionTurns, THREAD_STRIP_TURN: preset.SlipDetectionTurns} as relationalStore.ValuesBucket)
+  }
+
+  /**
+   * 查询电动螺丝刀预设列表
+   */
+  async getPresetList(): Promise<ElectricScrewdriverPreset[]> {
+    // 1.构建查询条件
+    let predicates = new relationalStore.RdbPredicates(this.tableName)
+    // 2.查询
+    let result: relationalStore.ResultSet | undefined = await this.rdbStore?.query(predicates, ['ID', 'SET_NAME', 'SET_VALUE', 'UNIT_STRING'])
+    // 3.解析查询结果
+    // 3.1.定义一个数组,组装最终的查询结果
+    let presets: ElectricScrewdriverPreset[] = []
+    // 3.2.遍历封装
+    while(result && !result?.isAtLastRow){
+      // 3.3.指针移动到下一行
+      result.goToNextRow()
+      // 3.4.获取数据
+      let id = result.getLong(result.getColumnIndex('ID'))
+      let setName = result.getString(result.getColumnIndex('SET_NAME'))
+      let TighteRotationDirection = result.getDouble(result.getColumnIndex('ROTATION_DIRECTION'))
+      let TorqueHoldTime = result.getDouble(result.getColumnIndex('HOLD_TIME'))
+      let TightenTorqueTarget = result.getDouble(result.getColumnIndex('TARGET_TORQUE'))
+      let TorqueUpperLimit = result.getDouble(result.getColumnIndex('MAX_TORQUE_DEVIATION'))
+      let TorqueLowerLimit = result.getDouble(result.getColumnIndex('MIN_TORQUE_DEVIATION'))
+      let EnableFloatSlipDetection = result.getLong(result.getColumnIndex('DETECTION_STATUS'))
+      let FloatDetectionTurns = result.getDouble(result.getColumnIndex('FLOAT_HEIGHT_TURN'))
+      let SlipDetectionTurns = result.getDouble(result.getColumnIndex('THREAD_STRIP_TURN'))
+      // 3.5.封装到数组
+      presets.push({id, setName, TighteRotationDirection, TorqueHoldTime, TightenTorqueTarget, TorqueUpperLimit, TorqueLowerLimit, EnableFloatSlipDetection, FloatDetectionTurns, SlipDetectionTurns })
+    }
+    return presets
+  }
+
+  /**
+   * 根据id更新电动螺丝刀预设
+   * @param preset 预设
+   */
+  async updatePreset(preset: ElectricScrewdriverPreset): Promise<number | undefined> {
+
+    // 1.要更新的数据
+    let data: relationalStore.ValuesBucket = {SET_NAME: preset.setName as string,
+      ROTATION_DIRECTION: preset.TighteRotationDirection as number, HOLD_TIME: preset.TorqueHoldTime as number,
+      TARGET_TORQUE: preset.TightenTorqueTarget as number, MAX_TORQUE_DEVIATION: preset.TorqueUpperLimit as number,
+      MIN_TORQUE_DEVIATION: preset.TorqueLowerLimit as number, DETECTION_STATUS: preset.EnableFloatSlipDetection as number,
+      FLOAT_HEIGHT_TURN: preset.FloatDetectionTurns as number, THREAD_STRIP_TURN: preset.SlipDetectionTurns as number
+    }
+    // 2.更新的条件
+    if (preset && preset.id!) {
+      let predicates = new relationalStore.RdbPredicates(this.tableName)
+      predicates.equalTo('ID', preset.id!)
+      // 3.更新操作
+      return this.rdbStore?.update(data, predicates)
+    }
+    return 0
+  }
+
+  /**
+   * 根据id删除电动螺丝刀预设
+   * @param id 预设id
+   */
+  async deletePresetById(id: number): Promise<number | undefined> {
+    // 1.删除的条件
+    let predicates = new relationalStore.RdbPredicates(this.tableName)
+    predicates.equalTo('ID', id)
+    // 2.删除操作
+    return this.rdbStore?.delete(predicates)
+  }
+}
+
+let electricScrewdriverPresetModel = new ElectricScrewdriverPresetModel();
+
+export default electricScrewdriverPresetModel as ElectricScrewdriverPresetModel;

+ 120 - 0
entry/src/main/ets/model/ElectricSolderingIronPresetModel.ets

@@ -0,0 +1,120 @@
+import relationalStore from '@ohos.data.relationalStore';
+import CommonConstants from '../common/constants/CommonConstants';
+import ElectricSolderingIronPreset from '../viewmodel/device/ElectricSolderingIronPreset';
+
+/**
+ * 电烙铁预设数据库model
+ */
+class ElectricSolderingIronPresetModel {
+
+  private rdbStore: relationalStore.RdbStore |undefined = undefined
+  private tableName: string = 'ElectricSolderingIronPreset'
+
+  /**
+   * 初始化表
+   */
+  async initTaskDB(context:Context){
+    // 1.rdb配置
+    const config = {
+      name: CommonConstants.RDB_NAME,
+      securityLevel: relationalStore.SecurityLevel.S1
+    }as relationalStore.StoreConfig
+    try {
+      // 获取数据库存储对象
+      this.rdbStore = await relationalStore.getRdbStore(context, config);
+    } catch (err) {
+      console.info('hhtest', `getRdbStore err ${JSON.stringify(err)}`);
+    }
+    // 2.初始化SQL语句
+    const sql = `CREATE TABLE IF NOT EXISTS ElectricSolderingIronPreset (
+                  ID INTEGER PRIMARY KEY AUTOINCREMENT,
+                  SET_NAME VARCHAR NOT NULL,
+                  SET_VALUE INTEGER NOT NULL,
+                  UNIT_STRING VARCHAR NOT NULL
+                 )`
+    // 3.获取rdb
+    relationalStore.getRdbStore(context, config, (err, rdbStore) => {
+      if(err){
+        console.log('hhtest', '获取rdbStore失败!')
+        return
+      }
+      // 执行Sql
+      rdbStore.executeSql(sql).then(()=>{
+        console.log('hhtest', '创建电烙铁预设表成功!')
+      }).catch((err: object)=>{
+        console.log('hhtest', '创建失败')
+      })
+      // 保存rdbStore
+      this.rdbStore = rdbStore
+    })
+  }
+
+  /**
+   * 添加电烙铁预设
+   * @param preset 预设信息
+   * @returns 预设id
+   */
+  async addPreset(preset: ElectricSolderingIronPreset): Promise<number | undefined> {
+    return this.rdbStore?.insert(this.tableName, {SET_NAME: preset.setName, SET_VALUE: preset.setValue, UNIT_STRING: preset.unitString} as relationalStore.ValuesBucket)
+  }
+
+  /**
+   * 查询电烙铁预设列表
+   */
+  async getPresetList(): Promise<ElectricSolderingIronPreset[]> {
+    // 1.构建查询条件
+    let predicates = new relationalStore.RdbPredicates(this.tableName)
+    // 2.查询
+    let result: relationalStore.ResultSet | undefined = await this.rdbStore?.query(predicates, ['ID', 'SET_NAME', 'SET_VALUE', 'UNIT_STRING'])
+    // 3.解析查询结果
+    // 3.1.定义一个数组,组装最终的查询结果
+    let presets: ElectricSolderingIronPreset[] = []
+    // 3.2.遍历封装
+    while(result && !result?.isAtLastRow){
+      // 3.3.指针移动到下一行
+      result.goToNextRow()
+      // 3.4.获取数据
+      let id = result.getLong(result.getColumnIndex('ID'))
+      let setName = result.getString(result.getColumnIndex('SET_NAME'))
+      let setValue = result.getLong(result.getColumnIndex('SET_VALUE'))
+      let unitString = result.getString(result.getColumnIndex('UNIT_STRING'))
+      // 3.5.封装到数组
+      presets.push({id, setName, setValue, unitString})
+    }
+    return presets
+  }
+
+  /**
+   * 根据id更新电烙铁预设
+   * @param preset 预设
+   */
+  async updatePreset(preset: ElectricSolderingIronPreset): Promise<number | undefined> {
+
+    // 1.要更新的数据
+    let data: relationalStore.ValuesBucket = {SET_NAME: preset.setName as string, SET_VALUE: preset.setValue as number, UNIT_STRING: preset.unitString as string}
+    // 2.更新的条件
+    if (preset && preset.id!) {
+      let predicates = new relationalStore.RdbPredicates(this.tableName)
+      predicates.equalTo('ID', preset.id!)
+      // 3.更新操作
+      return this.rdbStore?.update(data, predicates)
+    }
+    return 0
+  }
+
+  /**
+   * 根据id删除电烙铁预设
+   * @param id 预设id
+   */
+  async deletePresetById(id: number): Promise<number | undefined> {
+    // 1.删除的条件
+    let predicates = new relationalStore.RdbPredicates(this.tableName)
+    predicates.equalTo('ID', id)
+    // 2.删除操作
+    return this.rdbStore?.delete(predicates)
+  }
+}
+
+let electricSolderingIronPresetModel = new ElectricSolderingIronPresetModel();
+
+export default electricSolderingIronPresetModel as ElectricSolderingIronPresetModel;

File diff suppressed because it is too large
+ 1034 - 0
entry/src/main/ets/pages/CameraView.ets


File diff suppressed because it is too large
+ 1290 - 0
entry/src/main/ets/pages/DeviceControllerPage.ets


File diff suppressed because it is too large
+ 1530 - 0
entry/src/main/ets/pages/ProcessIndex.ets


File diff suppressed because it is too large
+ 1067 - 0
entry/src/main/ets/view/AuxiliaryOperationView.ets


+ 265 - 0
entry/src/main/ets/view/BarcodeAssociationDialog.ets

@@ -0,0 +1,265 @@
+//条码关联
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import promptAction from '@ohos.promptAction'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import WorkOrderSeq from '../viewmodel/WorkOrderSeq'
+
+@CustomDialog
+export struct BarcodeAssociationDialog {
+  private scrollerMaterial: Scroller = new Scroller()
+  // 工单编号
+  workOrderCode: string = ''
+  //0:序列号排序,1:铭牌号排序 -1:默认
+  @State currentClick:number = -1
+  // 工单下流转卡号列表
+  @Link seqList: WorkOrderSeq[]
+  controller: CustomDialogController
+  onConfirm: () => void = async () => {
+     await ProcessRequest.post('/api/v1/plan/seq/bindCode', this.seqList)
+     // 重新刷新
+     this.seqList = await ProcessRequest.post('/api/v1/plan/seq/list', {
+       workOrderCode: this.workOrderCode
+     } as RequestParamModel)
+  }
+
+  //序列号排序,没有的在前面
+  onSerialNumberClick() {
+    if (this.currentClick === 0) {
+       this.seqList.sort((a, b) => {
+         const aStream = a.seqNo ?? "";
+         const bStream = b.seqNo ?? "";
+         return aStream.localeCompare(bStream);
+       })
+      this.currentClick = -1;
+    } else {
+      this.seqList.sort((a, b) => {
+        const aSerial = a.supportingCode ?? "";
+        const bSerial = b.supportingCode ?? "";
+        const aStream = a.seqNo ?? "";
+        const bStream = b.seqNo ?? "";
+
+        if (!aSerial && !bSerial) {
+          return aStream.localeCompare(bStream);
+        } else if (!aSerial) {
+          return -1;
+        } else if (!bSerial) {
+          return 1;
+        } else {
+          return aSerial.localeCompare(bSerial);
+        }
+      });
+      this.currentClick = 0;
+    }
+  }
+  //铭牌号排序,没有的在前面
+  onNameplateClick() {
+    if (this.currentClick === 1) {
+      this.seqList.sort((a, b) => {
+        const aStream = a.seqNo ?? "";
+        const bStream = b.seqNo ?? "";
+        return aStream.localeCompare(bStream);
+      })
+      this.currentClick = -1;
+    } else {
+      this.seqList.sort((a, b) => {
+        const aNameplate = a.nameplateNo ?? "";
+        const bNameplate = b.nameplateNo ?? "";
+        const aStream = a.seqNo ?? "";
+        const bStream = b.seqNo ?? "";
+
+        if (!aNameplate && !bNameplate) {
+          return aStream.localeCompare(bStream);
+        } else if (!aNameplate) {
+          return -1;
+        } else if (!bNameplate) {
+          return 1;
+        } else {
+          return aNameplate.localeCompare(bNameplate);
+        }
+      });
+      this.currentClick = 1;
+    }
+  }
+
+  async aboutToAppear() {
+    if (!this.seqList && this.workOrderCode) {
+      this.seqList = await ProcessRequest.post('/api/v1/plan/seq/list', {
+        workOrderCode: this.workOrderCode
+      } as RequestParamModel)
+    }
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("条码关联")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+      // 表头和排序操作
+      Row() {
+        Row(){
+          Text(`流水号`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_24'))
+        }
+        .width('33%')
+        .justifyContent(FlexAlign.Center)
+        Row(){
+          Text(`序列号`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_24'))
+          Image($r('app.media.process_sort'))
+            .width($r('app.float.virtualSize_48'))
+            .height($r('app.float.virtualSize_48'))
+            .fillColor(this.currentClick === 0 ?$r('app.color.0A84FF'):$r('app.color.FFFFFF'))
+        }
+        .width('33%')
+        .justifyContent(FlexAlign.Center)
+        .onClick(()=>{
+          this.onSerialNumberClick();
+        })
+        Row(){
+          Text(`铭牌号`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_24'))
+          Image($r('app.media.process_sort'))
+            .width($r('app.float.virtualSize_48'))
+            .height($r('app.float.virtualSize_48'))
+            .fillColor(this.currentClick === 1 ?$r('app.color.0A84FF'):$r('app.color.FFFFFF'))
+        }
+        .width('33%')
+        .justifyContent(FlexAlign.Center)
+        .onClick(()=>{
+         this.onNameplateClick();
+        })
+      }
+      .height('4%')
+      .width('96%')
+      .justifyContent(FlexAlign.End)
+      .margin({left:'2%',right:'2%',top:'1%'})
+
+      Divider()
+        .vertical(false)
+        .strokeWidth(1)
+        .color($r('app.color.15FFFFFF'))
+        .margin({ top: '1%' ,bottom:'2%',left:'2%',right:'2%'})
+      // 绑定区
+      Row(){
+        Column(){
+          List({space: 5, scroller:this.scrollerMaterial}) {
+            ForEach(this.seqList, (item: WorkOrderSeq, index: number) => {
+              ListItem() {
+                Row() {
+                  Row(){
+                    Text(item.seqNo)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                  }
+                  .width('33%')
+                  .justifyContent(FlexAlign.Center)
+                  Row() {
+                    Image($r('app.media.material_qr_code'))
+                      .width($r('app.float.virtualSize_24'))
+                      .height($r('app.float.virtualSize_24'))
+                      .fillColor($r('app.color.FFFFFF'))
+                      .margin({left:'4%'})
+                    TextInput({text: item.supportingCode, placeholder: item.supportingCode})
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .enableKeyboardOnFocus(false)
+                      .width('90%')
+                      .textAlign(TextAlign.Start)
+                      .onChange((value: string) =>{
+                        item.supportingCode = value
+                      })
+                  }
+                  .backgroundColor($r('app.color.000000'))
+                  .borderRadius($r('app.float.virtualSize_16'))
+                  .width('29%')
+                  .margin({left:'2%',right:'2%'})
+                  .justifyContent(FlexAlign.Center)
+                  Row() {
+                    Image($r('app.media.material_qr_code'))
+                      .width($r('app.float.virtualSize_24'))
+                      .height($r('app.float.virtualSize_24'))
+                      .fillColor($r('app.color.FFFFFF'))
+                      .margin({left:'4%'})
+                    TextInput({text: item.nameplateNo, placeholder: item.nameplateNo})
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .enableKeyboardOnFocus(false)
+                      .width('90%')
+                      .textAlign(TextAlign.Start)
+                      .onChange((value: string) =>{
+                        item.nameplateNo = value
+                      })
+                  }
+                  .backgroundColor($r('app.color.000000'))
+                  .borderRadius($r('app.float.virtualSize_16'))
+                  .width('29%')
+                  .margin({left:'2%',right:'2%'})
+                  .justifyContent(FlexAlign.Center)
+                }
+                  .width('100%')
+              }
+              .height('10%')
+            })
+          }
+          .width('100%')
+          .height('100%')
+        }
+          .width('100%')
+          .alignItems(HorizontalAlign.Center)
+      }
+      .height('72%')
+      .margin({left:'2%',right:'2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('保存')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.onConfirm();
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 219 - 0
entry/src/main/ets/view/CompleteReceiveDialog.ets

@@ -0,0 +1,219 @@
+//齐套接收
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import MaterialInfo from '../viewmodel/MaterialInfo'
+import OperationInfo from '../viewmodel/process/OperationInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import VehicleInfo from '../viewmodel/VehicleInfo'
+
+@CustomDialog
+export struct CompleteReceiveDialog {
+  private scrollerMaterial: Scroller = new Scroller()
+  private scrollerVehicle: Scroller = new Scroller()
+  //工序载具列表
+  @Link processVehicleList: VehicleInfo[]
+  //当前工单号
+  currentWorkOrderCode:string = ''
+  //当前工序号
+  @Link currentOperationId:string
+  //当前载具物料列表
+  @State vehicleMaterialsList: MaterialInfo[] = [];
+  //载具绑定工序id
+  @State vehicleOperationId:string =''
+  //选择的载具索引
+  @State selectedVehicleIndex: number = -1
+  controller: CustomDialogController
+  onConfirm: () => void = () => {}
+
+  //加载工序所有载具
+  // loadProcessVehicle = async () => {
+  //   let res = await ProcessRequest.post('/api/v1/process/vehicleOperation/list', {
+  //     workOrderCode:this.currentWorkOrderCode,
+  //     operationId:this.currentOperationId
+  //   } as RequestParamModel) as OperationInfo;
+  //   this.processVehicleList = res.processVehicleList??[]
+  // };
+
+  //加载载具物料信息
+  loadVehicleMaterial = async () => {
+    this.vehicleMaterialsList = await ProcessRequest.post('/api/v1/process/vehicleMaterial/list', {
+      vehicleOperationId:this.vehicleOperationId
+    } as RequestParamModel) as MaterialInfo[];
+  };
+
+  //确认出库
+  confirmOutBound = async ()=>{
+    let res = await ProcessRequest.post('/api/v1/process/vehicleOperation/vehicleOut', {
+      vehicleOperationId:this.vehicleOperationId
+    } as RequestParamModel) as VehicleInfo;
+  }
+
+
+  private onSelectOrder(index: number) {
+    this.selectedVehicleIndex = index
+  }
+
+  // aboutToAppear(): void {
+  //   this.loadProcessVehicle();
+  // }
+
+  build() {
+    Column() {
+      Column() {
+        Text("齐套接收")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Row(){
+        Column() {
+          List({scroller:this.scrollerVehicle}) {
+            ForEach(this.processVehicleList, (item: VehicleInfo, index) => {
+              ListItem(){
+                Column() {
+                  Text(`${item.vehicleCode}`)
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .textAlign(TextAlign.Start)
+                    .margin({bottom:'2%' })
+                  Text(`工单编号: ${item.workOrderCode}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .margin({bottom:'1%' })
+                  Text(`工序: ${item.operationName}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                }
+                .alignItems(HorizontalAlign.Start)
+                .height('90%')
+                .width('90%')
+                .margin({left:'2%' })
+                .justifyContent(FlexAlign.Center)
+              }
+              .height('15%')
+              .width('100%')
+              .backgroundColor(index === this.selectedVehicleIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF'))
+              .borderRadius($r('app.float.virtualSize_24'))
+              .border({
+                width: index === this.selectedVehicleIndex ? 2 : 0,
+                color: index === this.selectedVehicleIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF')
+              })
+              .onClick(() => {
+                this.onSelectOrder(index)
+                this.vehicleOperationId = item.id??""
+                this.loadVehicleMaterial()
+              })
+
+            })
+          }
+          .width('90%')
+          .height('100%')
+        }
+        .width('48%')
+        .alignItems(HorizontalAlign.Center)
+        Divider()
+          .vertical(true)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+          .margin({ bottom: '2%',left:'2%',right:'2%'})
+        Column() {
+          List({scroller:this.scrollerMaterial}) {
+            ForEach(this.vehicleMaterialsList, (item:MaterialInfo) => {
+              ListItem() {
+                Row() {
+                  Column(){
+                    Text(item.materialName)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                    Text(`型号: ${item.materialCode}`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                  }
+                  .width('80%')
+                  .alignItems(HorizontalAlign.Start)
+                  Row(){
+                    Text(`${item.num}`)
+                      .fontSize($r('app.float.fontSize_30'))
+                      .fontColor($r('app.color.FFFFFF'))
+                    Text(`${item.unit}`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                      .margin({left:'1%'})
+                  }
+                  .width('20%')
+                  .alignItems(VerticalAlign.Bottom)
+                  .height('100%')
+                  .justifyContent(FlexAlign.Center)
+                }
+                .width('100%')
+                .justifyContent(FlexAlign.SpaceEvenly)
+                .height('8%')
+              }
+            })
+          }
+          .width('90%')
+          .height('100%')
+          .divider({
+            strokeWidth: 1,
+            color: $r('app.color.20FFFFFF')
+          })
+        }
+        .width('48%')
+
+      }
+      .height('81%')
+      .width('100%')
+      .margin({top:'2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('料箱出库')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            if(this.selectedVehicleIndex==-1) return
+            this.confirmOutBound()
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 66 - 0
entry/src/main/ets/view/ConfirmDialog.ets

@@ -0,0 +1,66 @@
+// 切换工单确认
+@CustomDialog
+export struct ConfirmDialog {
+  controller: CustomDialogController
+  onConfirm: () => void = () => {}
+  @Prop title:string = ''
+  @Prop message:string = ''
+
+  build() {
+    Column() {
+      // 标题
+      Column(){
+        Text(this.title)
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('25%')
+      .justifyContent(FlexAlign.Center)
+      Column(){
+        Text(this.message)
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_24'))
+      }.height('50%')
+      .justifyContent(FlexAlign.Center)
+      .margin({bottom:'5%'})
+
+      Column(){
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row(){
+          Row(){
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row(){
+            Text('确认')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.onConfirm();
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('16%')
+    }
+    .height('34%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 322 - 0
entry/src/main/ets/view/DefectRecordDialog.ets

@@ -0,0 +1,322 @@
+//添加或者编辑不良类型记录
+import ProcessRequest from '../common/util/request/ProcessRequest';
+import RequestParamModel from '../viewmodel/RequestParamModel';
+import DictValue from '../viewmodel/DictValue';
+import ProcessBaseBug from '../viewmodel/process/ProcessBaseBug';
+import ProcessInfo from '../viewmodel/process/ProcessInfo';
+import ProcessDefectRecord from '../viewmodel/process/ProcessDefectRecord';
+
+@CustomDialog
+export struct DefectRecordDialog {
+  controller: CustomDialogController
+  onConfirm: ()=> void = () => {}
+  scroller: Scroller = new Scroller()
+
+  //是否是编辑状态(需要传入的参数)
+  @Prop isEdit: boolean = false
+  //选择不良类型索引(需要传入的参数)
+  @Prop selectDefectType: number = -1
+  //选择缺陷类型索引(需要传入的参数)
+  @Prop selectedDefectBug: number= -1
+  // 不良类型(数据字典)
+  @Prop defectTypes: DictValue[] = []
+  // 不良类型对应的缺陷
+  defectBugs: ProcessBaseBug[] = []
+  // 不良类型对应的缺陷map(key为不良类型字典值,value为对应的缺陷)
+  defectBugMap: Map<string, ProcessBaseBug[]> = new Map<string, ProcessBaseBug[]>()
+  //不良类型
+  @State defectTypeOptions: Array<SelectOption> = []
+  //缺陷类型
+  @State defectBugOptions: Array<SelectOption> = []
+  // 如果是修改需要从自检页面传过来
+  @State defectRecord: ProcessDefectRecord = {}
+  // 不良类型记录列表
+  @Link defectRecords: ProcessDefectRecord[]
+  // 备注
+  @State defectRemark: string = ''
+
+  @Consume('currentUserName') currentUserName: string
+  @Consume('currentStationId') currentStationId: string
+  seqNo: string = ''
+  selectOperationId: string = ''
+  process: ProcessInfo = {}
+
+  // 刷新不良类型对应的缺陷
+  async refreshDefectBugs() {
+    let defectTypeValue: string = this.defectTypes[this.selectDefectType]?.dictValue!
+    this.defectBugOptions = []
+    if (this.defectBugMap.has(defectTypeValue)) {
+      this.defectBugs = this.defectBugMap.get(defectTypeValue)!
+    } else {
+      this.defectBugs = await ProcessRequest.post('/api/v1/op/baseBug/list', {
+        bugType: defectTypeValue
+      } as RequestParamModel)
+      this.defectBugMap.set(defectTypeValue, this.defectBugs)
+    }
+    // 遍历封装缺陷下拉选择框
+    for (let i = 0; i < this.defectBugs.length; i++) {
+      this.defectBugOptions.push({value: this.defectBugs[i].bugName!})
+    }
+    this.selectedDefectBug = 0;
+  }
+  // 刷新不良类型记录列表
+  async refreshDefectRecords() {
+    this.defectRecords = []
+    this.defectRecords = await ProcessRequest.post('/api/v1/process/defectRecord/list', {
+      seqNo: this.seqNo
+    } as RequestParamModel)
+    if (this.defectRecords && this.defectTypes) {
+      for (const element of this.defectRecords) {
+        for (const dict of this.defectTypes) {
+          if (element.defectType === dict.dictValue) {
+            element.defectTypeLabel = dict.dictLabel
+            break
+          }
+        }
+      }
+    }
+  }
+
+  async aboutToAppear() {
+    for (let i = 0; i < this.defectTypes.length; i++) {
+      this.defectTypeOptions.push({value: this.defectTypes[i].dictLabel!})
+    }
+    // 新增时,默认选中第一个不良类型及其第一种缺陷
+    if (!this.isEdit && this.defectTypes) {
+      this.defectRecord = {}
+      this.selectDefectType = 0
+      this.defectBugs = await ProcessRequest.post('/api/v1/op/baseBug/list', {
+        bugType: this.defectTypes[0].dictValue!
+      } as RequestParamModel)
+      this.defectBugMap.set(this.defectTypes[0].dictValue!, this.defectBugs)
+      for (let i = 0; i < this.defectBugs.length; i++) {
+        this.defectBugOptions.push({value: this.defectBugs[i].bugName!})
+      }
+      this.selectedDefectBug = 0;
+    } else if (this.isEdit && this.defectRecord && this.defectRecord.defectType) {
+      this.defectRemark = this.defectRecord.remark ? this.defectRecord.remark : ''
+      // 修改时,不良类型、缺陷名称直接选中
+      let defectTypeValue: string = this.defectRecord.defectType!
+      // 不良类型选中
+      for (let i = 0; i < this.defectTypes.length; i++) {
+        if (defectTypeValue === this.defectTypes[i].dictValue!) {
+          this.selectDefectType = i;
+          break
+        }
+      }
+      // 查询不良类型下所有的缺陷
+      let defectBugCode: string = this.defectRecord.bugCode!
+      this.defectBugs = await ProcessRequest.post('/api/v1/op/baseBug/list', {
+        bugType: defectTypeValue
+      } as RequestParamModel)
+      this.defectBugMap.set(defectTypeValue, this.defectBugs)
+      // 缺陷名称选中
+      for (let i = 0; i < this.defectBugs.length; i++) {
+        this.defectBugOptions.push({value: this.defectBugs[i].bugName!})
+        if (defectBugCode === this.defectBugs[i].bugCode) {
+          this.selectedDefectBug = i;
+        }
+      }
+    }
+  }
+
+  build() {
+    Column(){
+      Column() {
+        Text(this.isEdit ? '不良类型记录' : '添加不良类型记录')
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('12%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+      Column(){
+        Text('不良类型')
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+        Select(this.defectTypeOptions)
+          .height('16%')
+          .width('100%')
+          .borderRadius($r('app.float.virtualSize_16'))
+          .selected(this.selectDefectType === -1 ? 0 : this.selectDefectType)
+          .value(this.selectDefectType === -1 ? '请选择' : String(this.defectTypeOptions[this.selectDefectType].value))
+          .backgroundColor($r('app.color.20FFFFFF'))
+          .selectedOptionBgColor($r('app.color.20FFFFFF'))
+          .fontColor(this.selectDefectType === -1 ? $r('app.color.30FFFFFF'):$r('app.color.FFFFFF'))
+          .font({size:$r('app.float.fontSize_24')})
+          .selectedOptionFont({
+            size:$r('app.float.fontSize_24')
+          })
+          .onSelect((index: number) => {
+            if (this.selectDefectType === index) {
+              return
+            }
+            this.selectDefectType = index
+            this.refreshDefectBugs()
+          })
+          .margin({top:'3%',bottom:'3%'})
+        Text('缺陷名称')
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+        Select(this.defectBugOptions)
+          .height('16%')
+          .width('100%')
+          .borderRadius($r('app.float.virtualSize_16'))
+          .selected(this.selectedDefectBug === -1 ? 0 : this.selectedDefectBug)
+          .value(this.selectedDefectBug === -1 ? '请选择' : String(this.defectBugOptions[this.selectedDefectBug]?.value ? this.defectBugOptions[this.selectedDefectBug]?.value : ''))
+          .backgroundColor($r('app.color.20FFFFFF'))
+          .selectedOptionBgColor($r('app.color.20FFFFFF'))
+          .fontColor(this.selectedDefectBug === -1 ? $r('app.color.30FFFFFF'):$r('app.color.FFFFFF'))
+          .font({size:$r('app.float.fontSize_24')})
+          .selectedOptionFont({
+            size:$r('app.float.fontSize_24')
+          })
+          .onSelect((index: number) => {
+            this.selectedDefectBug = index
+          })
+          .margin({top:'3%',bottom:'3%'})
+        Text('备注')
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+        TextArea({placeholder:'请录入不良详情', text: this.defectRemark})
+          .backgroundColor($r('app.color.000000'))
+          .height('40%')
+          .width('100%')
+          .fontColor($r('app.color.FFFFFF'))
+          .placeholderColor($r('app.color.30FFFFFF'))
+          .placeholderFont({size:$r('app.float.fontSize_16')})
+          .fontSize($r('app.float.fontSize_16'))
+          .textAlign(TextAlign.Start)
+          .maxLength(200)
+          .margin({top:'3%',bottom:'3%'})
+          .borderRadius($r('app.float.virtualSize_16'))
+          .onChange((value: string) => {
+            this.defectRemark = value
+          })
+      }
+      .width('70%')
+      .height('80%')
+      .margin({left:'15%',right:'15%'})
+      .alignItems(HorizontalAlign.Start)
+      if(this.isEdit){
+        Column() {
+          Divider()
+            .vertical(false)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Row() {
+              Text('删除')
+                .fontColor($r('app.color.FF453A'))
+                .fontSize($r('app.float.fontSize_30'))
+            }
+            .justifyContent(FlexAlign.Center)
+            .width('33.3%')
+            .onClick(async () => {
+              await ProcessRequest.post('/api/v1/process/defectRecord/del', {
+                id: this.defectRecord.id!
+              } as RequestParamModel)
+              // 重新刷新不良类型记录列表
+              this.refreshDefectRecords()
+              if (this.controller) {
+                this.controller.close()
+              }
+            })
+            Divider()
+              .vertical(true)
+              .strokeWidth(1)
+              .color($r('app.color.15FFFFFF'))
+            Row() {
+              Text('取消')
+                .fontColor($r('app.color.60FFFFFF'))
+                .fontSize($r('app.float.fontSize_30'))
+            }
+            .justifyContent(FlexAlign.Center)
+            .width('33.3%')
+            .onClick(() => this.controller.close())
+            Divider()
+              .vertical(true)
+              .strokeWidth(1)
+              .color($r('app.color.15FFFFFF'))
+            Row() {
+              Text('确定')
+                .fontColor($r('app.color.007AFF'))
+                .fontSize($r('app.float.fontSize_30'))
+            }
+            .justifyContent(FlexAlign.Center)
+            .width('33.3%')
+            .onClick(async () => {
+              this.defectRecord.remark = this.defectRemark
+              if (this.defectRecord) {
+                this.defectRecord.defectType = this.defectBugs[this.selectedDefectBug].bugType!
+                this.defectRecord.bugCode = this.defectBugs[this.selectedDefectBug].bugCode!
+                this.defectRecord.bugName = this.defectBugs[this.selectedDefectBug].bugName!
+              }
+              await ProcessRequest.post('/api/v1/process/defectRecord/update', this.defectRecord)
+              // 重新刷新不良类型记录列表
+              this.refreshDefectRecords()
+              if (this.controller) {
+                this.controller.close()
+              }
+            })
+          }
+        }
+        .width('100%')
+        .height('8%')
+      }else{
+        Column() {
+          Divider()
+            .vertical(false)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Row() {
+              Text('取消')
+                .fontColor($r('app.color.60FFFFFF'))
+                .fontSize($r('app.float.fontSize_30'))
+            }
+            .justifyContent(FlexAlign.Center)
+            .width('50%')
+            .onClick(() => this.controller.close())
+            Divider()
+              .vertical(true)
+              .strokeWidth(1)
+              .color($r('app.color.15FFFFFF'))
+            Row() {
+              Text('确定')
+                .fontColor($r('app.color.007AFF'))
+                .fontSize($r('app.float.fontSize_30'))
+            }
+            .justifyContent(FlexAlign.Center)
+            .width('50%')
+            .onClick(async () => {
+              this.defectRecord.processId = this.process.id!
+              this.defectRecord.seqNo = this.seqNo
+              this.defectRecord.operationId = this.selectOperationId
+              this.defectRecord.remark = this.defectRemark
+              this.defectRecord.defectType = this.defectBugs[this.selectedDefectBug].bugType!
+              this.defectRecord.bugCode = this.defectBugs[this.selectedDefectBug].bugCode!
+              this.defectRecord.bugName = this.defectBugs[this.selectedDefectBug].bugName!
+              await ProcessRequest.post('/api/v1/process/defectRecord/add', this.defectRecord)
+              // 重新刷新不良类型记录列表
+              this.refreshDefectRecords()
+              if (this.controller) {
+                this.controller.close()
+              }
+            })
+          }
+        }
+        .width('100%')
+        .height('8%')
+      }
+    }
+    .height('71%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 274 - 0
entry/src/main/ets/view/DeviceInspectionDialog.ets

@@ -0,0 +1,274 @@
+//设备点检
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import ProcessDeviceDailyCheck from '../viewmodel/process/ProcessDeviceDailyCheck'
+import DictValue from '../viewmodel/DictValue'
+import promptAction from '@ohos.promptAction'
+import HashMap from '@ohos.util.HashMap'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import TimeUtil from '../common/util/TimeUtil'
+
+
+@CustomDialog
+export struct DeviceInspectionDialog {
+  private scrollerDevice: Scroller = new Scroller()
+  //查找设备编码
+  @State queryDeviceNo: string = 'test000'
+  //当前日期
+  @State currentDate: string = ''
+  // 设备点检列表
+  @Link deviceChecks: ProcessDeviceDailyCheck[]
+  //选中设备索引
+  @State selectDeviceIndex: number=-1
+  // 设备类型(key为数据字典值,value为数据字典标签)
+  @State deviceTypes: HashMap<string, string> = new HashMap()
+  deleteIds: string[] = []
+  @Consume('stationIp') stationIp: string
+  controller: CustomDialogController
+  onConfirm: () => void = () => {}
+
+  deviceTypeDictCode: string = 'device_type'
+
+  async aboutToAppear() {
+    this.deleteIds = []
+    let deviceDicts: DictValue[] = await ProcessRequest.get(`/api/v1/sys/dictData/queryByType/${this.deviceTypeDictCode}`)
+    if (deviceDicts) {
+      for (const dict of deviceDicts) {
+        this.deviceTypes.set(dict.dictValue!, dict.dictLabel!);
+      }
+    }
+    this.deviceChecks = await ProcessRequest.post('/api/v1/process/deviceDailyCheck/list', {
+      stationIp: this.stationIp,
+      createDate: TimeUtil.getCurrentDate()
+    } as RequestParamModel)
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("设备点检")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column() {
+        Text("扫描设备 ")
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_24'))
+        Row(){
+          Row() {
+            // 左侧二维码图标
+            Image($r('app.media.material_qr_code'))
+              .width($r('app.float.virtualSize_32'))
+              .height($r('app.float.virtualSize_32'))
+              .fillColor($r('app.color.FFFFFF'))
+              .objectFit(ImageFit.Contain)
+              .margin({left:'2%'})
+            // 扫码输入框
+            TextInput({text:this.queryDeviceNo, placeholder: '请扫描设备编码' })
+              .type(InputType.Normal)
+              .placeholderFont({ size: $r('app.float.fontSize_16') })
+              .placeholderColor($r('app.color.30FFFFFF'))
+              .fontSize($r('app.float.fontSize_16'))
+              .fontColor($r('app.color.FFFFFF'))
+              .enableKeyboardOnFocus(false)
+              .onSubmit(async () => {
+                if (this.deviceChecks) {
+                  for (const element of this.deviceChecks) {
+                    if (element.deviceNo === this.queryDeviceNo) {
+                      promptAction.showToast({
+                        message: `设备今日已点检,无需重复点检!`,
+                        duration: 1500,
+                        bottom: 100
+                      })
+                      return
+                    }
+                  }
+                }
+                let check: ProcessDeviceDailyCheck = await ProcessRequest.get(`/api/v1/process/deviceDailyCheck/getServiceLifeByDeviceNo/${this.queryDeviceNo}`)
+                this.deviceChecks.unshift(check)
+              })
+              .onChange((value: string) => {
+                this.queryDeviceNo = value;
+                //this.onQueryDeviceCode()
+              })
+          }
+          .height('100%')
+          .width('65%')
+          .borderRadius($r('app.float.virtualSize_16'))
+          .backgroundColor($r('app.color.000000'))
+          Text(this.currentDate)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+            .margin({left:'3%'})
+        }
+        .height('8%')
+        .width('50%')
+        .margin({bottom:'1%',top:'1%'})
+        List({scroller: this.scrollerDevice }) {
+            ForEach(this.deviceChecks, (item: ProcessDeviceDailyCheck, index) => {
+              ListItem() {
+                Row() {
+                  Column(){
+                    Text(this.deviceTypes && item.deviceType && this.deviceTypes.hasKey(item.deviceType) ? this.deviceTypes.get(item.deviceType) : '')
+                      .fontSize($r('app.float.fontSize_24'))
+                      .fontColor($r('app.color.FFFFFF'))
+                    Text(`名称:${item.deviceName!}`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                      .margin({top:'2%',bottom:'1%'})
+                    Text(`编码:${item.deviceNo!}`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                  }
+                  .width('30%')
+                  .height('100%')
+                  .justifyContent(FlexAlign.Center)
+                  .alignItems(HorizontalAlign.Start)
+                  .margin({right:'16%'})
+                  Column({space:5}){
+                    Text(`计量有效期:`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                    Row(){
+                      Image(item.meteringState! === 1 ? $r('app.media.device_normal') : $r('app.media.device_expire'))
+                        .width($r('app.float.virtualSize_24'))
+                        .height($r('app.float.virtualSize_24'))
+                        .margin({left:'2%'})
+                      Text(item.meteringDate ? `${item.meteringDate}` : '长期有效')
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontColor(item.meteringState! === 1 ? $r('app.color.30D158') : $r('app.color.FF453A'))
+                        .margin({left:'2%'})
+                    }
+                    .width('65%')
+                    .borderRadius($r('app.float.virtualSize_16'))
+                    .backgroundColor($r('app.color.000000'))
+                    .height('25%')
+                    .alignItems(VerticalAlign.Center)
+                  }
+                  .width('22%')
+                  .height('100%')
+                  .justifyContent(FlexAlign.Center)
+                  .alignItems(HorizontalAlign.Start)
+                  Column({space:5}){
+                    Text(`维保有效期:`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                    Row(){
+                      Image(item.warrantyState! === 1 ? $r('app.media.device_normal') :$r('app.media.device_expire'))
+                        .width($r('app.float.virtualSize_24'))
+                        .height($r('app.float.virtualSize_24'))
+                        .margin({left:'2%'})
+                      Text(item.warrantyPeriod ? `${item.warrantyPeriod}` : '长期有效')
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontColor(item.warrantyState! === 1 ? $r('app.color.30D158') : $r('app.color.FF453A'))
+                        .margin({left:'2%'})
+                    }
+                    .width('65%')
+                    .borderRadius($r('app.float.virtualSize_16'))
+                    .backgroundColor($r('app.color.000000'))
+                    .height('25%')
+                    .alignItems(VerticalAlign.Center)
+                  }
+                  .width('22%')
+                  .height('100%')
+                  .justifyContent(FlexAlign.Center)
+                  .alignItems(HorizontalAlign.Start)
+                  Column(){
+                   Image($r('app.media.process_delete_seq'))
+                     .width($r('app.float.virtualSize_48'))
+                     .height($r('app.float.virtualSize_48'))
+                     .fillColor($r('app.color.FF453A'))
+                     .onClick(()=>{
+                       let deleteCheck: ProcessDeviceDailyCheck = this.deviceChecks.splice(index, 1)[0];
+                       if (deleteCheck.id) {
+                         this.deleteIds.push(deleteCheck.id)
+                       }
+                     })
+                  }
+                  .width('10%')
+                  .height('100%')
+                  .justifyContent(FlexAlign.Center)
+                }
+                .height('90%')
+                .width('95%')
+                .justifyContent(FlexAlign.Start)
+              }
+              .height('18%')
+              .width('100%')
+              .margin({ bottom: 8})
+              .borderRadius($r('app.float.virtualSize_16'))
+              .backgroundColor($r('app.color.10FFFFFF'))
+            })
+          }.height('82%')
+        }
+        .width('96%')
+        .height('84%')
+        .alignItems(HorizontalAlign.Start)
+        .justifyContent(FlexAlign.Start)
+        .margin({left:'2%',right:'2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(async () => {
+            if (this.deleteIds && this.deleteIds.length > 0) {
+              await ProcessRequest.post('/api/v1/process/deviceDailyCheck/batchDel', {
+                ids: this.deleteIds
+              } as RequestParamModel)
+              this.deleteIds = []
+            }
+            if (!this.deviceChecks || this.deviceChecks.length <= 0) {
+              this.controller.close();
+              return
+            }
+            for (const check of this.deviceChecks) {
+              check.stationIp = this.stationIp
+            }
+            await ProcessRequest.post('/api/v1/process/deviceDailyCheck/batchSave', this.deviceChecks)
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 341 - 0
entry/src/main/ets/view/InAndOutBoundDialog.ets

@@ -0,0 +1,341 @@
+/*
+ * 入库台料口操作
+ * */
+import MqttManager from '../common/util/mqtt';
+import {MQTTPublishData} from '../common/util/mqtt';
+import promptAction from '@ohos.promptAction';
+import { ConfirmDialog } from './ConfirmDialog';
+import { ConfirmDialogParams } from '../viewmodel/ConfirmDialogParam';
+import ProcessRequest from '../common/util/request/ProcessRequest';
+import RequestParamModel from '../viewmodel/RequestParamModel';
+import MaterialInfo from '../viewmodel/MaterialInfo';
+
+@CustomDialog
+export struct InAndOutBoundDialog {
+  @State materialNum: number=0
+  @State scanMaterialList: MaterialInfo[] = []
+  controller: CustomDialogController
+  onConfirm: () => void = () => {}
+
+  build() {
+    Column() {
+      InBoundView({
+        materialNum:this.materialNum,
+        boxMaterials:this.scanMaterialList
+      }).height('93%')
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Text('关闭')
+            .fontColor($r('app.color.60FFFFFF'))
+            .fontSize($r('app.float.fontSize_30'))
+        }
+        .justifyContent(FlexAlign.Center)
+        .alignItems(VerticalAlign.Center)
+        .width('100%')
+        .height('100%')
+        .onClick(() => this.controller.close())
+      }
+      .width('100%')
+      .height('7%')
+    }
+    .height('80%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .borderRadius(16)
+  }
+}
+
+
+@Component
+export struct InBoundView {
+  //右下角抽屉日志
+  @Prop messages: string[] = []
+  //物料数量
+  @Link materialNum:number
+  //伸出按钮缩放
+  @State putOutButtonClick :number = 1
+  //缩回按钮缩放
+  @State putInButtonClick :number = 1
+  //入库按钮缩放
+  @State InboundButtonClick :number = 1
+  //入库料箱物料表
+  @Link private boxMaterials: Array<MaterialInfo>
+  //弹窗提示内容
+  @State reminds:string = ''
+  //全局变量 料箱重量
+  @StorageLink('materialBoxWeight') materialBoxWeight: number =0;
+  //全局变量 抽屉状态
+  @StorageLink('drawerPositionStatus') drawerPositionStatus: number=1 ;
+  //全局变量 抽屉内料箱ID
+  @StorageLink('materialBoxInRfid') materialBoxInRfid: string = '';
+
+  commonDialogController: CustomDialogController | null = null;
+  private showConfirmDialog(params: ConfirmDialogParams) {
+    if (this.commonDialogController) {
+      this.commonDialogController.close()
+    }
+
+    this.commonDialogController = new CustomDialogController({
+      builder: ConfirmDialog({
+        title: params.title || '提示',
+        message: params.message,
+        onConfirm: params.onConfirm
+      }),
+      cancel: () => console.log('用户取消操作'),
+      customStyle: true,
+      autoCancel:false,
+      maskColor: 'rgba(0,0,0,0.6)'
+    });
+
+    this.commonDialogController.open();
+  }
+  //入库命令
+  Warehousing = async () => {
+    let res: MaterialInfo[] = await ProcessRequest.post("/api/v1/wmsOrder/inBox", {
+       houseNo:'1',
+      stanCode:'GW0001',
+      vehicleCode:this.materialBoxInRfid,
+      detailsList: this.boxMaterials
+    } as RequestParamModel)
+    console.log('testTag', 'aaaaaaaa1' + JSON.stringify(res));
+    this.boxMaterials = []
+    this.materialNum = 0 ;
+    promptAction.showToast({
+      message: '请求成功,业务处理中。。。',
+      duration: 2000,
+      bottom: 150
+    });
+
+  }
+
+  //抽屉推出
+  pushOut =()=>{
+    const drawerCommand: MQTTPublishData = {
+      w: [
+        {
+          tag: "Station1Set",
+          value: 0
+        }
+      ]
+    };
+    MqttManager.publish('station100/cmd/devices', drawerCommand, 1, true);
+  }
+
+  //抽屉伸入
+  pushIn =()=>{
+    const drawerCommand: MQTTPublishData = {
+      w: [
+        {
+          tag: "Station1Set",
+          value: 1
+        }
+      ]
+    };
+    MqttManager.publish('station100/cmd/devices', drawerCommand, 1, true);
+  }
+
+  build() {
+    Column() {
+      Text('出入库料口操作')
+        .fontColor($r('app.color.FFFFFF'))
+        .fontSize($r('app.float.fontSize_38'))
+        .margin({ top: '3%' })
+      Column() {
+        Stack() {
+          Image(
+            this.drawerPositionStatus === 1
+              ? (this.materialBoxWeight >0 ? $r('app.media.drawer_in_box') : $r('app.media.drawer_in_no_box'))
+              : (this.materialBoxWeight >0 ? $r('app.media.drawer_out_box') : $r('app.media.drawer_out_no_box'))
+          )// 替换为您的电梯图片资源
+            .width('100%')
+            .height('100%')
+            .borderRadius($r('app.float.virtualSize_16'))
+            .objectFit(ImageFit.Fill)
+          // 文字信息层(左下角)
+          Column() {
+            Row() {
+              Image($r('app.media.material_weight'))
+                .width('5%')
+                .height('30%')
+                .fillColor($r('app.color.30D158'))
+              Text(String(this.materialBoxWeight)+"kg")
+                .margin({ left: 10 })
+                .fontSize($r('app.float.fontSize_38'))
+                .fontColor($r('app.color.30D158')) // 绿色文字
+            }
+
+            Row() {
+              Image($r('app.media.material_rfid'))
+                .width('5%')
+                .height('30%')
+                .fillColor($r('app.color.30D158'))
+
+              Text(this.materialBoxInRfid)
+                .margin({ left: 10 })
+                .fontSize($r('app.float.fontSize_38'))
+                .fontColor($r('app.color.30D158')) // 绿色文字
+            }
+          }.height('19%').alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.SpaceEvenly)
+          .position({ x: '3%', y: '78%' })
+
+          // 右上角物料信息
+          Row() {
+            Text(`物料:${this.materialNum}`)
+              .fontSize(18)
+              .fontColor($r('app.color.0A84FF'))// 使用图片中的蓝色
+              .fontWeight(FontWeight.Medium)
+          }
+          .justifyContent(FlexAlign.Center)
+          .backdropBlur(5) // 关键:背景模糊效果(模拟图片中的隐约线条)
+          .backgroundColor($r('app.color.40000000')) // 10%透明白底
+          .borderRadius($r('app.float.virtualSize_16')) // 圆角大小根据图片调整
+          .width('20%') // 自适应文字宽度
+          .height('10%') // 固定高度
+          .position({ x: '78%', y: '2%' })
+
+          // 右侧纵向时间信息
+          Column({ space: 5 }) {
+            ForEach(this.messages, (item: string) => {
+              Text(item)// .fontColor($r('app.color.0A84FF'))
+                // .fontSize($r('app.float.fontSize_16'))
+                .fontSize($r('app.float.fontSize_12'))
+                .fontColor($r('app.color.0A84FF')) // 图片中的蓝色
+            })
+          }
+          .position({ x: '68%', y: '80%' })
+          .alignItems(HorizontalAlign.End)
+        }
+        .width('95%')
+        .height('100%')
+      }
+      .height('67%')
+      .width('100%')
+      .margin({ top: '3%' })
+
+      //.justifyContent(FlexAlign.SpaceAround)
+      Row() {
+        Button({ type: ButtonType.Normal }) {
+          Text("伸出")
+            .fontSize($r('app.float.fontSize_30'))
+            .fontColor($r('app.color.0A84FF'))
+        }
+        .width('48%')
+        .height('100%')
+        .backgroundColor($r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.virtualSize_16'))
+        .scale({ x: this.putOutButtonClick, y: this.putOutButtonClick })
+        .animation({
+          duration: 200,
+          curve: Curve.Linear
+        })
+        .onClick(() => {
+          this.putOutButtonClick = 0.9; // 点击时缩小
+          setTimeout(() => {
+            this.putOutButtonClick = 1; // 0.2秒后恢复
+            this.showConfirmDialog({
+              title: '抽屉伸出',
+              message: `确定要抽屉伸出吗?`,
+              onConfirm: ()=> {
+                this.pushOut();
+              }
+            });
+          }, 200);
+        })
+
+        Button({ type: ButtonType.Normal }) {
+          Text("缩回")
+            .fontSize($r('app.float.fontSize_30'))
+            .fontColor($r('app.color.0A84FF'))
+        }
+        .width('48%')
+        .margin({left:'4%'})
+        .height('100%')
+        .backgroundColor($r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.virtualSize_16'))
+        .scale({ x: this.putInButtonClick, y: this.putInButtonClick })
+        .animation({
+          duration: 200,
+          curve: Curve.Linear
+        })
+        .onClick(() => {
+          this.putInButtonClick = 0.9; // 点击时缩小
+          setTimeout(() => {
+            this.putInButtonClick = 1; // 0.2秒后恢复
+            this.showConfirmDialog({
+              title: '抽屉缩回',
+              message: `确定要抽屉缩回吗?`,
+              onConfirm: () => {
+                this.pushIn();
+              }
+            })
+          }, 200);
+        })
+      }
+      .width('95%')
+      .height('6%')
+      .margin({ top: '2%' })
+      .justifyContent(FlexAlign.Start)
+      .alignItems(VerticalAlign.Top)
+
+      Button({ type: ButtonType.Normal }) {
+        Text("入库")
+          .fontSize($r('app.float.fontSize_30'))
+          .fontColor($r('app.color.0A84FF'))
+      }
+      .margin({ top: '9%' })
+      .width('95%')
+      .height('6%')
+      .backgroundColor($r('app.color.20FFFFFF'))
+      .borderRadius($r('app.float.virtualSize_16'))
+      .scale({ x: this.InboundButtonClick, y: this.InboundButtonClick })
+      .animation({
+        duration: 200,
+        curve: Curve.Linear
+      })
+      .onClick(() => {
+        this.InboundButtonClick = 0.9; // 点击时缩小
+        setTimeout(() => {
+          this.InboundButtonClick = 1; // 0.2秒后恢复
+        }, 200);
+        if(this.drawerPositionStatus == 0)
+        {
+          promptAction.showToast({
+            message: '抽屉未缩回',
+            duration: 2000
+          });
+          return
+        }
+
+        if(this.materialBoxWeight == 0)
+        {
+          promptAction.showToast({
+            message: '未放入料箱',
+            duration: 2000
+          });
+          return
+        }
+
+        this.showConfirmDialog({
+          title: '入库确认',
+          message: `确定要${this.materialBoxInRfid}料箱入库吗?`,
+          onConfirm: () => {
+            this.Warehousing();
+          }
+        })
+      })
+    }
+    .width('100%')
+    .height('100%')
+    .justifyContent(FlexAlign.Start)
+  }
+}
+
+
+
+
+

+ 215 - 0
entry/src/main/ets/view/JoinPersonNameDialog.ets

@@ -0,0 +1,215 @@
+//参与人员
+import { pinyin } from "pinyin-pro";
+import ProcessRequest from '../common/util/request/ProcessRequest';
+import { BindTaskSeq } from '../viewmodel/process/BindTaskSeq';
+import ReportInfo from '../viewmodel/process/ReporterInfo';
+import RequestParamModel from '../viewmodel/RequestParamModel';
+import { UserInfo } from '../viewmodel/UserInfo';
+import promptAction from '@ohos.promptAction';
+
+@CustomDialog
+export struct JoinPersonNameDialog{
+  controller: CustomDialogController
+
+  scroller: Scroller = new Scroller()
+  @Link currentReporterIndex: number
+  @State nameList:UserInfo[]=[]
+  @State selectNameIndex:number=-1
+  @State selectName:string = ''
+  @State queryName:string = ''
+  @Link reporterList: ReportInfo[]
+  @Consume ('bindTaskSeq') bindTaskSeq: BindTaskSeq[]
+
+  private value: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G',
+    'H', 'I', 'J', 'K', 'L', 'M', 'N',
+    'O', 'P', 'Q', 'R', 'S', 'T', 'U',
+    'V', 'W', 'X', 'Y', 'Z','#']
+
+  onSelectIndexItem(index:string) {
+    for (let i = 0; i < this.nameList.length; i++) {
+      console.log("名字:" + this.nameList[i].userName + " index1:" + this.nameList[i].nameInitial + " index2:" + index)
+      if (this.nameList[i].nameInitial == index) {
+        this.scroller.scrollToIndex(i) //滚动到索引位置
+        break;
+      }
+    }
+  }
+  private onSelectName(index: number) {
+    this.selectNameIndex = index
+    this.selectName = this.nameList[index].userName ?? ""
+    const nameExists = this.reporterList.some(item =>
+    item.userName && this.selectName &&
+      item.userName.toString().toLowerCase() === this.selectName.toString().toLowerCase().trim()
+    );
+    if (nameExists) {
+      promptAction.showToast({
+        message: '该用户已添加,请添加其他用户!',
+        duration: 2000
+      });
+      return;
+    }
+    if (this.reporterList[this.currentReporterIndex]) {
+      this.reporterList[this.currentReporterIndex].userName = this.selectName;
+      this.reporterList[this.currentReporterIndex].userId = this.nameList[index].id! as string;
+      this.reporterList = [...this.reporterList];
+    }
+    if (this.bindTaskSeq[this.currentReporterIndex]) {
+      this.bindTaskSeq[this.currentReporterIndex].userName = this.selectName;
+      this.bindTaskSeq[this.currentReporterIndex].userId = this.nameList[index].id! as string;
+    }
+
+  }
+
+  loadPersonNameList = async ()=>{
+    this.nameList = await ProcessRequest.post('/api/v1/sys/user/list', {
+      userName:this.queryName
+    } as RequestParamModel)
+    for(let i = 0; i < this.nameList.length; i++) {
+      let pinyinStr = pinyin(this.nameList[i].userName, { toneType: "none" });
+      let index = pinyinStr.substring(0, 1).toUpperCase();
+      if (!(new RegExp("^[A-Z]$").test(index))) {
+        index = "#";
+      }
+      this.nameList[i].nameInitial = index
+    }
+    this.nameList.sort((a: UserInfo, b: UserInfo): number => {
+      const aInitial = a.nameInitial || "#";
+      const bInitial = b.nameInitial || "#";
+      const aName = a.userName || a.userName || "";
+      const bName = b.userName || b.userName || "";
+      if (aInitial === bInitial) {
+        return aName.localeCompare(bName);
+      }
+      if (aInitial === "#") {
+        return 1;
+      }
+      if (bInitial === "#") {
+        return -1;
+      }
+      return aInitial.localeCompare(bInitial);
+    });
+  }
+
+  aboutToAppear(): void {
+    this.loadPersonNameList()
+  }
+
+  build() {
+    Column(){
+      Column() {
+        Text("参与人员")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+      Row() {
+        TextInput({  placeholder: '录入人员姓名' })
+          .type(InputType.Normal)
+          .placeholderFont({ size: $r('app.float.fontSize_16') })
+          .placeholderColor($r('app.color.30FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+          .enableKeyboardOnFocus(false)
+          .width('84%')
+          .onChange((value: string) => {
+            this.queryName = value
+            this.loadPersonNameList()
+          })
+        Row() {
+          Image($r('app.media.process_search'))
+            .width($r('app.float.virtualSize_48'))
+            .height($r('app.float.virtualSize_48'))
+            .fillColor($r('app.color.0A84FF'))
+            .onClick(() => {
+              this.loadPersonNameList()
+            })
+        }
+        .width('16%')
+        .height('100%')
+        .justifyContent(FlexAlign.Center)
+        .borderRadius($r('app.float.virtualSize_16'))
+        .backgroundColor($r('app.color.20FFFFFF'))
+      }
+      .borderRadius($r('app.float.virtualSize_16'))
+      .backgroundColor($r('app.color.000000'))
+      .height('7%')
+      .width('60%')
+      .margin({bottom:'2%',left:'20%',right:'20%'})
+      Row() {
+        List({ space: 0, initialIndex: 0,scroller:this.scroller}){
+          ForEach(this.nameList, (item:UserInfo,index) => {
+            ListItem() {
+              Column(){
+                if (index===0||this.nameList[index-1].nameInitial != item.nameInitial){
+                  Row(){
+                    Text(item.nameInitial)
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontSize($r('app.float.fontSize_60'))
+                  }
+                  .width($r('app.float.virtualSize_80'))
+                  .height($r('app.float.virtualSize_80'))
+                  .backgroundColor($r('app.color.0A84FF'))
+                  .justifyContent(FlexAlign.Center)
+                  .alignItems(VerticalAlign.Center)
+                  .margin({left:'2%'})
+                }
+                Row(){
+                  Text(item.userName)
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .margin({left:'2%'})
+                  Text(item.deptNames)
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontWeight(FontWeight.Lighter)
+                    .margin({left:'2%'})
+                }
+                .justifyContent(FlexAlign.Start)
+                .alignItems(VerticalAlign.Center)
+                .padding(20)
+                .width('100%')
+                .backgroundColor(this.selectNameIndex===index?$r('app.color.200A84FF'):'')
+                .onClick(()=>{
+                  this.onSelectName(index)
+                  this.controller.close();
+                })
+              }
+              .alignItems(HorizontalAlign.Start)
+            }
+          })
+        }
+        .layoutWeight(1)
+        .scrollBar(BarState.Off)
+        .height('100%')
+        .width('90%')
+
+        AlphabetIndexer({ arrayValue: this.value, selected: 0})
+          .color($r('app.color.FFFFFF'))
+          .selectedColor($r('app.color.FFFFFF'))
+          .selectedBackgroundColor($r('app.color.20FFFFFF'))
+          .usingPopup(false)
+          .selectedFont({ size: 16, weight: FontWeight.Bolder })
+          .itemSize(25.2)
+          .alignStyle(IndexerAlign.Right)
+          .onSelect((index: number) => {
+            this.onSelectIndexItem(this.value[index]);
+          })
+
+      }
+      .height('83%')
+      .width('100%')
+    }
+    .height('100%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+

+ 444 - 0
entry/src/main/ets/view/LittleMaterialRequestDialog.ets

@@ -0,0 +1,444 @@
+//零星叫料
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import MaterialInfo from '../viewmodel/MaterialInfo'
+import PageInfo from '../viewmodel/PageInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import promptAction from '@ohos.promptAction'
+import ProcessCallMaterial from '../viewmodel/process/ProcessCallMaterial'
+import ProcessInfo from '../viewmodel/process/ProcessInfo'
+import WorkOrderInfo from '../viewmodel/WorkOrderInfo'
+
+@CustomDialog
+export struct LittleMaterialRequestDialog {
+  private scrollerMaterial: Scroller = new Scroller()
+  //物料配件列表
+  @State materialTypesList: MaterialInfo[] = [];
+  //监控选中的索引
+  @State @Watch('onSelectedIndexesChange') selectedIndexes: number[] = [];
+  //选中物料
+  @State selectMaterials:MaterialInfo[]=[]
+  //查询物料名字
+  @State queryMaterialName: string = ''
+  //物料map(名称,数量)
+  @State materialNumMap: Record<string, number> = {};
+  // 当前工位id
+  @Consume('currentStationId') currentStationId: string
+
+  callMaterials: MaterialInfo[]=[]
+  // 扫码开工后的生产过程信息
+  process: ProcessInfo = {}
+  // 选中工单
+  selectWorkOder: WorkOrderInfo = {}
+
+  //实时更新选中物料
+  onSelectedIndexesChange() {
+    this.selectMaterials = this.selectedIndexes.map(index => this.materialTypesList[index]);
+  }
+  controller: CustomDialogController
+  onConfirm: () => void = () => {}
+
+  //加载物料配件类型
+  loadMaterialTypes = async () => {
+    let res = await ProcessRequest.post('/api/v1/base/material/page',
+      {
+        materialName: this.queryMaterialName
+      }  as RequestParamModel) as PageInfo;
+    this.materialTypesList = res.records as MaterialInfo[] || []
+    //初始所有物料数量为0
+    this.materialTypesList.forEach(item => {
+      this.materialNumMap[item.materialName??''] = 0;
+    });
+  };
+
+
+  private onDeleteMaterial(item: MaterialInfo) {
+    // 找到物料在已选列表中的索引
+    const selectedIndex = this.selectMaterials.findIndex(m => m.materialName === item.materialName);
+    if (selectedIndex !== -1) {
+      // 从已选列表中移除
+      this.selectMaterials.splice(selectedIndex, 1);
+      this.selectMaterials = [...this.selectMaterials]; // 触发状态更新
+      // 找到物料在总列表中的索引
+      const materialIndex = this.materialTypesList.findIndex(m => m.materialName === item.materialName);
+      if (materialIndex !== -1) {
+        // 从选中索引中移除
+        this.selectedIndexes = this.selectedIndexes.filter(i => i !== materialIndex);
+      }
+    }
+  }
+
+  private onSelectMaterial(index: number) {
+    if (this.selectedIndexes.includes(index)) {
+      this.selectedIndexes = this.selectedIndexes.filter(i => i !== index);
+    } else {
+      this.selectedIndexes = [index, ...this.selectedIndexes];
+    }
+  }
+  aboutToAppear(): void {
+    this.loadMaterialTypes()
+  }
+
+  async sendMaterialRequest() {
+    await ProcessRequest.post('/api/v1/process/vehicleOperation/sporadicCallItem', {
+      materials: this.callMaterials,
+      processId: this.process?.id!,
+      workOrderCode: this.selectWorkOder?.workOrderCode!,
+      stationId: this.currentStationId,
+    } as ProcessCallMaterial);
+    this.selectedIndexes = []
+    this.selectMaterials = []
+    this.callMaterials = []
+    this.materialNumMap = {}
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("零星叫料")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Row(){
+        Column() {
+          Text("查询物料")
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_24'))
+          Row() {
+            TextInput({ text: this.queryMaterialName, placeholder: '输入物料名称' })
+              .type(InputType.Normal)
+              .placeholderFont({ size: $r('app.float.fontSize_16') })
+              .placeholderColor($r('app.color.30FFFFFF'))
+              .fontSize($r('app.float.fontSize_16'))
+              .fontColor($r('app.color.FFFFFF'))
+              .enableKeyboardOnFocus(false)
+              .width('84%')
+              .onChange((value: string) => {
+                this.queryMaterialName = value
+                this.loadMaterialTypes()
+              })
+            Row() {
+              Image($r('app.media.process_search'))
+                .width($r('app.float.virtualSize_48'))
+                .height($r('app.float.virtualSize_48'))
+                .fillColor($r('app.color.0A84FF'))
+                .onClick(() => {
+                  this.loadMaterialTypes()
+                })
+            }
+            .width('16%')
+            .height('100%')
+            .justifyContent(FlexAlign.Center)
+            .borderRadius($r('app.float.virtualSize_16'))
+            .backgroundColor($r('app.color.20FFFFFF'))
+          }
+          .height('8%')
+          .width('60%')
+          .margin({bottom:'2%',top:'2%'})
+          .borderRadius($r('app.float.virtualSize_16'))
+          .backgroundColor($r('app.color.000000'))
+          List({ scroller: this.scrollerMaterial }) {
+            ForEach(this.materialTypesList, (item: MaterialInfo, index) => {
+              ListItem() {
+                Row() {
+                  Column({space:5}){
+                    Text(`${item.materialName}`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                    Text(`${item.spec}`)
+                      .fontSize($r('app.float.fontSize_12'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                  }
+                  .width('90%')
+                  .height('100%')
+                  .justifyContent(FlexAlign.Center)
+                  .alignItems(HorizontalAlign.Start)
+                  Column(){
+                    Text(`${item.unitDictValue}`)
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontWeight(FontWeight.Lighter)
+                  }
+                  .width('10%')
+                  .height('100%')
+                  .justifyContent(FlexAlign.Center)
+
+                }
+                .height('90%')
+                .width('95%')
+                .margin({ left: '2%' })
+                .justifyContent(FlexAlign.Center)
+              }
+              .height('10%')
+              .width('100%')
+              .margin({ bottom: 8})
+              .borderRadius($r('app.float.virtualSize_16'))
+              .backgroundColor(
+                this.selectedIndexes.includes(index) ?
+                $r('app.color.2030D158') :
+                $r('app.color.20FFFFFF')
+              )
+              .border({
+                width: this.selectedIndexes.includes(index) ? 2 : 0,
+                color: this.selectedIndexes.includes(index) ?
+                $r('app.color.2030D158') :
+                $r('app.color.20FFFFFF')
+              })
+              .onClick(() => {
+                this.onSelectMaterial(index)
+              })
+            })
+          }
+          .width('100%')
+          .height('82%')
+        }
+        .width('50%')
+        .alignItems(HorizontalAlign.Start)
+        .justifyContent(FlexAlign.Start)
+        .margin({left:'2%'})
+        .height('100%')
+        Image($r('app.media.arrow_right'))
+          .width($r('app.float.virtualSize_48'))
+          .height($r('app.float.virtualSize_48'))
+          .fillColor($r('app.color.FFFFFF'))
+          .margin({left:'2%',right:'2%'})
+        Column() {
+          Row(){
+           Text("已选物料")
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_24'))
+          }
+          .width('100%')
+          .height('6%')
+          .justifyContent(FlexAlign.Start)
+          List({ scroller: this.scrollerMaterial }) {
+            ForEach(this.selectMaterials, (item: MaterialInfo) => {
+              ListItem() {
+                Column(){
+                  Row(){
+                    Column({space:5}){
+                      Text(`${item.materialName}`)
+                        .fontSize($r('app.float.fontSize_24'))
+                        .fontColor($r('app.color.FFFFFF'))
+                      Text(`${item.spec}`)
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontWeight(FontWeight.Lighter)
+                    }
+                    .width('86%')
+                    .alignItems(HorizontalAlign.Start)
+                    Column(){
+                      Image($r('app.media.process_delete_seq'))
+                        .width($r('app.float.virtualSize_48'))
+                        .height($r('app.float.virtualSize_48'))
+                        .fillColor($r('app.color.FF453A'))
+                        .onClick(()=>{
+                          this.onDeleteMaterial(item)
+                        })
+                    }
+                    .width('14%')
+                    .alignItems(HorizontalAlign.End)
+                  }
+                  .height('38%')
+                  Text(`数量(${item.unitDictValue})`)
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .margin({bottom:'1%'})
+                    .width('100%')
+                    .textAlign(TextAlign.Start)
+                  Column(){
+                    AddAndSubButton({
+                      materialNum:this.materialNumMap[item.materialName??''],
+                      onChange: (num: number) => {
+                        this.materialNumMap[item.materialName??''] = num
+                      }
+                    })
+                  }
+                  .height('52%')
+                  .width('100%')
+                  .margin({bottom:'2%'})
+                }
+                .height('100%')
+                .width('100%')
+                .alignItems(HorizontalAlign.Start)
+              }
+              .height('30%')
+              .width('100%')
+              .margin({ bottom: 8})
+            })
+          }
+          .width('95%')
+          .height('92%')
+          .divider({
+            strokeWidth:1,
+            color:$r('app.color.15FFFFFF')
+          })
+        }
+        .width('40%')
+        .alignItems(HorizontalAlign.Start)
+        .justifyContent(FlexAlign.Start)
+        .height('100%')
+
+      }
+      .height('84%')
+      .width('100%')
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('发送叫料')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            if (!this.materialNumMap || this.materialNumMap.length <= 0) {
+              promptAction.showToast({
+                message: `请先选择物料!`,
+                duration: 1500,
+                bottom: 100
+              })
+              return
+            }
+            Object.keys(this.materialNumMap).forEach(key => {
+              if (this.materialNumMap[key] > 0) {
+                let material: MaterialInfo = {
+                  materialCode: key,
+                  num: this.materialNumMap[key]
+                }
+                this.callMaterials.push(material)
+              }
+            });
+            if (this.callMaterials.length <= 0) {
+              promptAction.showToast({
+                message: `请修改物料叫料数量!`,
+                duration: 1500,
+                bottom: 100
+              })
+              return
+            }
+            this.sendMaterialRequest()
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+@Component
+struct AddAndSubButton {
+  @State addClick: number = 1
+  @State subClick: number = 1
+  @Prop materialNum: number
+  onChange: (num: number) => void = () => {}
+  handleValueChange(delta: number) {
+    const newNum = this.materialNum + delta
+    if (newNum >= 0) {
+      this.onChange(newNum)
+    }
+  }
+
+  build() {
+    Row() {
+      Row() {
+        Button({ type: ButtonType.Normal }) {
+          Image($r('app.media.process_material_subtraction'))
+            .width('50%')
+            .height('50%')
+            .objectFit(ImageFit.Contain)
+            .fillColor($r('app.color.FFFFFF'))
+        }
+        .width('100%')
+        .height('100%')
+        .backgroundColor($r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.virtualSize_16'))
+        .scale({ x: this.addClick, y: this.addClick })
+        .animation({
+          duration: 200,
+          curve: Curve.Linear
+        })
+        .onClick(() => {
+          this.addClick = 0.9;
+          setTimeout(() => {
+            this.addClick = 1;
+            if(this.materialNum>0)
+            {
+              this.handleValueChange(-1)
+            }
+          }, 200);
+        })
+      }.width('22%')
+
+      Row() {
+        Text(String(this.materialNum))
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_38'))
+      }
+      .width('56%')
+      .justifyContent(FlexAlign.Center)
+
+      Row() {
+        Button({ type: ButtonType.Normal }) {
+          Image($r('app.media.process_material_add'))
+            .width('50%')
+            .height('50%')
+            .objectFit(ImageFit.Contain)
+            .fillColor($r('app.color.FFFFFF'))
+            .borderRadius($r('app.float.virtualSize_16'))
+        }
+        .width('100%')
+        .height('100%')
+        .backgroundColor($r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.virtualSize_16'))
+        .scale({ x: this.subClick, y: this.subClick })
+        .animation({
+          duration: 200,
+          curve: Curve.Linear
+        })
+        .onClick(() => {
+          this.subClick = 0.9;
+          setTimeout(() => {
+            this.subClick = 1;
+            this.handleValueChange(1)
+          }, 200);
+        })
+      }.width('22%')
+    }.width('100%')
+    .height('100%')
+    .backgroundColor($r('app.color.10FFFFFF'))
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 518 - 0
entry/src/main/ets/view/LoginInfoDialog.ets

@@ -0,0 +1,518 @@
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import { MessageInfo, MessagePage } from '../viewmodel/MessageInfo'
+import { UserInfo, WorkstationInfo } from '../viewmodel/UserInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import CommonConstants from '../common/constants/CommonConstants'
+import OperationComponent from '../viewmodel/process/OperationComponent'
+
+@CustomDialog
+export struct LoginInfoDialog {
+  private scrollerList: Scroller = new Scroller()
+  // 扫码开工状态(0:未开工 1:已开工)
+  @Link scanState: number
+  // 工序组件(工步)
+  @Link opComponents: OperationComponent[]
+  // 选中的工序组件类型
+  @Link selectComponentType: string
+  //当前工位
+  @Consume('currentStation') currentStation: string
+  //当前工位id
+  @Consume('currentStationId') @Watch('loadStationMessage') currentStationId: string
+  //当前部门
+  @Consume('currentDept') currentDept: string
+  //当前产线
+  @Consume('currentProductLine') currentProductLine: string
+  //当前产线code
+  @Consume('currentPLCode') currentPLCode: string
+  //当前用户
+  @Consume('currentUserName') currentUserName: string
+  //当前用户id
+  @Consume('currentUserId') currentUserId: number
+  //全部已读按钮缩放
+  @State allReadClick:number = 1
+  //消息列表
+  @State messages: MessageInfo[] = [];
+  //登录标志(1:工牌扫码 2:账号密码 3:已登录)
+  @State loginFlag: number = 2
+  // 用户名
+  @State userName: string = ''
+  // 密码
+  @State password : string = ''
+  // 人员工号
+  @State employeeCode : string = ''
+  // 登录用户信息
+  @State loginUser: UserInfo = {}
+  controller: CustomDialogController
+  //查找部门
+  searchDept: () => void = () => {}
+  //查找产线
+  searchProductLine: () => void = () => {}
+  //查找工位
+  searchStation: () => void = () => {}
+  //请求登录并查询用户信息,刷新页面
+  requestLoginAndRefresh = async ()=> {
+    let token:string = await ProcessRequest.post('api/auth/aioLogin', {
+      password: this.password,
+      userName: this.userName,
+      jobNumber: this.employeeCode,
+    } as RequestParamModel) ;
+    if (!token || token.length == 0) return
+    CommonConstants.AUTH_TOKEN = token
+    this.currentUserName = this.userName
+    let res:UserInfo = await ProcessRequest.get('/api/auth') ;
+    this.currentUserId = res.id! as number
+    this.loginUser = await ProcessRequest.post('/api/v1/sys/user/get', {id: this.currentUserId} as RequestParamModel) as UserInfo
+    if (this.loginUser.depts) {
+      this.loginUser.deptNames = this.loginUser?.depts[0]?.deptName!
+    }
+    this.loginFlag = 3
+  }
+
+  async loadStationMessage() {
+    let res = await ProcessRequest.post('/api/v1/sys/message/stationMessage', {
+      stationId: this.currentStationId
+    } as RequestParamModel) as MessagePage;
+    this.messages=res?.records??[]
+    this.messages = [...this.messages].sort((a, b) => {
+      const stateA = a.readState ?? '1';
+      const stateB = b.readState ?? '1';
+      return parseInt(stateA) - parseInt(stateB);
+    });
+  }
+
+  // 标记消息为已读
+  markAsRead = async (item: MessageInfo) => {
+    if (item.readState === '1') return // 已经是已读状态
+    try {
+      await ProcessRequest.post('http://192.168.1.3:20010/api/v1/sys/message/confirmMessage', {
+        ids:[item.id]
+      } as RequestParamModel);
+      //this.loadStationMessage();
+      item.readState = '1'
+      this.messages = [...this.messages].sort((a, b) => {
+        const stateA = a.readState ?? '1';
+        const stateB = b.readState ?? '1';
+        return parseInt(stateA) - parseInt(stateB);
+      });
+    } catch (e) {
+      console.error('标记已读失败:', e)
+    }
+  }
+
+  markAllAsRead = async () => {
+    try {
+      const messageIds = this.messages
+        .filter(item => item.readState === '0' && item.id)
+        .map(item => item.id);
+      if (messageIds.length === 0) return;
+      await ProcessRequest.post('http://192.168.1.3:20010/api/v1/sys/message/confirmMessage', {
+        ids:messageIds as string[]
+      } as RequestParamModel);
+      //this.loadStationMessage();
+      this.messages = this.messages.map(item => {
+        return {
+          readState: '1',
+          content: item.content,
+          created: item.created,
+        } as MessageInfo;
+      });
+      this.messages = this.messages.slice().sort((a, b) => {
+        const stateA = a.readState ?? '1';
+        const stateB = b.readState ?? '1';
+        return parseInt(stateA) - parseInt(stateB);
+      });
+    } catch (e) {
+      console.error('全部已读操作失败:', e);
+    }
+  }
+
+  async aboutToAppear() {
+    if (this.currentUserName) {
+      if (this.currentStationId) {
+        this.loadStationMessage()
+      }
+      this.loginUser = await ProcessRequest.post('/api/v1/sys/user/get', {id: this.currentUserId!} as RequestParamModel) as UserInfo
+      if (this.loginUser.depts) {
+        this.loginUser.deptNames = this.loginUser?.depts[0]?.deptName!
+      }
+      this.loginFlag = 3
+    }
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text('登录信息')
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+          .fontWeight(FontWeight.Medium)
+      }
+      .height('10%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Row(){
+        Column() {
+          if (this.loginFlag === 3) {
+            Column() {
+              Stack() {
+                Image(this.loginUser?.avatar ? CommonConstants.FILE_URL_PREFIX + this.loginUser.avatar : $r('app.media.process_user_default_avatar'))
+                  .objectFit(ImageFit.Auto)
+                Column({space: 1}) {
+                  Text(this.loginUser?.userName!)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontWeight(FontWeight.Medium)
+                  Text(this.loginUser?.deptNames!)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                }
+                .height('72%')
+                .justifyContent(FlexAlign.Center)
+              }
+              .height('78%')
+              .width('68.4%')
+              .alignContent(Alignment.Top)
+              Divider()
+                .vertical(false)
+                .strokeWidth(1)
+                .color($r('app.color.15FFFFFF'))
+                .width('100%')
+              Button('登出', {type: ButtonType.Normal })
+                .height('16.8%')
+                .width('100%')
+                .fontColor($r('app.color.0A84FF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .fontWeight(FontWeight.Medium)
+                .borderRadius({bottomRight: $r('app.float.virtualSize_16'), bottomLeft: $r('app.float.virtualSize_16')})
+                .backgroundColor($r('app.color.5FFFFFF'))
+                .onClick(async () => {
+                  await ProcessRequest.post('/api/auth/loginOut')
+                  this.currentUserName = ''
+                  this.currentUserId = 0
+                  CommonConstants.AUTH_TOKEN = ''
+                  this.loginUser = {}
+                  this.loginFlag = 2
+                  this.scanState = 0
+                  this.opComponents = []
+                  this.selectComponentType = ''
+                })
+            }
+            .width('69%')
+            .height('57.2%')
+            .justifyContent(FlexAlign.End)
+            .borderRadius($r('app.float.virtualSize_16'))
+            .backgroundColor($r('app.color.5FFFFFF'))
+            Blank().height('9.6%')
+          } else {
+            Row({space: 24}) {
+              Text('工牌扫码')
+                .fontColor(this.loginFlag === 1 ? $r('app.color.0A84FF') : $r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .fontWeight(FontWeight.Medium)
+                .onClick(()=>{
+                  this.loginFlag = 1
+                })
+              Text('账号密码')
+                .fontColor(this.loginFlag === 2 ? $r('app.color.0A84FF') : $r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .fontWeight(FontWeight.Medium)
+                .onClick(()=>{
+                  this.loginFlag = 2
+                })
+            }
+            .width('66.7%')
+            .height('6%')
+            Divider()
+              .vertical(false)
+              .strokeWidth(1)
+              .color($r('app.color.15FFFFFF'))
+              .width('69%')
+            if (this.loginFlag === 1) {
+              Column() {
+                Row() {
+                  Row().width('3.4%')
+                  // 左侧二维码图标
+                  Image($r('app.media.material_qr_code')) // 请替换为您的二维码图片资源
+                    .width($r('app.float.virtualSize_24'))
+                    .height($r('app.float.virtualSize_24'))
+                    .fillColor($r('app.color.FFFFFF'))
+                  // 扫码输入框
+                  TextInput({ placeholder: '请录入工牌号', text: this.employeeCode })
+                    .type(InputType.Normal)
+                    .placeholderFont({ size: $r('app.float.fontSize_16')})
+                    .placeholderColor($r('app.color.30FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .enableKeyboardOnFocus(false)
+                    .onChange((value: string) => {
+                      this.employeeCode = value
+                    })
+                }
+                .height('15.8%')
+                .width('100%')
+                .borderRadius($r('app.float.virtualSize_16'))
+                .backgroundColor($r('app.color.000000'))
+                .justifyContent(FlexAlign.Start)
+
+                Button('登录', {type: ButtonType.Normal })
+                  .height('15.8%')
+                  .width('100%')
+                  .fontColor($r('app.color.0A84FF'))
+                  .fontSize($r('app.float.fontSize_24'))
+                  .fontWeight(FontWeight.Medium)
+                  .backgroundColor($r('app.color.20FFFFFF'))
+                  .borderRadius($r('app.float.virtualSize_16'))
+                  .opacity(this.employeeCode ? 1 : 0.3)
+                  .onClick(async () => {
+                    this.requestLoginAndRefresh()
+                  })
+              }
+              .height('60.8%')
+              .width('69%')
+              .justifyContent(FlexAlign.SpaceAround)
+            } else if (this.loginFlag === 2) {
+              Column() {
+                Row() {
+                  Image($r('app.media.process_user_name'))
+                    .width($r('app.float.virtualSize_40'))
+                    .height($r('app.float.virtualSize_40'))
+                    .fillColor($r('app.color.FFFFFF'))
+                    .margin({left:'2%'})
+
+                  TextInput({text: this.userName, placeholder:'请录入账户'})
+                    .width('85%')
+                    .height('100%')
+                    .backgroundColor('#000000')
+                    .fontSize($r('app.float.fontSize_16'))
+                    .placeholderFont({size:$r('app.float.fontSize_16')})
+                    .placeholderColor($r('app.color.30FFFFFF'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .onChange((value: string) => {
+                      this.userName = value;
+                    })
+                }
+                .backgroundColor($r('app.color.000000'))
+                .height('15.8%')
+                .width('100%')
+                .borderRadius($r('app.float.virtualSize_16'))
+
+                Row() {
+                  Image($r('app.media.process_password'))
+                    .width($r('app.float.virtualSize_40'))
+                    .height($r('app.float.virtualSize_40'))
+                    .fillColor($r('app.color.FFFFFF'))
+                    .margin({left:'2%'})
+                  TextInput({text:this.password, placeholder:'请录入密码'} )
+                    .width('85%')
+                    .height('100%')
+                    .placeholderFont({size:$r('app.float.fontSize_16')})
+                    .placeholderColor($r('app.color.30FFFFFF'))
+                    .backgroundColor('#000000')
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .type(InputType.Password)
+                    .onChange((value: string) => {
+                      this.password = value;
+                    })
+                }
+                .height('15.8%')
+                .width('100%')
+                .backgroundColor($r('app.color.000000'))
+                .borderRadius($r('app.float.virtualSize_16'))
+
+                Button('登录', {type: ButtonType.Normal })
+                  .height('15.8%')
+                  .width('100%')
+                  .fontColor($r('app.color.0A84FF'))
+                  .fontSize($r('app.float.fontSize_24'))
+                  .fontWeight(FontWeight.Medium)
+                  .backgroundColor($r('app.color.20FFFFFF'))
+                  .borderRadius($r('app.float.virtualSize_16'))
+                  .opacity((this.userName && this.password) ? 1 : 0.3)
+                  .onClick(async () => {
+                    if (!this.userName || !this.password) {
+                      return
+                    }
+                    this.requestLoginAndRefresh()
+                  })
+              }
+              .height('60.8%')
+              .width('69%')
+              .justifyContent(FlexAlign.SpaceEvenly)
+            }
+          }
+          Divider()
+            .vertical(false)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+            .width('69%')
+          Column({space: 6}) {
+            Text('工位')
+              .fontColor($r('app.color.FFFFFF'))
+              .fontSize($r('app.float.fontSize_16'))
+              .fontWeight(FontWeight.Regular)
+              .width('95%')
+              .textAlign(TextAlign.Start)
+            Row(){
+              Text(this.currentStation)
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .margin({left:'8%'})
+                .width('82%')
+                .textAlign(TextAlign.Start)
+              Text('>')
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .textAlign(TextAlign.Start)
+                .width('10%')
+            }
+            .justifyContent(FlexAlign.Center)
+            .borderRadius($r('app.float.virtualSize_16'))
+            .width('100%')
+            .height('51.6%')
+            .backgroundColor($r('app.color.20FFFFFF'))
+            .onClick(()=>{
+              this.searchStation()
+            })
+          }
+          .height('26.6%')
+          .width('69%')
+          .justifyContent(FlexAlign.Center)
+        }
+        .justifyContent(FlexAlign.Start)
+        .alignItems(HorizontalAlign.Center)
+        .height('100%')
+        .width('46.6%')
+        Divider()
+          .vertical(true)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Column(){
+          Row(){
+            Text('工位通知')
+              .fontColor($r('app.color.FFFFFF'))
+              .fontSize($r('app.float.fontSize_24'))
+              .textAlign(TextAlign.Start)
+              .width('80%')
+            Row(){
+              Text('全部已读')
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_16'))
+            }
+            .backgroundColor($r('app.color.20FFFFFF'))
+            .width('20%')
+            .height('80%')
+            .borderRadius($r('app.float.virtualSize_16'))
+            .justifyContent(FlexAlign.Center)
+            .scale({ x: this.allReadClick, y: this.allReadClick })
+            .animation({
+              duration: 200,
+              curve: Curve.Linear
+            })
+            .onClick(()=>{
+              this.allReadClick = 0.9;
+              setTimeout(() => {
+                this.allReadClick = 1;
+                this.markAllAsRead()
+              }, 200);
+            })
+          }
+          .width('100%')
+          .height('8%')
+           Column(){
+             List({scroller:this.scrollerList}) {
+               ForEach(this.messages, (item:MessageInfo) => {
+                 ListItem() {
+                   Row() {
+                     Column(){
+                       Text(item.content)
+                         .fontSize($r('app.float.fontSize_16'))
+                         .fontColor($r('app.color.FFFFFF'))
+                         .width('85%')
+                         .textAlign(TextAlign.Start)
+                       Text(item.created)
+                         .fontSize($r('app.float.fontSize_12'))
+                         .fontColor($r('app.color.FFFFFF'))
+                         .width('85%')
+                         .textAlign(TextAlign.Start)
+                         .margin({top:'1%'})
+                         .fontWeight(FontWeight.Lighter)
+                     }
+                     Row(){
+                       Text(item.readState=='1'?`已读`:'已读确认')
+                         .fontSize($r('app.float.fontSize_16'))
+                         .fontColor(item.readState=='1'?$r('app.color.FFFFFF'):$r('app.color.0A84FF'))
+                         .width('15%')
+                         .textAlign(TextAlign.End)
+                         .onClick(() => this.markAsRead(item))
+                     }
+                   }.width('100%').justifyContent(FlexAlign.SpaceEvenly)
+                   .padding(5)
+                 }
+               })
+             }
+             .width('100%')
+             .height('100%')
+             .divider({
+               strokeWidth: 1,
+               color: $r('app.color.20FFFFFF')
+             })
+           }
+          .height('90%')
+
+
+        }
+        .width('46.6%')
+        .height('100%')
+      }
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .width('100%')
+      .height('76%')
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.SpaceBetween)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+

+ 332 - 0
entry/src/main/ets/view/ModifyMaterialNumDialog.ets

@@ -0,0 +1,332 @@
+// 数量修改
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import OperationItem from '../viewmodel/OperationItem'
+import ProcessMaterial from '../viewmodel/process/ProcessMaterial'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import promptAction from '@ohos.promptAction'
+
+@CustomDialog
+export struct ModifyMaterialNumDialog {
+  @State materialName :string = ''
+  @State materialModel :string = ''
+  @State unit :string = ''
+  // 总需求数
+  @State needNum: number = 0
+  workOrderCode :string = ''
+  operationId :string = ''
+  processId :string = ''
+  seqNo :string = ''
+  // 新增时扫码搜索到的物料
+  scanMaterial: ProcessMaterial = {}
+  // 新增或者修改采集物料(1:新增 2:修改)
+  addOrModifyCollect: number = 1
+  @State collectMaterials: ProcessMaterial[] = []
+  @State collectNumArray: number[] = []
+  // 当前工位ID
+  @Consume('currentStationId') currentStationId: string
+  // 批次高度
+  batchHeight: number = 93.6
+
+  controller: CustomDialogController
+  confirm: () => void = () => {}
+
+  async aboutToAppear() {
+    if (!this.scanMaterial) {
+      return
+    }
+    this.materialName = this.scanMaterial.materialName!
+    this.materialModel = this.scanMaterial.spec!
+    this.unit = this.scanMaterial.unitDictValue!
+    // 总需求数
+    this.needNum = this.scanMaterial.totalNum!
+    // 修改物料采集,则先查询采集历史
+    if (this.addOrModifyCollect === 2) {
+      let result: OperationItem[] = await ProcessRequest.post('/api/v1/process/itemRecord/itemInfo/list',{
+        itemCode: this.scanMaterial.materialCode!,
+        opId: this.operationId,
+        processId: this.processId
+      } as RequestParamModel)
+      if (result) {
+        for (let i = result.length - 1; i >= 0 ;i--) {
+          let material: ProcessMaterial = JSON.parse(JSON.stringify(result[i]))
+          material.materialName = result[i].itemName
+          material.materialCode = result[i].itemCode
+          material.spec = result[i].itemModel
+          material.workOrderCode = this.workOrderCode
+          this.collectMaterials.push(material)
+          this.collectNumArray.push(material.num ? material.num : 0)
+        }
+      }
+    } else {
+      if (this.scanMaterial) {
+        this.collectMaterials.push(this.scanMaterial)
+        let num: number = 0
+        // 如果需求数量小于扫码数量,则默认采集需求数量
+        if (this.scanMaterial.needNum! < this.scanMaterial.num!) {
+          num = this.needNum
+        } else {
+          num = this.scanMaterial.num!
+        }
+        this.collectNumArray.push(num)
+      }
+    }
+
+    // for (const element of this.collectMaterials) {
+    //   this.collectNumArray.push(element.num ? element.num : 0)
+    // }
+  }
+
+  build() {
+    Column() {
+      // 标题
+      Column(){
+        Text('数量')
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height($r('app.float.virtualSize_100'))
+      .justifyContent(FlexAlign.Center)
+      Column(){
+        Text(`物料名称:${this.materialName}`)
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .fontWeight(FontWeight.Lighter)
+        Text(`型号:${this.materialModel}`)
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .fontWeight(FontWeight.Lighter)
+        Text(`需求数量::${this.scanMaterial?.totalNum!}`)
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .fontWeight(FontWeight.Lighter)
+      }
+      .height($r('app.float.virtualSize_80'))
+      .width('70.4%')
+      .justifyContent(FlexAlign.Start)
+      .alignItems(HorizontalAlign.Start)
+
+      Scroll() {
+        Flex({ wrap: FlexWrap.Wrap, direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Start}) {
+          ForEach(this.collectMaterials, (item: ProcessMaterial, index: number) => {
+            Column({space: 5}) {
+              Row() {
+                Text('批次号:')
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Lighter)
+                Text(item?.batchNo!)
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Bold)
+              }
+              Row() {
+                Text(`数量(${this.unit})`)
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Regular)
+                Blank()
+                if (this.addOrModifyCollect === 1) {
+                  Text('还需采集:')
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                  Text(`${this.scanMaterial?.needNum!}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Regular)
+                }
+              }
+              .width('100%')
+
+              Row(){
+                Row() {
+                  Button({ type: ButtonType.Normal }) {
+                    Image($r('app.media.process_material_subtraction'))
+                      .width('50%')
+                      .height('50%')
+                      .objectFit(ImageFit.Contain)
+                      .fillColor($r('app.color.FFFFFF'))
+                  }
+                  .width('100%')
+                  .height('100%')
+                  .backgroundColor($r('app.color.20FFFFFF'))
+                  .borderRadius($r('app.float.virtualSize_16'))
+                  .opacity(((this.addOrModifyCollect === 1 && this.collectNumArray[index] <= 1) || (this.addOrModifyCollect === 2 && this.collectNumArray[index] <= 0)) ? 0.3 : 1)
+                  .onClick(() => {
+                    if (this.addOrModifyCollect === 1) {
+                      if (this.collectNumArray[index] > 1) {
+                        this.collectNumArray[index]--
+                      } else {
+                        this.collectNumArray[index] = 0
+                      }
+                    } else {
+                      if (this.collectNumArray[index] > 0) {
+                        this.collectNumArray[index]--
+                      } else {
+                        this.collectNumArray[index] = 0
+                      }
+                    }
+                  })
+                }.width('22%')
+                Row(){
+                  TextInput({text: (this.collectNumArray.length > index ? this.collectNumArray[index].toString() : '0')})
+                    .fontSize($r('app.float.fontSize_38'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .textAlign(TextAlign.Center)
+                    .type(InputType.Number)
+                    .onChange((value: string) => {
+                      this.collectNumArray[index] = parseInt(value)
+                      if (this.addOrModifyCollect === 1) {
+                        if (this.collectNumArray[index] > this.scanMaterial?.needNum!) {
+                          this.collectNumArray[index] = this.scanMaterial?.needNum!
+                        } else if (this.collectNumArray[index] < 1) {
+                          this.collectNumArray[index] = 1
+                        }
+                      } else {
+                        if (this.collectNumArray[index] > this.needNum) {
+                          this.collectNumArray[index] = this.needNum
+                        } else if (this.collectNumArray[index] < 0) {
+                          this.collectNumArray[index] = 0
+                        }
+                      }
+                    })
+                }
+                .width('56%')
+                .justifyContent(FlexAlign.Center)
+                Row() {
+                  Button({ type: ButtonType.Normal }) {
+                    Image($r('app.media.process_material_add'))
+                      .width('50%')
+                      .height('50%')
+                      .objectFit(ImageFit.Contain)
+                      .fillColor($r('app.color.FFFFFF'))
+                      .borderRadius($r('app.float.virtualSize_16'))
+                  }
+                  .width('100%')
+                  .height('100%')
+                  .backgroundColor($r('app.color.20FFFFFF'))
+                  .borderRadius($r('app.float.virtualSize_16'))
+                  .opacity(((this.addOrModifyCollect === 1 && this.collectNumArray[index] >= this.scanMaterial?.needNum!) || (this.addOrModifyCollect === 2 && this.collectNumArray[index] >= this.needNum)) ? 0.3 : 1)
+                  .onClick(() => {
+                    if (this.addOrModifyCollect === 1) {
+                      if (this.collectNumArray[index] < this.scanMaterial?.needNum!) {
+                        this.collectNumArray[index]++
+                      } else {
+                        this.collectNumArray[index] = this.scanMaterial?.needNum!
+                      }
+                    } else {
+                      if (this.collectNumArray[index] < this.needNum!) {
+                        this.collectNumArray[index]++
+                      } else {
+                        this.collectNumArray[index] = this.needNum!
+                      }
+                    }
+                  })
+                }.width('22%')
+              }
+              .height($r('app.float.virtualSize_80'))
+              .width('100%')
+              .backgroundColor($r('app.color.10FFFFFF'))
+              .borderRadius($r('app.float.virtualSize_16'))
+              if (index < this.collectMaterials.length - 1) {
+                Row() {
+                  Divider()
+                    .vertical(false)
+                    .color($r('app.color.15FFFFFF'))
+                }
+                .justifyContent(FlexAlign.Center)
+                .width('100%')
+              }
+            }
+            .height(this.batchHeight)
+            .alignItems(HorizontalAlign.Start)
+          })
+        }
+        .width('100%')
+      }
+      .scrollable(ScrollDirection.Vertical) // 垂直滚动
+      .scrollBar(BarState.Auto)
+      .width('70.4%')
+      .height(this.collectMaterials.length * this.batchHeight)
+      .align(Alignment.Top)
+      .constraintSize({ maxHeight: '70%' })
+
+      Column(){
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row(){
+          Row(){
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row(){
+            Text('确认')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(async () => {
+            if (this.addOrModifyCollect === 1) {
+              if (this.collectNumArray[0] > this.scanMaterial?.needNum!) {
+                promptAction.showToast({
+                  message: `采集数量超过需求数量,请检查...`,
+                  duration: 1500,
+                  bottom: 100
+                })
+                return
+              }
+              // 调接口新增采集
+              this.scanMaterial.stationId = this.currentStationId
+              this.scanMaterial.num = this.collectNumArray[0]
+              this.scanMaterial.seqNo = this.seqNo
+              let params: ProcessMaterial[] = []
+              params.push(this.scanMaterial)
+              await ProcessRequest.post('/api/v1/process/itemRecord/add', params)
+            } else {
+              let total: number = 0
+              for (const element of this.collectNumArray) {
+                total += element;
+              }
+              if (total > this.needNum) {
+                promptAction.showToast({
+                  message: `采集数量超过需求数量,请检查...`,
+                  duration: 1500,
+                  bottom: 100
+                })
+                return
+              }
+             // 调批量修改的接口
+              let stillNeed: number = this.needNum
+              for (let i = 0; i < this.collectMaterials.length; i++) {
+                this.collectMaterials[i].num = this.collectNumArray[i]
+                stillNeed -= this.collectNumArray[i]
+                this.collectMaterials[i].needNum = stillNeed
+              }
+              await ProcessRequest.post('/api/v1/process/itemRecord/batchUpdate', this.collectMaterials)
+            }
+            this.confirm();
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height($r('app.float.virtualSize_56'))
+    }
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 125 - 0
entry/src/main/ets/view/PictureDrawingDialog.ets

@@ -0,0 +1,125 @@
+//图纸资料
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import { DrawingInfo } from '../viewmodel/DrawingInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import CommonConstants from'../common/constants/CommonConstants'
+
+@CustomDialog
+export struct PictureDrawingDialog {
+  private scrollerMaterial: Scroller = new Scroller()
+  //作业图片列表
+  @State drawingList: DrawingInfo[] = []
+  materialCode: string = ''
+
+  controller: CustomDialogController
+  onConfirm: () => void = () => {}
+
+  //加载所有作业
+  loadWorkInstructions = async () => {
+    this.drawingList = await ProcessRequest.post('/api/v1/base/drawing/list', {
+      materialCode: this.materialCode,
+      notDrawingDictValue: 'esop'
+    } as RequestParamModel) as DrawingInfo[];
+  };
+
+  aboutToAppear(): void {
+    this.loadWorkInstructions();
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("图纸资料")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column() {
+        Grid(this.scrollerMaterial) {
+          ForEach(this.drawingList, (item: DrawingInfo) => {
+            GridItem() {
+              Column(){
+                Image(CommonConstants.PICTURE_URL_PREFIX+item.drawingPath)
+                  .width('100%')
+                  .height('65%')
+                  .objectFit(ImageFit.Fill)
+                  .borderRadius($r('app.float.virtualSize_24'))
+                Column() {
+                  Text(`文件名称:${item.fileName}`)
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .textAlign(TextAlign.Start)
+                    .fontWeight(FontWeight.Lighter)
+                  Text(`文件编号: ${item.drawingCode}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                  Text(`版本号: ${item.drawingVersion} `)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                  Text(`上传时间: ${item.updated}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                  Text(`编辑人员: ${item.updator}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                }
+                .alignItems(HorizontalAlign.Start)
+                .height('35%')
+                .width('96%')
+                .margin({left:'4%',top:"2%"})
+              }
+            }
+            .height('50%')
+            .backgroundColor( $r('app.color.20FFFFFF')) // 选中状态加深
+            .borderRadius($r('app.float.virtualSize_24'))
+            // .onClick(() => {
+            // })
+          })
+        }
+        .columnsTemplate('1fr 1fr 1fr')
+        .columnsGap(10)
+        .rowsGap(10)
+        .width('100%')
+        .height('97%')
+        .padding(10)
+      }
+      .height('81%')
+      .margin({left:'1%',right:'1%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Text('关闭')
+            .fontColor($r('app.color.60FFFFFF'))
+            .fontSize($r('app.float.fontSize_30'))
+        }
+        .width('100%')
+        .justifyContent(FlexAlign.Center)
+        .height('8%')
+        .width('50%')
+        .onClick(() => this.controller.close())
+      }
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 328 - 0
entry/src/main/ets/view/ReportDefectNumDialog.ets

@@ -0,0 +1,328 @@
+import ProcessRequest from '../common/util/request/ProcessRequest';
+import { BindTaskSeq } from '../viewmodel/process/BindTaskSeq';
+import TaskSeqVO from '../viewmodel/process/TaskSeqInfo';
+import RequestParamModel from '../viewmodel/RequestParamModel';
+import promptAction from '@ohos.promptAction';
+import preferencesUtil from '../common/util/PerferencesUtil';
+import CommonConstants from '../common/constants/CommonConstants';
+import ProcessDefectRecord from '../viewmodel/process/ProcessDefectRecord';
+
+//报工-不良品数量
+@CustomDialog
+export struct ReportDefectNumDialog {
+  controller: CustomDialogController
+  onConfirm: (num:number)=> void = () => {}
+  scroller: Scroller = new Scroller()
+
+  //是否全选
+  @State isAllSelected: boolean = false
+  //选择的数量
+  @State selectNum:number= 0
+  //当前工单
+  @State currentWorkOrderCode:string = ''
+  //当前工序号
+  @State currentOperationId:string = ''
+  //当前工位
+  @State currentStationId:string = ''
+  //总数量
+  @State totalNum: number= 0
+  //查询报工
+  @State queryTaskSeq: TaskSeqVO[] = []
+  @Consume ('bindTaskSeq') bindTaskSeq: BindTaskSeq[]
+  @Prop userName:string= ''
+  @State selectedIndexes:number[] =[]
+
+  onQueryTask=async ()=>{
+    let result: TaskSeqVO[] = await ProcessRequest.post('/api/v1/plan/task/list', {
+      stationId:this.currentStationId,
+      workOrderCode:this.currentWorkOrderCode,
+      operationId:this.currentOperationId,
+      stateList:[-1,0,1]
+    } as RequestParamModel) as TaskSeqVO[];
+    let seqNos: string[] = []
+    seqNos = await preferencesUtil.get(CommonConstants.PREFERENCE_INSTANCE_NAME, this.currentWorkOrderCode, seqNos)
+    if (!seqNos || seqNos.length <= 0) {
+      this.queryTaskSeq = []
+      this.totalNum = 0
+      return
+    }
+    // 根据流水号查询不良记录
+    let defectRecords = await ProcessRequest.post('/api/v1/process/defectRecord/list', {
+      seqNos: seqNos
+    } as RequestParamModel) as ProcessDefectRecord[];
+    let defectSeqNos: string[] = []
+    if (defectRecords && defectRecords.length > 0) {
+      for (const element of defectRecords) {
+        defectSeqNos.push(element.seqNo!);
+      }
+    }
+    for (const element of result) {
+      if (seqNos.includes(element.seqNo!) && defectSeqNos.includes(element.seqNo!)) {
+        this.queryTaskSeq.push(element)
+      }
+    }
+    this.totalNum = this.queryTaskSeq.length
+  }
+
+  private isSelectedByOthers(item: TaskSeqVO): boolean {
+    // 检查所有其他用户的选择记录
+    return this.bindTaskSeq.some(userSelection =>
+    userSelection.userName !== this.userName &&
+    userSelection.defectSeqNos?.some(seqNo => seqNo === item.seqNo));
+  }
+
+  // 获取选择当前项的用户名
+  private getSelectingUserName(item: TaskSeqVO): string | undefined {
+    const userSelection = this.bindTaskSeq.find(userSelection =>
+    userSelection.defectSeqNos?.some(seqNo => seqNo === item.seqNo));
+    return userSelection?.userName;
+  }
+
+  //选择单个
+  private onSelectSeqNo(index: number) {
+    const item = this.queryTaskSeq[index];
+
+    if (this.isSelectedByOthers(item)) {
+      return; // 已被其他用户选择,不允许操作
+    }
+
+    if (this.selectedIndexes.includes(index)) {
+      this.selectedIndexes = this.selectedIndexes.filter(i => i !== index);
+    } else {
+      this.selectedIndexes = [index, ...this.selectedIndexes];
+    }
+
+    this.updateSelectState();
+  }
+
+  //全选
+  private handleSelectAll() {
+    this.isAllSelected = !this.isAllSelected;
+
+    if (this.isAllSelected) {
+      // 只选择未被其他用户选中的项
+      this.selectedIndexes = [];
+      this.queryTaskSeq.forEach((item: TaskSeqVO, index: number) => {
+        if (!this.isSelectedByOthers(item)) {
+          this.selectedIndexes.push(index);
+        }
+      });
+    } else {
+      this.selectedIndexes = [];
+    }
+
+    this.updateSelectState();
+  }
+
+  //更新选择状态
+  private updateSelectState() {
+    this.selectNum = this.selectedIndexes.length;
+    const availableItems = this.queryTaskSeq.filter(item => !this.isSelectedByOthers(item));
+    this.isAllSelected = availableItems.length > 0 &&
+      this.selectedIndexes.length === availableItems.length;
+  }
+
+  aboutToAppear(): void {
+    this.onQueryTask().then(() => {
+      // 查询完成后检查是否有当前用户已选的项
+      this.queryTaskSeq.forEach((item, index) => {
+        const selectingUser = this.getSelectingUserName(item);
+        if (selectingUser === this.userName) {
+          this.selectedIndexes.push(index);
+        }
+      });
+      this.updateSelectState();
+    });
+  }
+
+  build() {
+    Column(){
+      Column() {
+        Text("不良品数量")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+      Row() {
+        Row(){
+          Row(){}.width('5%')
+          Checkbox()
+            .select(this.isAllSelected)
+            .selectedColor($r('app.color.0A84FF'))
+            .unselectedColor($r('app.color.60FFFFFF'))
+            .width($r('app.float.virtualSize_24'))
+            .mark({
+              strokeColor:$r('app.color.000000'),
+              size: $r('app.float.virtualSize_20'),
+              strokeWidth: 1
+            })
+            .height($r('app.float.virtualSize_24'))
+            .onChange(async (value: boolean) => {
+            })
+            .onClick(()=>{
+              this.handleSelectAll()
+            })
+          Text("全选")
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+        }
+        .width('20%')
+        .justifyContent(FlexAlign.Start)
+        .backgroundColor(this.isAllSelected?$r('app.color.200A84FF'):$r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.virtualSize_16'))
+        .onClick(()=>{
+          this.handleSelectAll()
+        })
+        Row(){
+          Text(`${this.selectNum}`)
+            .fontColor($r('app.color.30D158'))
+            .fontSize($r('app.float.fontSize_16'))
+            .fontWeight(FontWeight.Lighter)
+          Text(`/${this.totalNum}`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+            .fontWeight(FontWeight.Lighter)
+        }
+        .margin({left:'49%'})
+        .width('30%')
+        .justifyContent(FlexAlign.End)
+      }
+      .borderRadius($r('app.float.virtualSize_16'))
+      .height('7%')
+      .width('96%')
+      .margin({left:'2%',right:'2%',bottom:'1.5%'})
+      Row() {
+        List({space:8,scroller:this.scroller}){
+          ForEach(this.queryTaskSeq, (item:TaskSeqVO,index) => {
+            ListItem() {
+              Row(){
+                Checkbox()
+                  .select(this.selectedIndexes.includes(index)||this.isSelectedByOthers(item))
+                  .selectedColor($r('app.color.30D158'))
+                  .unselectedColor($r('app.color.60FFFFFF'))
+                  .width($r('app.float.virtualSize_24'))
+                  .mark({
+                    strokeColor:$r('app.color.000000'),
+                    size: $r('app.float.virtualSize_20'),
+                    strokeWidth: 1
+                  })
+                  .height($r('app.float.virtualSize_24'))
+                  .onClick(()=>{
+                    this.onSelectSeqNo(index)
+                    this.selectNum = this.selectedIndexes.length;
+                  })
+                Text('S/N')
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Lighter)
+                Text(item.seqNo)
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Bold)
+                  .margin({left:'2%'})
+                Text(this.getSelectingUserName(item))
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_12'))
+                  .fontWeight(FontWeight.Lighter)
+                  .width('50%')
+                  .textAlign(TextAlign.End)
+              }
+              .borderRadius($r('app.float.virtualSize_16'))
+              .backgroundColor(
+                this.selectedIndexes.includes(index) ||this.isSelectedByOthers(item)?
+                $r('app.color.2030D158') :
+                $r('app.color.20FFFFFF')
+              )
+              .border({
+                width:  1 ,
+                color: this.selectedIndexes.includes(index)||this.isSelectedByOthers(item) ?
+                $r('app.color.30D158') :
+                $r('app.color.20FFFFFF')
+              })
+              .width('100%')
+              .opacity(this.isSelectedByOthers(item) ? 0.3 : 1)
+              .onClick(()=>{
+                this.onSelectSeqNo(index)
+                this.selectNum = this.selectedIndexes.length;
+              })
+            }
+            .width('96%')
+            .margin({left:'2%',right:'2%'})
+          })
+        }
+        .height('100%')
+        .width('100%')
+      }
+      .height('73%')
+      .width('100%')
+      .margin({bottom:'1.5%'})
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            let selectTasks = this.queryTaskSeq.filter((_, index) => this.selectedIndexes.includes(index));
+            if (!selectTasks || selectTasks.length <= 0) {
+              promptAction.showToast({
+                message: '请先不良品的流水号',
+                duration: 2000
+              });
+              return;
+            }
+            let seqNos: string[] = []
+            for (const element of selectTasks) {
+              seqNos.push(element.seqNo)
+            }
+            const currentUserSelection: BindTaskSeq = {
+              userName: this.userName,
+              defectSeqNos: seqNos
+            };
+            const existingUserIndex = this.bindTaskSeq.findIndex(
+              item => item.userName === this.userName
+            );
+            if (existingUserIndex >= 0) {
+              this.bindTaskSeq[existingUserIndex].defectSeqNos = currentUserSelection.defectSeqNos;
+            } else {
+              this.bindTaskSeq.push(currentUserSelection);
+            }
+            this.onConfirm(this.selectedIndexes.length);
+            this.controller.close()
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+    }
+    .height('71%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 422 - 0
entry/src/main/ets/view/ReportPieceworkDialog.ets

@@ -0,0 +1,422 @@
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import { BindTaskSeq } from '../viewmodel/process/BindTaskSeq'
+import TaskSeqVO from '../viewmodel/process/TaskSeqInfo'
+import ReportInfo from '../viewmodel/process/ReporterInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import WorkOrderInfo from '../viewmodel/WorkOrderInfo'
+import promptAction from '@ohos.promptAction'
+import ProcessInfo from '../viewmodel/process/ProcessInfo'
+import preferencesUtil from '../common/util/PerferencesUtil'
+import CommonConstants from '../common/constants/CommonConstants'
+
+//工序(计件)报工
+@CustomDialog
+export struct ReportPieceworkDialog {
+  private scrollerList: Scroller = new Scroller()
+  //查询报工
+  @Link reporterList: ReportInfo[]
+  //选中的工序id
+  @Link selectOperationId: string
+  // 绩效模式(0:计时 1:计件)
+  @Link operationPerformance: string
+  @Link process: ProcessInfo
+  //当前工位
+  @Consume('currentStationId') currentStationId: string
+  //选择的按钮索引(默认加载全部)
+  @State addReportingClick: number = 1
+  //从首页传来的工单
+  @Link selectWorkOder: WorkOrderInfo
+  //工序名字
+  selectOperationName: string = ''
+  //当前工序已报工数量
+  @State reportedNum:number = 0
+  //计划报工数量
+  @State planReportNum:number = 0
+  // 当前登录用户名称
+  @Consume('currentUserName') userName: string
+  // 记录当前点击的报工人索引
+  @State currentReporterIndex: number = 0;
+  @Consume ('bindTaskSeq') bindTaskSeq: BindTaskSeq[]
+
+  controller: CustomDialogController
+  onConfirm: () => void = () => {}
+  //选择报工人
+  onSelectReporter: (index:number) => void = () => {}
+  //选择报工数量
+  onSelectReportNum: (index:number) => void = () => {}
+  //选择不良品数量
+  onSelectDefectNum: (index:number) => void = () => {}
+  // 报工后清除相关数据
+  clearSelectData: () => void = () => {}
+
+  updateReporterName(index: number, name: string) {
+    if (this.reporterList[index]) {
+      this.reporterList[index].userName = name;
+      // 强制刷新界面
+      this.reporterList = [...this.reporterList];
+    }
+  }
+
+  //加载第一个报工人(无法删除)
+  loadFirstReport=async ()=>{
+    if (this.reporterList.length > 0) {
+      return
+    }
+    const firstReporter:ReportInfo = {}
+    firstReporter.userName = this.userName
+    this.reporterList.push(firstReporter)
+  }
+
+  onQueryTask = async (currentStateList: Array<number>): Promise<number> => {
+    let res = await ProcessRequest.post('/api/v1/plan/task/list', {
+      stationId: this.currentStationId,
+      workOrderCode: this.selectWorkOder.workOrderCode!,
+      operationId: this.selectOperationId,
+      stateList: currentStateList
+    } as RequestParamModel) as TaskSeqVO[];
+    return res.length;
+  }
+
+  async handleAllClick(): Promise<void> {
+   this.planReportNum = await this.onQueryTask([]);
+  }
+
+  async handleReportedClick(): Promise<void> {
+    this.reportedNum = await this.onQueryTask([2]);
+  }
+
+  aboutToAppear(): void {
+    this.loadFirstReport()
+    this.handleAllClick()
+    this.handleReportedClick()
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("工序报工")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column(){
+          Row(){
+            Column({space:3}){
+              Text(this.selectWorkOder.materialName)
+                .fontSize($r('app.float.fontSize_24'))
+                .fontColor($r('app.color.FFFFFF'))
+              Text(this.selectWorkOder.materialCode)
+                .fontSize($r('app.float.fontSize_12'))
+                .fontColor($r('app.color.FFFFFF'))
+                .fontWeight(FontWeight.Lighter)
+              Row(){
+                Text("工单 ")
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontWeight(FontWeight.Lighter)
+                Text(this.selectWorkOder.workOrderCode)
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontColor($r('app.color.FFFFFF'))
+              }
+            }
+            .height('100%')
+            .width('30%')
+            .justifyContent(FlexAlign.End)
+            .alignItems(HorizontalAlign.Start)
+            Row(){
+              Text('工序')
+                .fontSize($r('app.float.fontSize_16'))
+                .fontColor($r('app.color.FFFFFF'))
+                .fontWeight(FontWeight.Lighter)
+              Text(this.selectOperationName)
+                .fontSize($r('app.float.fontSize_16'))
+                .fontColor($r('app.color.FFFFFF'))
+            }
+            .height('100%')
+            .width('40%')
+            .justifyContent(FlexAlign.Center)
+            .alignItems(VerticalAlign.Bottom)
+            Column(){
+              Row(){
+                Text(`${this.reportedNum}/`)
+                  .fontSize($r('app.float.fontSize_38'))
+                  .fontColor($r('app.color.FFFFFF'))
+                Text(`${this.planReportNum}`)
+                  .fontSize($r('app.float.fontSize_38'))
+                  .fontColor($r('app.color.60FFFFFF'))
+              }
+              Text('当前工序已报工/计划')
+                .fontSize($r('app.float.fontSize_12'))
+                .fontColor($r('app.color.FFFFFF'))
+                .fontWeight(FontWeight.Lighter)
+
+            }
+            .height('100%')
+            .width('30%')
+            .justifyContent(FlexAlign.End)
+            .alignItems(HorizontalAlign.End)
+
+            Divider()
+              .vertical(true)
+              .width('100%')
+              .strokeWidth(1)
+              .color($r('app.color.15FFFFFF'))
+          }
+          .height('10%')
+          .width('96%')
+          .margin({left:'2%',right:'2%'})
+          Row(){
+            Image($r('app.media.general_create'))
+              .width($r('app.float.virtualSize_24'))
+              .height($r('app.float.virtualSize_24'))
+              .fillColor($r('app.color.0A84FF'))
+            Text('添加报工人')
+              .fontColor($r('app.color.0A84FF'))
+              .fontSize($r('app.float.fontSize_24'))
+              .margin({left:'4%'})
+          }
+          .height('8%')
+          .width('14%')
+          .margin({left:'2%',top:'1.5%',bottom:'1.5%'})
+          .justifyContent(FlexAlign.Center)
+          .backgroundColor($r('app.color.20FFFFFF'))
+          .borderRadius($r('app.float.virtualSize_16'))
+          .scale({ x: this.addReportingClick, y: this.addReportingClick })
+          .animation({
+            duration: 200,
+            curve: Curve.Linear
+          })
+          .onClick(() => {
+            this.addReportingClick = 0.9;
+            setTimeout(() => {
+              this.addReportingClick = 1;
+              const newReporter:ReportInfo= {}
+              this.reporterList.push(newReporter)
+            }, 200);
+          })
+          Column(){
+            List({space: 8, scroller: this.scrollerList}) {
+              ForEach(this.reporterList, (item: ReportInfo, index: number) => {
+                ListItem() {
+                  Row(){
+                    Column(){
+                      Text(`报工人${index+1}`)
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontWeight(FontWeight.Regular)
+                        .height('30%')
+                        .margin({left:'2%'})
+                      Row(){
+                        Text(item.userName)
+                          .fontColor($r('app.color.FFFFFF'))
+                          .fontSize($r('app.float.fontSize_24'))
+                          .margin({left:'8%'})
+                          .width('82%')
+                          .textAlign(TextAlign.Start)
+                        Text(">")
+                          .fontColor($r('app.color.FFFFFF'))
+                          .fontSize($r('app.float.fontSize_24'))
+                          .textAlign(TextAlign.Start)
+                          .width('10%')
+                      }
+                      .justifyContent(FlexAlign.Start)
+                      .borderRadius($r('app.float.virtualSize_16'))
+                      .height('70%')
+                      .enabled(index!=0)
+                      .backgroundColor($r('app.color.20FFFFFF'))
+                      .onClick(()=>{
+                        this.onSelectReporter(index)
+                        this.currentReporterIndex = index;
+                      })
+                    }
+                    .width('29%')
+                    .height('100%')
+                    .alignItems(HorizontalAlign.Start)
+                    Column(){
+                      Text(`报工数量`)
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontWeight(FontWeight.Regular)
+                        .height('30%')
+                        .margin({left:'2%'})
+                      Row(){
+                        Text(item.reportNum)
+                          .fontColor($r('app.color.FFFFFF'))
+                          .fontSize($r('app.float.fontSize_24'))
+                          .margin({left:'8%'})
+                          .width('82%')
+                          .textAlign(TextAlign.Start)
+                        Text(">")
+                          .fontColor($r('app.color.FFFFFF'))
+                          .fontSize($r('app.float.fontSize_24'))
+                          .textAlign(TextAlign.Start)
+                          .width('10%')
+                      }
+                      .justifyContent(FlexAlign.Start)
+                      .borderRadius($r('app.float.virtualSize_16'))
+                      .height('70%')
+                      .backgroundColor($r('app.color.20FFFFFF'))
+                      .onClick(()=>{
+                        this.onSelectReportNum(index)
+                      })
+                    }
+                    .width('29%')
+                    .height('100%')
+                    .alignItems(HorizontalAlign.Start)
+                    .margin({left:'2%',right:'2%'})
+                    Column(){
+                      Text(`不良品数量`)
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontWeight(FontWeight.Regular)
+                        .height('30%')
+                        .margin({left:'2%'})
+                      Row(){
+                        Text(item.defectNum)
+                          .fontColor($r('app.color.FFFFFF'))
+                          .fontSize($r('app.float.fontSize_24'))
+                          .margin({left:'8%'})
+                          .width('82%')
+                          .textAlign(TextAlign.Start)
+                        Text(">")
+                          .fontColor($r('app.color.FFFFFF'))
+                          .fontSize($r('app.float.fontSize_24'))
+                          .textAlign(TextAlign.Start)
+                          .width('10%')
+                      }
+                      .justifyContent(FlexAlign.Start)
+                      .borderRadius($r('app.float.virtualSize_16'))
+                      .backgroundColor($r('app.color.20FFFFFF'))
+                      .height('70%')
+                      .onClick(()=>{
+                        this.onSelectDefectNum(index)
+                      })
+                    }
+                    .width('29%')
+                    .alignItems(HorizontalAlign.Start)
+                    .height('100%')
+                    if(index>0) {
+                      Column(){
+                        Image($r('app.media.process_delete_seq'))
+                          .width($r('app.float.virtualSize_48'))
+                          .height($r('app.float.virtualSize_48'))
+                          .fillColor($r('app.color.FF453A'))
+                          .margin({top:'35%'})
+                          .onClick(()=>{
+                            this.reporterList.splice(index,1)
+                            this.bindTaskSeq = this.bindTaskSeq.filter(i => i.userName !== item.userName);
+                          })
+                      }
+                      .width('9%')
+                      .alignItems(HorizontalAlign.Center)
+                      .height('100%')
+                    }
+                  }
+                  .height('100%')
+                }.height('20%')
+              })
+            }
+            .width('98%')
+            .height('100%')
+            .margin({left:'2%'})
+          }
+          .height('74%')
+          .width('100%')
+
+        }
+      .justifyContent(FlexAlign.Start)
+      .alignItems(HorizontalAlign.Start)
+      .width('100%')
+      .height('84%')
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确认报工')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(async () => {
+            if (!this.process || !this.process.id) {
+              promptAction.showToast({
+                message: '请先工序开工',
+                duration: 2000
+              });
+              return;
+            }
+            if (!this.bindTaskSeq || this.bindTaskSeq.length <= 0) {
+              promptAction.showToast({
+                message: '请先选择用户',
+                duration: 2000
+              });
+              return;
+            }
+            for (const taskSeq of this.bindTaskSeq) {
+              if ((!taskSeq.reportSeqNos || taskSeq.reportSeqNos.length <= 0) && (!taskSeq.defectSeqNos || taskSeq.defectSeqNos.length <= 0)) {
+                promptAction.showToast({
+                  message: `请选择${taskSeq.userName}}报工的流水号或者不良流水号`,
+                  duration: 2000
+                });
+                return;
+              }
+            }
+            this.bindTaskSeq[0].processId = this.process.id!
+            await ProcessRequest.post('/api/v1/process/info/reportByPiecework', this.bindTaskSeq)
+            let seqNos: string[] = []
+            for (const taskSeq of this.bindTaskSeq) {
+              if (taskSeq.reportSeqNos && taskSeq.reportSeqNos.length > 0) {
+                seqNos.concat(taskSeq.reportSeqNos)
+              }
+              if (taskSeq.defectSeqNos && taskSeq.defectSeqNos.length > 0) {
+                seqNos.concat(taskSeq.defectSeqNos)
+              }
+            }
+            // 删除已报工的seqNo,并保存到数据库中
+            let seqNoList: string[] = []
+            seqNoList = await preferencesUtil.get(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, seqNoList)
+            if (seqNoList.length === seqNos.length) {
+              preferencesUtil.put(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, [])
+            } else {
+              seqNoList = seqNoList.filter(item => !seqNos.includes(item));
+              await preferencesUtil.put(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, seqNoList)
+            }
+            this.clearSelectData()
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+

+ 481 - 0
entry/src/main/ets/view/ReportTimeBasedDialog.ets

@@ -0,0 +1,481 @@
+//工序(计时)报工
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import ProcessInfo from '../viewmodel/process/ProcessInfo'
+import ReporterInfo from '../viewmodel/process/ReporterInfo'
+import ReportInfo from '../viewmodel/process/ReporterInfo'
+import TaskSeqVO from '../viewmodel/process/TaskSeqInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import WorkOrderInfo from '../viewmodel/WorkOrderInfo'
+import promptAction from '@ohos.promptAction'
+import ProcessReportTimeBased from '../viewmodel/process/ProcessReportTimeBased'
+import { BindTaskSeq } from '../viewmodel/process/BindTaskSeq'
+import preferencesUtil from '../common/util/PerferencesUtil'
+import CommonConstants from '../common/constants/CommonConstants'
+
+@CustomDialog
+export struct ReportTimeBasedDialog {
+
+  controller: CustomDialogController
+  private scrollerList: Scroller = new Scroller()
+
+  //选中的工序id
+  @Link selectOperationId: string
+  //从首页传来的工单
+  @Link selectWorkOder: WorkOrderInfo
+  // 当前生产过程
+  @Link process: ProcessInfo
+  //查询报工
+  @Link reporterList: ReportInfo[]
+  //工序名字
+  selectOperationName: string = ''
+  //当前工序已报工数量
+  @State reportedNum: number = 0
+  //计划报工数量
+  @State planReportNum: number = 0
+  // 记录当前点击的报工人索引
+  @State currentReporterIndex: number = 0;
+  //当前工位
+  @Consume('currentStationId') currentStationId: string
+  // 当前登录用户名称
+  @Consume('currentUserName') userName: string
+  // 当前登录用户名称
+  @Consume('currentUserId') currentUserId: number
+  @Consume ('bindTaskSeq') bindTaskSeq: BindTaskSeq[]
+  // 开工时间
+  startWorkTime: string = ''
+
+  //选择报工数量
+  onSelectReportNum: (index:number) => void = () => {}
+  //选择不良品数量
+  onSelectDefectNum: (index:number) => void = () => {}
+  //选择用户用时占比
+  onSelectWorkHourRate: (index:number) => void = () => {}
+  //选择报工人
+  onSelectReporter: (index:number) => void = () => {}
+  // 报工后清除相关数据
+  clearSelectData: () => void = () => {}
+  //加载第一个报工人(无法删除)
+  loadFirstReporter=async ()=>{
+    if (this.reporterList.length > 0) {
+      return
+    }
+    const firstReporter: ReporterInfo = {}
+    firstReporter.userName = this.userName
+    firstReporter.userId = this.currentUserId.toString()
+    firstReporter.workingHoursRate = 1
+    this.reporterList.push(firstReporter)
+  }
+
+  async aboutToAppear() {
+    this.loadFirstReporter()
+    // 查询所有流转卡号
+    let res = await ProcessRequest.post('/api/v1/plan/task/list', {
+      stationId: this.currentStationId,
+      workOrderCode: this.selectWorkOder.workOrderCode!,
+      operationId: this.selectOperationId
+    } as RequestParamModel) as TaskSeqVO[];
+    if (!res || res.length <= 0) {
+      return
+    }
+    this.planReportNum = res.length
+    // 计算已报工数量
+    let num: number = 0
+    for (const element of res) {
+      if (element.state! === '2') {
+        num++
+      }
+    }
+    this.reportedNum = num
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text('工序报工')
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('10%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column(){
+        Row(){
+          Column({space:3}){
+            Text(this.selectWorkOder.materialName)
+              .fontSize($r('app.float.fontSize_24'))
+              .fontColor($r('app.color.FFFFFF'))
+            Text(this.selectWorkOder.materialCode)
+              .fontSize($r('app.float.fontSize_12'))
+              .fontColor($r('app.color.FFFFFF'))
+              .fontWeight(FontWeight.Lighter)
+            Row(){
+              Text('工单 ')
+                .fontSize($r('app.float.fontSize_16'))
+                .fontColor($r('app.color.FFFFFF'))
+                .fontWeight(FontWeight.Lighter)
+              Text(this.selectWorkOder.workOrderCode)
+                .fontSize($r('app.float.fontSize_16'))
+                .fontColor($r('app.color.FFFFFF'))
+                .fontWeight(FontWeight.Bold)
+            }
+          }
+          .height('100%')
+          .width('30%')
+          .justifyContent(FlexAlign.End)
+          .alignItems(HorizontalAlign.Start)
+          Row(){
+            Text('工序 ')
+              .fontSize($r('app.float.fontSize_16'))
+              .fontColor($r('app.color.FFFFFF'))
+              .fontWeight(FontWeight.Lighter)
+            Text(this.selectOperationName)
+              .fontSize($r('app.float.fontSize_16'))
+              .fontColor($r('app.color.FFFFFF'))
+              .fontWeight(FontWeight.Bold)
+          }
+          .height('100%')
+          .width('40%')
+          .justifyContent(FlexAlign.Center)
+          .alignItems(VerticalAlign.Bottom)
+          Column(){
+            Row(){
+              Text(`${this.reportedNum}/`)
+                .fontSize($r('app.float.fontSize_38'))
+                .fontColor($r('app.color.FFFFFF'))
+              Text(`${this.planReportNum}`)
+                .fontSize($r('app.float.fontSize_38'))
+                .fontColor($r('app.color.60FFFFFF'))
+            }
+            Text('当前工序已报工/计划')
+              .fontSize($r('app.float.fontSize_12'))
+              .fontColor($r('app.color.FFFFFF'))
+              .fontWeight(FontWeight.Lighter)
+          }
+          .height('100%')
+          .width('30%')
+          .justifyContent(FlexAlign.End)
+          .alignItems(HorizontalAlign.End)
+        }
+        .height('14.4%')
+        .width('100%')
+        .alignItems(VerticalAlign.Bottom)
+
+        Divider()
+          .vertical(false)
+          .width('100%')
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+          .height('3.1%')
+        // 报工流水号、不良流水号选择
+        Row({space: 5}) {
+          Column() {
+            Text(`报工数量`)
+              .fontColor($r('app.color.FFFFFF'))
+              .fontSize($r('app.float.fontSize_16'))
+              .fontWeight(FontWeight.Regular)
+              .height('27%')
+              .margin({left:'2%'})
+            Row(){
+              Text(this.reporterList && this.reporterList[0].reportNum ? this.reporterList[0].reportNum! : '0')
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .margin({left:'8%'})
+                .width('82%')
+                .textAlign(TextAlign.Start)
+              Text('>')
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .textAlign(TextAlign.Start)
+                .width('10%')
+            }
+            .justifyContent(FlexAlign.Start)
+            .borderRadius($r('app.float.virtualSize_16'))
+            .height('73%')
+            .backgroundColor($r('app.color.20FFFFFF'))
+            .onClick(()=>{
+              this.onSelectReportNum(0)
+            })
+          }
+          .width('30%')
+          .height('100%')
+          .alignItems(HorizontalAlign.Start)
+          Column(){
+            Text(`不良品数量`)
+              .fontColor($r('app.color.FFFFFF'))
+              .fontSize($r('app.float.fontSize_16'))
+              .fontWeight(FontWeight.Regular)
+              .height('27%')
+              .margin({left:'2%'})
+            Row(){
+              Text(this.reporterList && this.reporterList[0].defectNum ? this.reporterList[0].defectNum! : '0')
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .margin({left:'8%'})
+                .width('82%')
+                .textAlign(TextAlign.Start)
+              Text('>')
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_24'))
+                .textAlign(TextAlign.Start)
+                .width('10%')
+            }
+            .justifyContent(FlexAlign.Start)
+            .borderRadius($r('app.float.virtualSize_16'))
+            .backgroundColor($r('app.color.20FFFFFF'))
+            .height('73%')
+            .onClick(()=>{
+              this.onSelectDefectNum(0)
+            })
+          }
+          .width('30%')
+          .alignItems(HorizontalAlign.Start)
+          .height('100%')
+          Blank()
+          Text('开工时间:' +this.startWorkTime)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_12'))
+            .fontWeight(FontWeight.Lighter)
+        }
+        .width('100%')
+        .height('17.2%')
+        Row().height('7.9%')
+        // 添加报工人
+        Row(){
+          Image($r('app.media.general_create'))
+            .width($r('app.float.virtualSize_24'))
+            .height($r('app.float.virtualSize_24'))
+            .fillColor($r('app.color.0A84FF'))
+          Text('添加报工人')
+            .fontColor($r('app.color.0A84FF'))
+            .fontSize($r('app.float.fontSize_24'))
+            .margin({left:'4%'})
+        }
+        .height('8.8%')
+        .width('14%')
+        .justifyContent(FlexAlign.Center)
+        .backgroundColor($r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.virtualSize_16'))
+        .onClick(() => {
+          const newReporter: ReportInfo = {}
+          // 默认占比为剩余占比
+          if (this.reporterList && this.reporterList.length > 0) {
+            let rateTotal: number = 1
+            for (const element of this.reporterList) {
+              if (element.workingHoursRate && element.workingHoursRate > 0) {
+                rateTotal -= element.workingHoursRate
+              }
+            }
+            newReporter.workingHoursRate = rateTotal
+          }
+          this.reporterList.push(newReporter)
+        })
+
+        Column(){
+          List({space: 8, scroller: this.scrollerList}) {
+            ForEach(this.reporterList, (item: ReporterInfo, index: number) => {
+              ListItem() {
+                Row(){
+                  Column(){
+                    Text(`报工人${index+1}`)
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontWeight(FontWeight.Regular)
+                      .height('30%')
+                      .margin({left:'2%'})
+                    Row(){
+                      Text(item.userName)
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontSize($r('app.float.fontSize_24'))
+                        .margin({left:'8%'})
+                        .width('82%')
+                        .textAlign(TextAlign.Start)
+                      Text('>')
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontSize($r('app.float.fontSize_24'))
+                        .textAlign(TextAlign.Start)
+                        .width('10%')
+                    }
+                    .justifyContent(FlexAlign.Start)
+                    .borderRadius($r('app.float.virtualSize_16'))
+                    .height('70%')
+                    .enabled(index!=0)
+                    .backgroundColor($r('app.color.20FFFFFF'))
+                    .onClick(()=>{
+                      this.onSelectReporter(index)
+                      this.currentReporterIndex = index;
+                    })
+                  }
+                  .width('29%')
+                  .height('100%')
+                  .alignItems(HorizontalAlign.Start)
+                  Column(){
+                    Text(`用时占比`)
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontWeight(FontWeight.Regular)
+                      .height('30%')
+                      .margin({left:'2%'})
+                    Row(){
+                      Text(item.workingHoursRate ? item.workingHoursRate * 100 + '%' : '0%')
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontSize($r('app.float.fontSize_24'))
+                        .margin({left:'8%'})
+                        .width('82%')
+                        .textAlign(TextAlign.Start)
+                      Text('>')
+                        .fontColor($r('app.color.FFFFFF'))
+                        .fontSize($r('app.float.fontSize_24'))
+                        .textAlign(TextAlign.Start)
+                        .width('10%')
+                    }
+                    .justifyContent(FlexAlign.Start)
+                    .borderRadius($r('app.float.virtualSize_16'))
+                    .height('70%')
+                    .backgroundColor($r('app.color.20FFFFFF'))
+                    .onClick(()=>{
+                      this.onSelectWorkHourRate(index)
+                    })
+                  }
+                  .width('29%')
+                  .height('100%')
+                  .alignItems(HorizontalAlign.Start)
+                  .margin({left:'2%',right:'2%'})
+                  Blank()
+                  if(index > 0) {
+                    Column(){
+                      Image($r('app.media.process_delete_seq'))
+                        .width($r('app.float.virtualSize_48'))
+                        .height($r('app.float.virtualSize_48'))
+                        .fillColor($r('app.color.FF453A'))
+                        .margin({top:'35%'})
+                        .onClick(()=>{
+                          this.reporterList.splice(index,1)
+                        })
+                    }
+                    .width('9%')
+                    .alignItems(HorizontalAlign.Center)
+                    .height('100%')
+                  }
+                }
+                .height('100%')
+                .width('100%')
+              }
+              .height('40.7%')
+              .width('100%')
+            })
+          }
+          .width('100%')
+          .height('87%')
+        }
+        .height('48.6%')
+        .width('100%')
+        .justifyContent(FlexAlign.Center)
+      }
+      .justifyContent(FlexAlign.Start)
+      .alignItems(HorizontalAlign.Start)
+      .width('96%')
+      .height('82.7%')
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确认报工')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(async () => {
+            if (!this.process || !this.process.id) {
+              promptAction.showToast({
+                message: '请先工序开工',
+                duration: 2000
+              });
+              return;
+            }
+            let rateTotal = 0
+            for (const reporter of this.reporterList) {
+              if (!reporter.workingHoursRate) {
+                promptAction.showToast({
+                  message: `请给${reporter.userName!}选择用时占比`,
+                  duration: 2000
+                });
+                return;
+              }
+              rateTotal += reporter.workingHoursRate
+            }
+            if (rateTotal > 1) {
+              promptAction.showToast({
+                message: `用时占比超过100%,请修改`,
+                duration: 2000
+              });
+              return;
+            } else if (rateTotal < 1) {
+              promptAction.showToast({
+                message: `用时占比少于100%,请修改`,
+                duration: 2000
+              });
+              return;
+            }
+            // 报工
+            let seqNos: string [] = []
+            if (this.bindTaskSeq[0].reportSeqNos) {
+              seqNos = seqNos.concat(this.bindTaskSeq[0].reportSeqNos)
+            }
+            if (this.bindTaskSeq[0].defectSeqNos) {
+              seqNos = seqNos.concat(this.bindTaskSeq[0].defectSeqNos)
+            }
+            if (seqNos.length <= 0) {
+              promptAction.showToast({
+                message: `请选择报工流水号或者不良流水号`,
+                duration: 2000
+              });
+              return;
+            }
+            await ProcessRequest.post('/api/v1/process/info/reporting', {
+              processId: this.process.id!,
+              processUserReportList: this.reporterList,
+              seqList: seqNos
+            } as ProcessReportTimeBased)
+            // 删除已报工的seqNo,并保存到数据库中
+            let seqNoList: string[] = []
+            seqNoList = await preferencesUtil.get(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, seqNoList)
+            if (seqNoList.length === seqNos.length) {
+              preferencesUtil.put(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, [])
+            } else {
+              seqNoList = seqNoList.filter(item => !seqNos.includes(item));
+              await preferencesUtil.put(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, seqNoList)
+            }
+            this.clearSelectData()
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('7.3%')
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Center)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 91 - 0
entry/src/main/ets/view/ReportWorkHourRateDialog.ets

@@ -0,0 +1,91 @@
+//报工用户用时占比
+import { Font } from '@ohos.arkui.UIContext'
+
+@CustomDialog
+export struct ReportWorkHourRateDialog {
+
+  controller: CustomDialogController
+
+  onConfirm: (rate: number)=> void = () => {}
+
+  //选择的占比
+  @State selectIndex: number = 4
+  // 占比数字(eg: 50%的50)
+  rateNum: number = 50
+  private rateList: string[] = ['10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%']
+
+  aboutToAppear(): void {
+    this.rateNum = Number.parseFloat(this.rateList[this.selectIndex].slice(0, -1) as string)
+  }
+
+  build() {
+    Column(){
+      Column() {
+        Text('用时占比')
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+          .fontWeight(FontWeight.Medium)
+      }
+      .height('20%')
+      .width('100%')
+      .justifyContent(FlexAlign.End)
+
+      TextPicker({ range: this.rateList, selected: this.selectIndex })
+        .onChange((value: string | string[], index: number| number[]) => {
+          this.rateNum = Number.parseFloat(value.slice(0, -1) as string)
+          this.selectIndex = index as number
+        })
+        .selectedTextStyle({color: $r('app.color.FFFFFF'), font: {size: $r('app.float.fontSize_30'), weight: FontWeight.Medium }})
+        .textStyle({color: $r('app.color.FFFFFF'), font: {size: $r('app.float.fontSize_24'), weight: FontWeight.Medium }})
+        .disappearTextStyle({color: $r('app.color.FFFFFF'), font: {size: $r('app.float.fontSize_16'), weight: FontWeight.Lighter}})
+        .backgroundColor($r('app.color.2A2A2A'))
+        .width('85%')
+        .height('65%')
+
+      // 确认/取消栏
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.onConfirm(this.rateNum);
+            this.controller.close()
+          })
+        }
+      }
+      .width('100%')
+      .height('15%')
+    }
+    .height('34%')
+    .width('28%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Center)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+
+
+

+ 332 - 0
entry/src/main/ets/view/ReportWorkNumDialog.ets

@@ -0,0 +1,332 @@
+import ProcessRequest from '../common/util/request/ProcessRequest';
+import { BindTaskSeq } from '../viewmodel/process/BindTaskSeq';
+import TaskSeqVO from '../viewmodel/process/TaskSeqInfo';
+import RequestParamModel from '../viewmodel/RequestParamModel';
+import preferencesUtil from '../common/util/PerferencesUtil';
+import CommonConstants from '../common/constants/CommonConstants';
+import promptAction from '@ohos.promptAction';
+import ProcessDefectRecord from '../viewmodel/process/ProcessDefectRecord';
+
+//报工数量
+@CustomDialog
+export struct ReportWorkNumDialog{
+  controller: CustomDialogController
+  onConfirm: (num:number)=> void = () => {}
+  scroller: Scroller = new Scroller()
+
+  //是否全选
+  @State isAllSelected:boolean = false
+  //选择的数量
+  @State selectNum:number= 0
+  //当前工单
+  @State currentWorkOrderCode:string = ''
+  //当前工序号
+  @State currentOperationId:string = ''
+  //当前工位
+  @Consume('currentStationId') currentStationId:string
+  //总数量
+  @State totalNum:number= 0
+  //查询报工
+  @State queryTaskSeq: TaskSeqVO[] = []
+  @Consume ('bindTaskSeq') bindTaskSeq: BindTaskSeq[]
+  @Prop userName:string= ''
+  @State selectedIndexes:number[] =[]
+
+  onQueryTask=async ()=>{
+    let result: TaskSeqVO[] = await ProcessRequest.post('/api/v1/plan/task/list', {
+      stationId:this.currentStationId,
+      workOrderCode:this.currentWorkOrderCode,
+      operationId:this.currentOperationId,
+      stateList:[-1,0,1]
+    } as RequestParamModel) as TaskSeqVO[];
+    let seqNos: string[] = []
+    seqNos = await preferencesUtil.get(CommonConstants.PREFERENCE_INSTANCE_NAME, this.currentWorkOrderCode, seqNos)
+    if (!seqNos || seqNos.length <= 0) {
+      this.queryTaskSeq = []
+      this.totalNum = 0
+      return
+    }
+    // 根据流水号查询不良记录
+    let defectRecords = await ProcessRequest.post('/api/v1/process/defectRecord/list', {
+      seqNos: seqNos
+    } as RequestParamModel) as ProcessDefectRecord[];
+    let defectSeqNos: string[] = []
+    if (defectRecords && defectRecords.length > 0) {
+      for (const element of defectRecords) {
+        defectSeqNos.push(element.seqNo!);
+      }
+    }
+    for (const element of result) {
+      if (seqNos.includes(element.seqNo!) && !defectSeqNos.includes(element.seqNo!)) {
+        this.queryTaskSeq.push(element)
+      }
+    }
+    this.totalNum = this.queryTaskSeq.length
+  }
+
+  private isSelectedByOthers(item: TaskSeqVO): boolean {
+    // 检查所有其他用户的选择记录
+    return this.bindTaskSeq.some(userSelection =>
+    userSelection.userName !== this.userName &&
+    userSelection.reportSeqNos?.some(seqNo => seqNo === item.seqNo));
+  }
+
+  // 获取选择当前项的用户名
+  private getSelectingUserName(item: TaskSeqVO): string | undefined {
+    const userSelection = this.bindTaskSeq.find(userSelection =>
+    userSelection.reportSeqNos?.some(seqNo => seqNo === item.seqNo));
+    return userSelection?.userName;
+  }
+
+  //选择单个
+  private onSelectSeqNo(index: number) {
+    const item = this.queryTaskSeq[index];
+
+    if (this.isSelectedByOthers(item)) {
+      return; // 已被其他用户选择,不允许操作
+    }
+
+    if (this.selectedIndexes.includes(index)) {
+      this.selectedIndexes = this.selectedIndexes.filter(i => i !== index);
+    } else {
+      this.selectedIndexes = [index, ...this.selectedIndexes];
+    }
+
+    this.updateSelectState();
+  }
+
+  //全选
+  private handleSelectAll() {
+    this.isAllSelected = !this.isAllSelected;
+
+    if (this.isAllSelected) {
+      // 只选择未被其他用户选中的项
+      this.selectedIndexes = [];
+      this.queryTaskSeq.forEach((item: TaskSeqVO, index: number) => {
+        if (!this.isSelectedByOthers(item)) {
+          this.selectedIndexes.push(index);
+        }
+      });
+    } else {
+      this.selectedIndexes = [];
+    }
+
+    this.updateSelectState();
+  }
+
+  //更新选择状态
+  private updateSelectState() {
+    this.selectNum = this.selectedIndexes.length;
+    const availableItems = this.queryTaskSeq.filter(item => !this.isSelectedByOthers(item));
+    this.isAllSelected = availableItems.length > 0 &&
+      this.selectedIndexes.length === availableItems.length;
+  }
+
+  aboutToAppear(): void {
+    this.onQueryTask().then(() => {
+      // 查询完成后检查是否有当前用户已选的项
+      this.queryTaskSeq.forEach((item, index) => {
+        const selectingUser = this.getSelectingUserName(item);
+        if (selectingUser === this.userName) {
+          this.selectedIndexes.push(index);
+        }
+      });
+      this.updateSelectState();
+    });
+  }
+
+  build() {
+    Column(){
+      Column() {
+        Text("报工数量")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+      Row() {
+        Row(){
+          Row(){}.width('5%')
+          Checkbox()
+            .select(this.isAllSelected)
+            .selectedColor($r('app.color.0A84FF'))
+            .unselectedColor($r('app.color.60FFFFFF'))
+            .width($r('app.float.virtualSize_24'))
+            .mark({
+              strokeColor:$r('app.color.000000'),
+              size: $r('app.float.virtualSize_20'),
+              strokeWidth: 1
+            })
+            .height($r('app.float.virtualSize_24'))
+            .onChange(async (value: boolean) => {
+            })
+            .onClick(()=>{
+              this.handleSelectAll()
+            })
+          Text("全选")
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+        }
+        .width('20%')
+        .justifyContent(FlexAlign.Start)
+        .backgroundColor(this.isAllSelected?$r('app.color.200A84FF'):$r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.virtualSize_16'))
+        .onClick(()=>{
+          this.handleSelectAll()
+        })
+        Row(){
+          Text(`${this.selectNum}`)
+            .fontColor($r('app.color.30D158'))
+            .fontSize($r('app.float.fontSize_16'))
+            .fontWeight(FontWeight.Lighter)
+          Text(`/${this.totalNum}`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+            .fontWeight(FontWeight.Lighter)
+        }
+        .margin({left:'49%'})
+        .width('30%')
+        .justifyContent(FlexAlign.End)
+      }
+      .borderRadius($r('app.float.virtualSize_16'))
+      .height('7%')
+      .width('96%')
+      .margin({left:'2%',right:'2%',bottom:'1.5%'})
+      Row() {
+        List({space:8,scroller:this.scroller}){
+          ForEach(this.queryTaskSeq, (item:TaskSeqVO,index) => {
+            ListItem() {
+              Row(){
+                Checkbox()
+                  .select(this.selectedIndexes.includes(index)||this.isSelectedByOthers(item))
+                  .selectedColor($r('app.color.30D158'))
+                  .unselectedColor($r('app.color.60FFFFFF'))
+                  .width($r('app.float.virtualSize_24'))
+                  .mark({
+                    strokeColor:$r('app.color.000000'),
+                    size: $r('app.float.virtualSize_20'),
+                    strokeWidth: 1
+                  })
+                  .height($r('app.float.virtualSize_24'))
+                  .onClick(()=>{
+                    this.onSelectSeqNo(index)
+                    this.selectNum = this.selectedIndexes.length;
+                  })
+                Text('S/N')
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Lighter)
+                Text(item.seqNo)
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Bold)
+                  .margin({left:'2%'})
+                Text(this.getSelectingUserName(item))
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_12'))
+                  .fontWeight(FontWeight.Lighter)
+                  .width('50%')
+                  .textAlign(TextAlign.End)
+              }
+              .borderRadius($r('app.float.virtualSize_16'))
+              .backgroundColor(
+                this.selectedIndexes.includes(index) ||this.isSelectedByOthers(item)?
+                $r('app.color.2030D158') :
+                $r('app.color.20FFFFFF')
+              )
+              .border({
+                width:  1 ,
+                color: this.selectedIndexes.includes(index)||this.isSelectedByOthers(item) ?
+                $r('app.color.30D158') :
+                $r('app.color.20FFFFFF')
+              })
+              .width('100%')
+              .opacity(this.isSelectedByOthers(item) ? 0.3 : 1)
+              .onClick(()=>{
+                this.onSelectSeqNo(index)
+                this.selectNum = this.selectedIndexes.length;
+              })
+            }
+            .width('96%')
+            .margin({left:'2%',right:'2%'})
+          })
+        }
+        .height('100%')
+        .width('100%')
+      }
+      .height('73%')
+      .width('100%')
+      .margin({bottom:'1.5%'})
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            let selectTasks = this.queryTaskSeq.filter((_, index) => this.selectedIndexes.includes(index));
+            if (!selectTasks || selectTasks.length <= 0) {
+              promptAction.showToast({
+                message: '请先报工的流水号',
+                duration: 2000
+              });
+              return;
+            }
+            let seqNos: string[] = []
+            for (const element of selectTasks) {
+              seqNos.push(element.seqNo)
+            }
+            const currentUserSelection: BindTaskSeq = {
+              userName: this.userName,
+              reportSeqNos: seqNos
+            };
+            const existingUserIndex = this.bindTaskSeq.findIndex(
+              item => item.userName === this.userName
+            );
+            if (existingUserIndex >= 0) {
+              this.bindTaskSeq[existingUserIndex].reportSeqNos = currentUserSelection.reportSeqNos;
+            } else {
+              this.bindTaskSeq.push(currentUserSelection);
+            }
+            this.onConfirm(this.selectedIndexes.length);
+            this.controller.close()
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+    }
+    .height('71%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+
+
+

+ 232 - 0
entry/src/main/ets/view/SelectWorkOrderDialog.ets

@@ -0,0 +1,232 @@
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import WorkOrderInfo from '../viewmodel/WorkOrderInfo'
+import { WorkOrderPage } from '../viewmodel/WorkOrderPage'
+
+@CustomDialog
+export struct SelectWorkOrderDialog {
+  private scrollerMaterial: Scroller = new Scroller()
+  //工单列表
+  @State workOrderList: WorkOrderInfo[] = []
+  //选择的工单索引
+  @State selectedOrderIndex: number = -1
+  //扫描的工单号
+  @State scanOrderValue:string = ''
+  @Link selectWorkOder: WorkOrderInfo
+  // 当前工位ID
+  @Consume('currentStationId') currentStationId: string
+  controller: CustomDialogController
+  onConfirm: () => void = () => {
+  }
+
+  //加载所有工单
+  loadWorkOrders = async () => {
+    let queryRes = await ProcessRequest.post('/api/v1/plan/workOrder/taskPage2', {
+      queryCode:this.scanOrderValue,
+      queryComplete: 0,
+      stationId:this.currentStationId,
+      pageNo: 1,
+      pageSize: 99999,
+    } as RequestParamModel) as WorkOrderPage;
+    this.workOrderList = queryRes?.records??[]
+  };
+
+  private onSelectOrder(index: number) {
+    this.selectedOrderIndex = index
+  }
+
+  aboutToAppear(): void {
+    this.loadWorkOrders();
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("选择工单")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Row() {
+        // 左侧二维码图标
+        Image($r('app.media.material_qr_code'))
+          .width($r('app.float.virtualSize_32'))
+          .height($r('app.float.virtualSize_32'))
+          .fillColor($r('app.color.FFFFFF'))
+          .margin({ left: '2%'})
+        // 扫码输入框
+        TextInput({text:this.scanOrderValue, placeholder: '请扫描或录入订单或工单编号' })
+          .type(InputType.Normal)
+          .placeholderFont({ size: $r('app.float.fontSize_16') })
+          .placeholderColor($r('app.color.30FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+          .enableKeyboardOnFocus(false)
+          .onSubmit(async () => {
+            let queryRes = await ProcessRequest.post('/api/v1/plan/workOrder/taskPage2', {
+              queryCode:this.scanOrderValue,
+              pageNo: 1,
+              pageSize: 99999,
+            } as RequestParamModel) as WorkOrderPage;
+            this.workOrderList = queryRes?.records??[]
+          })
+          .onChange((value: string) => {
+            this.scanOrderValue = value;
+            this.loadWorkOrders()
+          })
+      }
+      .height('6%')
+      .width('30%')
+      .borderRadius($r('app.float.virtualSize_16'))
+      .backgroundColor($r('app.color.000000'))
+      .margin({ left: '2%' ,top:'2%'})
+
+      Divider()
+        .vertical(false)
+        .strokeWidth(1)
+        .color($r('app.color.15FFFFFF'))
+        .margin({ top: '2%' ,bottom:'2%',left:'2%',right:'2%'})
+
+      Column() {
+        Grid(this.scrollerMaterial) {
+          ForEach(this.workOrderList, (order: WorkOrderInfo, index) => {
+            GridItem() {
+              Row(){
+                Column({space: 1}) {
+                  // 订单标题(带订单号)
+                  Text(`${order.materialName}`)
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .width('100%')
+                    .textAlign(TextAlign.Start)
+                    .margin({left:'2%' })
+                  Column() {
+                    Text(`产品编号: ${order.materialCode}`)
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontWeight(FontWeight.Lighter)
+                      .textAlign(TextAlign.Start)
+                    Text(`订单编号: ${order.orderCode} `)
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontWeight(FontWeight.Lighter)
+                      .textAlign(TextAlign.Start)
+                    Text(`工单编号: ${order.workOrderCode}`)
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontWeight(FontWeight.Lighter)
+                      .textAlign(TextAlign.Start)
+                    Text(`完成/计划: ${order.completeNum}/${order.planNum}`)
+                      .fontColor($r('app.color.FFFFFF'))
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontWeight(FontWeight.Lighter)
+                      .textAlign(TextAlign.Start)
+                  }
+                  .width('100%')
+                  .margin({left:'2%'})
+                  .justifyContent(FlexAlign.Start)
+                  .alignItems(HorizontalAlign.Start)
+                }
+                .alignItems(HorizontalAlign.Start)
+                .width('70%')
+                .padding(8)
+                Stack(){
+                  if (order.workOrderState ==="6") {
+                    Image($r("app.media.process_complete_test"))
+                      .width($r('app.float.virtualSize_112'))
+                      .height($r('app.float.virtualSize_80'))
+                      .position({ x: '30%', y: '0%' })
+                      .fillColor($r('app.color.FFFFFF'))
+                      .zIndex(0)
+                  }
+                  Image($r('app.media.order_name_copy'))
+                    .width($r('app.float.virtualSize_56'))
+                    .height($r('app.float.virtualSize_56'))
+                    .fillColor($r('app.color.FFFFFF'))
+                    .zIndex(1)
+                    .onClick(()=>{
+                      this.scanOrderValue = this.workOrderList[index]?.workOrderCode ?? '';
+                    })
+                }
+                .width('30%')
+                .height('100%')
+              }
+            }
+            .height('27.6%')
+            .backgroundColor(index === this.selectedOrderIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF'))
+            .borderRadius($r('app.float.virtualSize_24'))
+            .border({
+              width: 2,
+              color: index === this.selectedOrderIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF')
+            })
+            .onClick(() => {
+              this.onSelectOrder(index)
+            })
+
+          })
+        }
+        .columnsTemplate('1fr 1fr 1fr')
+        .columnsGap(10)
+        .rowsGap(10)
+        .width('100%')
+        .height('97%')
+        .padding(10)
+      }
+      .height('68%')
+      .margin({left:'1%',right:'1%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            if(this.selectedOrderIndex==-1) return
+            // 重新选中的工单就是之前的工单,则不做任何操作
+            if (this.selectWorkOder && this.selectWorkOder.workOrderCode && this.workOrderList[this.selectedOrderIndex].workOrderCode! === this.selectWorkOder.workOrderCode!) {
+              this.controller.close();
+              return
+            }
+            this.selectWorkOder = this.workOrderList[this.selectedOrderIndex]
+            this.onConfirm();
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+      }
+      .height('71%')
+      .width('62%')
+      .backgroundColor($r('app.color.2A2A2A'))
+      .justifyContent(FlexAlign.End)
+      .alignItems(HorizontalAlign.Start)
+      .borderColor($r('app.color.000000'))
+      .borderWidth(1)
+      .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 169 - 0
entry/src/main/ets/view/SerialNoMaterialDialog.ets

@@ -0,0 +1,169 @@
+//本次扫描物料
+import TaskSeqVO from '../viewmodel/process/TaskSeqInfo'
+@CustomDialog
+export struct SerialNoMaterialDialog {
+  private scrollerMaterial: Scroller = new Scroller()
+  //当前物料名称
+  @State currentMaterialName: string = 'PCBA板'
+  //当前物料型号
+  @State currentMaterialNo: string = '12322221232321222332211233'
+  //当前物料计划数量
+  @State currentPlanNum:number = 100
+  //当前物料完成数量
+  @State currentCompleteNum:number = 10
+  //当前物料单位
+  @State currentMaterialUnit:string = '片'
+  //扫描的工单号
+  @State selectedSerialsIndex:number = -1
+  //
+  @State currentMaterialSerials:TaskSeqVO[]=[{
+    seqNo:"1212122121ewqeqweqw2131231231"
+  },
+    {
+      seqNo:"1212122121ewqeqweqw2131231231"
+    },
+    {
+      seqNo:"1212122121ewqeqweqw2131231231"
+    },
+    {
+      seqNo:"1212122121ewqeqweqw2131231231"
+    },
+    {
+      seqNo:"1212122121ewqeqweqw2131231231"
+    },
+  ]
+  controller: CustomDialogController
+  onConfirm: () => void = () => {
+  }
+
+  aboutToAppear(): void {
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("本次扫描物料")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Row() {
+        Column({space:5}){
+          Text(`物料名称:${this.currentMaterialName}`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+          Text(`型号:${this.currentMaterialNo}`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+        }
+        .alignItems(HorizontalAlign.Start)
+        .width('70%')
+        Row(){
+          Text(`${this.currentCompleteNum}/`)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_38'))
+          Text(`${this.currentCompleteNum}${this.currentMaterialUnit}`)
+            .fontColor($r('app.color.60FFFFFF'))
+            .fontSize($r('app.float.fontSize_38'))
+        } .width('30%')
+        .justifyContent(FlexAlign.End)
+      }
+      .height('6%')
+      .width('96%')
+      .justifyContent(FlexAlign.End)
+      .margin({left:'2%',right:'2%'})
+
+
+
+      Divider()
+        .vertical(false)
+        .strokeWidth(1)
+        .color($r('app.color.15FFFFFF'))
+        .margin({ top: '1%' ,bottom:'2%',left:'2%',right:'2%'})
+
+      Column() {
+        List({ space: 8,scroller:this.scrollerMaterial }) {
+          ForEach(this.currentMaterialSerials, (item: TaskSeqVO, index) => {
+            ListItem() {
+              Row() {
+                Row(){
+                  Text(`S/N`)
+                    .fontSize($r('app.float.fontSize_30'))
+                    .fontColor($r('app.color.60FFFFFF'))
+                    .margin({left:'2%',right:'2%'})
+                  Text(`${item.seqNo}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_30'))
+                }
+                .width('90%')
+                Row(){
+                  Image($r('app.media.process_delete_seq'))
+                    .width($r('app.float.virtualSize_48'))
+                    .height($r('app.float.virtualSize_48'))
+                    .fillColor($r('app.color.FF453A'))
+                }.width('10%')
+                .justifyContent(FlexAlign.Center)
+                .onClick(()=>{
+                  this.currentMaterialSerials.splice(index, 1);
+                })
+              }
+              .backgroundColor($r('app.color.20FFFFFF')) // 选中状态加深
+              .borderRadius($r('app.float.virtualSize_24'))
+              .padding(13)
+            }
+          })
+        }
+        .width('100%')
+        .flexGrow(1)
+      }
+      .height('68%')
+      .margin({left:'2%',right:'2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.onConfirm();
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 231 - 0
entry/src/main/ets/view/SpecialInspectionDialog.ets

@@ -0,0 +1,231 @@
+//专检
+import ProcessRequest from '../common/util/request/ProcessRequest';
+import { BindTaskSeqVO } from '../viewmodel/process/BindTaskSeq';
+import TaskSeqVO from '../viewmodel/process/TaskSeqInfo';
+import RequestParamModel from '../viewmodel/RequestParamModel';
+import { Font } from '@ohos.arkui.UIContext';
+import { UserInfo } from '../viewmodel/UserInfo';
+
+@CustomDialog
+export struct SpecialInspectionDialog{
+  controller: CustomDialogController
+  onConfirm: ()=> void = () => {}
+  onDelete:()=> void = () => {}
+
+  //扫描工牌号
+  @State scanCardValue:string= ''
+  //是否合格(-1 不合格,0:初始,1:合格)
+  @State isQualified:number  = 0
+  //合格按钮缩放
+  @State qualifiedClick:number  = 1
+  //不合格按钮缩放
+  @State unQualifiedClick:number  = 1
+  //当前检验员
+  @State currentInspector:UserInfo={}
+
+  queryWorkCard=async()=>{
+    this.currentInspector = await ProcessRequest.post('/api/v1/sys/user/get', {
+      id:this.scanCardValue,
+    } as RequestParamModel) as UserInfo;
+  }
+
+  build() {
+    Column(){
+      Column() {
+        Text('专检')
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('16%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+      Column(){
+        Text('专检人员')
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.FFFFFF'))
+        Row() {
+          // 左侧二维码图标
+          Image($r('app.media.material_qr_code'))
+            .width($r('app.float.virtualSize_20'))
+            .height($r('app.float.virtualSize_20'))
+            .fillColor($r('app.color.FFFFFF'))
+            .margin({ left: '2%'})
+          // 扫码输入框
+          TextInput({text:this.scanCardValue, placeholder: '请录入工牌或检验人员专码' })
+            .type(InputType.Normal)
+            .placeholderFont({ size: $r('app.float.fontSize_16') })
+            .placeholderColor($r('app.color.30FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+            .fontColor($r('app.color.FFFFFF'))
+            .enableKeyboardOnFocus(false)
+            .onSubmit(async () => {
+              this.queryWorkCard()
+            })
+            .onChange((value: string) => {
+              this.scanCardValue = value;
+            })
+        }
+        .height('10%')
+        .width('100%')
+        .borderRadius($r('app.float.virtualSize_16'))
+        .backgroundColor($r('app.color.000000'))
+        .margin({top:'3%',bottom:'3%'})
+
+        Column(){
+          if(this.currentInspector.id){
+            Row(){
+              Text(`检验员`)
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_16'))
+                .fontWeight(FontWeight.Lighter)
+              Text(`${this.currentInspector.userName}`)
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_16'))
+                .fontWeight(FontWeight.Bold)
+            }
+            .margin({left:'4%',top:'4%'})
+            Row(){
+              Text(`部门`)
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_16'))
+                .fontWeight(FontWeight.Lighter)
+              Text(`${this.currentInspector?.depts?.[0]?.deptName ?? ''}`)
+                .fontColor($r('app.color.FFFFFF'))
+                .fontSize($r('app.float.fontSize_16'))
+                .fontWeight(FontWeight.Bold)
+            }
+            .margin({left:'4%',top:'2%'})
+            Text(`电子签章`)
+              .fontColor($r('app.color.FFFFFF'))
+              .fontSize($r('app.float.fontSize_16'))
+              .fontWeight(FontWeight.Lighter)
+              .margin({left:'4%',top:'5%'})
+            Row(){
+
+            }
+            .height('48%')
+            .width('92%')
+            .margin({left:'4%',right:'4%',top:'2%'})
+            .backgroundColor($r('app.color.FFFFFF'))
+          }else{
+            Image($r('app.media.process_user_name'))
+              .height('80%')
+              .width('50%')
+              .margin({left:'25%',right:'25%',top:'10%',bottom:'10%'})
+              .fillColor($r('app.color.15FFFFFF'))
+              .objectFit(ImageFit.Fill)
+
+          }
+        }
+        .height('55%')
+        .width('100%')
+        .border({width:1,color:$r('app.color.15FFFFFF')})
+        .borderRadius($r('app.float.virtualSize_16'))
+        .alignItems(HorizontalAlign.Start)
+        Row(){
+          Row(){
+            Image($r('app.media.unqualified_special_inspection'))
+              .height($r('app.float.virtualSize_24'))
+              .width($r('app.float.virtualSize_24'))
+              .fillColor(this.isQualified === -1?$r('app.color.FFFFFF'):$r('app.color.FF453A'))
+            Text('不合格')
+              .fontSize($r('app.float.fontSize_24'))
+              .fontColor(this.isQualified === -1?$r('app.color.FFFFFF'):$r('app.color.FF453A'))
+              .margin({left:'5%'})
+          }
+          .justifyContent(FlexAlign.Center)
+          .backgroundColor(this.isQualified === -1?$r('app.color.FF453A'):$r('app.color.20FFFFFF'))
+          .borderRadius($r('app.float.virtualSize_16'))
+          .width('45%')
+          .height('100%')
+          .scale({ x: this.unQualifiedClick, y: this.unQualifiedClick })
+          .animation({
+            duration: 200,
+            curve: Curve.Linear
+          })
+          .onClick(() => {
+            this.unQualifiedClick = 0.9;
+            setTimeout(() => {
+              this.isQualified = -1
+              this.unQualifiedClick = 1;
+            }, 200);
+          })
+          Row(){
+            Image($r('app.media.device_normal'))
+              .height($r('app.float.virtualSize_24'))
+              .width($r('app.float.virtualSize_24'))
+              .fillColor(this.isQualified === 1?$r('app.color.FFFFFF'):$r('app.color.30D158'))
+            Text('合格')
+              .fontSize($r('app.float.fontSize_24'))
+              .fontColor(this.isQualified === 1?$r('app.color.FFFFFF'):$r('app.color.30D158'))
+              .margin({left:'5%'})
+          }
+          .width('45%')
+          .height('100%')
+          .margin({left:'10%'})
+          .justifyContent(FlexAlign.Center)
+          .backgroundColor(this.isQualified === 1?$r('app.color.30D158'):$r('app.color.20FFFFFF'))
+          .borderRadius($r('app.float.virtualSize_16'))
+          .scale({ x: this.qualifiedClick, y: this.qualifiedClick })
+          .animation({
+            duration: 200,
+            curve: Curve.Linear
+          })
+          .onClick(() => {
+            this.qualifiedClick = 0.9;
+            setTimeout(() => {
+              this.isQualified = 1
+              this.qualifiedClick = 1;
+            }, 200);
+          })
+        }
+        .height('15%')
+        .width('100%')
+        .margin({top:'5%'})
+      }
+      .width('70%')
+      .height('74%')
+      .margin({left:'15%',right:'15%'})
+      .alignItems(HorizontalAlign.Start)
+      Column() {
+          Divider()
+            .vertical(false)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Row() {
+              Text('取消')
+                .fontColor($r('app.color.60FFFFFF'))
+                .fontSize($r('app.float.fontSize_30'))
+            }
+            .justifyContent(FlexAlign.Center)
+            .width('50%')
+            .onClick(() => this.controller.close())
+            Divider()
+              .vertical(true)
+              .strokeWidth(1)
+              .color($r('app.color.15FFFFFF'))
+            Row() {
+              Text('确定')
+                .fontColor($r('app.color.007AFF'))
+                .fontSize($r('app.float.fontSize_30'))
+            }
+            .justifyContent(FlexAlign.Center)
+            .width('50%')
+            .onClick(() => {
+            })
+          }
+        }
+        .width('100%')
+        .height('10%')
+    }
+    .height('55%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 431 - 0
entry/src/main/ets/view/SwitchProductDialog.ets

@@ -0,0 +1,431 @@
+import CommonConstants from '../common/constants/CommonConstants'
+import preferencesUtil from '../common/util/PerferencesUtil'
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import OperationComponent from '../viewmodel/process/OperationComponent'
+import ProcessInfo from '../viewmodel/process/ProcessInfo'
+import TaskSeqVO from '../viewmodel/process/TaskSeqInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import WorkOrderInfo from '../viewmodel/WorkOrderInfo'
+import { WorkOrderPage } from '../viewmodel/WorkOrderPage'
+import promptAction from '@ohos.promptAction'
+
+//切换产品
+@CustomDialog
+export struct SwitchProductDialog {
+  private scrollerList: Scroller = new Scroller()
+  //查询报工
+  @State queryTaskSeq: TaskSeqVO[] = []
+  //选中的工单
+  @Link selectWorkOder: WorkOrderInfo
+  //工单集合
+  @Link workOrders: WorkOrderInfo[]
+  //当前工序号
+  @Link currentOperationId: string
+  //当前工位
+  @Consume('currentStationId') currentStationId: string
+  //当前工序已报工数量
+  @State reportedNum:number = 0
+  //计划报工数量
+  @State planReportNum:number = 0
+  //选择的按钮索引(默认加载全部)
+  @State selectedButtonIndex: number = 0
+  //工步
+  @Link opComponents: OperationComponent[]
+  //扫描的流水号/序列/铭牌号
+  @Link scanSeqValue: string
+  // 扫码开工后的生产过程信息
+  @Link process: ProcessInfo
+  // 扫码开工状态(0:未开工 1:已开工)
+  @Link scanState: number
+  // 当前流转卡号
+  @Link seqNo: string
+  // 当前订单扫码流水号数量
+  @Link scanSeqNos: number
+  //所有流水号
+  allTaskSeq: TaskSeqVO[] = []
+
+  controller: CustomDialogController
+  onQueryTask = async (currentStateList: Array<number>): Promise<number> => {
+    let res = await ProcessRequest.post('/api/v1/plan/task/list', {
+      stationId:this.currentStationId,
+      workOrderCode:this.selectWorkOder.workOrderCode,
+      operationId:this.currentOperationId,
+      stateList:currentStateList
+    } as RequestParamModel) as TaskSeqVO[];
+    this.queryTaskSeq = res
+    if (currentStateList.length <= 0) {
+      this.allTaskSeq = this.queryTaskSeq
+    }
+    return res.length
+  }
+
+  //扫码流水号不属于当前工单,确认是否切换工单弹窗
+  switchWorkOrderConfirm: () => void = () => {}
+
+  onSwitchProduct=async ()=> {
+    if (!this.selectWorkOder.workOrderCode || !this.currentStationId) {
+      return
+    }
+    this.opComponents = []
+    this.opComponents = await ProcessRequest.get('/api/v1/op/compent/get/' + this.currentOperationId)
+    if (this.opComponents) {
+      for (const element of this.opComponents) {
+        if (CommonConstants.OPERATION_COMPONENT_TYPE.has(element.compentType!)) {
+          element.compentType = CommonConstants.OPERATION_COMPONENT_TYPE.get(element.compentType!)
+        }
+      }
+    }
+    // 过滤掉点检工步
+    this.opComponents = this.opComponents.filter(item => item.compentType !== '5');
+    this.opComponents.unshift({
+      compentName: '自检',
+      compentType: '5',
+      deleted: 0,
+      operationId: this.currentOperationId,
+      processRouteId: '0',
+      remark: '',
+      sortNum: 0
+    })
+  }
+
+  handleAllClick():void {
+    this.onQueryTask([])
+  }
+
+  handleReportedClick():void{
+    this.onQueryTask([2])
+  }
+
+  handleUnreportedClick():void{
+    this.onQueryTask([-1,0,1])
+  }
+
+  async aboutToAppear() {
+    this.reportedNum = await this.onQueryTask([2]);
+    this.planReportNum = await this.onQueryTask([]);
+  }
+
+  @Builder
+  buildButton(index: number, text: string,onClick: () => void) {
+    Row(){
+      if(this.selectedButtonIndex === index)
+      {
+        Image($r('app.media.process_radio_check'))
+          .width($r('app.float.virtualSize_24'))
+          .height($r('app.float.virtualSize_24'))
+          .fillColor($r('app.color.0A84FF'))
+      }else{
+        Image($r('app.media.process_radio_no_check'))
+          .width($r('app.float.virtualSize_24'))
+          .height($r('app.float.virtualSize_24'))
+          .fillColor($r('app.color.FFFFFF'))
+      }
+      Row(){}.width('10%')
+      Text(text)
+        .fontSize($r('app.float.fontSize_16'))
+        .fontColor($r('app.color.FFFFFF'))
+    }
+    .justifyContent(FlexAlign.Center)
+    .width('31%')
+    .height('100%')
+    .backgroundColor(this.selectedButtonIndex === index ? $r('app.color.200A84FF') : $r('app.color.20FFFFFF')) // 选中蓝/未选中灰
+    .borderRadius($r('app.float.virtualSize_16'))
+    .margin({ right:'3%'})
+    .onClick(() => {
+      this.selectedButtonIndex = index
+      onClick();
+    })
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("切换产品")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+     Row(){
+       Column(){
+         Row(){
+           Column({space:3}){
+             Text(this.selectWorkOder.materialName)
+               .fontSize($r('app.float.fontSize_24'))
+               .fontColor($r('app.color.FFFFFF'))
+             Text(this.selectWorkOder.materialCode)
+               .fontSize($r('app.float.fontSize_12'))
+               .fontColor($r('app.color.FFFFFF'))
+               .fontWeight(FontWeight.Lighter)
+             Row(){
+               Text("工单 ")
+                 .fontSize($r('app.float.fontSize_16'))
+                 .fontColor($r('app.color.FFFFFF'))
+                 .fontWeight(FontWeight.Lighter)
+               Text(this.selectWorkOder.workOrderCode)
+                 .fontSize($r('app.float.fontSize_16'))
+                 .fontColor($r('app.color.FFFFFF'))
+             }
+           }
+           .height('100%')
+           .width('70%')
+           .justifyContent(FlexAlign.End)
+           .alignItems(HorizontalAlign.Start)
+           Column(){
+             Row(){
+               Text(`${this.reportedNum}/`)
+                 .fontSize($r('app.float.fontSize_38'))
+                 .fontColor($r('app.color.FFFFFF'))
+               Text(`${this.planReportNum}`)
+                 .fontSize($r('app.float.fontSize_38'))
+                 .fontColor($r('app.color.60FFFFFF'))
+             }
+             Text('当前工序已报工/计划')
+               .fontSize($r('app.float.fontSize_12'))
+               .fontColor($r('app.color.FFFFFF'))
+               .fontWeight(FontWeight.Lighter)
+
+           }
+           .height('100%')
+           .width('30%')
+           .justifyContent(FlexAlign.End)
+           .alignItems(HorizontalAlign.End)
+         }
+         .height('10%')
+         .width('100%')
+         Row(){
+           Row() {
+             this.buildButton(0, '全部', () => this.handleAllClick())
+             this.buildButton(1, '已报工', () => this.handleReportedClick())
+             this.buildButton(2, '未报工', () => this.handleUnreportedClick())
+           }.width('100%')
+           .height('55%')
+         }
+         .height('10%')
+         .width('80%')
+         .margin({right:'20%'})
+         .justifyContent(FlexAlign.Start)
+         Column(){
+           List({space: 8, scroller:this.scrollerList}) {
+             ForEach(this.queryTaskSeq, (item:TaskSeqVO) => {
+               ListItem() {
+                 taskSeqItem({
+                   item:item,
+                   scanSeqValue:this.scanSeqValue,
+                   selectedButtonIndex:this.selectedButtonIndex
+                 })
+                   .onClick(()=>{
+                     this.scanSeqValue = item.seqNo!
+                   })
+               }
+             })
+           }
+           .width('100%')
+           .height('100%')
+         }
+         .height('80%')
+         .width('100%')
+
+       }
+       .height('100%')
+       .width('46%')
+       Divider()
+         .vertical(true)
+         .strokeWidth(1)
+         .color($r('app.color.15FFFFFF'))
+         .margin({ bottom: '2%'})
+       Column(){
+         Text("扫描流水/序列/铭牌号")
+           .fontSize($r('app.float.fontSize_16'))
+           .fontColor($r('app.color.FFFFFF'))
+           .margin({left:'22%'})
+         Row() {
+           // 左侧二维码图标
+           Image($r('app.media.material_qr_code'))
+             .width($r('app.float.virtualSize_24'))
+             .height($r('app.float.virtualSize_24'))
+             .fillColor($r('app.color.FFFFFF'))
+             .objectFit(ImageFit.Contain)
+             .margin({left:'5%'})
+           // 扫码输入框
+           TextInput({text:this.scanSeqValue, placeholder: '请扫描或录入流水、序列或铭牌号' })
+             .type(InputType.Normal)
+             .placeholderFont({ size: $r('app.float.fontSize_16') })
+             .placeholderColor($r('app.color.30FFFFFF'))
+             .fontSize($r('app.float.fontSize_16'))
+             .fontColor($r('app.color.FFFFFF'))
+             .enableKeyboardOnFocus(false)
+             // .onSubmit(() => {
+             //   // 流转卡号是否有效
+             //   let seqNoFlag = false
+             //   for (const element of this.allTaskSeq) {
+             //     if (element.seqNo === this.scanSeqValue) {
+             //       seqNoFlag = true
+             //       break;
+             //     }
+             //   }
+             //   if (!seqNoFlag) {
+             //     promptAction.showToast({
+             //       message: `${this.scanSeqValue}不是有效的流水号`,
+             //       duration: 1500,
+             //       bottom: 100
+             //     })
+             //     this.scanSeqValue = ''
+             //     return
+             //   }
+             //   // this.seqNo = this.scanSeqValue
+             //   // this.controller.close();
+             // })
+             .onChange((value: string) => {
+               this.scanSeqValue = value;
+             })
+         }
+         .height('8%')
+         .width('65%')
+         .borderRadius($r('app.float.virtualSize_16'))
+         .backgroundColor($r('app.color.000000'))
+         .margin({top:'2%',bottom:'5%',left:'20%'})
+       }
+       .justifyContent(FlexAlign.Center)
+       .alignItems(HorizontalAlign.Start)
+       .width('46%')
+       .height('100%')
+     }
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .width('100%')
+      .height('81%')
+      .margin({ top: '2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(async () => {
+            // 流转卡号是否有效
+            let seqNoFlag = false
+            for (const element of this.allTaskSeq) {
+              if (element.seqNo === this.scanSeqValue) {
+                seqNoFlag = true
+                break;
+              }
+            }
+            if (!seqNoFlag) {
+              let queryRes = await ProcessRequest.post('/api/v1/plan/workOrder/taskPage2', {
+                stationId: this.currentStationId,
+                seqNo: this.scanSeqValue,
+                queryComplete: 0,
+              } as RequestParamModel) as WorkOrderPage;
+              this.workOrders = queryRes?.records ?? []
+              if (this.workOrders && this.workOrders.length > 0) {
+                this.switchWorkOrderConfirm()
+              } else {
+                promptAction.showToast({
+                  message: `${this.scanSeqValue}不是有效的流水号`,
+                  duration: 1500,
+                  bottom: 100
+                })
+                this.scanSeqValue = ''
+                return
+              }
+              return
+            }
+            this.scanState = 0
+            this.seqNo = this.scanSeqValue
+            this.process = {}
+            this.onSwitchProduct();
+            // 保存此流转卡号到数据库中
+            let seqNos: string[] = []
+            seqNos = await preferencesUtil.get(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, seqNos)
+            if (seqNos.includes(this.seqNo)) {
+              this.controller.close();
+              return
+            }
+            seqNos.push(this.seqNo)
+            preferencesUtil.put(CommonConstants.PREFERENCE_INSTANCE_NAME, this.selectWorkOder.workOrderCode!, seqNos)
+            this.scanSeqNos = seqNos.length
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+@Component
+export struct taskSeqItem{
+  @Prop item: TaskSeqVO
+  @Link scanSeqValue:string
+  @State scaleValue : number = 1
+  @Link selectedButtonIndex:number
+  build() {
+    Row() {
+      Text('S/N ')
+        .fontSize($r('app.float.fontSize_16'))
+        .fontColor($r('app.color.FFFFFF'))
+        .fontWeight(FontWeight.Lighter)
+        .margin({ left:'2%'})
+      Text(this.item.seqNo)
+        .fontSize($r('app.float.fontSize_16'))
+        .fontColor($r('app.color.FFFFFF'))
+        .textAlign(TextAlign.Start)
+      Blank()
+      if(this.selectedButtonIndex != 2) {
+        Text(this.item.state! === '2' ? `报工时间:${this.item.updated}` : '')
+          .fontSize($r('app.float.fontSize_16'))
+          .fontColor($r('app.color.60FFFFFF'))
+          .fontWeight(FontWeight.Lighter)
+          .margin({ right:'2%'})
+      }
+    }.width('100%')
+    .justifyContent(FlexAlign.Start)
+    .backgroundColor($r('app.color.20FFFFFF'))
+    .padding(10)
+    .borderRadius($r('app.float.virtualSize_16'))
+    .scale({ x: this.scaleValue, y: this.scaleValue })
+    .animation({
+      duration: 200,
+      curve: Curve.Linear  // 弹性曲线更生动
+    })
+    .onClick(() => {
+      this.scaleValue = 0.9;  // 点击时缩小
+      setTimeout(() => {
+        this.scanSeqValue = this.item?.seqNo??''
+        this.scaleValue = 1;
+      }, 200);
+    })
+  }
+}

+ 353 - 0
entry/src/main/ets/view/SwitchStationDialog.ets

@@ -0,0 +1,353 @@
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import { DeptInfo, ProductionLine, WorkstationInfo } from '../viewmodel/UserInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+
+@CustomDialog
+export struct SwitchStationDialog {
+  private scrollerList: Scroller = new Scroller()
+  //当前工位
+  @Consume('currentStation') currentStation:string
+  //当前部门
+  @Consume('currentDept') currentDept:string
+  //当前产线code
+  @Consume('currentPLCode') currentPLCode:string
+  // 当前工位id
+  @Consume('currentStationId') currentStationId:string
+
+  @State selectStationIndex: number = -1
+  //工位列表
+  @State stationsList : WorkstationInfo[]=[]
+  loadStations = async () => {
+    this.stationsList = await ProcessRequest.post(`/api/v1/base/station/list`, {} as RequestParamModel) as WorkstationInfo[];
+  };
+  controller: CustomDialogController
+  aboutToAppear(): void {
+    this.loadStations();
+  }
+
+  private onSelectStation(index: number) {
+    this.selectStationIndex = index
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("切换工位")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column(){
+        List({space:10,scroller:this.scrollerList}) {
+          ForEach(this.stationsList, (item:WorkstationInfo,index) => {
+            ListItem() {
+                Column(){
+                  Text(item.name)
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor($r('app.color.FFFFFF'))
+                }  .backgroundColor(index === this.selectStationIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF'))
+                .borderRadius($r('app.float.virtualSize_16'))
+                .padding(8)
+                .width('100%')
+                .height('12%')
+                .alignItems(HorizontalAlign.Center)
+                .justifyContent(FlexAlign.Center)
+                .border({
+                  width: index === this.selectStationIndex ? 2 : 0,
+                  color: index === this.selectStationIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF')
+                })
+                .onClick(() => {
+                  this.onSelectStation(index)
+                })
+            }
+          })
+        }
+        .width('70%')
+        .height('100%')
+      }
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .width('100%')
+      .height('81%')
+      .margin({ top: '2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            if (this.stationsList && this.selectStationIndex >=0 && this.stationsList[this.selectStationIndex]) {
+              this.currentStation = this.stationsList[this.selectStationIndex].name ?? ""
+              this.currentStationId = this.stationsList[this.selectStationIndex].id ?? ""
+            }
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+@CustomDialog
+export struct SwitchProductLineDialog{
+  private scrollerList: Scroller = new Scroller()
+  //当前产线
+  @Consume('currentProductLine') currentProductLine:string
+  //当前产线code
+  @Consume('currentPLCode') currentPLCode:string
+
+  @State selectProductLineIndex: number = -1
+  //产线列表
+  @State productLineList : ProductionLine[]=[]
+  loadStations = async () => {
+    this.productLineList = await ProcessRequest.post('api/v1/base/productionLine/list/list', {}as RequestParamModel)  as ProductionLine[];
+  };
+  controller: CustomDialogController
+  aboutToAppear(): void {
+    this.loadStations();
+  }
+
+  private onSelectProductLine(index: number) {
+    this.selectProductLineIndex = index
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("切换工位")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column(){
+        List({space:10,scroller:this.scrollerList}) {
+          ForEach(this.productLineList, (item:ProductionLine,index) => {
+            ListItem() {
+              Column(){
+                Text(item.name)
+                  .fontSize($r('app.float.fontSize_24'))
+                  .fontColor($r('app.color.FFFFFF'))
+              }  .backgroundColor(index === this.selectProductLineIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF'))
+              .borderRadius($r('app.float.virtualSize_16'))
+              .padding(8)
+              .width('100%')
+              .height('12%')
+              .alignItems(HorizontalAlign.Center)
+              .justifyContent(FlexAlign.Center)
+              .border({
+                width: index === this.selectProductLineIndex ? 2 : 0,
+                color: index === this.selectProductLineIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF')
+              })
+              .onClick(() => {
+                this.onSelectProductLine(index)
+              })
+            }
+          })
+        }
+        .width('70%')
+        .height('100%')
+      }
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .width('100%')
+      .height('81%')
+      .margin({ top: '2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.currentProductLine = this.productLineList[this.selectProductLineIndex].name??""
+            this.currentPLCode =  this.productLineList[this.selectProductLineIndex].code??""
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+@CustomDialog
+export struct SwitchDeptDialog{
+  private scrollerList: Scroller = new Scroller()
+  //当前部门
+  @Consume('currentDept') currentDept:string
+  // 当前组织id
+  @Consume('currentOrgId') currentOrgId:number
+
+  @State selectDeptIndex: number = -1
+  //部门列表
+  @State departmentsList : DeptInfo[]=[]
+  loadStations = async () => {
+    this.departmentsList = await ProcessRequest.get('/api/v1/sys/dept/orgList', {})  as DeptInfo[];
+  };
+  controller: CustomDialogController
+  aboutToAppear(): void {
+    this.loadStations();
+  }
+
+  private onSelectStation(index: number) {
+    this.selectDeptIndex = index
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("切换部门")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }
+      .height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column(){
+        List({space:10,scroller:this.scrollerList}) {
+          ForEach(this.departmentsList, (item:DeptInfo,index) => {
+            ListItem() {
+              Column(){
+                Text(item.deptName)
+                  .fontSize($r('app.float.fontSize_24'))
+                  .fontColor($r('app.color.FFFFFF'))
+              }  .backgroundColor(index === this.selectDeptIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF'))
+              .borderRadius($r('app.float.virtualSize_16'))
+              .padding(8)
+              .width('100%')
+              .height('12%')
+              .alignItems(HorizontalAlign.Center)
+              .justifyContent(FlexAlign.Center)
+              .border({
+                width: index === this.selectDeptIndex ? 2 : 0,
+                color: index === this.selectDeptIndex ? $r('app.color.2030D158') : $r('app.color.20FFFFFF')
+              })
+              .onClick(() => {
+                this.onSelectStation(index)
+              })
+            }
+          })
+        }
+        .width('70%')
+        .height('100%')
+      }
+      .justifyContent(FlexAlign.SpaceEvenly)
+      .width('100%')
+      .height('81%')
+      .margin({ top: '2%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Row() {
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => this.controller.close())
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row() {
+            Text('确定')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.currentDept = this.departmentsList[this.selectDeptIndex].deptName??""
+            this.currentOrgId = this.departmentsList[this.selectDeptIndex].id??0
+            this.controller.close();
+          })
+        }
+      }
+      .width('100%')
+      .height('8%')
+
+    }
+    .height('71%')
+    .width('30%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+

+ 156 - 0
entry/src/main/ets/view/SwitchUserDialog.ets

@@ -0,0 +1,156 @@
+// 切换用户
+import CommonConstants from '../common/constants/CommonConstants'
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import { UserInfo } from '../viewmodel/UserInfo'
+
+@CustomDialog
+export struct SwitchUserDialog {
+  //当前用户
+  @Consume('currentUserName') currentUserName: string
+  @State userName: string = ''
+  @State password : string = ''
+  // 当前组织id
+  @Consume('currentOrgId') currentOrgId: number
+  //当前产线code
+  @Consume('currentPLCode') currentPLCode: string
+  // 当前工位id
+  @Consume('currentStationId') currentStationId: string
+  @Consume('currentUserId') currentUserId: number
+
+  controller: CustomDialogController
+
+  login=async ()=>{
+    let token:string = await ProcessRequest.post('api/auth/aioLogin', {
+      orgId:this.currentOrgId,
+      password: this.password,
+      userName: this.userName,
+      proCode: this.currentPLCode,
+      stationId: this.currentStationId
+      } as RequestParamModel) ;
+    if (!token || token.length == 0)  return
+    CommonConstants.AUTH_TOKEN = token
+    this.currentUserName = this.userName
+    let res:UserInfo = await ProcessRequest.get('/api/auth') ;
+    this.currentUserId = res.id! as number
+    this.controller.close()
+}
+
+  build() {
+    Column() {
+      Column(){
+        Text("切换用户")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('25%')
+      .justifyContent(FlexAlign.Center)
+
+      Column() {
+        Text("账户")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .width('70%')
+          .textAlign(TextAlign.Start)
+          .margin({left:'2%',bottom:'2%'})
+        Row() {
+          Image($r('app.media.process_user_name'))
+            .width($r('app.float.virtualSize_40'))
+            .height($r('app.float.virtualSize_40'))
+            .fillColor($r('app.color.FFFFFF'))
+            .margin({left:'2%'})
+
+          TextInput({text:this.currentUserName,placeholder:"请录入账户"})
+            .width('85%')
+            .height('100%')
+            .backgroundColor('#000000')
+            .fontSize($r('app.float.fontSize_16'))
+            .placeholderFont({size:$r('app.float.fontSize_16')})
+            .placeholderColor($r('app.color.30FFFFFF'))
+            .fontColor($r('app.color.FFFFFF'))
+            .onChange((value: string) => {
+              this.userName = value;
+            })
+        }
+        .backgroundColor($r('app.color.000000'))
+        .width('70%')
+        .height('30%')
+        .borderRadius($r('app.float.virtualSize_16'))
+        Text("密码")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .width('70%')
+          .textAlign(TextAlign.Start)
+          .margin({left:'2%',bottom:'2%',top:"5%"})
+        Row() {
+          Image($r('app.media.process_password'))
+            .width($r('app.float.virtualSize_40'))
+            .height($r('app.float.virtualSize_40'))
+            .fillColor($r('app.color.FFFFFF'))
+            .margin({left:'2%'})
+          TextInput({text:this.password, placeholder:"请录入密码"} )
+            .width('85%')
+            .height('100%')
+            .placeholderFont({size:$r('app.float.fontSize_16')})
+            .placeholderColor($r('app.color.30FFFFFF'))
+            .backgroundColor('#000000')
+            .fontSize($r('app.float.fontSize_16'))
+            .fontColor($r('app.color.FFFFFF'))
+            .type(InputType.Password)
+            .onChange((value: string) => {
+              this.password = value;
+            })
+        }
+        .width('70%')
+        .height('30%')
+        .backgroundColor($r('app.color.000000'))
+        .borderRadius($r('app.float.virtualSize_16'))
+      }
+      .justifyContent(FlexAlign.Start)
+      .height('40%')
+      .width('100%')
+      .margin({bottom:'16%'})
+      // 按钮区域
+      Column(){
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row(){
+          Row(){
+            Text('取消')
+              .fontColor($r('app.color.60FFFFFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.controller.close()
+          })
+          Divider()
+            .vertical(true)
+            .strokeWidth(1)
+            .color($r('app.color.15FFFFFF'))
+          Row(){
+            Text('确认')
+              .fontColor($r('app.color.007AFF'))
+              .fontSize($r('app.float.fontSize_30'))
+          }
+          .justifyContent(FlexAlign.Center)
+          .width('50%')
+          .onClick(() => {
+            this.login()
+          })
+        }
+      }
+      .width('100%')
+      .height('13%')
+
+    }
+    .height('44%')
+    .width('31%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .borderColor($r('app.color.000000'))
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 127 - 0
entry/src/main/ets/view/WorkInstructionsDialog.ets

@@ -0,0 +1,127 @@
+//作业指导
+import ProcessRequest from '../common/util/request/ProcessRequest'
+import { DrawingInfo } from '../viewmodel/DrawingInfo'
+import RequestParamModel from '../viewmodel/RequestParamModel'
+import CommonConstants from'../common/constants/CommonConstants'
+
+@CustomDialog
+export struct WorkInstructionsDialog {
+  private scrollerMaterial: Scroller = new Scroller()
+  //作业图片列表
+  @State drawingList: DrawingInfo[] = []
+
+  materialCode: string = ''
+
+  controller: CustomDialogController
+  onConfirm: () => void = () => {
+  }
+
+  //加载所有作业
+  loadWorkInstructions = async () => {
+    this.drawingList = await ProcessRequest.post('/api/v1/base/drawing/list', {
+      materialCode: this.materialCode!,
+      drawingDictValue: 'esop',
+    } as RequestParamModel) as DrawingInfo[];
+  };
+
+  aboutToAppear(): void {
+    this.loadWorkInstructions();
+  }
+
+  build() {
+    Column() {
+      Column() {
+        Text("作业指导")
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_30'))
+      }.height('8%')
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+
+      Column() {
+        Grid(this.scrollerMaterial) {
+          ForEach(this.drawingList, (item: DrawingInfo) => {
+            GridItem() {
+              Column(){
+                Image(CommonConstants.PICTURE_URL_PREFIX+item.drawingPath)
+                  .width('100%')
+                  .height('65%')
+                  .objectFit(ImageFit.Fill)
+                  .borderRadius($r('app.float.virtualSize_24'))
+                Column() {
+                  Text(`文件名称:${item.fileName}`)
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .textAlign(TextAlign.Start)
+                    .fontWeight(FontWeight.Lighter)
+                  Text(`文件编号: ${item.drawingCode}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                  Text(`版本号: ${item.drawingVersion} `)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                  Text(`上传时间: ${item.updated}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                  Text(`编辑人员: ${item.updator}`)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .textAlign(TextAlign.Start)
+                }
+                .alignItems(HorizontalAlign.Start)
+                .height('35%')
+                .width('96%')
+                .margin({left:'4%',top:"2%"})
+              }
+            }
+            .height('50%')
+            .backgroundColor( $r('app.color.20FFFFFF')) // 选中状态加深
+            .borderRadius($r('app.float.virtualSize_24'))
+            // .onClick(() => {
+            // })
+          })
+        }
+        .columnsTemplate('1fr 1fr 1fr')
+        .columnsGap(10)
+        .rowsGap(10)
+        .width('100%')
+        .height('97%')
+        .padding(10)
+      }
+      .height('81%')
+      .margin({left:'1%',right:'1%'})
+
+      Column() {
+        Divider()
+          .vertical(false)
+          .strokeWidth(1)
+          .color($r('app.color.15FFFFFF'))
+        Row() {
+          Text('关闭')
+            .fontColor($r('app.color.60FFFFFF'))
+            .fontSize($r('app.float.fontSize_30'))
+        }
+        .width('100%')
+        .justifyContent(FlexAlign.Center)
+        .height('8%')
+        .width('50%')
+        .onClick(() => this.controller.close())
+      }
+    }
+    .height('71%')
+    .width('62%')
+    .backgroundColor($r('app.color.2A2A2A'))
+    .justifyContent(FlexAlign.End)
+    .alignItems(HorizontalAlign.Start)
+    .borderColor($r('app.color.000000'))
+    .borderWidth(1)
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}

+ 226 - 0
entry/src/main/ets/view/process/DeviceCheckView.ets

@@ -0,0 +1,226 @@
+import ProcessRequest from '../../common/util/request/ProcessRequest'
+import ProcessCheck from '../../viewmodel/process/ProcessCheck'
+import ProcessInfo from '../../viewmodel/process/ProcessInfo'
+import RequestParamModel from '../../viewmodel/RequestParamModel'
+import promptAction from '@ohos.promptAction'
+import PageInfo from '../../viewmodel/PageInfo'
+import DictValue from '../../viewmodel/DictValue'
+import ProcessDeviceDailyCheck from '../../viewmodel/process/ProcessDeviceDailyCheck'
+import HashMap from '@ohos.util.HashMap'
+import TimeUtil from '../../common/util/TimeUtil'
+import CommonConstants from '../../common/constants/CommonConstants'
+
+// 设备点检工序
+@Component
+export struct DeviceCheckView {
+  // 设备每日点检
+  @Link deviceChecks: ProcessDeviceDailyCheck[]
+  // 扫码开工状态(0:未开工 1:已开工)
+  @Link @Watch('queryDeviceCheckByScanState') scanState: number
+  // 当前流转卡号
+  @Link seqNo: string
+  // 选中工序id
+  @Link selectOperationId: string
+  // 扫码开工后的生产过程信息
+  @Link process: ProcessInfo
+  //查找设备编码
+  @State queryDeviceNo: string = 'test000'
+  // 设备类型(key为数据字典值,value为数据字典标签)
+  @State deviceTypes: HashMap<string, string> = new HashMap()
+  // 当前工位IP
+  @Consume('stationIp') stationIp: string
+  deviceTypeDictCode: string = 'device_type'
+  private scrollerDevice: Scroller = new Scroller()
+
+  // 根据开工状态查询 工序设备点检/工序设备点检历史操作
+  async queryDeviceCheckByScanState() {
+    if (this.scanState === 1) {
+      this.deviceChecks = await ProcessRequest.post('/api/v1/process/deviceCheck/queryAndSave', {
+        stationIp: this.stationIp,
+        processId: this.process.id!,
+      } as RequestParamModel)
+    } else {
+      let result = await ProcessRequest.post('/api/v1/operation/device/page', {
+        operationId: this.selectOperationId,
+        pageNo: 1,
+        pageSize: 99999
+      } as RequestParamModel) as PageInfo;
+      this.deviceChecks = result.records as ProcessCheck[] || []
+    }
+  }
+
+  async aboutToAppear() {
+    let deviceDicts: DictValue[] = await ProcessRequest.get(`/api/v1/sys/dictData/queryByType/${this.deviceTypeDictCode}`)
+    if (deviceDicts) {
+      this.deviceTypes = new HashMap()
+      for (const dict of deviceDicts) {
+        this.deviceTypes.set(dict.dictValue!, dict.dictLabel!);
+      }
+    }
+    this.queryDeviceCheckByScanState()
+  }
+
+  build() {
+    Column() {
+      Text("扫描设备 ")
+        .fontColor($r('app.color.FFFFFF'))
+        .fontSize($r('app.float.fontSize_24'))
+        .height('7.9%')
+        .width('97.2%')
+      Row(){
+        Row() {
+          // 左侧二维码图标
+          Image($r('app.media.material_qr_code'))
+            .width($r('app.float.virtualSize_32'))
+            .height($r('app.float.virtualSize_32'))
+            .fillColor($r('app.color.FFFFFF'))
+            .objectFit(ImageFit.Contain)
+            .margin({left:'0.7%'})
+          // 扫码输入框
+          TextInput({text:this.queryDeviceNo, placeholder: '请扫描设备编码' })
+            .type(InputType.Normal)
+            .placeholderFont({ size: $r('app.float.fontSize_16') })
+            .placeholderColor($r('app.color.30FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+            .fontColor($r('app.color.FFFFFF'))
+            .enableKeyboardOnFocus(false)
+            .onSubmit(async () => {
+              if (this.deviceChecks) {
+                for (const element of this.deviceChecks) {
+                  if (element.deviceNo === this.queryDeviceNo) {
+                    promptAction.showToast({
+                      message: `设备今日已点检,无需重复点检!`,
+                      duration: 1500,
+                      bottom: 100
+                    })
+                    return
+                  }
+                }
+              }
+              let check: ProcessDeviceDailyCheck = await ProcessRequest.get(`/api/v1/process/deviceDailyCheck/getServiceLifeByDeviceNo/${this.queryDeviceNo}`)
+              check.stationIp = this.stationIp
+              await ProcessRequest.post('/api/v1/process/deviceDailyCheck/add', check)
+              this.deviceChecks = []
+              this.deviceChecks = await ProcessRequest.post('/api/v1/process/deviceCheck/queryAndSave', {
+                stationIp: this.stationIp,
+                processId: this.process.id!,
+              } as RequestParamModel)
+            })
+            .onChange((value: string) => {
+              this.queryDeviceNo = value;
+            })
+        }
+        .height('100%')
+        .width('27.2%')
+        .borderRadius($r('app.float.virtualSize_16'))
+        .backgroundColor($r('app.color.000000'))
+        Text(TimeUtil.getCurrentDate())
+          .fontColor($r('app.color.FFFFFF'))
+          .fontSize($r('app.float.fontSize_16'))
+          .margin({left:'3%'})
+      }
+      .height('7.1%')
+      .width('97.2%')
+
+      Column() {
+        List({scroller: this.scrollerDevice }) {
+          ForEach(this.deviceChecks, (item: ProcessDeviceDailyCheck) => {
+            ListItem() {
+              Row() {
+                Column(){
+                  Text(this.deviceTypes && item.deviceType && this.deviceTypes.hasKey(item.deviceType) ? this.deviceTypes.get(item.deviceType) : '')
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor($r('app.color.FFFFFF'))
+                  Text('名称:' + item.deviceNo && item.deviceName ? item.deviceName : '')
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontWeight(FontWeight.Lighter)
+                    .margin({top:'2%',bottom:'1%'})
+                  Text('编码:' + item.deviceNo ? item.deviceNo : '')
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontWeight(FontWeight.Lighter)
+                }
+                .width('30%')
+                .height('100%')
+                .justifyContent(FlexAlign.Center)
+                .alignItems(HorizontalAlign.Start)
+                .margin({right:'16%'})
+
+                Column({space:5}){
+                  Text(`计量有效期:`)
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontWeight(FontWeight.Lighter)
+                  if (this.scanState === 1 && item.deviceNo) {
+                    Row(){
+                      Image(item.meteringState! === 1 ? $r('app.media.device_normal') : $r('app.media.device_expire'))
+                        .width($r('app.float.virtualSize_24'))
+                        .height($r('app.float.virtualSize_24'))
+                        .margin({left:'2%'})
+                      Text(item.meteringDate ? `${item.meteringDate}` : '长期有效')
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontColor(item.meteringState! === 1 ? $r('app.color.30D158') : $r('app.color.FF453A'))
+                        .margin({left:'2%'})
+                    }
+                    .width('65%')
+                    .borderRadius($r('app.float.virtualSize_16'))
+                    .backgroundColor($r('app.color.000000'))
+                    .height('25%')
+                    .alignItems(VerticalAlign.Center)
+                  }
+                }
+                .width('22%')
+                .height('100%')
+                .justifyContent(FlexAlign.Center)
+                .alignItems(HorizontalAlign.Start)
+                Column({space:5}){
+                  Text(`维保有效期:`)
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontColor($r('app.color.FFFFFF'))
+                    .fontWeight(FontWeight.Lighter)
+                  if (this.scanState === 1 && item.deviceNo) {
+                    Row() {
+                      Image(item.warrantyState! === 1 ? $r('app.media.device_normal') : $r('app.media.device_expire'))
+                        .width($r('app.float.virtualSize_24'))
+                        .height($r('app.float.virtualSize_24'))
+                        .margin({ left: '2%' })
+                      Text(item.warrantyPeriod ? `${item.warrantyPeriod}` : '长期有效')
+                        .fontSize($r('app.float.fontSize_16'))
+                        .fontColor(item.warrantyState! === 1 ? $r('app.color.30D158') : $r('app.color.FF453A'))
+                        .margin({ left: '2%' })
+                    }
+                    .width('65%')
+                    .borderRadius($r('app.float.virtualSize_16'))
+                    .backgroundColor($r('app.color.000000'))
+                    .height('25%')
+                    .alignItems(VerticalAlign.Center)
+                  }
+                }
+                .width('22%')
+                .height('100%')
+                .justifyContent(FlexAlign.Center)
+                .alignItems(HorizontalAlign.Start)
+              }
+              .height('90%')
+              .width('97%')
+              .justifyContent(FlexAlign.Start)
+            }
+            .height('17%')
+            .width('100%')
+            .margin({ bottom: 8})
+            .borderRadius($r('app.float.virtualSize_16'))
+            .backgroundColor($r('app.color.10FFFFFF'))
+          })
+        }
+        .height('94%')
+        .width('100%')
+      }
+      .justifyContent(FlexAlign.Center)
+      .height('85%')
+      .width('97.2%')
+    }
+    .width('100%')
+    .height('100%')
+  }
+}

+ 284 - 0
entry/src/main/ets/view/process/MaterialCollectView.ets

@@ -0,0 +1,284 @@
+import ProcessRequest from '../../common/util/request/ProcessRequest'
+import OperationItem from '../../viewmodel/OperationItem'
+import ProcessInfo from '../../viewmodel/process/ProcessInfo'
+import ProcessMaterial from '../../viewmodel/process/ProcessMaterial'
+import RequestParamModel from '../../viewmodel/RequestParamModel'
+import WorkOrderInfo from '../../viewmodel/WorkOrderInfo'
+import promptAction from '@ohos.promptAction'
+import { ModifyMaterialNumDialog } from '../ModifyMaterialNumDialog'
+import PageInfo from '../../viewmodel/PageInfo'
+
+// 物料采集工序
+@Component
+export struct MaterialCollectView {
+  @State scanCode: string = ''
+  // 需要采集的工序物料
+  @State itemArray: OperationItem[] = []
+  // 总需求数
+  @State needNum: number = 0
+  // 实际数量(已采集数)
+  @State realNum: number = 0
+  // 扫码查到物料信息
+  scanMaterial: ProcessMaterial = {}
+  // 新增或者修改采集物料(1:新增 2:修改)(采集物料数量修改弹窗使用)
+  addOrModifyCollect: number = 1
+  // 扫码开工状态(0:未开工 1:已开工)
+  @Link @Watch('queryByScanState') scanState: number
+  // 当前流转卡号
+  @Link seqNo: string
+  // 选中工单
+  @Link selectWorkOder: WorkOrderInfo
+  // 选中工序id
+  @Link selectOperationId: string
+  // 扫码开工后的生产过程信息
+  @Link process: ProcessInfo
+  // 当前工位ID
+  @Consume('currentStationId') currentStationId: string
+
+  modifyMaterialNumDialogController: CustomDialogController = new CustomDialogController({
+    builder: ModifyMaterialNumDialog({
+      scanMaterial: this.scanMaterial,
+      addOrModifyCollect: this.addOrModifyCollect,
+      operationId: this.selectOperationId!,
+      processId: this.process.id!,
+      seqNo: this.seqNo,
+      confirm: async ()=>{
+        this.queryCollectHistory()
+      },
+    }),
+    autoCancel: true, // 点击遮罩关闭
+    customStyle: true,
+    alignment: DialogAlignment.Center,
+    maskColor: 'rgba(0,0,0,0.8)',  // 黑色遮罩
+  })
+
+  // 采集历史查询
+  async queryCollectHistory() {
+    this.itemArray = await ProcessRequest.post('/api/v1/process/itemRecord/list', {
+      operationId: this.selectOperationId,
+      workOrderCode: this.selectWorkOder.workOrderCode!,
+      seqNo: this.seqNo,
+      processId: this.process.id,
+      pageNo: 1,
+      pageSize: 999999} as RequestParamModel)
+  }
+
+  // 根据开工状态查询 需要采集的物料信息/物料采集历史
+  async queryByScanState() {
+    if (this.scanState === 1) {
+      this.queryCollectHistory()
+    } else {
+      let res = await ProcessRequest.post('/api/v1/op/operationItem/page', {
+        operationId: this.selectOperationId,
+        pageNo: 1,
+        pageSize: 999999} as RequestParamModel) as PageInfo
+      this.itemArray = res.records as OperationItem[] || []
+    }
+    if (this.itemArray) {
+      for (const element of this.itemArray) {
+        this.needNum += element.needNum || 0
+        this.realNum += element.realNum || 0
+      }
+    }
+  }
+
+  async aboutToAppear() {
+    this.queryByScanState()
+  }
+  
+  build() {
+    Column() {
+      // 扫码框和已采集统计
+      Row() {
+        Row() {
+          // 左侧二维码图标
+          Image($r('app.media.material_qr_code'))
+            .width($r('app.float.virtualSize_32'))
+            .height($r('app.float.virtualSize_32'))
+            .fillColor($r('app.color.FFFFFF'))
+            .margin({ left: '2%'})
+          // 扫码输入框
+          TextInput({text:this.scanCode, placeholder: '请扫描物料编码' })
+            .type(InputType.Normal)
+            .placeholderFont({ size: $r('app.float.fontSize_16') })
+            .placeholderColor($r('app.color.30FFFFFF'))
+            .fontSize($r('app.float.fontSize_30'))
+            .fontColor($r('app.color.FFFFFF'))
+            .enableKeyboardOnFocus(false)
+            .onSubmit(async () => {
+              if (this.scanState === 0) {
+                promptAction.showToast({
+                  message: '工序未开工,请先开工',
+                  duration: 1500,
+                  bottom: 100
+                })
+                return
+              }
+              let result: ProcessMaterial[] = await ProcessRequest.post('/api/v1/process/itemRecord/searchMaterial', {
+              operationId: this.selectOperationId,
+              processId: this.process.id!,
+              seqNo: this.seqNo,
+              scanCode: this.scanCode,
+              workOrderCode: this.selectWorkOder.workOrderCode!} as RequestParamModel)
+              if (result && result.length > 0) {
+                if (result[0].num! <= 0) {
+                  promptAction.showToast({
+                    message: `${result[0].batchNo!}批次物料已用完`,
+                    duration: 1500,
+                    bottom: 100
+                  })
+                }
+                this.scanMaterial = result[0]
+                for (const element of result) {
+                  element.operationId = this.selectOperationId
+                  element.processId = this.process.id!
+                  element.seqNo = this.seqNo
+                  element.workOrderCode = this.selectWorkOder.workOrderCode!
+                  element.trackBy = 'S'
+                  element.itemSeq = this.scanCode
+                }
+                if ('BATCH' === result[0].codeType!) {
+                  this.modifyMaterialNumDialogController.open()
+                } else {
+                  result[0].stationId = this.currentStationId
+                  await ProcessRequest.post('/api/v1/process/itemRecord/add', result)
+                }
+                this.queryCollectHistory()
+              }
+            })
+            .onChange((value: string) => {
+              this.scanCode = value
+            })
+        }
+        .height('52.8%')
+        .width('28.4%')
+        .borderRadius($r('app.float.virtualSize_16'))
+        .backgroundColor($r('app.color.000000'))
+
+        Column() {
+          Text(this.realNum + '/' + this.needNum)
+            .fontColor($r('app.color.FFFFFF'))
+            .fontSize($r('app.float.fontSize_16'))
+            .fontWeight(FontWeight.Regular)
+          Row() {
+            Row()
+              .width(((this.realNum * 100)/ this.needNum).toFixed(2) + '%')
+              .height('100%')
+              .borderRadius($r('app.float.virtualSize_24'))
+              .linearGradient({
+                angle: 90,
+                colors: [[$r('app.color.1050FF'), 0.0], [$r('app.color.73C3FF'), 1]]
+              })
+          }
+          .height('30%')
+          .width('100%')
+          .backgroundColor($r('app.color.10FFFFFF'))
+          .borderRadius($r('app.float.virtualSize_24'))
+        }
+        .width('13.1%')
+        .height('31.3%')
+        .alignItems(HorizontalAlign.End)
+        .justifyContent(FlexAlign.SpaceBetween)
+      }
+      .height('13.5%')
+      .width('93%')
+      .justifyContent(FlexAlign.SpaceBetween)
+      // 需要采集物料展示,动态排列容器
+      Scroll() {
+        Flex({ wrap: FlexWrap.Wrap, direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Start}) {
+          ForEach(this.itemArray, (item: OperationItem, index: number) => {
+            Column() {
+              Row() {
+                Column() {
+                  Text(item.itemName!)
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontWeight(FontWeight.Medium)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .maxLines(1)
+                  Text(item.itemCode!)
+                    .fontSize($r('app.float.fontSize_12'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .maxLines(1)
+                }
+                .width('45.5%')
+                .height('100%')
+                .justifyContent(FlexAlign.Center)
+                .alignItems(HorizontalAlign.Start)
+                Row() {
+                  if (item.realNum! >= item.needNum!) {
+                    Image($r('app.media.process_complete_test'))
+                      .height('100%')
+                      .width('45.5%')
+                      .objectFit(ImageFit.Fill)
+                  }
+                }
+                .width('50%')
+                .height('100%')
+                .alignItems(VerticalAlign.Top)
+                .justifyContent(FlexAlign.End)
+              }
+              .height('45.8%')
+              .width('100%')
+              .justifyContent(FlexAlign.End)
+              Row() {
+                Text(item.codeTypeStr ? item.codeTypeStr : '')
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Regular)
+                  .fontColor($r('app.color.FFFFFF'))
+                Row() {
+                  Text((item.realNum ? item.realNum : 0) + '/')
+                    .fontSize($r('app.float.fontSize_38'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                  Text((item.needNum ? item.needNum : 0) + item.unit!)
+                    .fontSize($r('app.float.fontSize_38'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                    .opacity(0.6)
+                }
+              }
+              .height('42.2%')
+              .width('91%')
+              .justifyContent(FlexAlign.SpaceBetween)
+              .alignItems(VerticalAlign.Bottom)
+            }
+            .width('32.8%')
+            .height('25.2%')
+            .borderRadius($r('app.float.virtualSize_16'))
+            .backgroundColor($r('app.color.20FFFFFF'))
+            .margin({ top: index > 2 ? '1%' : '0%', left: (index % 3) === 0 ? '0%' : '0.8%' })
+            .onClick(()=>{
+              if (this.scanState === 0) {
+                promptAction.showToast({
+                  message: '工序未开工,请先开工',
+                  duration: 1500,
+                  bottom: 100
+                })
+                return
+              }
+              this.scanMaterial = {
+                materialCode: item.itemCode!,
+                materialName: item.itemName!,
+                spec: item.itemModel!,
+                unitDictValue: item.unit!,
+                totalNum: item.needNum!,
+              }
+              this.addOrModifyCollect = 2
+              this.modifyMaterialNumDialogController.open()
+            })
+          })
+        }
+        .width('100%')
+      }
+      .scrollable(ScrollDirection.Vertical) // 垂直滚动
+      .scrollBar(BarState.Auto)
+      .width('93%')
+      .height('84%')
+      .align(Alignment.Top)
+    }
+    .width('100%')
+    .height('100%')
+  }
+
+}

+ 829 - 0
entry/src/main/ets/view/process/MultiMediaCollect.ets

@@ -0,0 +1,829 @@
+import CommonEventManager from '@ohos.commonEventManager'
+import uploadInstance from '../../common/util/UploadUtil';
+import { DrawingInfo, DrawingPage } from '../../viewmodel/DrawingInfo';
+import CommonConstants from '../../common/constants/CommonConstants';
+import ProcessRequest from '../../common/util/request/ProcessRequest';
+import RequestParamModel from '../../viewmodel/RequestParamModel';
+import { ConfirmDialogParams } from '../../viewmodel/ConfirmDialogParam';
+import { ConfirmDialog } from '../ConfirmDialog';
+import promptAction from '@ohos.promptAction';
+import ProcessInfo from '../../viewmodel/process/ProcessInfo';
+import { PreviewManager } from '../../common/util/PreviewManager';
+
+const TAG = "sony camera upload"
+
+// 多媒体采集工序
+@Component
+export struct MultiMediaCollect {
+  private scrollerPhoto: Scroller = new Scroller()
+  //
+  @State isExpanded: boolean = false
+  //闪光模式 0:自动 1:开启 2:关闭
+  @State selectedFlashMode:number = 0
+  //选择的照片索引
+  @State selectedPhotoIndex:number = -1
+  //拍照按键缩放
+  @State shootButtonClick:number =1
+  // 旋转角度
+  @State rotateAngle: number = 0
+  // 照片缩放比例
+  @State scaleValue: number = 1
+  // 照片X轴偏移
+  @State offsetX: number = 0
+  // 照片Y轴偏移
+  @State offsetY: number = 0
+  // 照片上次缩放值
+  @State lastScale: number = 1
+  // 照片上次X偏移
+  @State lastOffsetX: number = 0
+  // 照片上次Y偏移
+  @State lastOffsetY: number = 0
+  // 正在上传
+  @State isUploading: boolean = false
+  // 照片列表
+  @State photoList:DrawingInfo[]=[]
+  //双指缩放的中心点X
+  @State pinchCenterX: number = 0;
+  //双指缩放的中心点Y
+  @State pinchCenterY: number = 0;
+  // 获取本地live照片
+  @State commodityPixelMap: PixelMap | null = null;
+  // 拍照动作是否完成
+  @State isCapturing: boolean = false;
+  // 是否停止预览
+  @State isStopView: boolean = false;
+  // 控制帧率
+  @State readTimer:number = 0
+  // 预览操作
+  private previewManager: PreviewManager | null = null;
+
+  // 当前流转卡号
+  @Link seqNo: string
+  // 选中工序id
+  @Link selectOperationId: string
+  // 扫码开工后的生产过程信息
+  @Link process: ProcessInfo
+
+  //创建订阅者
+  subscriber: CommonEventManager.CommonEventSubscriber | null = null;
+  //订阅相机回调
+  subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { events: ["sonycamera_callback"] };
+
+  //提示确认弹窗
+  commonDialogController: CustomDialogController | null = null;
+  private showConfirmDialog(params: ConfirmDialogParams) {
+    if (this.commonDialogController) {
+      this.commonDialogController.close()
+    }
+
+    this.commonDialogController = new CustomDialogController({
+      builder: ConfirmDialog({
+        title: params.title || '提示',
+        message: params.message,
+        onConfirm: params.onConfirm
+      }),
+      cancel: () => console.log('用户取消操作'),
+      customStyle: true,
+      autoCancel:false,
+      maskColor: 'rgba(0,0,0,0.6)'
+    });
+
+    this.commonDialogController.open();
+  }
+
+  //订阅回调(code=3代表连接成功,code=2代表拍照成功)
+  createSubscriber = async () => {
+    this.subscriber = await CommonEventManager.createSubscriber(this.subscribeInfo);
+    if (this.subscriber) {
+      console.info(TAG, "创建订阅回调成功");
+      CommonEventManager.subscribe(this.subscriber, (err, data) => {
+        if (err?.code) {
+          console.error(TAG, "SubscribeCallBack err=" + JSON.stringify(err));
+          return;
+        }
+        console.info(TAG, "SubscribeCallBack data=" + JSON.stringify(data));
+        if (data?.code !== undefined) {
+          switch (data.code) {
+            case 2:
+              this.isCapturing = false
+              this.uploadPhoto(); // 拍照成功
+              break;
+            case 3:
+              console.info(TAG, "开始预览");
+              this.startView(); // 连接成功
+              break;
+            case -1:
+              console.info(TAG,'连接故障')
+              promptAction.showToast({
+                message: '相机连接失败,请检查usb',
+                duration: 3000,
+              })
+              break;
+            case 5:
+              console.info(TAG,'暂停预览')
+              break;
+          }
+        }
+        //0001:自动闪光 0002:关闭闪光 0003:开启闪光
+        if (data?.parameters?.flash_mode) {
+          const flashMode:string = data.parameters.flash_mode
+          console.info(TAG, `收到闪光模式: ${flashMode}`);
+          switch (flashMode) {
+            case '0001':
+              this.selectedFlashMode = 0
+              break;
+            case '0002':
+              this.selectedFlashMode = 2
+              break;
+            case '0003':
+              this.selectedFlashMode = 1
+              break;
+            default:
+              console.warn(TAG, `未知闪光模式: ${flashMode}`);
+          }
+        }
+      });
+    }
+  };
+
+  //加载照片
+  loadPhotos=async()=>{
+    let res = await ProcessRequest.post('/api/v1/process/media/page', {
+      seqNo:this.seqNo,
+      operationId: this.selectOperationId,
+      processId: this.process.id,
+      pageNo: 1,
+      pageSize: 999999
+    } as RequestParamModel) as DrawingPage;
+    this.photoList=res.records??[]
+  }
+
+  //删除照片
+  deletePhoto=async(photoId:string)=>{
+    let res = await ProcessRequest.post('/api/v1/process/media/del', {
+      id: photoId
+    } as RequestParamModel) as DrawingPage;
+    this.loadPhotos()
+  }
+
+
+  //旋转照片
+  private rotateImage(angle: number) {
+    this.offsetX = 0;
+    this.offsetY = 0;
+    this.rotateAngle += angle
+    if (this.rotateAngle >= 360) {
+      this.rotateAngle -= 360
+    } else if (this.rotateAngle < 0) {
+      this.rotateAngle += 360
+    }
+    if(this.rotateAngle==90||this.rotateAngle==270)
+    {
+      this.scaleValue=0.63
+    }else {
+      this.scaleValue=1
+    }
+  }
+
+  // 重置图片变换
+  private resetImageTransform() {
+    this.rotateAngle = 0
+    this.scaleValue = 1
+    this.offsetX = 0
+    this.offsetY = 0
+    this.lastScale = 1
+    this.lastOffsetX = 0
+    this.lastOffsetY = 0
+  }
+
+  //连接相机
+  connectCamera = async () => {
+    CommonEventManager.publish("opensession", (err) => {
+      if (err?.code) {
+        console.info(TAG,"Publish openSession err=" + JSON.stringify(err))
+      } else {
+        console.info(TAG,"Publish openSession succeed ")
+        this.queryFlashMode();
+      }
+    })
+  }
+  //查询闪光模式
+  queryFlashMode = async () => {
+    CommonEventManager.publish("queryflashmode", (err) => {
+      if (err?.code) {
+        console.info(TAG,"Publish queryFlashMode err=" + JSON.stringify(err))
+      } else {
+        console.info(TAG,"Publish queryFlashMode succeed ")
+      }
+    })
+  }
+
+  //开始预览
+  startView=()=>{
+    CommonEventManager.publish("startview", (err) => {
+      if (err?.code) {
+        console.error(TAG, JSON.stringify(err));
+      } else {
+        console.info(TAG, "Publish startView succeed");
+        this.isStopView =false;
+        this.liveShow();
+      }
+    });
+  }
+
+  //停止预览
+  stopView=async()=>{
+    CommonEventManager.publish("stopview", (err) => {
+      if (err?.code) {
+        console.error(TAG, JSON.stringify(err));
+      } else {
+        this.isStopView =true;
+        console.info(TAG, 'publish stopview succeed');
+        if (this.previewManager) {
+          this.previewManager.release();
+        }
+        if (this.readTimer) {
+          clearInterval(this.readTimer);
+          this.readTimer = 0 ;
+        }
+      }
+    });
+  }
+
+  //重连相机
+  reconnectCamera=async()=>{
+    this.showConfirmDialog({
+      title: '重连USB',
+      message: `请重连USB后点击确定!`,
+      onConfirm: async()=> {
+        await new Promise<void>((resolve, reject) => {
+          CommonEventManager.publish("stopview", (err) => {
+            if (err) return reject(err);
+            CommonEventManager.publish("closesession", (err) => {
+              err ? reject(err) : resolve();
+            });
+          });
+        });
+        await new Promise<void>((resolve, reject) => {
+          CommonEventManager.publish("reconnect", (err) => {
+            if (err) return reject(err);
+            resolve();
+          });
+        });
+        promptAction.showToast({
+          message: '相机正在重连中...',
+          duration: 3000,
+        });
+        await sleep(3000);
+        await this.connectCamera()
+        //await this.liveShow()
+      }
+    });
+  }
+
+  liveShow = async () => {
+    let index = 0;
+    let retryCount = 0;
+    const MAX_RETRIES = 1000; // 最大重试次数
+
+    const previewLoop = async () => {
+      if (!this.isStopView && !this.isCapturing) {
+        try {
+          const success = await this.previewManager!.processFrame(index);
+          if (success) {
+            this.commodityPixelMap = await this.previewManager!.getActiveBuffer();
+            retryCount = 0;
+          } else {
+            retryCount++;
+          }
+        } catch (error) {
+          retryCount++;
+          console.warn(TAG, "预览更新失败:", error);
+        }
+      }
+      if (retryCount === MAX_RETRIES) {
+        clearInterval(this.readTimer)
+        this.readTimer = 0;
+        console.info(TAG,"GG")
+        return;
+        //this.reconnectCamera();
+      }
+      if (this.readTimer !== 0) { // 检查是否应该继续循环
+        this.readTimer = setTimeout(previewLoop, 50);
+      }
+    };
+
+    this.readTimer = setTimeout(previewLoop, 50);
+  };
+
+  //断开相机(停止预览->关闭连接)
+  disconnectCamera = async () => {
+    if(!this.isStopView)
+    {
+      await this.stopView()
+    }
+    CommonEventManager.publish("closesession", (err) => {
+      if (err?.code) {
+        console.info(TAG,"Publish closesession err=" + JSON.stringify(err))
+      } else {
+        console.info(TAG,"Publish closesession succeed ")
+      }
+    })
+  }
+
+  //拍照(停止预览->拍照->开始预览)
+  takePhoto = async () => {
+    this.isStopView = true;
+    this.isCapturing = true;
+    CommonEventManager.publish("stopview", (err) => {
+      if (err?.code) {
+        console.info(TAG,"Publish stopview err=" + JSON.stringify(err));
+      } else {
+        console.info(TAG,"Publish Publish stopview succeed");
+        CommonEventManager.publish("shoot", (err) => {
+          if (err?.code) {
+            console.info(TAG,"Publish  shoot error=" + JSON.stringify(err));
+          } else {
+            console.info(TAG,"Publish Publish shoot succeed");
+          }
+        });
+      }
+    });
+  };
+
+  //设置闪光模式
+  setFlashMode = (mode: 'openflash' | 'closeflash' | 'autoflash') => {
+    this.isStopView = true;
+    CommonEventManager.publish("stopview", (err) => {
+      if (err?.code) {
+        console.info(TAG,"Publish stopview err=" + JSON.stringify(err));
+      } else {
+        console.info(TAG,"Publish Publish stopview succeed");
+        CommonEventManager.publish(mode, (err) => {
+          if (err?.code) {
+            console.info(TAG,"Publish"+mode+" error=" + JSON.stringify(err));
+          } else {
+            console.info(TAG,"Publish "+mode+" succeed");
+            CommonEventManager.publish("startview", (err) => {
+              this.isStopView = false;
+              if (err?.code) {
+                console.info(TAG,"Publish  startview error=" + JSON.stringify(err));
+              }else{
+                console.info(TAG,"Publish Publish startview succeed");
+              }
+            })
+          }
+        });
+      }
+    });
+  }
+
+
+  //上传照片
+  uploadPhoto=()=>{
+    console.info(TAG,"start upload:");
+    let imageUri: string = "/data/storage/el2/base/haps/entry/files/image_base64.txt"
+    try {
+      uploadInstance.startUploadBase64(imageUri, () => {
+        this.loadPhotos()
+        this.isUploading = false
+        this.startView();//上传完恢复预览
+      })
+    } catch (error) {
+      console.error(TAG,"upload failed:", error.code);
+    }
+  }
+
+  private adjustOffsetWithAnimation() {
+    const isRotated = (this.rotateAngle === 90 || this.rotateAngle === 270);
+    let moveScaleY = (this.scaleValue - (isRotated ? 0.667 : 1)) / 2;
+    let moveScaleX = (this.scaleValue - (isRotated ? 1.50 : 1)) / 2;
+    let maxOffsetX = (this.scaleValue<1.5&&isRotated) ?this.offsetX: (isRotated ? 520 : 820) * moveScaleX;
+    let maxOffsetY = (isRotated ? 820 : 520) * moveScaleY;
+    // let maxOffsetX = (this.scaleValue<1.5&&isRotated) ?this.offsetX: (isRotated ? 1038 : 1635) * moveScaleX;
+    // let maxOffsetY = (isRotated ? 1635 : 1038) * moveScaleY;
+    const clampedX = Math.max(-maxOffsetX, Math.min(maxOffsetX, this.offsetX));
+    const clampedY = Math.max(-maxOffsetY, Math.min(maxOffsetY, this.offsetY));
+    if (this.offsetX !== clampedX || this.offsetY !== clampedY) {
+      animateTo({
+        duration: 100,
+        curve: Curve.EaseOut
+      }, () => {
+        this.offsetX = clampedX;
+        this.offsetY = clampedY;
+      });
+    }
+  }
+
+  async aboutToAppear() {
+    this.previewManager = new PreviewManager(getContext(this));
+    this.loadPhotos();
+    await this.createSubscriber();
+    await sleep(50)
+    await this.connectCamera();
+    uploadInstance.uploadParams = {
+      token: "your_token_here", // 替换为实际 token
+      operationMediaId: this.selectOperationId, // 默认值
+      processId:this.process.id, // 根据需求设置
+      seqNo:this.seqNo, // 根据需求设置
+      methodName: "", // 根据需求设置
+      apiUrlPath: "/api/v1/process/media/add", // 默认上传接口
+      method: "post", // 默认方法
+      stationIP: "", // 根据需求设置
+      messageKey: "" // 根据需求设置
+    };
+  }
+
+  aboutToDisappear(): void {
+    this.disconnectCamera()
+    if (this.subscriber) {
+      CommonEventManager.unsubscribe(this.subscriber);
+    }
+    if (this.commodityPixelMap) {
+      this.commodityPixelMap.release();
+      this.commodityPixelMap = null;
+    }
+  }
+  build() {
+    Row() {
+      Stack(){
+        if (this.commodityPixelMap) {
+          if (this.selectedPhotoIndex === -1) {
+            Image(this.commodityPixelMap)
+              .width('100%')
+              .height('100%')
+              .objectFit(ImageFit.Fill)
+          }
+        }
+        if(this.isUploading){
+          Column() {
+            Text('正在上传中...')
+              .fontSize($r('app.float.fontSize_30'))
+              .margin({bottom:'10%'})
+            LoadingProgress()
+              .color(Color.Blue)
+              .width('25%')
+              .width('25%')
+          }
+          .height('30%')
+          .width('25%')
+          .backgroundColor(Color.White)
+          .borderRadius($r('app.float.virtualSize_16'))
+          .justifyContent(FlexAlign.Center)
+        }
+        if(this.selectedPhotoIndex === -1){
+          Row() {
+            Image(
+              this.selectedFlashMode === 0 ? $r('app.media.process_flash_auto') :
+                this.selectedFlashMode === 1 ? $r('app.media.process_flash_open') :
+                $r('app.media.process_flash_close')
+            )
+              .width($r('app.float.virtualSize_48'))
+              .height($r('app.float.virtualSize_48'))
+              .enabled(!this.isUploading)
+              .onClick(() => {
+                this.isExpanded = !this.isExpanded
+              })
+              .margin({right:'5%'})
+            if (this.isExpanded) {
+              Row() {
+                Row(){
+                  Text('自动')
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor(this.selectedFlashMode===0?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF'))
+                }
+                .width('33.3%')
+                .height('100%')
+                .justifyContent(FlexAlign.Center)
+                .borderRadius($r('app.float.virtualSize_16'))
+                .backgroundColor(this.selectedFlashMode===0?$r('app.color.0A84FF'):$r('app.color.60000000'))
+                .onClick(() => {
+                  this.setFlashMode('autoflash')
+                  this.isExpanded = false
+                  this.selectedFlashMode = 0
+                })
+                Row(){
+                  Text('开启')
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor(this.selectedFlashMode===1?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF'))
+                }
+                .width('33.3%')
+                .height('100%')
+                .justifyContent(FlexAlign.Center)
+                .borderRadius($r('app.float.virtualSize_16'))
+                .backgroundColor(this.selectedFlashMode===1?$r('app.color.0A84FF'):$r('app.color.60000000'))
+                .onClick(() => {
+                  this.setFlashMode('openflash')
+                  this.isExpanded = false
+                  this.selectedFlashMode = 1
+                })
+
+                Row(){
+                  Text('关闭')
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontColor(this.selectedFlashMode===2?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF'))
+                }
+                .width('33.3%')
+                .height('100%')
+                .justifyContent(FlexAlign.Center)
+                .borderRadius($r('app.float.virtualSize_16'))
+                .backgroundColor(this.selectedFlashMode===2?$r('app.color.0A84FF'):$r('app.color.60000000'))
+                .onClick(() => {
+                  this.setFlashMode('closeflash')
+                  this.isExpanded = false
+                  this.selectedFlashMode = 2
+                })
+              }
+              .animation({ duration: 300, curve: Curve.EaseOut }) // 展开动画
+              .backgroundColor($r('app.color.60000000'))
+              .borderRadius($r('app.float.virtualSize_16'))
+              .width('85%')
+              .height('100%')
+            }
+          }
+          .justifyContent(FlexAlign.Start)
+          .alignItems(VerticalAlign.Bottom)
+          .position({ x: '2%', y: '92%' })
+          .height('5%')
+          .width('24%')
+          Image(this.shootButtonClick===1?$r('app.media.process_no_shoot'):$r('app.media.process_shoot'))
+            .width($r('app.float.virtualSize_88'))
+            .height($r('app.float.virtualSize_88'))
+            .scale({ x: this.shootButtonClick, y: this.shootButtonClick })
+            .borderRadius($r('app.float.fontSize_16'))
+            .enabled(!this.isUploading)
+            .animation({
+              duration: 200,
+              curve: Curve.Linear
+            })
+            .onClick(() => {
+              this.shootButtonClick = 0.9;
+              setTimeout(() => {
+                this.shootButtonClick = 1;
+              }, 200);
+              if(this.isUploading){
+                return
+              }
+              this.isUploading = true
+              this.takePhoto();
+            })
+            .position({ x: '48%', y: '90%' })
+          Row(){
+            Text("重连相机")
+              .fontColor($r('app.color.FFFFFF'))
+              .fontSize($r('app.float.fontSize_24'))
+          }
+          .width('10%')
+          .height('6%')
+          .backgroundColor($r('app.color.60000000'))
+          .position({ x: '88%', y: '91%' })
+          .borderRadius($r('app.float.virtualSize_16'))
+          .justifyContent(FlexAlign.Center)
+          .onClick(()=>{
+            this.reconnectCamera()
+          })
+        }
+        else{
+          Stack() {
+            Image(CommonConstants.PICTURE_URL_PREFIX + this.photoList[this.selectedPhotoIndex].filePath)
+              .width('100%')
+              .height('100%')
+              // .onComplete((event) => {
+              //   console.info(TAG,"width:"+event!.componentWidth +"height:"+ event!.componentHeight)
+              //   })  // 获取Image组件的长宽(除以1.5获取vp)
+              .objectFit(ImageFit.Fill)
+              .rotate({ angle: this.rotateAngle })
+              .scale({ x: this.scaleValue, y: this.scaleValue })
+              .borderRadius($r('app.float.virtualSize_16'))
+              .translate({ x: this.offsetX, y: this.offsetY })
+              .gesture(
+                GestureGroup(GestureMode.Exclusive,
+                  // 替换原有的 PinchGesture 部分代码
+                  PinchGesture()
+                    .onActionStart((event: GestureEvent) => {
+                      this.lastScale = this.scaleValue;
+                      this.lastOffsetX = this.offsetX;
+                      this.lastOffsetY = this.offsetY;
+                      // 记录双指中心点(相对于图片中心)
+                      this.pinchCenterX = event.pinchCenterX - 285 - 820 / 2;  //rk3588
+                      this.pinchCenterY = event.pinchCenterY - 121 - 520 / 2;   //rk3588
+                      // this.pinchCenterX = event.pinchCenterX - 1090 / 2;     //rk3568
+                      // this.pinchCenterY = event.pinchCenterY - 692 / 2;     //rk3568
+                      // console.info(TAG,event.pinchCenterX,TAG,event.pinchCenterY)
+                      // console.info(TAG,this.pinchCenterX,TAG,this.pinchCenterY)
+                    })
+                    .onActionUpdate((event: GestureEvent) => {
+                      const newScale = this.lastScale * event.scale;
+                      //保持双指中心点不变
+                      const scaleRatio = newScale / this.lastScale;
+                      const newOffsetX = this.lastOffsetX + (1 - scaleRatio) * this.pinchCenterX;
+                      const newOffsetY = this.lastOffsetY + (1 - scaleRatio) * this.pinchCenterY;
+
+                      this.offsetX = newOffsetX;
+                      this.offsetY = newOffsetY;
+                      this.scaleValue = newScale;
+                    })
+                    .onActionEnd(() => {
+                      // 缩放最小比例为1
+                      if (this.scaleValue < 1) {
+                        // 旋转90°或者270°的最小缩小比例为0.667
+                        if(this.rotateAngle==90||this.rotateAngle==270) {
+                          if(this.scaleValue<0.667){
+                            this.scaleValue = 0.667
+                          }
+                        } else {
+                          this.scaleValue = 1
+                        }
+                      }
+                      // 缩放最大比例为3
+                      if (this.scaleValue > 3) this.scaleValue = 3
+                      this.adjustOffsetWithAnimation()
+                    }),
+
+                  // 单指滑动手势
+                  PanGesture()
+                    .onActionStart(() => {
+                      if(this.rotateAngle === 90 || this.rotateAngle === 270)
+                      {
+                        if (this.scaleValue <= 0.667)
+                        {
+                          return;
+                        }
+                      }else{
+                        if (this.scaleValue <= 1) return;
+                      }
+                      this.lastOffsetX = this.offsetX;
+                      this.lastOffsetY = this.offsetY;
+                    })
+                    .onActionUpdate((event: GestureEvent) => {
+                      if(this.rotateAngle === 90 || this.rotateAngle === 270)
+                      {
+                        if (this.scaleValue <= 0.667)
+                        {
+                          return;
+                        }
+                      }else{
+                        if (this.scaleValue <= 1) return;
+                      }
+                      let dx = event.offsetX;
+                      let dy = event.offsetY;
+                      const sensitivity = 0.5 * this.scaleValue;
+                      // 临时计算新位置
+                      let newOffsetX = this.lastOffsetX;
+                      let newOffsetY = this.lastOffsetY;
+                      newOffsetX += dx * sensitivity;
+                      newOffsetY += dy * sensitivity;
+                      this.offsetX = newOffsetX;
+                      this.offsetY = newOffsetY;
+                      //console.info(TAG,this.offsetX,TAG,this.offsetY)
+                    })
+                    .onActionEnd(() => {
+                      const isRotated = (this.rotateAngle === 90 || this.rotateAngle === 270);
+                      if(isRotated)
+                      {
+                        if(this.scaleValue<=1.5)
+                        {
+                          this.offsetX = 0
+                        }
+                      }else{
+                        if (this.scaleValue <= 1) {
+                          // 如果缩放比例<=1,直接重置位置
+                          this.offsetX = 0;
+                          this.offsetY = 0;
+                          return;
+                        }
+                      }
+                      this.adjustOffsetWithAnimation()
+                    })
+                )
+              )
+          }
+          .width('100%')
+          .height('100%')
+          .borderRadius($r('app.float.virtualSize_16'))
+          .clip(true)
+          Row()
+          {
+            Row(){
+              Image($r('app.media.process_back_camera'))
+                .width($r('app.float.virtualSize_80'))
+                .height($r('app.float.virtualSize_80'))
+                .onClick(()=>{
+                  this.selectedPhotoIndex=-1
+                  this.startView()
+                })
+            }
+            .width('10%')
+            .justifyContent(FlexAlign.Start)
+            Row({space:20}){
+              Image($r("app.media.process_photo_reset"))
+                .width($r('app.float.virtualSize_80'))
+                .height($r('app.float.virtualSize_80'))
+                .onClick(()=>{
+                  this.resetImageTransform()
+                })
+              Image($r('app.media.process_photo_turn_left'))
+                .width($r('app.float.virtualSize_80'))
+                .height($r('app.float.virtualSize_80'))
+                .onClick(()=>{
+                  this.rotateImage(-90)
+                })
+              Image($r('app.media.process_photo_turn_right'))
+                .width($r('app.float.virtualSize_80'))
+                .height($r('app.float.virtualSize_80'))
+                .onClick(()=>{
+                  this.rotateImage(90)
+                })
+              Image($r('app.media.process_photo_delete'))
+                .width($r('app.float.virtualSize_80'))
+                .height($r('app.float.virtualSize_80'))
+                .onClick(()=>{
+                  this.showConfirmDialog({
+                    title: '删除照片',
+                    message: `确定要删除照片吗?`,
+                    onConfirm: ()=> {
+                      const photosNum = this.photoList.length
+                      const photoId=this.photoList[this.selectedPhotoIndex].id
+                      //如果是图册最后一张照片,显示前一张照片,没有照片则返回拍照页面
+                      if(this.selectedPhotoIndex===photosNum-1)
+                      {
+                        this.selectedPhotoIndex--
+                      }
+                      this.deletePhoto(photoId)
+                      if(this.selectedPhotoIndex==-1)
+                      {
+                        this.startView()
+                      }
+                    }
+                  });
+                })
+            }.width('88%')
+            .justifyContent(FlexAlign.End)
+            .margin({right :'2%'})
+          }.width('98%')
+          .height('10%')
+          .position({x:'2%',y:'90%'})
+        }
+      }
+      .width('86%')
+      .height('100%')
+      .backgroundColor($r('app.color.000000'))
+      .border({width:2})
+      Column(){
+        List({ space: 8,scroller:this.scrollerPhoto }) {
+          ForEach(this.photoList, (item: DrawingInfo, index) => {
+            ListItem() {
+              Column({space:4}){
+                Column(){
+                  Image(CommonConstants.PICTURE_URL_PREFIX+item.filePath)
+                    .objectFit(ImageFit.Fill)
+                    .borderRadius($r('app.float.virtualSize_16'))
+                    .height('97%')
+                    .width('98%')
+                    .opacity(index === this.selectedPhotoIndex ? 0.8 : 1)  // 20% 透明度
+                }
+                .backgroundColor(index === this.selectedPhotoIndex ? $r('app.color.30D158') : '')
+                .width('90%')
+                .justifyContent(FlexAlign.Center)
+                .alignItems(HorizontalAlign.Center)
+                .borderRadius($r('app.float.virtualSize_16'))
+                .height('85%')
+                Text(`${item.updated}`)
+                  .fontSize($r('app.float.fontSize_12'))
+                  .fontColor($r('app.color.FFFFFF'))
+                  .width('90%')
+                  .textAlign(TextAlign.Start)
+              }
+              .width('100%')
+              .height('100%')
+              .justifyContent(FlexAlign.Start)
+              .alignItems(HorizontalAlign.Center)
+              .enabled(!this.isUploading)
+              .onClick(()=>{
+                this.selectedPhotoIndex = index;
+                this.resetImageTransform()
+                this.stopView();
+
+              })
+            }.height('19%')
+            .margin({bottom:'2%'})
+          })
+        }
+        .width('100%')
+        .margin({top:'2%',bottom:'2%'})
+        .height('96%')
+      }
+      .width('14%')
+      .height('100%')
+    }
+    .width('100%')
+    .height('100%')
+    //.backgroundColor($r('app.color.10FFFFFF'))
+    .borderRadius($r('app.float.virtualSize_16'))
+  }
+}
+
+function sleep(ms: number): Promise<void> {
+  return new Promise((resolve) => setTimeout(resolve, ms));
+}
+

+ 270 - 0
entry/src/main/ets/view/process/SelfInspectView.ets

@@ -0,0 +1,270 @@
+import ProcessRequest from '../../common/util/request/ProcessRequest'
+import ProcessCheck from '../../viewmodel/process/ProcessCheck'
+import ProcessInfo from '../../viewmodel/process/ProcessInfo'
+import RequestParamModel from '../../viewmodel/RequestParamModel'
+import promptAction from '@ohos.promptAction'
+import PageInfo from '../../viewmodel/PageInfo'
+import { DefectRecordDialog } from '../DefectRecordDialog'
+import DictValue from '../../viewmodel/DictValue'
+import ProcessDefectRecord from '../../viewmodel/process/ProcessDefectRecord'
+
+// 自检工序
+@Component
+export struct SelfInspectView {
+  // 点检列表
+  @State checkArray: ProcessCheck[] = []
+  @State selectCheck: number = -1
+  // 不良记录列表
+  @State defectRecords: ProcessDefectRecord[] = []
+  // 不良类型(数据字典)
+  @State defectTypes: DictValue[] = []
+  // 扫码开工状态(0:未开工 1:已开工)
+  @Link @Watch('queryByScanState') scanState: number
+  // 当前流转卡号
+  @Link seqNo: string
+  // 选中工序id
+  @Link selectOperationId: string
+  // 扫码开工后的生产过程信息
+  @Link process: ProcessInfo
+  // 是否是修改不良类型记录
+  editDefectRecord: boolean = false
+  // 修改时选中的不良类型记录
+  selectDefectRecord: ProcessDefectRecord = {}
+
+  defectTypeDictCode: string = 'defect_mana'
+
+  // 根据开工状态查询 工序点检项/工序点检历史操作
+  async queryByScanState() {
+    if (this.scanState === 1) {
+      this.checkArray = await ProcessRequest.post('/api/v1/process/check/list', {
+        operationId: this.selectOperationId,
+        processId: this.process.id!,
+        seqNo: this.seqNo
+      } as RequestParamModel)
+      this.queryDefectRecord()
+    } else {
+      let result = await ProcessRequest.post('/api/v1/op/operationCheck/page', {
+        operationId: this.selectOperationId,
+        pageNo: 1,
+        pageSize: 999999
+      } as RequestParamModel) as PageInfo;
+      this.checkArray = result.records as ProcessCheck[] || []
+    }
+  }
+
+  async queryDefectRecord() {
+    if (this.seqNo) {
+      this.defectRecords = await ProcessRequest.post('/api/v1/process/defectRecord/list', {
+        seqNo: this.seqNo
+      } as RequestParamModel)
+      if (this.defectRecords && this.defectTypes) {
+        for (const element of this.defectRecords) {
+          for (const dict of this.defectTypes) {
+            if (element.defectType === dict.dictValue) {
+              element.defectTypeLabel = dict.dictLabel
+              break
+            }
+          }
+        }
+      }
+    }
+  }
+
+  async aboutToAppear() {
+    this.queryByScanState()
+    this.defectTypes = await ProcessRequest.get(`/api/v1/sys/dictData/queryByType/${this.defectTypeDictCode}`)
+    this.queryDefectRecord()
+  }
+
+  build() {
+    Row() {
+      // 点检
+      Column() {
+        Row() {
+          Text('点检列表')
+            .fontSize($r('app.float.fontSize_24'))
+            .fontWeight(FontWeight.Medium)
+            .fontColor($r('app.color.FFFFFF'))
+        }
+        .height('10.2%')
+        .width('100%')
+
+        List({space: 6}) {
+          ForEach(this.checkArray, (item: ProcessCheck, index: number) => {
+            ListItem() {
+              Column() {
+                Row() {
+                  Text(item.checkName ? item.checkName : '')
+                    .fontSize($r('app.float.fontSize_24'))
+                    .fontWeight(FontWeight.Medium)
+                    .fontColor($r('app.color.FFFFFF'))
+                  Row() {
+                    Text('检查确认')
+                      .fontSize($r('app.float.fontSize_16'))
+                      .fontWeight(FontWeight.Regular)
+                      .fontColor($r('app.color.FFFFFF'))
+                    Toggle({ type: ToggleType.Switch, isOn: item.result === '1' ? true : false })
+                      .selectedColor($r('app.color.30D158'))
+                      .switchPointColor($r('app.color.FFFFFF'))
+                      .enabled(this.scanState === 1 ? true : false)
+                      .onChange(async (isOn: boolean) => {
+                        this.selectCheck = index
+                        if (this.scanState === 0) {
+                          promptAction.showToast({
+                            message: '工序未开工,请先开工',
+                            duration: 1500,
+                            bottom: 100
+                          })
+                          return
+                        }
+                        await ProcessRequest.post('/api/v1/process/check/update', {
+                          id: item.id,
+                          result: isOn ? '1': '2'
+                        } as RequestParamModel)
+                      })
+                  }
+                  .height('100%')
+                }
+                .width('95.3%')
+                .justifyContent(FlexAlign.SpaceBetween)
+                .height($r('app.float.virtualSize_88'))
+                Column() {
+                  Text('点检内容:' + item.content ? item.content : '')
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                  Text('标准值:' + item.standard ? item.standard : '')
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                  Text('上限值:' + (item.upper ? item.upper : ' '))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                  Text('下限值:' + (item.lower ? item.lower : ' '))
+                    .fontSize($r('app.float.fontSize_16'))
+                    .fontWeight(FontWeight.Lighter)
+                    .fontColor($r('app.color.FFFFFF'))
+                }
+                .width('95.3%')
+                .alignItems(HorizontalAlign.Start)
+              }
+              .borderRadius($r('app.float.virtualSize_16'))
+              .backgroundColor(this.selectCheck === index && this.scanState === 1 ? $r('app.color.2030D158') : $r('app.color.10FFFFFF'))
+              .width('100%')
+              .onClick(()=>{
+                this.selectCheck = index
+              })
+            }
+          })
+        }
+        .width('100%')
+        .height('87.6%')
+        .alignListItem(ListItemAlign.Center)
+      }
+      .height('100%')
+      .width('59%')
+      Divider()
+        .vertical(true)
+        .strokeWidth(1)
+        .color($r('app.color.15FFFFFF'))
+        .height('100%')
+        .padding({top: '5.3%', bottom: '2.2%'})
+      // 不良类型记录
+      Column() {
+        Row() {
+          Text('不良类型记录')
+            .fontSize($r('app.float.fontSize_24'))
+            .fontWeight(FontWeight.Medium)
+            .fontColor($r('app.color.FFFFFF'))
+        }
+        .height('10.2%')
+        .width('100%')
+        // 添加不良类型
+        Row({space: 4}) {
+          Image($r("app.media.general_create"))
+            .width($r('app.float.virtualSize_24'))
+            .height($r('app.float.virtualSize_24'))
+            .fillColor($r('app.color.0A84FF'))
+          Text('添加')
+            .fontColor($r('app.color.0A84FF'))
+            .fontSize($r('app.float.fontSize_24'))
+            .fontWeight(FontWeight.Medium)
+        }
+        .width('28.2%')
+        .height('7.1%')
+        .justifyContent(FlexAlign.Center)
+        .backgroundColor($r('app.color.20FFFFFF'))
+        .borderRadius($r('app.float.fontSize_16'))
+        .onClick(()=>{
+          if(this.scanState === 0) {
+            promptAction.showToast({
+              message: '工序未开工,请先开工',
+              duration: 1500,
+              bottom: 100
+            })
+            return
+          }
+          this.defectRecordController.open()
+        })
+        // 不良类型记录列表
+        List({space: 6}) {
+          ForEach(this.defectRecords, (item: ProcessDefectRecord, index: number) => {
+            ListItem() {
+              Column() {
+                Text(item.defectTypeLabel ? item.defectTypeLabel! + '(' + item.bugName! + ')' : '')
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Bold)
+                  .height($r('app.float.virtualSize_48'))
+                Text('备注:' + (item.remark ? item.remark : ''))
+                  .fontColor($r('app.color.FFFFFF'))
+                  .fontSize($r('app.float.fontSize_16'))
+                  .fontWeight(FontWeight.Lighter)
+                  .padding({bottom: 10})
+              }
+              .width('92%')
+              .alignItems(HorizontalAlign.Start)
+            }
+            .backgroundColor($r('app.color.20FFFFFF'))
+            .borderRadius($r('app.float.fontSize_16'))
+            .width('100%')
+            .onClick(()=>{
+              this.selectDefectRecord = item
+              this.editDefectRecord = true
+              this.defectRecordController.open()
+            })
+          })
+        }
+        .width('100%')
+        .height('77.9%')
+        .alignListItem(ListItemAlign.Center)
+        .margin({top: 10})
+      }
+      .height('100%')
+      .width('35.4%')
+      .alignItems(HorizontalAlign.Start)
+    }
+    .width('100%')
+    .height('100%')
+    .justifyContent(FlexAlign.SpaceEvenly)
+    .alignItems(VerticalAlign.Top)
+  }
+
+  // 添加/修改不良类型记录弹窗
+  defectRecordController: CustomDialogController = new CustomDialogController({
+    builder: DefectRecordDialog({
+      isEdit: this.editDefectRecord,
+      defectTypes: this.defectTypes,
+      defectRecord: this.selectDefectRecord,
+      seqNo: this.seqNo,
+      selectOperationId: this.selectOperationId,
+      process: this.process,
+      defectRecords: this.defectRecords,
+    }),
+    autoCancel: true, // 点击遮罩关闭
+    customStyle: true,
+    alignment:DialogAlignment.Center,
+    maskColor: 'rgba(0,0,0,0.8)',  // 黑色遮罩
+  })
+}

+ 8 - 0
entry/src/main/ets/viewmodel/ConfirmDialogParam.ets

@@ -0,0 +1,8 @@
+export interface ConfirmDialogParams {
+  title?: string
+  message: string
+  confirmText?: string
+  cancelText?: string
+  onConfirm: () => void
+}
+

+ 10 - 0
entry/src/main/ets/viewmodel/DictInfo.ets

@@ -0,0 +1,10 @@
+import DictValue from './DictValue'
+
+export default class DictInfo {
+  // 字典编码
+  dictCode?: string
+  // 字典值
+  list?: DictValue[]
+  //配件类型
+  accessories_type?:DictValue[]
+}

+ 8 - 0
entry/src/main/ets/viewmodel/DictValue.ets

@@ -0,0 +1,8 @@
+export default class DictValue {
+  // 字典标签
+  dictLabel?: string
+  // 字典排序
+  dictSort?: number
+  // 字典值
+  dictValue?: string
+}

+ 27 - 0
entry/src/main/ets/viewmodel/DrawingInfo.ets

@@ -0,0 +1,27 @@
+export interface DrawingInfo{
+  //图片编号
+  drawingCode?:string
+  //图片标题
+  drawingTitle?:string
+  //版本
+  drawingVersion?:string
+  //上传时间
+  updated?:string
+  //上传人
+  updator?:string
+  //地址
+  drawingPath?:string
+  //文件名称
+  fileName?:string
+  //地址
+  filePath?:string
+  //照片id
+  id?:string
+}
+
+export interface DrawingPage{
+  pageNo?:string,
+  pageSize?:string,
+  //消息记录
+  records?:DrawingInfo[]
+}

+ 24 - 0
entry/src/main/ets/viewmodel/MaterialInfo.ets

@@ -0,0 +1,24 @@
+// 库存物料信息
+export default class MaterialInfo {
+  // 物料编号
+  materialNo?: string;
+  // 物料编号
+  materialCode?: string;
+  // 物料名称
+  materialName?: string;
+  // 物料型号
+  spec?: string;
+  // 出入库数量
+  num?: number;
+  // 单位
+  unit?: string;
+  // 流转卡号/序列号
+  batchCode?: string;
+  // 流转卡号/序列号
+  batchNo?: string;
+  // 物料单位
+  unitDictLabel?:string
+  //物料单位
+  unitDictValue?:string
+}
+

+ 18 - 0
entry/src/main/ets/viewmodel/MessageInfo.ets

@@ -0,0 +1,18 @@
+export  interface MessageInfo{
+  //状态
+  readState?:string,
+  //消息内容
+  content?:string,
+  //创建时间
+  created?: string,
+  //消息id
+  id?:string
+
+}
+
+export interface MessagePage{
+  pageNo?:string,
+  pageSize?:string,
+  //消息记录
+  records?:MessageInfo[]
+}

+ 33 - 0
entry/src/main/ets/viewmodel/OperationItem.ets

@@ -0,0 +1,33 @@
+// 工序物料信息
+export default class OperationItem {
+  // 工序id
+  operationId?: string;
+  // 物料编码
+  itemCode?: string;
+  // 物料名称
+  itemName?: string;
+  // 物料规格
+  itemModel?: string;
+  // 所需数量
+  num?: number;
+  // 单位
+  unit?: string;
+  // 齐套数量
+  kitNum?: number;
+  // 入库数量
+  storageNum?: number;
+  // 实际数量(生产过程-已采集数量)
+  realNum?: number;
+  // 需求数量(生产过程)
+  needNum?: number;
+  // 批次号(生产过程)
+  batchNo?: string;
+  // 生产过程id(生产过程)
+  processId?: string;
+  // 工单编码(生产过程)
+  workOrderCode?: string;
+  //序列号/批次号
+  codeTypeStr?: string;
+}
+
+

+ 11 - 0
entry/src/main/ets/viewmodel/PageInfo.ets

@@ -0,0 +1,11 @@
+import MaterialInfo from './MaterialInfo'
+import OperationItem from './OperationItem'
+import ProcessCheck from './process/ProcessCheck'
+import { UserInfo } from './UserInfo'
+
+export default interface PageInfo {
+  pageNo?:string,
+  pageSize?:string,
+  //消息记录
+  records?: MaterialInfo[] | ProcessCheck[] | OperationItem[] | UserInfo[]
+}

+ 111 - 0
entry/src/main/ets/viewmodel/RequestParamModel.ets

@@ -0,0 +1,111 @@
+import MaterialInfo from './MaterialInfo';
+
+//通用请求后台参数
+export default class RequestParamModel {
+  // 工单状态 0 未完成 1已完成
+  queryComplete?: number
+  //工单类型 1 入库单 2 出库单
+  type?: number
+  //储位类型
+  houseType?: string;
+  //储位编号
+  houseNo?: string
+  //物料名称
+  materialName?: string;
+  //物料编号
+  materialNo?:string
+  //工位编号
+  stanCode?:string
+  //料箱编号
+  vehicleNo?:string
+  //料箱编号
+  vehicleCode?:string
+  // 工艺路线id
+  processRouteId?: number
+  // 物料标签
+  label?: string
+  // 工序id数组
+  operationIds?: string[]
+  //入库详情
+  detailsList?: MaterialInfo[]
+  //用户名
+  userName?:string
+  //密码
+  password?:string
+  //部门id
+  orgId?:number
+  //生产线id
+  proCode?:string
+  //工位id
+  stationId?:string
+  //工单编号
+  workOrderCode?:string
+  // 工序id
+  operationId?: number | string
+  // 工序绑定载具物料参数
+  processVehicleMaterialList?: MaterialInfo[]
+  //载具容量
+  capacity?:number
+  //载具类别
+  category?: string;
+  //载具编号
+  code?: string;
+  //载具名称
+  name?: string;
+  //资产编号
+  propertyCode?: string;
+  //流转卡号/序列号
+  seqNo?: string;
+  //生产过程id
+  processId?: string;
+  //载具通用性
+  universal?: number;
+  // 分页参数-第几页
+  pageNo?: number;
+  // 分页参数-每页几行
+  pageSize?: number;
+  //工单表
+  orderCode?: string
+  //查询工单或订单号
+  queryCode?:string
+  //工序编码
+  operationCode?:string
+  //报工状态
+  stateList?:Array<number>
+  //开工二维码
+  qrCode?:string
+  //id集合
+  ids?:string[]
+  //载具绑定工序id
+  vehicleOperationId?:string
+  //扫描码
+  scanCode?:string
+  //工序id
+  opId?:string
+  //物料编码
+  itemCode?:string
+  //主键id
+  id?: string | number
+  //设备编号
+  deviceNo?:string
+  //工号
+  jobNumber?:string
+  //物料编码
+  materialCode?:string
+  //检查方式
+  checkType?:string
+  //操作结果
+  result?:string
+  //查询的图纸资料类型
+  drawingDictValue?:string
+  //不查询的图纸资料类型
+  notDrawingDictValue?:string
+  //缺陷类型(数据字典值)
+  bugType?:string
+  //工位ip
+  stationIp?:string
+  //创建日期
+  createDate?:string
+  //流转卡号/序列号集合
+  seqNos?:string[]
+}

+ 112 - 0
entry/src/main/ets/viewmodel/UserInfo.ets

@@ -0,0 +1,112 @@
+// 用户信息
+export class UserInfo {
+  // id
+  id?: number | string;
+  // 用户id
+  userId?: number;
+  // 用户名
+  userName?: string;
+  // 登录密码
+  password?: string;
+  // 组织id
+  orgId?: number;
+  // 工位id
+  stationId?: number;
+  // 用户头像
+  avatar?: string
+  // 保持登录状态(1:是 2:否)
+  maintainLoginStatus?: number;
+  // 更新时间戳
+  updateTime?: number;
+  stationCode?: string
+  stationIp?: string
+  //昵称
+  nickName?:string
+  //部门名
+  deptNames?: string
+  //名字首字母
+  nameInitial?:string
+  // 部门信息
+  depts?: DeptInfo[]
+}
+
+// 用户登陆信息
+export  class UserAuthInfo {
+  // id
+  id?: number;
+  // 仓储id
+  userId?: number;
+  // 用户名
+  userName?: string;
+  // 登录密码
+  password?: string;
+  // 组织id
+  orgId?: number;
+  // 工位id
+  stationId?: number;
+  // 工位名称
+  stationName?: string;
+  // 维护站类型
+  stationDictValue?: string;
+  // 保持登录状态(1:是 2:否)
+  maintainLoginStatus?: number;
+  // 更新时间戳
+  updateTime?: number;
+  // 用户头像
+  avatar?: string;
+
+  isLogin?:boolean //是否是登录的状态
+  // 部门信息
+  depts?: DeptInfo[]
+}
+
+export class DeptInfo {
+  id?: number
+  // 部门名称
+  deptName?: string
+  // 组织id
+  orgId?: number
+}
+
+export class WorkstationInfo {
+  id?: string
+  // 工位名称
+  name?: string
+  //工位类型
+  stationDictValue?: string
+}
+
+export class ProductionLine {
+  // 产线code
+  code?: string
+  // 创建时间
+  created?: string
+  // 创建人
+  creato?: string
+  // 是否删除
+  deleted?: number
+  // 部门ID
+  deptId?: string
+  // 主键
+  id?: string
+  // 数级结构层级
+  level?: string
+  // 产线管理者
+  manager?: string
+  // 产线名称
+  name?: string
+  // 组织ID
+  orgId?: string
+  // 所属车间code
+  parentCode?: string
+  // 产线位置
+  position?: string
+  // 产线描述
+  remark?: string
+  // 修改时间
+  updated?: string
+  // 上次修改人
+  updator?: string
+  // 所属车间
+  workShopName?: string
+}

+ 23 - 0
entry/src/main/ets/viewmodel/VehicleInfo.ets

@@ -0,0 +1,23 @@
+
+export default class VehicleInfo {
+  // 料箱编号
+  vehicleNo?: string;
+  // 料箱名称
+  vehicleName?: string;
+  // 料箱ID
+  vehicleId?: string;
+  // 仓库编号
+  houseNo?: string;
+  // 储位编号
+  locationNo?: string;
+  // 坐标
+  coordinate?: string;
+  //工单编号
+  workOrderCode?:string
+  //工序名称
+  operationName?:string
+  //载具编号
+  vehicleCode?:string
+  //工序绑定载具id
+  id?:string
+}

+ 74 - 0
entry/src/main/ets/viewmodel/WorkOrderInfo.ets

@@ -0,0 +1,74 @@
+import OperationInfo from './process/OperationInfo'
+
+export default class WorkOrderInfo {
+  // 工单编码
+  workOrderCode?: string
+  // 工单id
+  orderId?: number
+  // 订单编码
+  orderCode?: string
+  // 订单名称
+  orderName?: string
+  // 物料编号
+  materialCode?: string
+  // 物料名称
+  materialName?: string
+  //物料批次号
+  batchCode?:string
+  //物料序列号
+  batchNo?:string
+  //扫码物料数量
+  num?:number
+  //物料单位
+  unitDictLabel?:string
+  // 物料型号
+  materialModel?: string
+  // 工艺版本
+  processRouteVersion?: string
+  // 优先级
+  priority?: string
+  // 产线id
+  productLineId?: string
+  // 产线名称
+  productLineName?: string
+  //工艺路线id
+  processRouteId?: number
+  // 工艺路线编码
+  processRouteCode?: string
+  // 工艺路线名称
+  processRouteName?: string
+  // 计划开始时间
+  planStartWhen?: string
+  // 计划结束时间
+  planStartEnd?: string
+  // 生产车间id
+  workshopId?: string
+  // 生产车间名
+  workshopName?: string
+  // 计划数量
+  planNum?: string
+  // 完成数量
+  completeNum?: string
+  // 报废数量
+  scrapNum?: string
+  // 入库数量
+  inventoryNum?: string
+  // 提前下线数量
+  beforeNum?: string
+  // 工单状态
+  workOrderState?: string
+  // 物料分配状态
+  itemAllotState?: number
+  orderType?: string
+  // 数据包地址
+  packageUrl?: string
+  engineeringCode?: string
+  // 更新时间
+  updated?: string
+  // 齐套进度
+  kitCompleteProgress?: number
+  // 工序信息
+  ops?: OperationInfo[]
+  //是否完成
+  isCompleted?:boolean
+}

+ 8 - 0
entry/src/main/ets/viewmodel/WorkOrderPage.ets

@@ -0,0 +1,8 @@
+import WorkOrderInfo from './WorkOrderInfo'
+
+export interface WorkOrderPage {
+
+  records?: WorkOrderInfo[]
+  pageNo?: number,
+  pageSize?: number,
+}

+ 13 - 0
entry/src/main/ets/viewmodel/WorkOrderSeq.ets

@@ -0,0 +1,13 @@
+// 工单序列信息
+export default class WorkOrderSeq {
+  // 主键id
+  id?: string
+  // 流转卡号
+  seqNo?: string
+  // 序列号
+  supportingCode?: string
+  // 铭牌号
+  nameplateNo?: string
+  // 工单编码
+  workOrderCode?: string
+}

+ 11 - 0
entry/src/main/ets/viewmodel/device/AntiWristStrap.ets

@@ -0,0 +1,11 @@
+//防静电手环
+export default class AntiWristStrap {
+  // 1-正常 0-其它状态
+  RingWearNormal?: number
+  // 1-待机 0-其它状态
+  RingWearStandby?: number
+  // 1-异常 0-其它状态
+  RingWearFault?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 9 - 0
entry/src/main/ets/viewmodel/device/CardReader.ets

@@ -0,0 +1,9 @@
+//读卡器
+export default class CardReader {
+  //RFID卡号前4位
+  RfidCardNum1?: string
+  //RFID卡号后4位
+  RfidCardNum2?: string
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 31 - 0
entry/src/main/ets/viewmodel/device/ElectricScrewdriver.ets

@@ -0,0 +1,31 @@
+//电动螺丝刀
+export default class ElectricScrewdriver {
+
+  // 旋转方向(0:顺时针 1:逆时针)
+  TighteRotationDirection?: number
+  // 保持时间(ms)
+  TorqueHoldTime?: number
+  // 目标扭力(Kgf.cm)
+  TightenTorqueTarget?: number
+  // 扭力偏差上限(Kgf.cm)
+  TorqueUpperLimit?: number
+  // 扭力偏差下限(Kgf.cm)
+  TorqueLowerLimit?: number
+  // 浮高滑牙检测(1:开启 0:关闭)
+  EnableFloatSlipDetection?: number
+  // 浮高界定圈数(r)
+  FloatDetectionTurns?: number
+  // 滑牙导航圈数(r)
+  SlipDetectionTurns?: number
+
+  // 拧紧过程中最大扭力值(单位 mN·m)
+  TightenTorqueMax?: number
+  //最终拧紧结果(-1:NG 1:OK 0:未完成)
+  TightenResult?: number
+  // 电动螺丝刀警报编码(0:无警告 1:浮高 2:滑牙 3:过流(断电重启) 4:过压(检查供电电压是否偏高) 5:欠压(检查供电电压是否偏低)
+  // 6:飞车 7:过热(检查批头是否打滑,螺丝是否打滑) 8:反转不到位 9:位置偏差过大(检测电机线与encoder线接触是否良好)
+  // 10:电批断线 11:力矩偏差过大 12:拧松失败;                                                                                                    ")
+  ScrewWarning?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 25 - 0
entry/src/main/ets/viewmodel/device/ElectricScrewdriverPreset.ets

@@ -0,0 +1,25 @@
+//电动螺丝刀预设
+import ElectricScrewdriver from './ElectricScrewdriver'
+
+export default class ElectricScrewdriverPreset extends ElectricScrewdriver {
+  // id
+  id?: number
+  // 预设名称
+  setName?: string
+  // 旋转方向(1:顺时针 2:逆时针)
+  // rotationDirection?: number
+  // // 保持时间(ms)
+  // holdTime?: number
+  // // 目标扭力(Kgf.cm)
+  // targetTorque?: number
+  // // 扭力偏差上限(Kgf.cm)
+  // maxTorqueDeviation?: number
+  // // 扭力偏差下限(Kgf.cm)
+  // minTorqueDeviation?: number
+  // // 浮高滑牙检测(1:开启 0:关闭)
+  // detectionStatus?: number
+  // // 浮高界定圈数(r)
+  // floatHeightTurn?: number
+  // // 滑牙导航圈数(r)
+  // threadStripTurn?: number
+}

+ 9 - 0
entry/src/main/ets/viewmodel/device/ElectricSolderingIron.ets

@@ -0,0 +1,9 @@
+//电烙铁
+export default class ElectricSolderingIron {
+  // 电烙铁当前温度
+  SolderingCurrTemp?: number
+  // 设置电烙铁温度
+  SolderingSetTemp?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 11 - 0
entry/src/main/ets/viewmodel/device/ElectricSolderingIronPreset.ets

@@ -0,0 +1,11 @@
+//电烙铁预设
+export default class ElectricSolderingIronPreset {
+  // id
+  id?: number
+  // 预设名称
+  setName?: string
+  // 预设值
+  setValue?: number
+  // 单位字符串
+  unitString?: string
+}

+ 7 - 0
entry/src/main/ets/viewmodel/device/Lighting.ets

@@ -0,0 +1,7 @@
+//照明
+export default class Lighting {
+  // 1-亮,0-灭
+  lighting?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 11 - 0
entry/src/main/ets/viewmodel/device/Robot.ets

@@ -0,0 +1,11 @@
+//机器人
+export default class Robot {
+  // 高位称重
+  Station1Weight?: number
+  // 低位称重
+  Station2Weight?: number
+  // 高低位设置
+  StationSet?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 9 - 0
entry/src/main/ets/viewmodel/device/TempHumiditySensor.ets

@@ -0,0 +1,9 @@
+//温湿度传感器
+export default class TempHumiditySensor {
+  // 实际温度 = 当前温度/10,单位°c
+  Temperature?: number
+  // 实际湿度 = 当前湿度/10,单位%
+  Humidity?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 15 - 0
entry/src/main/ets/viewmodel/device/ThreeColourLight.ets

@@ -0,0 +1,15 @@
+//三色灯
+export default class ThreeColourLight {
+  // 三色灯红灯控制(1:灯亮 0:灯灭)
+  LedRed?: number
+  //   三色灯黄灯控制(1:灯亮 0:灯灭)
+  LedOrange?: number
+  //   三色灯绿灯控制(1:灯亮 0:灯灭)
+  LedGreen?: number
+  //   三色灯蜂鸣控制(1:蜂鸣开 0:蜂鸣关)
+  Buzzer?: number
+  // 开关状态(1:开 0:关)
+  OpenStatus?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 7 - 0
entry/src/main/ets/viewmodel/device/WeldFumeExtractor.ets

@@ -0,0 +1,7 @@
+// 焊接排烟机
+export default class WeldFumeExtractor {
+  // 焊烟排烟机控制(1:排烟开 0:排烟关)
+  ExhaustFan?: number
+  // 在线状态(1:在线 0:离线)
+  OnlineStatus?: number
+}

+ 6 - 0
entry/src/main/ets/viewmodel/mqtt/MqttCmdData.ets

@@ -0,0 +1,6 @@
+//mqtt控制数据格式
+import MqttDataItem from './MqttDataItem'
+
+export default class MqttCmdData {
+  w?: MqttDataItem[]
+}

+ 9 - 0
entry/src/main/ets/viewmodel/mqtt/MqttDataItem.ets

@@ -0,0 +1,9 @@
+//mqtt数据项
+export default class MqttDataItem {
+  //采集属性编码
+  tag?: string
+  //采集属性值
+  value?: number
+  //采集属性所属设备类型
+  deviceType?: string
+}

+ 6 - 0
entry/src/main/ets/viewmodel/mqtt/MqttUploadData.ets

@@ -0,0 +1,6 @@
+//mqtt上传数据格式
+import MqttDataItem from './MqttDataItem'
+
+export default class MqttUploadData {
+  d?: MqttDataItem[]
+}

+ 9 - 0
entry/src/main/ets/viewmodel/process/BindTaskSeq.ets

@@ -0,0 +1,9 @@
+export interface BindTaskSeq
+{
+  userName?:string,
+  userId?:string,
+  // workOrderCode?:string,
+  processId?: string,
+  reportSeqNos?: string[]
+  defectSeqNos?: string[]
+}

+ 19 - 0
entry/src/main/ets/viewmodel/process/OperationComponent.ets

@@ -0,0 +1,19 @@
+// 工序组件
+export default class OperationComponent {
+  // 组件id
+  id?: number | string
+  // 工艺路线id
+  processRouteId?: string
+  // 工序id
+  operationId?: string
+  // 删除标识
+  deleted?: number
+  // 备注
+  remark?: string
+  // 组件名称
+  compentName?: string
+  // 组件类型 1:物料采集 2:记录项 3:多媒体采集 4:ESOP 5:点检 6:设备记录 7:紧固 8:调试配对 9:铭牌绑定 10:工序表单 11:自动测试 12:设备确认)
+  compentType?: string
+  // 排序标识
+  sortNum?: number
+}

+ 41 - 0
entry/src/main/ets/viewmodel/process/OperationInfo.ets

@@ -0,0 +1,41 @@
+import VehicleInfo from '../VehicleInfo'
+import TaskSeqInfo from './TaskSeqInfo'
+// 工序信息
+export default class OperationInfo {
+  // 工单编码
+  workOrderCode?: string
+  // 工序ID
+  operationId?: string
+  // 工序码
+  operationCode?: string
+  // 工序名称
+  operationName?: string
+  // 工序排序
+  operationSort?: number
+  // 工序是否完成
+  opComplete?: boolean
+  // 绩效模式(0:计时 1:计件)
+  performance?: string
+  // 当前工位是否有该工序任务
+  exists?: boolean
+  // 序列号
+  seqs?: TaskSeqInfo[]
+  // 是否自检
+  selfCheck?: number
+  // 是否互检
+  mutualInspection?: number
+  // 是否专检
+  specialInspection?: number
+  // 是否巡检
+  inspection?: number
+  // 是否首检
+  firstCheck?: number
+  // 工单下工序是否完成
+  isEnd?: boolean
+  // 工位id
+  stationId?: string
+  // 工位名称
+  stationName?: string
+  //工序载具列表
+  processVehicleList?:VehicleInfo[]
+}

+ 11 - 0
entry/src/main/ets/viewmodel/process/ProcessBaseBug.ets

@@ -0,0 +1,11 @@
+// 缺陷
+export default class ProcessBaseBug {
+  // id
+  id?: string
+  // 缺陷类型(数据字典:defect_mana)
+  bugType?: string
+  // 缺陷编码
+  bugCode?: string
+  // 缺陷名称
+  bugName?: string
+}

+ 13 - 0
entry/src/main/ets/viewmodel/process/ProcessCallMaterial.ets

@@ -0,0 +1,13 @@
+// 生产过程叫料参数
+import MaterialInfo from '../MaterialInfo'
+
+export default class ProcessCallMaterial {
+  // 叫料明细
+  materials?: MaterialInfo[]
+  // 生产id
+  processId?: string
+  // 工位id
+  stationId?: string
+  // 工单code
+  workOrderCode?: string
+}

+ 37 - 0
entry/src/main/ets/viewmodel/process/ProcessCheck.ets

@@ -0,0 +1,37 @@
+// 生产过程点检
+export default class ProcessCheck {
+  // 主键id
+  id?: string;
+  //  生产过程id
+  processId?: string;
+  // 步骤id
+  stepInstanceId?: string;
+  // 工序检验项id
+  operationCheckId?: string;
+  // 操作人
+  operator?: string;
+  // 结果
+  result?: string;
+  // 序列号
+  seqNo?: string;
+  // 排序号
+  sortNum?: number;
+  // 工序点检项名称
+  checkName?: string;
+  // 工序点检项编码
+  checkCode?: string;
+  // 内容
+  content?: string;
+  // 标准值
+  standard?: string;
+  // 上限值
+  upper?: string;
+  // 下限值
+  lower?: string;
+  // 单位
+  unit?: string;
+  // 工序编码
+  operationCode?: string;
+  // 工序名称
+  operationName?: string;
+}

+ 21 - 0
entry/src/main/ets/viewmodel/process/ProcessDefectRecord.ets

@@ -0,0 +1,21 @@
+// 不良记录
+export default class ProcessDefectRecord {
+  // 主键id
+  id?: string
+  // 生产过程id
+  processId?: string
+  // 序列号
+  seqNo?: string
+  // 工序id
+  operationId?: string
+  // 缺陷类型(大类) 字典
+  defectType?: string
+  // 缺陷类型(大类) 字典标签
+  defectTypeLabel?: string
+  // 缺陷编码(小类)
+  bugCode?: string
+  // 缺陷名称(小类)
+  bugName?: string
+  // 备注
+  remark?: string
+}

+ 23 - 0
entry/src/main/ets/viewmodel/process/ProcessDeviceDailyCheck.ets

@@ -0,0 +1,23 @@
+// 生产过程点检
+export default class ProcessCheck {
+  // 主键id
+  id?: string;
+  // 工位ip
+  stationIp?: string;
+  // 设备编码
+  deviceNo?: string;
+  // 设备名字
+  deviceName?: string
+  // 设备型号(数据字典)
+  deviceType?: string
+  // 计量有效期
+  meteringDate?: string;
+  // 计量状态(1:有效 0:过期)
+  meteringState?: number;
+  // 维保有效期
+  warrantyPeriod?: string;
+  // 维保状态(1:有效 0:过期)
+  warrantyState?: number;
+  // 创建时间
+  created?: number;
+}

+ 53 - 0
entry/src/main/ets/viewmodel/process/ProcessInfo.ets

@@ -0,0 +1,53 @@
+// 生产过程信息
+export default class ProcessInfo {
+  // 过程id
+  id?: string
+  // 序列号
+  seqNo?: string
+  // 工序id
+  operationId?: number
+  // 工序名称
+  operationName?: string
+  // 当前状态-字典process_state
+  currentState?: string
+  // 实际开始时间
+  realStartWhen?: string
+  // 实际结束时间
+  realEndWhen?: string
+  // 状态更改时间
+  changeWhen?: string
+  // 总共用时
+  totalTime?: string
+  // 操作人
+  operator?: string
+  // 工单id
+  workOrderId?: string
+  // 订单id
+  orderId?: string
+  // 工位id
+  stanId?: string
+  // 是否委外 0否  1是
+  outsource?: number
+  // 是否下线0 否 1是
+  offLine?: number
+  // 返工id
+  reworkId?: string
+  // 维修id
+  repairId?: string
+  batchReport?: number
+  // 订单编码
+  orderCode?: string
+  // 工单编码
+  workOrderCode?: string
+  // 物料编号
+  materialCode?: string
+  // 物料名称
+  materialName?: string
+  // 物料型号
+  materialModel?: string
+  // 下一个工位
+  nextStation?: string
+  // 超期原因
+  outTimeReason?: string
+  outTime?: boolean
+}

+ 85 - 0
entry/src/main/ets/viewmodel/process/ProcessMaterial.ets

@@ -0,0 +1,85 @@
+// 生产过程物料信息
+export default class ProcessMaterial {
+  // 主键
+  id?: string;
+  // 物料编码
+  materialCode?: string;
+  // 物料名称
+  materialName?: string;
+  // 物料属性字典code
+  attributeDictCode?: string;
+  // 物料属性字典value
+  attributeDictValue?: string;
+  // 物料规格
+  spec?: string;
+  // 物料单位字典code
+  unitDictCode?: string;
+  // 物料单位字典value
+  unitDictValue?: string;
+  // 物料级别字典code
+  levelDictCode?: string;
+  // 物料级别字典value
+  levelDictValue?: string;
+  // 生产厂家
+  manufacturer?: string;
+  // 质检方案字典code
+  inspectDictCode?: string;
+  // 质检方案字典value
+  inspectDictValue?: string;
+  // 适用平台字典code
+  applicablePlatformsDictCode?: string;
+  // 适用平台字典value
+  applicablePlatformsDictValue?: string;
+  // 质量等级字典code
+  qualityLevelDictCode?: string;
+  // 质量等级字典value
+  qualityLevelDictValue?: string;
+  // 选用类型字典code
+  selectionDictCode?: string;
+  // 选用类型字典value
+  selectionDictValue?: string;
+  // 阶段字典code
+  stageDictCode?: string;
+  // 阶段字典value
+  stageDictValue?: string;
+  // 客户型号
+  customerModel?: string;
+  // 保质期
+  qualityGuaranteePeriod?: number;
+  // 封装方法字典code
+  packageDictCode?: string;
+  // 封装方法字典value
+  packageDictValue?: string;
+  // 是否工装
+  frock?: number;
+  // 筛选规范
+  selectionSpec?: string;
+  // 备注
+  remark?: string;
+  // 还需要多少数量
+  needNum?: number;
+  // 总数量
+  totalNum?: number;
+  // 扫码数量
+  num?: number;
+  // SEQ/BATCH(流水号/批次号)
+  codeType?: string;
+  // 批次号
+  batchNo?: string;
+  // 物料序列号
+  itemSeq?: string;
+  // 追溯类型
+  trackType?: string;
+  // 采集人
+  trackBy?: string;
+  // 工单编码
+  workOrderCode?: string;
+  // 工序id
+  operationId?: string;
+  // 生产过程id
+  processId?: string;
+  // 流转卡号
+  seqNo?: string;
+  // 工位id
+  stationId?: string;
+}

+ 11 - 0
entry/src/main/ets/viewmodel/process/ProcessReportTimeBased.ets

@@ -0,0 +1,11 @@
+// 报工(计时)参数
+import ReporterInfo from './ReporterInfo';
+
+export default interface ProcessReportTimeBased {
+  // 生产过程id
+  processId?: string,
+  // 报工卡号
+  seqList?: string[]
+  // 用户报工占比
+  processUserReportList?: ReporterInfo[],
+}

+ 13 - 0
entry/src/main/ets/viewmodel/process/ReporterInfo.ets

@@ -0,0 +1,13 @@
+// 报工人信息
+export default class ReportInfo {
+  // 报工人
+  userName?: string
+  // 报工人id
+  userId?: string
+  // 报工数量
+  reportNum?: string
+  //不良数量
+  defectNum?: string
+  // 用户占用工时比列
+  workingHoursRate?: number
+}

+ 12 - 0
entry/src/main/ets/viewmodel/process/TaskSeqInfo.ets

@@ -0,0 +1,12 @@
+export default class TaskSeqVO {
+  // 序列号
+  seqNo?: string
+  // 序列号状态 (-1:未下发 0:执行中 1:待执行 2:已完成 3:已报废)
+  state?: string
+  // 待进行
+  currentTask?: string
+  //报工时间
+  updated?:string
+  //工序id
+  operationId?:string
+}

+ 45 - 0
entry/src/main/module.json5

@@ -0,0 +1,45 @@
+{
+  "module": {
+    "name": "entry",
+    "type": "entry",
+    "description": "$string:module_desc",
+    "mainElement": "EntryAbility",
+    "deviceTypes": [
+      "default",
+      "tablet"
+    ],
+    "deliveryWithInstall": true,
+    "installationFree": false,
+    "pages": "$profile:main_pages",
+    "requestPermissions":[
+      {
+        "name": "ohos.permission.INTERNET"
+      },
+      {
+        "name": "ohos.permission.GET_NETWORK_INFO"
+      }
+    ],
+    "abilities": [
+      {
+        "name": "EntryAbility",
+        "srcEntry": "./ets/entryability/EntryAbility.ets",
+        "description": "$string:EntryAbility_desc",
+        "icon": "$media:icon",
+        "label": "$string:EntryAbility_label",
+        "startWindowIcon": "$media:startIcon",
+        "startWindowBackground": "$color:start_window_background",
+        "exported": true,
+        "skills": [
+          {
+            "entities": [
+              "entity.system.home"
+            ],
+            "actions": [
+              "action.system.home"
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}

+ 0 - 0
entry/src/main/resources/base/element/color.json


Some files were not shown because too many files changed in this diff