完善生产查询与登录权限配置
This commit is contained in:
@@ -0,0 +1,719 @@
|
||||
# mas-code-scan iOS 端支持方案
|
||||
|
||||
> 文档状态:方案设计,尚未开始 iOS 原生实现。
|
||||
>
|
||||
> 目标是在不改变现有业务调用方式的前提下,为 `mas-code-scan` 增加 iOS App 支持,并与 Android 端尽量保持相同的功能、参数语义、回调顺序和错误码。
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
当前 `mas-code-scan` 是 Android UTS API 插件,Android 原生层基于 CameraX + Google ML Kit 实现。对外 API 已沉淀在 `utssdk/interface.uts`,业务页面通过 `openCodeScan` 调用,因此具备扩展 iOS 实现的良好基础。
|
||||
|
||||
### 1.1 当前状态
|
||||
|
||||
| 项目 | 当前情况 |
|
||||
| --- | --- |
|
||||
| 对外 API | `openCodeScan(options)` |
|
||||
| 公共类型 | `CodeScanOptions`、`CodeScanSuccess`、`CodeScanFail`、`ScanRegion`、`CodeScanStyle` |
|
||||
| Android 实现 | `utssdk/app-android/`,CameraX + Google ML Kit |
|
||||
| iOS 实现 | 暂无 `utssdk/app-ios/` 目录 |
|
||||
| 业务调用 | `pages/scan/prototype.vue` 只需调用统一 API |
|
||||
| 当前支持平台 | Android App(uni-app Vue2/Vue3) |
|
||||
|
||||
### 1.2 建设目标
|
||||
|
||||
1. **API 不变**:iOS 与 Android 均使用 `openCodeScan`,业务页面不增加平台分支。
|
||||
2. **回调一致**:统一 `success`、`fail`、`complete` 的数据结构和触发时机。
|
||||
3. **优先原生能力**:使用 iOS 系统框架,尽量不新增第三方识别库。
|
||||
4. **功能分阶段交付**:先完成可真机使用的最小链路,再逐步补齐 Android 已有能力。
|
||||
5. **平台差异显式处理**:对系统不支持或实现成本较高的能力,明确降级或延后,不以静默不生效代替。
|
||||
|
||||
### 1.3 非目标
|
||||
|
||||
本期方案不包含:
|
||||
|
||||
- 鸿蒙、H5、小程序、nvue 支持;
|
||||
- 修改 Android 识别链路;
|
||||
- 将扫码页改造为 Vue 组件;
|
||||
- 为尚未经过真机验证的 iOS 能力提前修改 README 平台声明。
|
||||
|
||||
---
|
||||
|
||||
## 2. 总体架构
|
||||
|
||||
### 2.1 目录规划
|
||||
|
||||
在保持现有 Android 目录不变的基础上新增 iOS 实现:
|
||||
|
||||
```text
|
||||
frontend/scanapp/uni_modules/mas-code-scan/
|
||||
├── package.json
|
||||
├── readme.md
|
||||
├── changelog.md
|
||||
└── utssdk/
|
||||
├── interface.uts # Android / iOS 共用公开类型
|
||||
├── unierror.uts # Android / iOS 共用错误对象
|
||||
├── app-android/ # 现有 Android 实现
|
||||
│ ├── index.uts
|
||||
│ ├── CodeCaptureActivity.kt
|
||||
│ ├── MultiCodePickView.kt
|
||||
│ └── ScanFrameDecorView.kt
|
||||
└── app-ios/ # 新增 iOS 实现
|
||||
├── index.uts # UTS 桥接与回调编排
|
||||
├── CodeCaptureViewController.swift
|
||||
├── CodeScanner.swift
|
||||
├── ScanOverlayView.swift
|
||||
├── MultiCodePickView.swift
|
||||
├── ScanStyleParser.swift
|
||||
└── Info.plist # 或按 UTS iOS 配置规范声明权限
|
||||
```
|
||||
|
||||
> `app-ios` 的最终配置文件和 Swift 文件参与编译方式,必须以当前 HBuilderX 版本的 UTS iOS 官方规范为准;开始编码前需要用官方最小示例验证目录结构。
|
||||
|
||||
### 2.2 分层职责
|
||||
|
||||
```text
|
||||
业务页面
|
||||
↓ openCodeScan(options)
|
||||
UTS 平台入口(app-android/index.uts 或 app-ios/index.uts)
|
||||
↓ 参数转换 / 生命周期 / 回调派发
|
||||
原生扫码控制器
|
||||
↓ 相机、识别、相册、交互
|
||||
原生绘制层
|
||||
↓ 扫描框、扫描线、多码点选、呼吸动画
|
||||
```
|
||||
|
||||
- `interface.uts`:作为跨端 API 契约,不应写入 Android 或 iOS 私有字段。
|
||||
- `app-ios/index.uts`:负责将 `CodeScanOptions` 转为原生配置,处理单次与连续扫码回调。
|
||||
- `CodeCaptureViewController`:负责页面展示、权限、相机生命周期、工具栏和结果模式。
|
||||
- `CodeScanner`:负责相机帧分析、Vision 识别、格式过滤、区域过滤和缩放策略。
|
||||
- `ScanOverlayView`:负责 `scanStyle` 与 `scanRegion` 的视觉绘制。
|
||||
- `MultiCodePickView`:负责多码定格、点选和绿色呼吸波纹。
|
||||
|
||||
---
|
||||
|
||||
## 3. iOS 技术选型
|
||||
|
||||
| 能力 | 推荐技术 | 选择理由 |
|
||||
| --- | --- | --- |
|
||||
| 相机预览与帧采集 | `AVFoundation` | 系统原生、稳定、无需新增依赖 |
|
||||
| 条码识别 | `Vision` 的 `VNDetectBarcodesRequest` | 覆盖常见二维码与条形码,识别结果可获取码制和边界框 |
|
||||
| 相册选图 | `PHPickerViewController` | iOS 14+ 可按用户选择读取图片,通常无需完整相册权限 |
|
||||
| 低版本相册兼容 | `UIImagePickerController` | 仅在确认最低系统版本低于 iOS 14 时补充 |
|
||||
| 扫描线动画 | `CAShapeLayer` + `CABasicAnimation` | 使用 Core Animation,避免定时器逐帧重绘 |
|
||||
| 多码呼吸动画 | `CAShapeLayer` + `CABasicAnimation` | 与 Android 绿点呼吸波纹实现保持视觉一致 |
|
||||
| 手势缩放 | `UIPinchGestureRecognizer` | 对应 Android 双指捏合 |
|
||||
| 震动反馈 | `UIImpactFeedbackGenerator` | 适配 iOS 系统触感反馈 |
|
||||
|
||||
### 3.1 不引入第三方扫码库
|
||||
|
||||
首版不建议接入 ZXing、ML Kit iOS 等第三方库,原因:
|
||||
|
||||
- 系统 `Vision` 已能覆盖主要码制;
|
||||
- 避免 CocoaPods/SPM 与 UTS iOS 构建的额外集成风险;
|
||||
- 减少安装包和维护成本;
|
||||
- 便于优先验证 UTS + Swift 桥接与相机链路。
|
||||
|
||||
若后续测试确认某些业务关键码制在 `Vision` 上识别率不足,再单独评估第三方引擎,不在首版预置依赖。
|
||||
|
||||
---
|
||||
|
||||
## 4. 公共 API 与行为约束
|
||||
|
||||
### 4.1 保持现有 API
|
||||
|
||||
现有 `CodeScanOptions` 不新增必填字段,iOS 端识别并实现以下参数:
|
||||
|
||||
| 参数 | iOS 首版目标 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `formats` | 支持 | 映射到 Vision 支持的 symbology;不支持值忽略 |
|
||||
| `vibrate` | 支持 | 识别成功后触发轻触感反馈 |
|
||||
| `fromAlbum` | 支持 | 打开扫码页后直接进入相册选择 |
|
||||
| `scanRegion` | 支持 | 控制实时识别区域与扫描框;相册识图不受限制 |
|
||||
| `scanStyle` | 支持 | 控制遮罩、边框、扫描线及动画 |
|
||||
| `resultMode` | 支持 | `immediate`、`confirm`、`pause` |
|
||||
| `continuous` | 支持 | 多次 `success`,关闭时 `complete` |
|
||||
| `showPauseButton` | 支持 | 仅连续扫码时展示暂停/继续控制 |
|
||||
| `torch: 'off'` | 支持 | 默认关闭 |
|
||||
| `torch: 'on'` | 支持 | 设备支持时尝试开启 |
|
||||
| `smartZoom` | 第二阶段 | 首版可读取但不自动变焦,后续补齐 |
|
||||
| `torch: 'auto'` | 第二阶段 | 需要基于视频帧亮度策略验证 |
|
||||
|
||||
### 4.2 成功数据
|
||||
|
||||
iOS 识别结果必须统一转换为:
|
||||
|
||||
```ts
|
||||
{
|
||||
content: string,
|
||||
format: string,
|
||||
fromAlbum: boolean
|
||||
}
|
||||
```
|
||||
|
||||
格式名称与 Android 对齐,至少包括:
|
||||
|
||||
```text
|
||||
QR_CODE
|
||||
EAN_13
|
||||
EAN_8
|
||||
UPC_A
|
||||
UPC_E
|
||||
CODE_128
|
||||
CODE_39
|
||||
CODE_93
|
||||
ITF
|
||||
CODABAR
|
||||
PDF417
|
||||
AZTEC
|
||||
DATA_MATRIX
|
||||
```
|
||||
|
||||
当 Vision 不支持某个 Android 已支持的格式时:
|
||||
|
||||
1. `formats` 中的该值不参与 Vision 请求;
|
||||
2. 不因单个非法或不支持值使整个调用失败;
|
||||
3. 若过滤后没有任何 iOS 支持的格式,可按全格式或返回 `9021004`,最终策略需在编码前固定,并同步文档;
|
||||
4. 推荐与 Android 保持一致:过滤为空时退回全格式识别。
|
||||
|
||||
### 4.3 回调一致性
|
||||
|
||||
| 场景 | `success` | `fail` | `complete` |
|
||||
| --- | --- | --- | --- |
|
||||
| 单次扫码成功 | 调用一次 | 不调用 | 紧随成功调用一次,参数为成功结果 |
|
||||
| 单次取消/关闭 | 不调用 | `9021003` | 紧随失败调用一次 |
|
||||
| 相机权限拒绝 | 不调用 | `9021002` | 紧随失败调用一次 |
|
||||
| 无有效扫码结果的异常结束 | 不调用 | `9021004` | 紧随失败调用一次 |
|
||||
| 连续扫码单次命中 | 每次命中调用 | 不调用 | 页面未关闭前不调用 |
|
||||
| 连续扫码关闭且已有命中 | 不再调用 | 不调用 | `complete({ closed: true, hitCount })` |
|
||||
| 连续扫码关闭且无命中 | 不调用 | `9021003` | 紧随失败调用一次 |
|
||||
|
||||
所有业务回调必须在主线程派发,控制器关闭、权限回调和异步识别完成之间要有会话状态保护,确保不会重复回调。
|
||||
|
||||
### 4.4 错误码复用
|
||||
|
||||
继续使用 `unierror.uts` 中的错误码:
|
||||
|
||||
| 错误码 | 语义 | iOS 对应场景 |
|
||||
| --- | --- | --- |
|
||||
| `9021001` | 当前无法获取页面 Activity | 无法获取当前 `UIViewController` 或不能展示扫码页 |
|
||||
| `9021002` | 相机权限被拒绝 | `.denied` / `.restricted` 或用户拒绝授权 |
|
||||
| `9021003` | 用户取消扫码 | 点击关闭、系统返回或取消图片选择 |
|
||||
| `9021004` | 未获取到有效的扫码结果 | 扫码处理异常结束、相册图片无有效条码后由调用方选择结束 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 核心功能设计
|
||||
|
||||
### 5.1 相机权限与页面生命周期
|
||||
|
||||
流程:
|
||||
|
||||
```text
|
||||
openCodeScan
|
||||
↓
|
||||
获取当前 UIViewController
|
||||
↓
|
||||
检查 AVCaptureDevice 相机权限
|
||||
├─ 已授权 → 初始化扫码控制器与会话
|
||||
├─ 未决定 → 请求授权 → 成功后初始化
|
||||
└─ 拒绝/受限 → fail(9021002) → complete
|
||||
```
|
||||
|
||||
控制器生命周期要求:
|
||||
|
||||
- 页面展示时创建并启动 `AVCaptureSession`;
|
||||
- 页面进入后台时暂停会话;
|
||||
- 页面恢复前台时,未完成且未暂停时恢复会话;
|
||||
- 页面关闭、成功返回或发生不可恢复错误时停止会话;
|
||||
- 释放 `AVCaptureVideoDataOutput` 委托、图片引用、动画图层和 UTS 回调引用;
|
||||
- 防止异步 Vision 回调在页面关闭后再提交结果。
|
||||
|
||||
### 5.2 实时扫码链路
|
||||
|
||||
```text
|
||||
AVCaptureVideoDataOutput
|
||||
↓ CMSampleBuffer
|
||||
Vision VNDetectBarcodesRequest
|
||||
↓ VNBarcodeObservation 列表
|
||||
formats 过滤
|
||||
↓
|
||||
scanRegion 区域过滤
|
||||
↓
|
||||
单码 / 多码 / 连续扫码 / 结果模式处理
|
||||
```
|
||||
|
||||
性能策略:
|
||||
|
||||
- 使用串行 `DispatchQueue` 处理 Vision 请求,避免同一时间处理多帧;
|
||||
- 在上一帧识别尚未结束时丢弃新帧;
|
||||
- 多码定格、确认弹窗、暂停状态、页面关闭后停止提交识别请求;
|
||||
- 连续扫码需按 `content + format` 做短时间去重,建议冷却窗口 `800ms ~ 1200ms`;
|
||||
- 不在 Vision 回调线程直接更新 UIKit,统一回主线程更新界面。
|
||||
|
||||
### 5.3 formats 映射
|
||||
|
||||
新增 iOS 私有映射表:
|
||||
|
||||
```text
|
||||
QR_CODE → VNBarcodeSymbologyQR
|
||||
EAN_13 → VNBarcodeSymbologyEAN13
|
||||
EAN_8 → VNBarcodeSymbologyEAN8
|
||||
UPC_A → VNBarcodeSymbologyUPCE(需以实际 Vision 结果确认映射)
|
||||
UPC_E → VNBarcodeSymbologyUPCE
|
||||
CODE_128 → VNBarcodeSymbologyCode128
|
||||
CODE_39 → VNBarcodeSymbologyCode39
|
||||
CODE_93 → VNBarcodeSymbologyCode93
|
||||
ITF → VNBarcodeSymbologyI2of5
|
||||
CODABAR → VNBarcodeSymbologyCodabar
|
||||
PDF417 → VNBarcodeSymbologyPDF417
|
||||
AZTEC → VNBarcodeSymbologyAztec
|
||||
DATA_MATRIX → VNBarcodeSymbologyDataMatrix
|
||||
```
|
||||
|
||||
> `VNBarcodeSymbology` 的实际可用范围与命名随 iOS SDK 变化。开发时必须以目标 Xcode / iOS SDK 编译结果为准,不能假定每个 Android 格式都一一对应。
|
||||
|
||||
### 5.4 scanRegion 坐标转换与过滤
|
||||
|
||||
`scanRegion` 是业务 API 中最容易出现跨平台差异的部分,iOS 必须确保“画面上显示的扫描框”与“实际允许识别的区域”一致。
|
||||
|
||||
处理步骤:
|
||||
|
||||
1. 根据 `width`、`height`、`centerX`、`centerY` 计算预览层的扫描框 `CGRect`;
|
||||
2. 使用 `AVCaptureVideoPreviewLayer` 将 Vision 的归一化 `boundingBox` 转换为 UIKit 坐标;
|
||||
3. 判断条码边界框中心点是否在扫描框中;
|
||||
4. 仅保留区域内条码;
|
||||
5. `fullScreen: true` 时不绘制扫描框且不进行区域过滤;
|
||||
6. 相册识图跳过 `scanRegion` 过滤,保持 Android 行为。
|
||||
|
||||
验证重点:
|
||||
|
||||
- Vision 坐标原点与 UIKit 坐标原点不同;
|
||||
- `.resizeAspectFill` 会产生裁切偏移;
|
||||
- 前置镜像、横竖屏和刘海安全区可能影响坐标;
|
||||
- 多码点选标记必须与真实条码中心重合。
|
||||
|
||||
### 5.5 scanStyle 绘制
|
||||
|
||||
`ScanOverlayView` 使用 `CAShapeLayer` 与 `CAGradientLayer` 绘制,配置语义与 Android 一致:
|
||||
|
||||
| 参数 | iOS 实现 |
|
||||
| --- | --- |
|
||||
| `showMask` | 显示/隐藏扫描框外遮罩层 |
|
||||
| `showBeam` | 显示/隐藏扫描线图层 |
|
||||
| `borderColor` | `CGColor` 边框颜色 |
|
||||
| `maskColor` | 解析 `#RRGGBB`、`#AARRGGBB`、`rgba()` 后填充遮罩 |
|
||||
| `beamColor` | 扫描线颜色 |
|
||||
| `beamDuration` | 扫描线 `CABasicAnimation.duration`,限制 `100 ~ 60000ms` |
|
||||
| `cornerStyle: 'corner'` | 绘制四角边框 |
|
||||
| `cornerStyle: 'full'` | 绘制完整矩形边框 |
|
||||
|
||||
默认值必须沿用当前接口默认行为:
|
||||
|
||||
```ts
|
||||
{
|
||||
showMask: true,
|
||||
showBeam: true,
|
||||
borderColor: '#19be6b',
|
||||
maskColor: 'rgba(0, 0, 0, 0.45)',
|
||||
beamColor: '#12e880',
|
||||
beamDuration: 1800,
|
||||
cornerStyle: 'corner'
|
||||
}
|
||||
```
|
||||
|
||||
颜色解析建议单独实现 `ScanStyleParser`:
|
||||
|
||||
- 支持 `#RRGGBB`;
|
||||
- 支持 `#AARRGGBB`;
|
||||
- 支持 `rgba(r,g,b,a)`;
|
||||
- 解析失败时回退默认色;
|
||||
- 所有回退规则与 Android `readScanStyle()` 保持一致。
|
||||
|
||||
### 5.6 单码、多码和点选
|
||||
|
||||
#### 单码
|
||||
|
||||
单个有效条码按 `resultMode` 处理:
|
||||
|
||||
- `immediate`:停止识别并立即返回;
|
||||
- `confirm`:暂停识别,弹出确认框;
|
||||
- `pause`:暂停识别,显示继续扫描和确认返回操作。
|
||||
|
||||
#### 多码
|
||||
|
||||
同一帧出现两个及以上有效、有内容且有边界框的条码时:
|
||||
|
||||
1. 停止实时识别;
|
||||
2. 抓取当前预览帧;
|
||||
3. 显示定格画面和半透明遮罩;
|
||||
4. 把各码的边界框映射到屏幕;
|
||||
5. 在中心位置绘制点选标记;
|
||||
6. 显示“已识别到多个码,请选择一个”提示;
|
||||
7. 用户点击最近标记后提交对应条码结果。
|
||||
|
||||
`MultiCodePickView` 的标记视觉与 Android 对齐:
|
||||
|
||||
```text
|
||||
绿色中心圆点
|
||||
+ 白色圆形底
|
||||
+ 绿色半透明外圈
|
||||
+ 周期性呼吸扩散动画
|
||||
```
|
||||
|
||||
建议使用 `CAKeyframeAnimation` 或 `CABasicAnimation` 同步变化外圈的 `transform.scale` 与 `opacity`,周期暂定 `1500ms`。
|
||||
|
||||
点击策略:
|
||||
|
||||
- 以点位中心为基准选择最近候选;
|
||||
- 设置最小可点击半径,建议约 `28pt`;
|
||||
- 无命中点时继续等待选择,不直接取消;
|
||||
- 用户主动关闭时按取消流程处理。
|
||||
|
||||
### 5.7 相册识图
|
||||
|
||||
优先采用 `PHPickerViewController`:
|
||||
|
||||
```text
|
||||
fromAlbum: true 或点击相册按钮
|
||||
↓
|
||||
系统图片选择器
|
||||
↓
|
||||
获取 UIImage / CGImage
|
||||
↓
|
||||
Vision VNDetectBarcodesRequest
|
||||
↓
|
||||
单码直接返回;多码进入点选
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 相册识图结果的 `fromAlbum` 固定为 `true`;
|
||||
- 相册图片不受 `scanRegion` 限制;
|
||||
- 用户取消图片选择:单次扫码按 `9021003` 处理;
|
||||
- 图片中未识别到条码时,提示“未识别到有效条码”,并返回扫码页继续操作;
|
||||
- 多码相册图片使用图片本身作为定格背景并进入点选。
|
||||
|
||||
### 5.8 连续扫码与暂停
|
||||
|
||||
`continuous: true` 时:
|
||||
|
||||
- 命中一次条码调用一次 `success`;
|
||||
- 扫码页不关闭;
|
||||
- 同一内容需通过短时冷却避免高频重复提交;
|
||||
- `showPauseButton: true` 时展示暂停/继续按钮;
|
||||
- 用户关闭时,若已有命中,则仅调用:
|
||||
|
||||
```ts
|
||||
complete({ closed: true, hitCount })
|
||||
```
|
||||
|
||||
- 用户关闭时,若没有命中,则按 `fail(9021003)` 后 `complete`;
|
||||
- 多码点选确认后,在连续模式下应投递一次成功结果、清除定格层、恢复相机识别,而不是关闭控制器。
|
||||
|
||||
### 5.9 手电筒、缩放与震动
|
||||
|
||||
#### 手电筒
|
||||
|
||||
- 使用 `AVCaptureDevice.hasTorch` 判断设备能力;
|
||||
- 使用 `lockForConfiguration()` + `torchMode` 变更状态;
|
||||
- `off`:默认关闭;
|
||||
- `on`:启动后尝试开启;
|
||||
- 无手电筒设备隐藏或禁用页面按钮;
|
||||
- 页面结束时关闭手电筒。
|
||||
|
||||
#### 手势缩放
|
||||
|
||||
- 使用 `UIPinchGestureRecognizer`;
|
||||
- 映射至 `videoZoomFactor`;
|
||||
- 限制在 `1.0 ... activeFormat.videoMaxZoomFactor` 的安全范围;
|
||||
- 变焦期间禁止智能变焦抢占。
|
||||
|
||||
#### 智能变焦
|
||||
|
||||
作为第二阶段能力:
|
||||
|
||||
- 根据条码边界框占画面比例计算建议倍率;
|
||||
- 设定最大尝试次数、最大倍率和平滑过渡;
|
||||
- 检测到多个码时先恢复 `1.0x`,避免多码点选区域被放大裁切;
|
||||
- 与手势缩放、连续扫码、定格状态互斥。
|
||||
|
||||
#### 震动
|
||||
|
||||
- `vibrate: true` 时使用 `UIImpactFeedbackGenerator(style: .light)`;
|
||||
- 连续扫码每次实际成功投递时触发;
|
||||
- 系统可能因“减少动态效果”、静音模式或设备策略弱化触感反馈,业务层不应依赖其作为唯一成功提示。
|
||||
|
||||
---
|
||||
|
||||
## 6. UTS 桥接设计
|
||||
|
||||
### 6.1 app-ios/index.uts 职责
|
||||
|
||||
`app-ios/index.uts` 负责:
|
||||
|
||||
1. 获取可展示扫码页的当前 `UIViewController`;
|
||||
2. 校验当前是否已有扫码会话,避免重复打开;
|
||||
3. 归一化 `CodeScanOptions` 默认值;
|
||||
4. 创建并展示 `CodeCaptureViewController`;
|
||||
5. 接收原生事件并转换为 `CodeScanSuccess` / `CodeScanFailImpl`;
|
||||
6. 保证回调顺序和只执行一次的终止语义;
|
||||
7. 连续扫码时维护 `hitCount`;
|
||||
8. 控制器退出后解除事件与对象引用。
|
||||
|
||||
### 6.2 建议的原生事件模型
|
||||
|
||||
控制器向桥接层输出有限事件,不直接感知 UTS 回调:
|
||||
|
||||
```text
|
||||
onResult(content, format, fromAlbum)
|
||||
onCancel()
|
||||
onPermissionDenied()
|
||||
onFatalError()
|
||||
onClosed(hitCount, hadHits)
|
||||
```
|
||||
|
||||
桥接层统一决定:
|
||||
|
||||
- 单次成功:`success` → `complete`;
|
||||
- 单次失败:`fail` → `complete`;
|
||||
- 连续扫码:每次 `onResult` → `success`,关闭时再 `complete`;
|
||||
- 已结束会话的后续事件全部丢弃。
|
||||
|
||||
### 6.3 会话状态
|
||||
|
||||
建议 iOS 桥接层维护:
|
||||
|
||||
```text
|
||||
idle → presenting → scanning → paused / picking → finishing → finished
|
||||
```
|
||||
|
||||
关键约束:
|
||||
|
||||
- `finished` 后不允许再次派发回调;
|
||||
- 权限请求、Vision 回调、相册回调都需检查当前状态;
|
||||
- 控制器 dismissal 完成后再释放强引用;
|
||||
- 业务层再次调用时必须创建新会话,不复用旧回调。
|
||||
|
||||
---
|
||||
|
||||
## 7. 权限与配置
|
||||
|
||||
### 7.1 必需权限
|
||||
|
||||
iOS 必须声明:
|
||||
|
||||
```xml
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>需要使用相机扫描二维码和条形码</string>
|
||||
```
|
||||
|
||||
若采用 `PHPickerViewController` 且最低系统版本为 iOS 14,一般不需要完整相册访问权限。
|
||||
|
||||
若最终需要 `UIImagePickerController` 或直接读取照片库,则补充:
|
||||
|
||||
```xml
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>需要访问相册以识别图片中的二维码和条形码</string>
|
||||
```
|
||||
|
||||
### 7.2 最低系统版本建议
|
||||
|
||||
建议首版目标:
|
||||
|
||||
```text
|
||||
iOS 14+
|
||||
```
|
||||
|
||||
理由:
|
||||
|
||||
- 可以使用 `PHPickerViewController`;
|
||||
- Vision 条码能力更成熟;
|
||||
- 降低图片权限和兼容逻辑复杂度。
|
||||
|
||||
若产品必须支持 iOS 13,需要增加相册选择兼容分支,并重新评估 Vision 可识别格式集合。
|
||||
|
||||
### 7.3 打包与调试
|
||||
|
||||
iOS 原生 UTS 代码不能通过 Web 预览验证,必须使用:
|
||||
|
||||
- 包含插件的 iOS 自定义调试基座;或
|
||||
- iOS 云打包 / 本地原生打包产物;
|
||||
- 真机设备与 Xcode 控制台。
|
||||
|
||||
模拟器不应作为相机扫码主验收环境;可用于页面布局、权限分支和相册图片识别的部分验证。
|
||||
|
||||
---
|
||||
|
||||
## 8. 分阶段实施计划
|
||||
|
||||
### 阶段 0:UTS iOS 可编译性验证
|
||||
|
||||
目标:确认当前 HBuilderX 与项目配置可编译 `app-ios` 原生实现。
|
||||
|
||||
任务:
|
||||
|
||||
- 创建最小 `app-ios/index.uts`;
|
||||
- 验证获取当前 iOS 控制器与展示原生页面的官方方式;
|
||||
- 验证 Swift 文件参与 UTS iOS 编译的目录和配置;
|
||||
- 在 iPhone 真机执行一个最小“打开原生页并返回”示例;
|
||||
- 固化项目实际约定后再开始完整扫码实现。
|
||||
|
||||
验收:iOS 自定义基座可编译并成功打开、关闭原生测试页面。
|
||||
|
||||
### 阶段 1:最小可用扫码
|
||||
|
||||
范围:
|
||||
|
||||
- 相机权限;
|
||||
- 后置相机预览;
|
||||
- Vision 单码实时识别;
|
||||
- `success`、`fail`、`complete`;
|
||||
- 用户关闭;
|
||||
- 常见二维码与条形码格式。
|
||||
|
||||
验收:iPhone 真机可扫描 QR Code 和 Code 128,并按既定顺序回调。
|
||||
|
||||
### 阶段 2:跨端核心功能对齐
|
||||
|
||||
范围:
|
||||
|
||||
- `formats`;
|
||||
- `scanRegion`;
|
||||
- `scanStyle`;
|
||||
- 相册识图;
|
||||
- 多码定格点选;
|
||||
- 绿色呼吸波纹;
|
||||
- `resultMode`;
|
||||
- `vibrate`。
|
||||
|
||||
验收:Android 与 iOS 对同一配置的显示、区域过滤和数据结构一致。
|
||||
|
||||
### 阶段 3:会话与相机增强
|
||||
|
||||
范围:
|
||||
|
||||
- `continuous`;
|
||||
- `showPauseButton`;
|
||||
- `torch: 'off' | 'on'`;
|
||||
- 手势缩放;
|
||||
- 连续扫码去重与关闭结算。
|
||||
|
||||
验收:连续扫码有命中和无命中关闭时的回调行为与 Android 一致。
|
||||
|
||||
### 阶段 4:高级策略
|
||||
|
||||
范围:
|
||||
|
||||
- `smartZoom`;
|
||||
- `torch: 'auto'`;
|
||||
- 复杂机型上的性能优化;
|
||||
- 必要时评估第三方识别引擎。
|
||||
|
||||
验收:在目标机型上无明显误触发、频繁跳变、发热或回调重复问题。
|
||||
|
||||
---
|
||||
|
||||
## 9. 验收矩阵
|
||||
|
||||
### 9.1 功能验收
|
||||
|
||||
| 场景 | 预期 |
|
||||
| --- | --- |
|
||||
| 单个二维码 | 返回 `QR_CODE`、`fromAlbum: false` |
|
||||
| 单个 Code 128 | 返回 `CODE_128` |
|
||||
| formats 过滤 | 仅接受指定码制 |
|
||||
| 全屏识别 | 不显示扫描框,整画面可识别 |
|
||||
| 自定义区域 | 框外条码不返回,框内条码正常返回 |
|
||||
| 相册单码 | 返回 `fromAlbum: true` |
|
||||
| 相册多码 | 显示定格图片与多个可点击标记 |
|
||||
| 实时多码 | 显示定格画面、提示语和绿色呼吸点 |
|
||||
| confirm 模式 | 确认后返回,取消后继续识别 |
|
||||
| pause 模式 | 可继续扫描或确认结果 |
|
||||
| 连续扫码 | 多次 success,关闭后返回正确 hitCount |
|
||||
| 连续扫码无命中关闭 | fail `9021003` 后 complete |
|
||||
| 相机权限拒绝 | fail `9021002` 后 complete |
|
||||
| 无手电筒设备 | 不崩溃,按钮隐藏或禁用 |
|
||||
|
||||
### 9.2 机型与系统验收
|
||||
|
||||
至少覆盖:
|
||||
|
||||
- iPhone 小屏机型;
|
||||
- 刘海屏机型;
|
||||
- 灵动岛机型;
|
||||
- 无手电筒或手电筒不可用场景;
|
||||
- iOS 14、16、17 及项目实际要求的最新系统;
|
||||
- 深色模式;
|
||||
- 权限首次授权、拒绝、设置页重新授权;
|
||||
- 前后台切换、来电或控制中心打断后恢复;
|
||||
- 快速打开、关闭、再次打开扫码页。
|
||||
|
||||
### 9.3 性能与稳定性验收
|
||||
|
||||
- 连续识别 5 分钟不出现明显内存持续增长;
|
||||
- 多次进入、退出扫码页后相机可再次正常打开;
|
||||
- 多码定格帧不会造成明显卡顿或黑屏;
|
||||
- 页面关闭后不再收到 `success`;
|
||||
- 不发生重复 `complete` 或 success/fail 同时触发;
|
||||
- 扫描框与实际识别区域、点选标记位置对齐。
|
||||
|
||||
---
|
||||
|
||||
## 10. 风险与应对
|
||||
|
||||
| 风险 | 影响 | 应对措施 |
|
||||
| --- | --- | --- |
|
||||
| UTS iOS 原生目录/Swift 桥接规则与预期不同 | 无法编译或调用 | 阶段 0 使用官方最小示例先验证,不直接大规模开发 |
|
||||
| Vision 支持的码制与 Android 不完全一致 | formats 行为差异 | 建立格式能力矩阵,过滤不支持项并在 README 明示 |
|
||||
| Vision 到预览层坐标转换错误 | scanRegion、点选位置偏移 | 单独编写坐标转换测试清单,覆盖 Aspect Fill、旋转和安全区 |
|
||||
| 定格帧转换开销高 | 多码时卡顿、内存上涨 | 限制图像分辨率,及时释放 `CGImage` / `UIImage`,仅保留一帧 |
|
||||
| 异步回调竞争 | 重复回调、页面关闭后回调 | 使用会话状态机和主线程串行化终止事件 |
|
||||
| Torch / zoom 在机型上差异大 | 功能不稳定 | 将 `auto` 与 smartZoom 放到后续阶段,先实现基础能力 |
|
||||
| iOS 权限文案或 Info.plist 缺失 | 审核或运行时崩溃 | 将权限配置纳入构建前检查和真机回归 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 代码与文档更新清单
|
||||
|
||||
### 11.1 开发完成后需要修改
|
||||
|
||||
| 文件 | 修改内容 |
|
||||
| --- | --- |
|
||||
| `utssdk/app-ios/*` | 新增 iOS UTS 桥接和 Swift 原生实现 |
|
||||
| `package.json` | `displayName`、描述、关键字、`platforms.client.uni-app.app.ios` 改为支持 |
|
||||
| `readme.md` | 支持平台改为 Android / iOS;补充 iOS 权限、最低版本和格式差异说明 |
|
||||
| `changelog.md` | 记录 iOS 首次支持及功能范围 |
|
||||
| `pages/scan/prototype.vue` | 将 Android 专属提示改为 Android / iOS App 提示 |
|
||||
|
||||
### 11.2 开发完成前不得修改
|
||||
|
||||
以下内容必须等 iOS 自定义基座成功编译且完成真机验收后再调整:
|
||||
|
||||
- README 的“支持 iOS”声明;
|
||||
- `package.json` 中的 `ios: '√'`;
|
||||
- 业务页面中“Android 专属”的运行提示;
|
||||
- changelog 中的正式 iOS 发布记录。
|
||||
|
||||
---
|
||||
|
||||
## 12. 推荐实施顺序
|
||||
|
||||
1. 确认项目的 HBuilderX 版本、Xcode 版本和最低 iOS 版本;
|
||||
2. 用官方 UTS iOS 最小原生页面验证 `app-ios` 目录与 Swift 编译方式;
|
||||
3. 实现相机权限 + 单码 QR 实时识别闭环;
|
||||
4. 固化 `success` / `fail` / `complete` 的 iOS 回调行为;
|
||||
5. 加入 formats、scanRegion 和 scanStyle;
|
||||
6. 加入相册识图和多码定格点选;
|
||||
7. 加入连续扫码、暂停、手电筒和手势缩放;
|
||||
8. 最后实现 smartZoom、auto torch 等设备敏感能力;
|
||||
9. 真机回归通过后更新插件元信息、README、changelog 和演示页文案。
|
||||
|
||||
---
|
||||
|
||||
## 13. 结论
|
||||
|
||||
推荐采用 **AVFoundation + Vision + PHPickerViewController** 的原生 iOS 实现方案,并在 `utssdk/app-ios/` 中增加独立平台入口。现有 `interface.uts`、`unierror.uts` 和 `openCodeScan` 调用方式继续作为跨端契约,Android 代码无需重构。
|
||||
|
||||
实施上应先完成 iOS UTS 编译与原生页面呈现验证,再逐步建立单码扫码、多码点选、相册识图和连续扫码能力。这样能将最大的构建与坐标转换风险前置,同时避免在未验证前修改对外平台支持声明。
|
||||
@@ -0,0 +1,617 @@
|
||||
# mas-code-scan 功能与设置规划
|
||||
|
||||
> 本文用于整理 `mas-code-scan` 当前已有能力、可继续增加的功能,以及建议的开发优先级。
|
||||
>
|
||||
> 状态说明:
|
||||
>
|
||||
> - **已实现**:当前插件代码已经支持;
|
||||
> - **建议增加**:功能方向已经明确,但尚未实现;
|
||||
> - **后续评估**:需要结合实际业务、设备兼容性和实现成本再决定。
|
||||
|
||||
---
|
||||
|
||||
## 1. 插件当前能力
|
||||
|
||||
| 功能 | 状态 | 说明 |
|
||||
|---|---|---|
|
||||
| Android 相机扫码 | 已实现 | 基于 CameraX + Google ML Kit |
|
||||
| 全格式识别 | 已实现 | 未传 `formats` 时按全格式识别 |
|
||||
| 指定码制识别 | 已实现 | 通过 `formats` 限定二维码、条形码等格式 |
|
||||
| 单次扫码 | 已实现 | 成功后自动返回结果 |
|
||||
| 连续扫码 | 已实现 | `continuous: true`,识别后继续停留在扫码页 |
|
||||
| 相册识图 | 已实现 | 支持打开后直接进入相册,也支持扫码页内打开相册 |
|
||||
| 多码识别 | 已实现 | 同屏多个码时进入点选逻辑 |
|
||||
| 智能变焦 | 已实现 | `smartZoom: true` 时对较小条码自动放大 |
|
||||
| 双指手势缩放 | 已实现 | 扫码页内手动调整相机倍率 |
|
||||
| 手电筒 | 已实现 | 扫码页右下角手动开关 |
|
||||
| 成功震动 | 已实现 | 通过 `vibrate` 控制 |
|
||||
| 自定义有效区域 | 已实现 | 通过 `scanRegion` 控制识别区域 |
|
||||
| 二维码正方形区域 | 已实现 | `width` 与 `height` 使用相同值,锁定 1:1 |
|
||||
| 横向条码区域 | 已实现 | 默认宽度较大、高度可调 |
|
||||
| 条码框上下位置 | 已实现 | 支持上、中、下、自定义位置 |
|
||||
| 条码框高度选择 | 已实现 | 原型页支持 24%、32%、40% 三档 |
|
||||
| 全屏识别模式 | 已实现 | `scanRegion: { fullScreen: true }`,整画面识别且不显示扫描框 |
|
||||
| 扫描框直角样式 | 已实现 | 当前不绘制圆角 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前 API 概览
|
||||
|
||||
### 2.1 基础调用
|
||||
|
||||
```ts
|
||||
import { openCodeScan } from '@/uni_modules/mas-code-scan'
|
||||
|
||||
openCodeScan({
|
||||
smartZoom: true,
|
||||
vibrate: true,
|
||||
success: (res) => {
|
||||
console.log(res.content, res.format)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err.errCode, err.errMsg)
|
||||
},
|
||||
complete: () => {
|
||||
console.log('扫码结束')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 2.2 全屏识别
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
scanRegion: {
|
||||
fullScreen: true
|
||||
},
|
||||
success: (res) => {
|
||||
console.log(res.content)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
全屏模式的行为:
|
||||
|
||||
- 整个相机预览区域均可识别;
|
||||
- 不显示扫描框;
|
||||
- 不显示扫描框遮罩;
|
||||
- 不过滤扫描框外的条码;
|
||||
- 不影响 `formats`、`smartZoom`、`vibrate`、`continuous` 等其他设置。
|
||||
|
||||
### 2.3 二维码正方形区域
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
formats: ['QR_CODE'],
|
||||
scanRegion: {
|
||||
width: 0.74,
|
||||
height: 0.74,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
二维码区域要求:
|
||||
|
||||
- `width` 和 `height` 使用相同值;
|
||||
- 严格保持 1:1 正方形比例;
|
||||
- 扫描框使用直角,不使用圆角。
|
||||
|
||||
### 2.4 横向条码区域
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
formats: ['CODE_128', 'EAN_13'],
|
||||
scanRegion: {
|
||||
width: 0.96,
|
||||
height: 0.24,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `width` 控制横向宽度;
|
||||
- `height` 控制扫描框内高度;
|
||||
- `centerY` 控制扫描框上下位置;
|
||||
- 目前原型页提供 24%、32%、40% 三档高度;
|
||||
- 框内条码才会进入有效识别结果。
|
||||
|
||||
---
|
||||
|
||||
## 3. 建议优先增加的功能
|
||||
|
||||
以下功能对仓库、设备管理、物料盘点等场景最有价值。
|
||||
|
||||
### 3.1 手电筒 API 控制
|
||||
|
||||
当前已经有扫码页手电筒按钮,建议继续增加调用参数,用于控制打开扫码页时的默认状态。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
torch?: 'auto' | 'on' | 'off'
|
||||
```
|
||||
|
||||
建议行为:
|
||||
|
||||
| 值 | 行为 |
|
||||
|---|---|
|
||||
| `auto` | 使用系统或插件默认状态,不强制打开 |
|
||||
| `on` | 打开扫码页后自动开启手电筒 |
|
||||
| `off` | 打开扫码页后保持关闭 |
|
||||
|
||||
适用场景:弱光仓库、货架底部、设备背面标签、夜间扫码。
|
||||
|
||||
---
|
||||
|
||||
### 3.2 连续扫码去重策略
|
||||
|
||||
连续扫码时建议开放更明确的去重配置。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
continuous: true,
|
||||
duplicatePolicy: 'cooldown',
|
||||
duplicateCooldown: 1500
|
||||
})
|
||||
```
|
||||
|
||||
建议支持:
|
||||
|
||||
| 策略 | 行为 |
|
||||
|---|---|
|
||||
| `allow` | 同一个码可以重复回调 |
|
||||
| `ignore` | 同一次扫码会话中,相同内容只回调一次 |
|
||||
| `cooldown` | 相同内容在指定时间内不重复回调 |
|
||||
|
||||
建议参数:
|
||||
|
||||
- `duplicatePolicy`:去重策略;
|
||||
- `duplicateCooldown`:去重时间,单位毫秒;
|
||||
- `maxResults`:连续扫码最多保存的结果数量。
|
||||
|
||||
这是批量盘点场景中最值得增加的功能之一。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 多码识别策略
|
||||
|
||||
当前同屏多个条码时可以进入点选。建议增加自动选择策略,减少用户额外操作。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
multiCodeMode: 'nearestCenter'
|
||||
})
|
||||
```
|
||||
|
||||
建议取值:
|
||||
|
||||
| 值 | 行为 |
|
||||
|---|---|
|
||||
| `select` | 保持当前交互,暂停画面并由用户点选 |
|
||||
| `first` | 取 ML Kit 返回的第一个有效条码 |
|
||||
| `nearestCenter` | 选择距离扫描框中心最近的条码 |
|
||||
| `largest` | 选择画面中面积最大的条码 |
|
||||
| `all` | 一次返回全部有效条码 |
|
||||
|
||||
建议默认使用 `nearestCenter`,更符合扫描框交互习惯。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 扫码结果确认模式
|
||||
|
||||
目前单次扫码识别成功后会自动返回。建议增加确认模式,避免误扫。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
resultMode: 'confirm'
|
||||
})
|
||||
```
|
||||
|
||||
建议取值:
|
||||
|
||||
| 值 | 行为 |
|
||||
|---|---|
|
||||
| `immediate` | 识别成功后立即返回,保持当前行为 |
|
||||
| `confirm` | 显示结果,用户确认后返回 |
|
||||
| `pause` | 识别成功后暂停相机,用户可确认或继续扫描 |
|
||||
|
||||
适用场景:设备绑定、物料入库、资产登记等不能容忍误扫的业务。
|
||||
|
||||
---
|
||||
|
||||
### 3.5 前后摄像头切换
|
||||
|
||||
建议支持默认摄像头和扫码页内切换。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
camera: 'back'
|
||||
})
|
||||
```
|
||||
|
||||
建议取值:
|
||||
|
||||
- `back`:后置摄像头,默认;
|
||||
- `front`:前置摄像头;
|
||||
- `auto`:由插件选择可用摄像头。
|
||||
|
||||
扫码页可以增加一个左下角或右上角切换按钮。
|
||||
|
||||
---
|
||||
|
||||
## 4. 识别准确率和性能设置
|
||||
|
||||
### 4.1 稳定识别帧数
|
||||
|
||||
要求同一个条码连续出现在若干帧后才判定成功。
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
stableFrames: 2
|
||||
})
|
||||
```
|
||||
|
||||
优点:
|
||||
|
||||
- 减少模糊画面误触发;
|
||||
- 减少条码刚进入画面时的误扫;
|
||||
- 对连续扫码更稳定。
|
||||
|
||||
注意:数值过高会增加识别延迟,建议默认 1 或 2。
|
||||
|
||||
---
|
||||
|
||||
### 4.2 智能变焦参数
|
||||
|
||||
当前只有开关,后续可以细化为:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
smartZoom: true,
|
||||
smartZoomMaxRatio: 2.5,
|
||||
smartZoomTriggerSize: 0.12
|
||||
})
|
||||
```
|
||||
|
||||
建议参数:
|
||||
|
||||
- `smartZoomMaxRatio`:最大自动变焦倍率;
|
||||
- `smartZoomTriggerSize`:条码小于指定比例时触发;
|
||||
- `smartZoomResetAfterSuccess`:识别成功后是否恢复倍率。
|
||||
|
||||
---
|
||||
|
||||
### 4.3 最小条码尺寸过滤
|
||||
|
||||
用于忽略过远或过小的无关条码。
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
minBarcodeWidth: 0.08,
|
||||
minBarcodeHeight: 0.03
|
||||
})
|
||||
```
|
||||
|
||||
注意:该功能和 `scanRegion` 不同:
|
||||
|
||||
- `scanRegion` 解决“条码是否在有效区域内”;
|
||||
- 最小尺寸过滤解决“条码是否足够大、是否值得识别”。
|
||||
|
||||
---
|
||||
|
||||
## 5. 扫描框和交互设置
|
||||
|
||||
### 5.1 扫描框视觉样式
|
||||
|
||||
目前扫描框已经支持尺寸、位置和直角样式。增加独立的视觉配置:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
scanRegion: {
|
||||
width: 0.96,
|
||||
height: 0.24,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
},
|
||||
scanStyle: {
|
||||
showMask: true,
|
||||
showBeam: true,
|
||||
borderColor: '#19be6b',
|
||||
maskColor: 'rgba(0, 0, 0, 0.45)',
|
||||
beamColor: '#12e880'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
建议支持:
|
||||
|
||||
- `showMask`:是否显示框外遮罩;
|
||||
- `showBeam`:是否显示扫描线;
|
||||
- `borderColor`:边框颜色;
|
||||
- `maskColor`:遮罩颜色和透明度;
|
||||
- `beamColor`:扫描线颜色;
|
||||
- `beamDuration`:扫描线移动周期;
|
||||
- `cornerStyle`:完整边框或四角边框。
|
||||
|
||||
建议将这些属性放在 `scanStyle` 中,不要和识别区域参数混在一起。
|
||||
|
||||
---
|
||||
|
||||
### 5.2 扫码页提示文案
|
||||
|
||||
不同业务可以使用不同的引导语。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
title: '设备绑定',
|
||||
hint: '请将设备条码放入扫描框内'
|
||||
})
|
||||
```
|
||||
|
||||
建议支持:
|
||||
|
||||
- `title`:页面标题;
|
||||
- `hint`:扫描框下方提示;
|
||||
- `successHint`:识别成功提示;
|
||||
- `emptyHint`:暂未识别到条码时的提示。
|
||||
|
||||
---
|
||||
|
||||
### 5.3 暂停与继续扫描
|
||||
|
||||
连续扫码可以增加暂停和继续按钮。
|
||||
|
||||
建议行为:
|
||||
|
||||
- 点击暂停后停止分析帧;
|
||||
- 相机预览可以继续显示,也可以冻结画面;
|
||||
- 点击继续后恢复分析;
|
||||
- 连续扫码结果列表不清空。
|
||||
- 实时帧中只有一条已解码条码时必须直接上报;ML Kit 额外返回的无内容候选框不能触发多码点选或跳过该单码。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
continuous: true,
|
||||
showPauseButton: true
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 扫码结果增强
|
||||
|
||||
### 6.1 返回条码位置
|
||||
|
||||
当前结果主要返回内容和码制。后续可以返回条码在图像中的位置。
|
||||
|
||||
建议结果:
|
||||
|
||||
```ts
|
||||
{
|
||||
content: 'EQ-12345678',
|
||||
format: 'CODE_128',
|
||||
fromAlbum: false,
|
||||
bounds: {
|
||||
left: 120,
|
||||
top: 360,
|
||||
width: 480,
|
||||
height: 96
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
用途:
|
||||
|
||||
- 调试扫描框映射;
|
||||
- 保存条码位置;
|
||||
- 多码场景二次判断;
|
||||
- 识别结果可视化。
|
||||
|
||||
---
|
||||
|
||||
### 6.2 结果校验
|
||||
|
||||
建议在插件外层或调用参数中增加结果校验能力。
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
formats: ['CODE_128'],
|
||||
validator: {
|
||||
pattern: '^EQ-[A-Z0-9]{8}$',
|
||||
message: '请扫描有效设备编码'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
建议支持:
|
||||
|
||||
- 正则表达式;
|
||||
- 固定前缀;
|
||||
- 固定长度;
|
||||
- 自定义校验回调;
|
||||
- 校验失败后继续扫描,不关闭扫码页。
|
||||
|
||||
建议优先在业务层实现,避免把业务规则过度耦合到通用插件。
|
||||
|
||||
---
|
||||
|
||||
### 6.3 扫码状态回调
|
||||
|
||||
用于业务页面展示状态或进行调试。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
onStatus: (status) => {
|
||||
console.log(status)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
建议状态:
|
||||
|
||||
- `opening`:正在打开扫码页;
|
||||
- `cameraReady`:相机已准备完成;
|
||||
- `scanning`:正在识别;
|
||||
- `paused`:已暂停;
|
||||
- `result`:识别到结果;
|
||||
- `closed`:扫码页关闭。
|
||||
|
||||
---
|
||||
|
||||
## 7. 相册识图增强
|
||||
|
||||
### 7.1 多图批量识别
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
fromAlbum: true,
|
||||
albumMultiple: true,
|
||||
maxAlbumImages: 9
|
||||
})
|
||||
```
|
||||
|
||||
适用场景:
|
||||
|
||||
- 批量导入物料标签照片;
|
||||
- 历史图片盘点;
|
||||
- 一次处理多个设备照片。
|
||||
|
||||
### 7.2 图片裁剪后识别
|
||||
|
||||
相册选择图片后增加裁剪步骤:
|
||||
|
||||
- 用户手动裁剪识别区域;
|
||||
- 使用当前扫描框比例作为裁剪建议;
|
||||
- 裁剪后再交给 ML Kit 识别。
|
||||
|
||||
复杂照片中存在多个标签时,该功能可以明显提高识别准确率。
|
||||
|
||||
---
|
||||
|
||||
## 8. 调试和错误处理
|
||||
|
||||
### 8.1 调试模式
|
||||
|
||||
建议增加:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
debug: true
|
||||
})
|
||||
```
|
||||
|
||||
调试信息可包括:
|
||||
|
||||
- 预览 View 尺寸;
|
||||
- 实际扫描框像素坐标;
|
||||
- 输入图像尺寸;
|
||||
- 图像旋转角度;
|
||||
- cropRect;
|
||||
- 区域过滤前后的条码数量;
|
||||
- 智能变焦触发记录;
|
||||
- 每帧识别耗时。
|
||||
|
||||
正式环境默认关闭,避免性能损耗和敏感信息泄露。
|
||||
|
||||
### 8.2 更细的错误码
|
||||
|
||||
**状态:已实现。** 现有 `9021001 ~ 9021004` 的含义保持不变,新增错误码从 `9021005` 开始:
|
||||
|
||||
| 错误码 | 场景 | 处理方式 |
|
||||
| --- | --- | --- |
|
||||
| `9021005` | 相机被其他应用占用或当前不可用 | 关闭其他相机应用后重试 |
|
||||
| `9021006` | 设备没有闪光灯 | 仅在扫码页提示,不中断扫码会话 |
|
||||
| `9021007` | 相机初始化失败 | 检查设备相机状态,必要时重启应用 |
|
||||
| `9021008` | 相册图片无法读取 | 重新选择本地图片 |
|
||||
| `9021009` | 图片中没有有效条码 | 仅打开即相册且无可用相机回退时结束会话 |
|
||||
| `9021010` | 指定码制不支持 | `formats` 非空且全部不受支持时返回 |
|
||||
| `9021011` | 扫描超时 | 单次扫码传入 `scanTimeout: 1000 ~ 600000` 后启用;连续扫码不启用 |
|
||||
|
||||
错误码增加遵循兼容原则:不修改已有错误码的含义;致命错误走 `fail` 后 `complete`;无闪光灯不会影响相机扫码,因此只提示,不触发 `fail`。
|
||||
|
||||
---
|
||||
|
||||
## 9. 横屏和平板支持
|
||||
|
||||
当前 Android 扫码页固定竖屏。后续可以评估:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
orientation: 'portrait'
|
||||
})
|
||||
```
|
||||
|
||||
建议取值:
|
||||
|
||||
- `portrait`:竖屏,当前默认行为;
|
||||
- `landscape`:横屏;
|
||||
- `auto`:跟随设备方向。
|
||||
|
||||
适用设备:
|
||||
|
||||
- 平板;
|
||||
- 工业手持终端;
|
||||
- 横向固定安装设备;
|
||||
- 长条码集中扫描场景。
|
||||
|
||||
---
|
||||
|
||||
## 10. 推荐开发优先级
|
||||
|
||||
### 第一阶段:直接提升日常可用性
|
||||
|
||||
1. 手电筒默认状态 API;
|
||||
2. 连续扫码去重策略;
|
||||
3. 多码自动选择策略;
|
||||
4. 扫码结果确认模式;
|
||||
5. 前后摄像头切换。
|
||||
|
||||
### 第二阶段:提升业务适配能力
|
||||
|
||||
1. 扫码页提示文案;
|
||||
2. 扫描状态回调;
|
||||
3. 稳定识别帧数;
|
||||
4. 扫描框视觉样式配置;
|
||||
5. 结果位置 `bounds` 返回。
|
||||
|
||||
### 第三阶段:扩展复杂业务场景
|
||||
|
||||
1. 相册多图批量识别;
|
||||
2. 图片裁剪后识别;
|
||||
3. 横屏和平板支持;
|
||||
4. 扫码结果校验器;
|
||||
5. 调试模式和性能指标。
|
||||
|
||||
---
|
||||
|
||||
## 11. 开发注意事项
|
||||
|
||||
1. 新增功能应优先保持 `openCodeScan(options)` 的兼容性,不要破坏现有调用。
|
||||
2. 识别区域参数和视觉样式参数应分开设计。
|
||||
3. 全屏模式必须保持“整画面识别、不绘制扫描框、不进行区域过滤”的语义。
|
||||
4. 二维码区域必须继续保持 1:1,不能因为屏幕比例或边界收敛发生变形。
|
||||
5. 连续扫码新增去重策略时,不能削弱当前已有的基本去重行为。
|
||||
6. 相册识图和实时相机识别应保持清晰区分,相册识图默认不受实时扫描框限制。
|
||||
7. 新增 Android 原生能力后,应同步更新 UTS 类型定义、插件 README 和演示页。
|
||||
8. 每次修改原生相机、图像坐标映射或区域过滤逻辑后,都需要使用 Android 真机或自定义调试基座验证。
|
||||
9. 新增错误码时只能追加,不能改变已有错误码的含义。
|
||||
10. 涉及权限、相机、闪光灯和设备方向的功能,需要至少验证主流 Android 设备和不同屏幕尺寸。
|
||||
@@ -0,0 +1,498 @@
|
||||
# mas-code-scan 自定义识别区域方案
|
||||
|
||||
## 1. 目标
|
||||
|
||||
在 Android 扫码页面中提供可配置的实时识别区域(ROI,Region of Interest):
|
||||
|
||||
- 摄像头打开后显示一个矩形扫描框;
|
||||
- 只有条码位于矩形区域内时才允许识别成功;
|
||||
- 扫描框外的条码不触发单次扫码成功;
|
||||
- 支持自定义扫描框宽度、高度、水平位置和垂直位置;
|
||||
- 支持不同屏幕尺寸和横竖屏适配;
|
||||
- 不破坏现有的单次扫码、连续扫码、相册识图、多码点选、智能变焦和码制过滤能力。
|
||||
|
||||
## 2. 当前实现核对
|
||||
|
||||
### 2.1 现有接口
|
||||
|
||||
文件:`uni_modules/mas-code-scan/utssdk/interface.uts`
|
||||
|
||||
当前 `CodeScanOptions` 支持:
|
||||
|
||||
```ts
|
||||
{
|
||||
smartZoom?: boolean
|
||||
formats?: string[]
|
||||
vibrate?: boolean
|
||||
continuous?: boolean
|
||||
fromAlbum?: boolean
|
||||
success?: (res) => void
|
||||
fail?: (res) => void
|
||||
complete?: (res) => void
|
||||
}
|
||||
```
|
||||
|
||||
当前没有识别区域配置字段。
|
||||
|
||||
### 2.2 预览层
|
||||
|
||||
文件:`uni_modules/mas-code-scan/utssdk/app-android/CodeCaptureActivity.kt`
|
||||
|
||||
- 使用 `PreviewView.ScaleType.FILL_CENTER` 显示相机预览;
|
||||
- `ScanFrameDecorView` 覆盖在预览层上方绘制扫描动画;
|
||||
- 预览层和装饰层都是全屏 `FrameLayout`;
|
||||
- 触摸缩放绑定在 `PreviewView` 上。
|
||||
|
||||
文件:`uni_modules/mas-code-scan/utssdk/app-android/ScanFrameDecorView.kt`
|
||||
|
||||
当前扫描框是固定的正方形:
|
||||
|
||||
```kotlin
|
||||
val frameWidth = ...
|
||||
val frameHeight = frameWidth
|
||||
```
|
||||
|
||||
它只负责画扫描线,不参与实际识别。
|
||||
|
||||
### 2.3 识别层
|
||||
|
||||
文件:`uni_modules/mas-code-scan/utssdk/app-android/CodeCaptureActivity.kt`
|
||||
|
||||
当前把完整的 `ImageProxy` 图像交给 ML Kit:
|
||||
|
||||
```kotlin
|
||||
val inputImage = InputImage.fromMediaImage(mediaImage, rotationDegrees)
|
||||
scanner.process(inputImage)
|
||||
```
|
||||
|
||||
因此当前扫描框只是视觉提示,框外条码仍可能被识别。
|
||||
|
||||
## 3. 推荐接口设计
|
||||
|
||||
建议新增 `scanRegion`,使用相对比例配置,不使用固定像素作为公共接口。
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
scanRegion: {
|
||||
width: 0.92,
|
||||
height: 0.18,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
字段定义:
|
||||
|
||||
| 字段 | 类型 | 默认值 | 范围 | 含义 |
|
||||
|---|---|---:|---|---|
|
||||
| `fullScreen` | `boolean` | `false` | - | 为 `true` 时整画面识别,不绘制扫描框且不进行区域过滤 |
|
||||
| `width` | `number` | `0.74` | `0.1 ~ 1` | 扫描框宽度,占预览宽度的比例 |
|
||||
| `height` | `number` | `0.74` | `0.1 ~ 1` | 扫描框高度,占预览宽度的比例 |
|
||||
| `centerX` | `number` | `0.5` | `0 ~ 1` | 扫描框中心的水平位置比例 |
|
||||
| `centerY` | `number` | `0.5` | `0 ~ 1` | 扫描框中心的垂直位置比例 |
|
||||
|
||||
全屏识别示例:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
scanRegion: { fullScreen: true }
|
||||
})
|
||||
```
|
||||
|
||||
二维码区域约束:
|
||||
|
||||
- `width` 与 `height` 使用相同值,严格锁定 `1:1` 正方形比例;
|
||||
- 默认值为 `width: 0.74`、`height: 0.74`;
|
||||
- 原生扫描框使用直角,不绘制圆角。
|
||||
|
||||
推荐默认值:
|
||||
|
||||
```ts
|
||||
{
|
||||
width: 0.74,
|
||||
height: 0.74,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
}
|
||||
```
|
||||
|
||||
业务页面可针对一维码使用横向区域,并通过 `centerY` 调整上下位置、通过 `height` 调整框高:
|
||||
|
||||
```ts
|
||||
{
|
||||
width: 0.96,
|
||||
height: 0.24,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
}
|
||||
```
|
||||
|
||||
原型页提供三档横向条码框高度:
|
||||
|
||||
- 中:`0.24`(24%);
|
||||
- 大:`0.32`(32%);
|
||||
- 特大:`0.40`(40%)。
|
||||
|
||||
### 3.1 为什么使用比例
|
||||
|
||||
- 不依赖具体手机分辨率和 density;
|
||||
- 适配不同 Android 设备;
|
||||
- 与当前全屏预览布局一致;
|
||||
- 后续可在页面中通过滑块或预设配置调整;
|
||||
- 避免将前端 `rpx` 直接与原生像素混用。
|
||||
|
||||
## 4. 实现分层
|
||||
|
||||
### 4.1 UTS 接口层
|
||||
|
||||
修改:`utssdk/interface.uts`
|
||||
|
||||
新增:
|
||||
|
||||
```ts
|
||||
export type ScanRegion = {
|
||||
width?: number
|
||||
height?: number
|
||||
centerX?: number
|
||||
centerY?: number
|
||||
}
|
||||
```
|
||||
|
||||
并在 `CodeScanOptions` 中增加:
|
||||
|
||||
```ts
|
||||
scanRegion?: ScanRegion
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 字段全部可选;
|
||||
- 不传时保持现有默认扫描框;
|
||||
- 对非法值进行原生端裁剪和兜底;
|
||||
- 不把 UI 颜色、文案等表现层参数与识别区域参数混在一起。
|
||||
|
||||
### 4.2 UTS 到 Android Intent
|
||||
|
||||
修改:`utssdk/app-android/index.uts`
|
||||
|
||||
新增一个 JSON 字符串 Extra,例如:
|
||||
|
||||
```text
|
||||
mas_scan_region
|
||||
```
|
||||
|
||||
将 `scanRegion` 序列化后传递给 `CodeCaptureActivity`。
|
||||
|
||||
推荐传递 JSON,而不是增加多个 Intent Extra,原因是:
|
||||
|
||||
- 字段集中;
|
||||
- 后续增加圆角、边框颜色等非识别参数时更容易扩展;
|
||||
- 保持接口和 Activity 参数数量可控。
|
||||
|
||||
### 4.3 Activity 参数解析
|
||||
|
||||
修改:`CodeCaptureActivity.kt`
|
||||
|
||||
新增:
|
||||
|
||||
```kotlin
|
||||
private data class ScanRegionConfig(
|
||||
val width: Float,
|
||||
val height: Float,
|
||||
val centerX: Float,
|
||||
val centerY: Float
|
||||
)
|
||||
```
|
||||
|
||||
初始化时:
|
||||
|
||||
1. 读取 Intent Extra;
|
||||
2. 解析 JSON;
|
||||
3. 将数值限制在安全范围;
|
||||
4. 对过小区域、越界中心点进行兜底;
|
||||
5. 将同一份配置传给 `ScanFrameDecorView` 和识别过滤逻辑。
|
||||
|
||||
最终必须保证:
|
||||
|
||||
```text
|
||||
视觉扫描框区域 == 实际有效识别区域
|
||||
```
|
||||
|
||||
## 5. 坐标体系设计
|
||||
|
||||
这是本需求的核心难点。项目中至少存在三套坐标:
|
||||
|
||||
1. **屏幕/装饰层坐标**:`ScanFrameDecorView` 的 view 坐标;
|
||||
2. **预览可视区域坐标**:`PreviewView` 中用户看到的相机画面;
|
||||
3. **ImageProxy 图像坐标**:ML Kit `Barcode.boundingBox` 使用的图像坐标。
|
||||
|
||||
由于预览使用 `FILL_CENTER`,图像可能在屏幕两侧或上下被裁剪,不能简单按照屏幕宽高直接换算。
|
||||
|
||||
### 5.1 统一区域定义
|
||||
|
||||
推荐先在预览层坐标中计算扫描框:
|
||||
|
||||
```text
|
||||
frameLeft = viewWidth * (centerX - width / 2)
|
||||
frameTop = viewHeight * (centerY - height / 2)
|
||||
frameRight = viewWidth * (centerX + width / 2)
|
||||
frameBottom = viewHeight * (centerY + height / 2)
|
||||
```
|
||||
|
||||
并限制在预览层范围内。
|
||||
|
||||
### 5.2 预览坐标映射到图像坐标
|
||||
|
||||
当前 `PreviewView` 使用 `FILL_CENTER`,映射应考虑:
|
||||
|
||||
```text
|
||||
scale = max(viewWidth / rotatedImageWidth, viewHeight / rotatedImageHeight)
|
||||
```
|
||||
|
||||
相机图像在屏幕上的实际显示尺寸为:
|
||||
|
||||
```text
|
||||
displayWidth = rotatedImageWidth * scale
|
||||
displayHeight = rotatedImageHeight * scale
|
||||
offsetX = (viewWidth - displayWidth) / 2
|
||||
offsetY = (viewHeight - displayHeight) / 2
|
||||
```
|
||||
|
||||
扫描框转回旋转后的图像坐标:
|
||||
|
||||
```text
|
||||
imageLeft = (frameLeft - offsetX) / scale
|
||||
imageTop = (frameTop - offsetY) / scale
|
||||
imageRight = (frameRight - offsetX) / scale
|
||||
imageBottom = (frameBottom - offsetY) / scale
|
||||
```
|
||||
|
||||
然后再根据 `rotationDegrees` 转换到 `ImageProxy` 原始坐标。
|
||||
|
||||
注意:实际代码需要以 `PreviewView` 的 `viewPort`、绑定的 UseCase 分辨率和设备方向为准验证,不能只凭静态公式上线。
|
||||
|
||||
## 6. 识别限制策略
|
||||
|
||||
### 6.1 第一阶段:结果过滤,推荐先实现
|
||||
|
||||
ML Kit 仍分析整帧,但只接受扫描区域内的条码:
|
||||
|
||||
```kotlin
|
||||
val validBarcodes = barcodes.filter { barcode ->
|
||||
val box = barcode.boundingBox ?: return@filter false
|
||||
isBarcodeInsideScanRegion(box, imageProxy, rotationDegrees)
|
||||
}
|
||||
```
|
||||
|
||||
区域判断建议使用条码边界框中心点,默认规则:
|
||||
|
||||
```text
|
||||
条码中心点位于扫描框内 => 允许识别
|
||||
条码中心点位于扫描框外 => 忽略
|
||||
```
|
||||
|
||||
对于严格业务场景,可以改为:
|
||||
|
||||
```text
|
||||
条码边界框完全位于扫描框内 => 允许识别
|
||||
```
|
||||
|
||||
本项目建议默认使用“中心点在区域内”,原因是:
|
||||
|
||||
- 用户将条码移入框内时体验更自然;
|
||||
- 条码边缘轻微超出时不会频繁丢失;
|
||||
- 对一维横向条码更友好。
|
||||
|
||||
### 6.2 多码识别
|
||||
|
||||
过滤应发生在多码判断之前:
|
||||
|
||||
```text
|
||||
原始 barcodes
|
||||
-> 过滤无 boundingBox 的结果
|
||||
-> 过滤框外结果
|
||||
-> 再执行单码 / 多码判断
|
||||
```
|
||||
|
||||
这样框外多码不会触发点选页面,也不会影响智能变焦。
|
||||
|
||||
### 6.3 相册识图
|
||||
|
||||
实时摄像头的 `scanRegion` 不应默认限制相册识图:
|
||||
|
||||
- 相册图片没有实时预览框坐标;
|
||||
- 用户选择的是整张图片;
|
||||
- 继续保持相册识别整图的现有行为。
|
||||
|
||||
如果未来确实需要对相册图片裁剪,应单独设计 `albumRegion`,不要复用实时预览坐标。
|
||||
|
||||
### 6.4 识别前裁剪
|
||||
|
||||
第二阶段可选做真正的 ROI 图像裁剪,只将区域图像提交给 ML Kit。
|
||||
|
||||
暂不作为第一阶段实现,原因:
|
||||
|
||||
- 需要处理 ImageProxy YUV 裁剪;
|
||||
- 需要处理旋转和 cropRect;
|
||||
- 需要重新映射多码点选坐标;
|
||||
- 可能影响智能变焦依据和现有连续扫码稳定性;
|
||||
- 设备兼容性验证成本更高。
|
||||
|
||||
## 7. 扫描框视觉设计
|
||||
|
||||
修改:`ScanFrameDecorView.kt`
|
||||
|
||||
扫描框应从固定正方形改为配置矩形:
|
||||
|
||||
- 宽度由 `width` 决定;
|
||||
- 高度由 `height` 决定;
|
||||
- 位置由 `centerX`、`centerY` 决定;
|
||||
- 扫描线只在矩形内部移动;
|
||||
- 扫描线左右留出固定 inset;
|
||||
- 其他区域可增加半透明遮罩,增强用户对有效范围的理解。
|
||||
|
||||
建议提供清晰的 UI 语义:
|
||||
|
||||
```text
|
||||
框内:有效识别区域
|
||||
框外:不识别
|
||||
```
|
||||
|
||||
当前截图的横向条码场景推荐:
|
||||
|
||||
```ts
|
||||
scanRegion: {
|
||||
width: 0.96,
|
||||
height: 0.16,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 原型页面示例规划
|
||||
|
||||
文件:`pages/scan/prototype.vue`
|
||||
|
||||
原型页增加“识别区域”配置区,但不直接实现摄像头 UI。页面负责展示和传递配置:
|
||||
|
||||
### 8.1 预设
|
||||
|
||||
提供三个预设:
|
||||
|
||||
- 默认正方形:适合二维码;
|
||||
- 横向条码:适合设备码、物料码;
|
||||
- 自定义:通过宽度、高度、水平位置、垂直位置调整。
|
||||
|
||||
### 8.2 示例数据
|
||||
|
||||
```js
|
||||
scanRegion: {
|
||||
width: 0.96,
|
||||
height: 0.16,
|
||||
centerX: 0.5,
|
||||
centerY: 0.5
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 原型页交互
|
||||
|
||||
- 使用滑块或步进按钮调整四个比例参数;
|
||||
- 页面实时显示当前区域参数;
|
||||
- 单次扫码、连续扫码都使用当前 `scanRegion`;
|
||||
- 相册识图按钮不传实时扫描区域,继续整图识别;
|
||||
- 在 Web 预览中展示“仅 Android App-Plus 支持原生扫码”的提示。
|
||||
|
||||
## 9. 兼容性策略
|
||||
|
||||
### 9.1 不传配置
|
||||
|
||||
现有调用全部保持兼容:
|
||||
|
||||
```ts
|
||||
openCodeScan({
|
||||
smartZoom: true,
|
||||
vibrate: true
|
||||
})
|
||||
```
|
||||
|
||||
行为继续使用默认正方形区域。
|
||||
|
||||
### 9.2 旧构建包
|
||||
|
||||
如果前端代码传入 `scanRegion`,但运行的是旧插件包,旧原生代码会忽略未知 Extra,仍可正常扫码,但不会限制区域。因此必须重新制作自定义基座或正式 APK。
|
||||
|
||||
### 9.3 非 Android 平台
|
||||
|
||||
继续由 `#ifdef APP-PLUS` 控制插件导入和调用,Web、iOS、小程序不加载 Android UTS 实现。
|
||||
|
||||
### 9.4 参数安全
|
||||
|
||||
原生端必须再次校验参数,不能只依赖前端:
|
||||
|
||||
- `width`、`height` 限制在 `0.1 ~ 1`;
|
||||
- `centerX`、`centerY` 限制在 `0 ~ 1`;
|
||||
- 区域超出屏幕时进行边界收敛;
|
||||
- 宽高过小导致不可用时回退默认值。
|
||||
|
||||
## 10. 实施顺序
|
||||
|
||||
### 阶段一:方案验证
|
||||
|
||||
1. 增加 `scanRegion` 接口定义;
|
||||
2. 将参数传入 Android Activity;
|
||||
3. 扫描框支持矩形和位置配置;
|
||||
4. 根据条码中心点过滤框外结果;
|
||||
5. 原型页提供横向条码预设;
|
||||
6. 保持相册整图识别。
|
||||
|
||||
### 阶段二:真机验证
|
||||
|
||||
至少验证:
|
||||
|
||||
- 二维码默认正方形识别;
|
||||
- 横向一维码识别;
|
||||
- 框内识别成功;
|
||||
- 框外完全不识别;
|
||||
- 条码横跨边界时的行为;
|
||||
- 多个条码一内一外时只处理框内条码;
|
||||
- 连续扫码只回传框内结果;
|
||||
- 智能变焦不会被框外条码触发;
|
||||
- 横竖屏和不同分辨率设备;
|
||||
- 相册识图不受实时区域影响;
|
||||
- 关闭、取消、权限拒绝和页面销毁流程。
|
||||
|
||||
### 阶段三:可选优化
|
||||
|
||||
根据真机效果再决定是否:
|
||||
|
||||
- 改为识别前裁剪 ROI;
|
||||
- 增加边框颜色、圆角、遮罩透明度配置;
|
||||
- 增加“中心点在框内 / 完全包含”两种命中规则;
|
||||
- 增加一维码专用默认模板。
|
||||
|
||||
## 11. 验收标准
|
||||
|
||||
功能验收:
|
||||
|
||||
- 扫描框宽高和位置可以通过 `openCodeScan` 参数调整;
|
||||
- 用户看到的扫描框与实际识别区域一致;
|
||||
- 框外条码不会触发成功回调;
|
||||
- 框内条码可以正常触发成功回调;
|
||||
- 多码点选只展示有效区域内的条码;
|
||||
- 旧调用不传 `scanRegion` 时行为不变;
|
||||
- 单次、连续、相册三种模式互不回归。
|
||||
|
||||
工程验收:
|
||||
|
||||
- UTS 接口类型、Intent Extra、Activity 解析三层字段一致;
|
||||
- 不在页面中直接修改原生插件内部逻辑;
|
||||
- 所有新增配置有默认值和越界保护;
|
||||
- 真机重新构建自定义基座后验证;
|
||||
- 不以 Web 预览结果代替 Android 真机验证。
|
||||
|
||||
## 12. 结论
|
||||
|
||||
本项目适合采用“**比例配置 + 原生扫描框绘制 + 条码结果区域过滤**”作为第一版实现。
|
||||
|
||||
该方案能满足截图所示的横向矩形识别场景,改动范围可控,且能保留当前插件已有能力。真正的图像裁剪 ROI 作为后续优化,不在第一版中引入,以降低旋转、坐标映射和多码点选的回归风险。
|
||||
Reference in New Issue
Block a user