752 lines
30 KiB
Markdown
752 lines
30 KiB
Markdown
# mas-code-scan
|
||||
|
|
|
|||
|
|
Android 端扫码 **UTS API 插件**(非 Vue 组件)。
|
|||
|
|
基于 CameraX + Google ML Kit,支持全格式条码/二维码、多码同屏点选、识别区域过滤、扫描框视觉配置、智能变焦、手势缩放、相册识图、连续扫码、手电筒策略、结果确认和暂停控制。
|
|||
|
|
|
|||
|
|
插件对外只提供 `openCodeScan` 一个 UTS API。扫码页由原生 Android Activity 提供,识别结果通过 `success`、`fail`、`complete` 回调返回。
|
|||
|
|
|
|||
|
|
支持:**Android App(uni-app Vue2/Vue3)**。
|
|||
|
|
暂不支持:iOS / 鸿蒙 / H5 / 小程序 / nvue。
|
|||
|
|
|
|||
|
|
> 依赖原生三方库,**必须使用自定义调试基座或正式云打包**后才能真机调用。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 插件定位与工作流程
|
|||
|
|
|
|||
|
|
`mas-code-scan` 是一个打开原生 Android 扫码 Activity 的 UTS API 插件,不是 Vue 组件,也不是对 `uni.scanCode` 的简单包装。一次扫码流程如下:
|
|||
|
|
|
|||
|
|
1. 业务页面调用 `openCodeScan(options)`。
|
|||
|
|
2. 插件启动全屏竖屏原生扫码页,并根据参数初始化 CameraX、ML Kit 和界面控件。
|
|||
|
|
3. 相机帧交给 ML Kit 识别;如果配置了 `scanRegion`,实时结果还会经过扫描框中心点过滤。
|
|||
|
|
4. 单码按照 `resultMode` 返回;多码进入定格点选;连续模式通过 `success` 多次上报。
|
|||
|
|
5. Activity 结束后通过 `complete` 收尾;失败场景同时通过 `fail` 和 `complete` 通知。
|
|||
|
|
|
|||
|
|
### 能力边界
|
|||
|
|
|
|||
|
|
- 识别引擎:Google ML Kit Barcode Scanning。
|
|||
|
|
- 相机管线:CameraX,使用后置摄像头,预览采用 `FILL_CENTER` 填充显示。
|
|||
|
|
- 图片来源:实时相机帧、系统相册图片。
|
|||
|
|
- 原生页面:全屏、竖屏、无标题栏;关闭、相册、手电筒和连续扫码暂停按钮由插件绘制。
|
|||
|
|
- 生命周期:每次调用创建一个扫码会话;关闭、成功、权限失败、超时或初始化失败后会话结束。
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
|
|||
|
|
1. [功能一览](#功能一览)
|
|||
|
|
2. [安装](#安装)
|
|||
|
|
3. [制作自定义基座(必做)](#制作自定义基座必做)
|
|||
|
|
4. [快速上手](#快速上手)
|
|||
|
|
5. [进阶用法](#进阶用法)
|
|||
|
|
6. [API](#api)
|
|||
|
|
7. [错误码](#错误码)
|
|||
|
|
8. [扫码页交互说明](#扫码页交互说明)
|
|||
|
|
9. [码制 formats 取值](#码制-formats-取值)
|
|||
|
|
10. [常见问题](#常见问题)
|
|||
|
|
11. [注意事项](#注意事项)
|
|||
|
|
12. [目录结构](#目录结构)
|
|||
|
|
13. [版本](#版本)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 功能一览
|
|||
|
|
|
|||
|
|
| 能力 | 说明 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 全格式识别 | QR / EAN / CODE_128 / PDF417 / AZTEC 等 |
|
|||
|
|
| 码制过滤 | `formats` 只扫指定类型 |
|
|||
|
|
| 扫描区域 | `scanRegion` 配置扫描框尺寸、位置,并过滤框外条码 |
|
|||
|
|
| 扫描框样式 | `scanStyle` 配置遮罩、扫描线、边框颜色、动画周期、边框形态和圆角大小 |
|
|||
|
|
| 单码自动返回 | 识别成功后关闭并回调 |
|
|||
|
|
| 多码点选 | 同屏多个码定格,点选其中一个 |
|
|||
|
|
| 智能变焦 | `smartZoom`:码偏小时自动放大 |
|
|||
|
|
| 手势缩放 | 双指捏合 |
|
|||
|
|
| 手电筒 | 扫码页右下角开关,支持关闭、启动时开启和低照度自动补光 |
|
|||
|
|
| 相册识图 | 页内相册按钮,或 `fromAlbum: true` |
|
|||
|
|
| 连续扫码 | `continuous: true`,多次 `success`,点关闭结束;可显示暂停/继续按钮 |
|
|||
|
|
| 结果处理 | 单次扫码支持立即返回、确认后返回、暂停后选择 |
|
|||
|
|
| 成功震动 | `vibrate`,可关 |
|
|||
|
|
| 错误码区分 | 权限拒绝 `9021002` ≠ 用户取消 `9021003` |
|
|||
|
|
| 原生回收 | 页面结束时停止分析、释放相机、关闭 ML Kit Scanner、关闭手电筒并清理临时图片 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 安装
|
|||
|
|
|
|||
|
|
将插件拷贝到业务项目:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
你的项目/uni_modules/mas-code-scan/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
UTS 插件需 **import 后调用**,不会 easycom 自动注册组件。
|
|||
|
|
|
|||
|
|
要求:
|
|||
|
|
|
|||
|
|
- HBuilderX ≥ 3.6.8(建议较新版本)
|
|||
|
|
- uni-app Vue2 / Vue3 均可
|
|||
|
|
- **仅 Android**;需制作自定义基座
|
|||
|
|
|
|||
|
|
本仓库演示页:`frontend/scanapp/pages/scan/prototype.vue`,用于在 Android 自定义基座中验证插件能力。页面包含:
|
|||
|
|
|
|||
|
|
- 智能变焦、成功震动、手电筒策略、结果处理模式和连续扫码暂停按钮。
|
|||
|
|
- 扫码页标题、扫描引导语、成功提示语、无结果提示语自定义。
|
|||
|
|
- 连续扫码成功提示停留时长、单次扫码超时配置。
|
|||
|
|
- 二维码正方形框、横向条码框和全屏识别三种识别区域预设;横向条码框支持上/中/下位置、自定义上下微调和三档高度。
|
|||
|
|
- 扫描框遮罩、扫描线、边框颜色、遮罩颜色、扫描线颜色、扫描线周期、四种边框形态和圆角半径配置。
|
|||
|
|
- 二维码、CODE 128、EAN-13 码制筛选,相册识图、单次扫码、连续扫码和本地结果列表。
|
|||
|
|
|
|||
|
|
演示页的“最近结果”仅保存在当前页面内,用于展示回调结果,不会提交业务接口。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 制作自定义基座(必做)
|
|||
|
|
|
|||
|
|
插件依赖:
|
|||
|
|
|
|||
|
|
- `androidx.camera:*` 1.4.1
|
|||
|
|
- `com.google.mlkit:barcode-scanning` 17.3.0
|
|||
|
|
|
|||
|
|
标准基座不含上述库,**不制作自定义基座会调用失败 / 编译不过**。
|
|||
|
|
|
|||
|
|
### 步骤
|
|||
|
|
|
|||
|
|
1. HBuilderX 菜单:**运行** → **运行到手机或模拟器** → **制作自定义调试基座**
|
|||
|
|
2. 选择当前项目,填写 Android 包名、证书(可用公共测试证书)
|
|||
|
|
3. 云打包,等待完成并安装到手机
|
|||
|
|
4. 之后每次运行选择 **「自定义调试基座」**,再打开演示页或业务页调试扫码
|
|||
|
|
|
|||
|
|
正式发版:使用 **云打包 / 本地打包** 生成安装包即可(打包流程会带上 UTS 原生依赖)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 快速上手
|
|||
|
|
|
|||
|
|
### 1. uni-app(Vue3 `<script setup>`)
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<template>
|
|||
|
|
<view>
|
|||
|
|
<button type="primary" @click="doScan">扫码</button>
|
|||
|
|
<text>{{ tip }}</text>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { ref } from 'vue'
|
|||
|
|
import { openCodeScan } from '@/uni_modules/mas-code-scan'
|
|||
|
|
|
|||
|
|
const tip = ref('')
|
|||
|
|
|
|||
|
|
const doScan = () => {
|
|||
|
|
openCodeScan({
|
|||
|
|
success: (res) => {
|
|||
|
|
tip.value = res.content + ' / ' + res.format
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
tip.value = err.errMsg + '(' + err.errCode + ')'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. uni-app(Options API)
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
import { openCodeScan } from '@/uni_modules/mas-code-scan'
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
methods: {
|
|||
|
|
doScan() {
|
|||
|
|
openCodeScan({
|
|||
|
|
success: (res) => {
|
|||
|
|
console.log(res.content, res.format, res.fromAlbum)
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.log(err.errCode, err.errMsg)
|
|||
|
|
},
|
|||
|
|
complete: () => {
|
|||
|
|
console.log('扫码流程结束')
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 最少参数
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
import { openCodeScan } from '@/uni_modules/mas-code-scan'
|
|||
|
|
|
|||
|
|
openCodeScan({
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 进阶用法
|
|||
|
|
|
|||
|
|
### 自定义扫码页文案
|
|||
|
|
|
|||
|
|
以下参数只影响原生扫码页显示文字,不影响识别逻辑:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
title: '扫描商品码',
|
|||
|
|
hint: '请将商品条码放入框内',
|
|||
|
|
successHint: '识别成功',
|
|||
|
|
emptyHint: '图片中没有商品条码',
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `title`:顶部标题,默认 `扫一扫`。
|
|||
|
|
- `hint`:扫描框下方引导语,默认 `请将条码放入扫描框内`。
|
|||
|
|
- `successHint`:连续扫码成功提示层标题,也用于部分原生提示,默认 `扫码成功`。
|
|||
|
|
- `emptyHint`:相册图片未识别到条码时的提示,默认 `未识别到有效条码`。
|
|||
|
|
- 空字符串或全空格会回退到默认文案;原生层会去除首尾空格。
|
|||
|
|
|
|||
|
|
### 只扫二维码
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
formats: ['QR_CODE'],
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 手电筒和结果处理
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
torch: 'auto', // 'off' | 'on' | 'auto'
|
|||
|
|
resultMode: 'confirm', // 'immediate' | 'confirm' | 'pause'
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`confirm` 会弹出结果确认框;`pause` 会暂停识别并提供“继续扫描/确认”操作。两种模式下,点击确认才会结束并触发 `success`、`complete`;点击重新扫描或继续扫描会恢复相机识别,不会触发失败回调。
|
|||
|
|
|
|||
|
|
### 设置单次扫码超时
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
scanTimeout: 15000,
|
|||
|
|
success: (res) => console.log(res.content),
|
|||
|
|
fail: (err) => {
|
|||
|
|
if (err.errCode === 9021011) console.log('15 秒内未识别到条码')
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`scanTimeout` 单位为毫秒。只有单次扫码且值在 `1000 ~ 600000` 时启用;不传、传 `0` 或小于 `1000` 时不启用。连续扫码始终不启用单次超时。
|
|||
|
|
|
|||
|
|
### 关闭智能变焦 / 震动
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
smartZoom: false,
|
|||
|
|
vibrate: false,
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 配置扫描区域
|
|||
|
|
|
|||
|
|
`scanRegion` 同时决定扫描框的位置和实时识别过滤区域。宽度、高度、中心点均使用 `0 ~ 1` 的比例值;宽高以预览宽度为参考,越界值会由原生层收敛到可显示范围。`scanRegion` 只作用于实时相机,不裁剪或过滤相册图片。
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
scanRegion: {
|
|||
|
|
width: 0.96,
|
|||
|
|
height: 0.24,
|
|||
|
|
centerX: 0.5,
|
|||
|
|
centerY: 0.5
|
|||
|
|
},
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 不传 `scanRegion`:保持全屏识别,同时保留默认扫描框显示。
|
|||
|
|
- `scanRegion: { fullScreen: true }`:整画面识别,不显示扫描框,也不进行区域过滤。
|
|||
|
|
- 配置扫描区域后,条码中心点必须落在扫描框内才会被实时相机识别结果接受。
|
|||
|
|
- 相册识图不受 `scanRegion` 限制。
|
|||
|
|
|
|||
|
|
### 配置扫描框视觉样式
|
|||
|
|
|
|||
|
|
`scanStyle` 只控制扫描页视觉表现,不改变识别区域。颜色支持 `#RRGGBB`、`#AARRGGBB`;遮罩还支持 `rgba(r, g, b, a)`。
|
|||
|
|
|
|||
|
|
```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',
|
|||
|
|
beamDuration: 1800,
|
|||
|
|
cornerStyle: 'rounded',
|
|||
|
|
cornerRadius: 20
|
|||
|
|
},
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `showMask: false` 仅隐藏框外遮罩,不会扩大或改变识别范围。
|
|||
|
|
- `showBeam: false` 隐藏扫描线;扫描线周期 `beamDuration` 单位为毫秒,原生层限制在 `100 ~ 60000`。
|
|||
|
|
- `cornerStyle: 'corner'` 显示直角四角边框,`'full'` 显示完整矩形边框,`'rounded'` 显示完整圆角边框,`'rounded-corner'` 显示四角圆角边框。
|
|||
|
|
- `cornerRadius` 为圆角半径,单位为 dp,范围 `0 ~ 160`,在两种圆角边框样式下均生效;圆角遮罩缺口和扫描线会同步裁剪为相同圆角。
|
|||
|
|
- `rounded` 是四边完整的圆角边框;`rounded-corner` 只绘制四个带圆弧过渡的角,中间不连接四条边。
|
|||
|
|
- 圆角半径最终还会限制在扫描框短边的一半以内,适合二维码框和横向条码框。
|
|||
|
|
- 当未启用 `scanRegion` 时,视觉样式仅作用于默认扫描框;`fullScreen: true` 时不会显示扫描框。
|
|||
|
|
|
|||
|
|
### 打开后直接进相册
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
fromAlbum: true,
|
|||
|
|
success: (res) => {
|
|||
|
|
console.log(res.content, res.fromAlbum) // fromAlbum === true
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
扫码页内也可随时点左下角 **相册** 按钮选图识别。
|
|||
|
|
|
|||
|
|
### 连续扫码
|
|||
|
|
|
|||
|
|
页内不关闭,每识别一次触发一次 `success`;用户点左上角关闭(或系统返回)后走 `complete`(若已有命中,**不会**再走 `fail`)。
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
const list = []
|
|||
|
|
|
|||
|
|
openCodeScan({
|
|||
|
|
continuous: true,
|
|||
|
|
showPauseButton: true,
|
|||
|
|
vibrate: true,
|
|||
|
|
success: (res) => {
|
|||
|
|
list.push(res.content)
|
|||
|
|
console.log('命中', res.content, res.format)
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
// 一次都没扫到就关闭 → 一般是 9021003 取消
|
|||
|
|
console.log('失败', err.errCode, err.errMsg)
|
|||
|
|
},
|
|||
|
|
complete: (res) => {
|
|||
|
|
if (res && res.closed) {
|
|||
|
|
console.log('结束,共', res.hitCount, '次', list)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 组合:仅二维码 + 连续扫 + 相册入口
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
openCodeScan({
|
|||
|
|
formats: ['QR_CODE'],
|
|||
|
|
continuous: true,
|
|||
|
|
smartZoom: true,
|
|||
|
|
vibrate: true,
|
|||
|
|
success: (res) => console.log(res),
|
|||
|
|
complete: (res) => console.log('done', res)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API
|
|||
|
|
|
|||
|
|
### openCodeScan(options?)
|
|||
|
|
|
|||
|
|
打开全屏扫码页。
|
|||
|
|
|
|||
|
|
#### CodeScanOptions
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必填 | 默认 | 说明 |
|
|||
|
|
| --- | --- | :---: | --- | --- |
|
|||
|
|
| `title` | `string` | 否 | `扫一扫` | 原生扫码页顶部标题 |
|
|||
|
|
| `hint` | `string` | 否 | `请将条码放入扫描框内` | 扫描框下方引导语 |
|
|||
|
|
| `successHint` | `string` | 否 | `扫码成功` | 连续扫码成功提示层标题 |
|
|||
|
|
| `emptyHint` | `string` | 否 | `未识别到有效条码` | 图片无条码时提示语 |
|
|||
|
|
| `smartZoom` | `boolean` | 否 | `true` | 条码占画面较小时自动平滑放大;最多尝试 6 次 |
|
|||
|
|
| `formats` | `string[]` | 否 | 全部 | 限定码制,见下方取值表;空/不传 = 全格式 |
|
|||
|
|
| `vibrate` | `boolean` | 否 | `true` | 识别成功后是否短震动约 40ms |
|
|||
|
|
| `scanRegion` | `ScanRegion` | 否 | 未启用 | 实时相机识别区域,控制扫描框尺寸、位置和中心点过滤 |
|
|||
|
|
| `scanStyle` | `CodeScanStyle` | 否 | 见下表 | 扫描框视觉样式,不改变识别区域 |
|
|||
|
|
| `torch` | `'off' \| 'on' \| 'auto'` | 否 | `'off'` | 手电筒策略;`on` 启动后开启,`auto` 低照度连续满足条件后自动开启 |
|
|||
|
|
| `resultMode` | `'immediate' \| 'confirm' \| 'pause'` | 否 | `'immediate'` | 单次扫码结果处理方式;连续扫码固定按立即上报处理 |
|
|||
|
|
| `continuous` | `boolean` | 否 | `false` | 连续扫:页内不关闭,每次命中触发一次 `success` |
|
|||
|
|
| `continuousSuccessDuration` | `number` | 否 | `1000` | 连续扫码成功提示停留时长,单位毫秒,范围 `0 ~ 600000` |
|
|||
|
|
| `showPauseButton` | `boolean` | 否 | `false` | 仅 `continuous: true` 时显示右上角暂停/继续按钮 |
|
|||
|
|
| `fromAlbum` | `boolean` | 否 | `false` | `true` 时打开扫码页后立即拉起系统相册 |
|
|||
|
|
| `scanTimeout` | `number` | 否 | 不启用 | 单次扫码超时,单位毫秒,范围 `1000 ~ 600000` |
|
|||
|
|
| `success` | `(res: CodeScanSuccess) => void` | 否 | - | 成功;连续模式下可多次触发 |
|
|||
|
|
| `fail` | `(err: CodeScanFail) => void` | 否 | - | 失败或取消;连续模式已有命中后关闭时不触发 |
|
|||
|
|
| `complete` | `(res: any) => void` | 否 | - | 会话结束;成功、失败、取消和连续扫码关闭都会触发 |
|
|||
|
|
|
|||
|
|
#### ScanRegion
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 默认 | 说明 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| `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`,越界会收敛 |
|
|||
|
|
|
|||
|
|
区域映射会补偿 `PreviewView` 的 `FILL_CENTER` 裁剪偏移和图像旋转;最终判断的是 ML Kit 包围盒的中心点是否在区域内,而不是要求整个条码完全位于框内。
|
|||
|
|
|
|||
|
|
建议:二维码使用接近正方形的区域,横向条码使用较宽且较矮的区域;如果业务只需要识别、不需要框选提示,可使用 `fullScreen: true`。
|
|||
|
|
|
|||
|
|
#### 组合约束与默认行为
|
|||
|
|
|
|||
|
|
| 组合 | 实际行为 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 不传 `scanRegion` | 实时相机按全画面识别;使用原生默认扫描框布局 |
|
|||
|
|
| `scanRegion.fullScreen: true` | 全画面识别,不显示扫描框,不过滤框外结果 |
|
|||
|
|
| `fromAlbum: true` | 先打开相册;选图识别失败后,在相机可用时仍可继续相机扫码 |
|
|||
|
|
| `continuous: true` | 页内持续识别,`resultMode` 不参与单码确认;关闭页结束会话 |
|
|||
|
|
| `continuous: true` + `showPauseButton: true` | 右上角显示暂停/继续按钮,暂停期间不处理相机帧 |
|
|||
|
|
| `continuous: true` + `scanTimeout` | 忽略 `scanTimeout`,连续扫码没有会话超时 |
|
|||
|
|
| `continuous: true` + `resultMode` | 连续扫码固定立即上报,每次命中通过 `success` 返回 |
|
|||
|
|
| `formats` 含未知值和有效值 | 忽略未知值,使用有效码制 |
|
|||
|
|
| `formats` 非空且全部未知 | 立即失败并返回 `9021010` |
|
|||
|
|
| 单次识别到多个有定位框的条码 | 定格当前画面,显示绿色点选标记,点击后返回选中条码 |
|
|||
|
|
| 连续扫码短时间重复同一内容 | 约 1.2 秒内去重,避免重复触发 `success` |
|
|||
|
|
|
|||
|
|
### CodeScanStyle
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 默认 | 说明 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| `showMask` | `boolean` | `true` | 是否显示扫描框外遮罩 |
|
|||
|
|
| `showBeam` | `boolean` | `true` | 是否显示扫描线 |
|
|||
|
|
| `borderColor` | `string` | `#19be6b` | 边框颜色,支持 `#RRGGBB`、`#AARRGGBB` |
|
|||
|
|
| `maskColor` | `string` | `rgba(0, 0, 0, 0.45)` | 遮罩颜色和透明度,支持 `rgba()` |
|
|||
|
|
| `beamColor` | `string` | `#12e880` | 扫描线颜色,支持 `#RRGGBB`、`#AARRGGBB` |
|
|||
|
|
| `beamDuration` | `number` | `1800` | 扫描线移动周期,单位为毫秒,范围 `100 ~ 60000` |
|
|||
|
|
| `cornerStyle` | `'corner' \| 'full' \| 'rounded' \| 'rounded-corner'` | `'corner'` | `corner` 为直角四角边框,`full` 为完整边框,`rounded` 为完整圆角边框,`rounded-corner` 为四角圆角边框 |
|
|||
|
|
| `cornerRadius` | `number` | `20` | 圆角半径,单位 dp,范围 `0 ~ 160`,两种圆角样式均生效 |
|
|||
|
|
|
|||
|
|
默认值:`showMask: true`、`showBeam: true`、`borderColor: '#19be6b'`、`maskColor: 'rgba(0, 0, 0, 0.45)'`、`beamColor: '#12e880'`、`beamDuration: 1800`、`cornerStyle: 'corner'`、`cornerRadius: 20`。非法颜色会回退到默认值,`beamDuration` 会限制在 `100 ~ 60000` 毫秒,`cornerRadius` 会限制在 `0 ~ 160` dp。
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
|
|||
|
|
```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',
|
|||
|
|
beamDuration: 1800,
|
|||
|
|
cornerStyle: 'rounded',
|
|||
|
|
cornerRadius: 20
|
|||
|
|
},
|
|||
|
|
success: (res) => console.log(res.content)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`scanRegion` 控制实时识别区域,`scanStyle` 只控制扫描页的视觉表现;两者可以独立配置。不传 `scanRegion` 时实时相机按全画面识别,但仍显示原生默认扫描框;需要隐藏扫描框时请传 `scanRegion: { fullScreen: true }`。
|
|||
|
|
|
|||
|
|
#### 回调返回值与生命周期
|
|||
|
|
|
|||
|
|
单次扫码成功:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
success({ content: '...', format: 'QR_CODE', fromAlbum: false })
|
|||
|
|
complete({ content: '...', format: 'QR_CODE', fromAlbum: false })
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
单次扫码失败或用户取消:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
fail({ errCode: 9021003, errMsg: '用户取消扫码', errSubject: 'mas-code-scan' })
|
|||
|
|
complete({ errCode: 9021003, ... })
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
连续扫码关闭:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
complete({ closed: true, hitCount: 3 })
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
连续扫码如果一次命中都没有就关闭,通常会先触发 `fail(9021003)`,随后触发 `complete({ closed: true, hitCount: 0, failed: true, errCode: 9021003 })`。已有命中后关闭只触发 `complete`,不会触发 `fail`。
|
|||
|
|
|
|||
|
|
| 回调 | 触发时机 | 注意 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `success` | 单次确认成功;或连续模式每次命中 | 多码点选只有点击某个码后触发 |
|
|||
|
|
| `fail` | Activity 无法启动、权限失败、相机失败、相册失败、超时、用户取消等 | 用户取消错误码为 `9021003` |
|
|||
|
|
| `complete` | 每次会话结束 | 不能只依赖 `success` 清理页面状态 |
|
|||
|
|
|
|||
|
|
#### CodeScanSuccess
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 说明 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `content` | `string` | 识别到的文本内容 |
|
|||
|
|
| `format` | `string` | 码制名,如 `QR_CODE`、`CODE_128` |
|
|||
|
|
| `fromAlbum` | `boolean` | 是否来自相册识图(可选) |
|
|||
|
|
|
|||
|
|
#### CodeScanFail
|
|||
|
|
|
|||
|
|
继承 `UniError`:
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 说明 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `errCode` | `number` | 见错误码表 |
|
|||
|
|
| `errMsg` | `string` | 错误描述 |
|
|||
|
|
| `errSubject` | `string` | 固定 `"mas-code-scan"` |
|
|||
|
|
|
|||
|
|
回调规则:
|
|||
|
|
|
|||
|
|
- 单次扫码成功:依次调用 `success`、`complete`,成功对象包含 `content`、`format` 和 `fromAlbum`。
|
|||
|
|
- 单次扫码失败或取消:依次调用 `fail`、`complete`。
|
|||
|
|
- 连续扫码:每次命中调用一次 `success`;关闭页面时调用 `complete({ closed: true, hitCount })`。
|
|||
|
|
- 连续扫码在已有命中后关闭:不会再调用 `fail`,只调用 `complete`。
|
|||
|
|
- 相册识图成功:`fromAlbum` 为 `true`;相机实时识别成功时为 `false`。
|
|||
|
|
|
|||
|
|
#### 类型定义(摘录)
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
type CodeScanOptions = {
|
|||
|
|
title?: string
|
|||
|
|
hint?: string
|
|||
|
|
successHint?: string
|
|||
|
|
emptyHint?: string
|
|||
|
|
smartZoom?: boolean
|
|||
|
|
formats?: string[]
|
|||
|
|
vibrate?: boolean
|
|||
|
|
torch?: 'off' | 'on' | 'auto'
|
|||
|
|
resultMode?: 'immediate' | 'confirm' | 'pause'
|
|||
|
|
continuous?: boolean
|
|||
|
|
continuousSuccessDuration?: number
|
|||
|
|
showPauseButton?: boolean
|
|||
|
|
fromAlbum?: boolean
|
|||
|
|
scanRegion?: {
|
|||
|
|
fullScreen?: boolean
|
|||
|
|
width?: number
|
|||
|
|
height?: number
|
|||
|
|
centerX?: number
|
|||
|
|
centerY?: number
|
|||
|
|
}
|
|||
|
|
scanStyle?: {
|
|||
|
|
showMask?: boolean
|
|||
|
|
showBeam?: boolean
|
|||
|
|
borderColor?: string
|
|||
|
|
maskColor?: string
|
|||
|
|
beamColor?: string
|
|||
|
|
beamDuration?: number
|
|||
|
|
cornerStyle?: 'corner' | 'full' | 'rounded' | 'rounded-corner'
|
|||
|
|
cornerRadius?: number
|
|||
|
|
}
|
|||
|
|
// 单次扫码超时(毫秒);小于 1000 或不传则不启用,最大 600000;连续扫码不启用超时
|
|||
|
|
scanTimeout?: number
|
|||
|
|
success?: (res: CodeScanSuccess) => void
|
|||
|
|
fail?: (res: CodeScanFail) => void
|
|||
|
|
complete?: (res: any) => void
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type CodeScanSuccess = {
|
|||
|
|
content: string
|
|||
|
|
format: string
|
|||
|
|
fromAlbum?: boolean
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 错误码
|
|||
|
|
|
|||
|
|
| 错误码 | 说明 | 常见场景 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| `9021001` | 无法获取页面 Activity / 平台不支持 | 非 Android、页面上下文异常 |
|
|||
|
|
| `9021002` | 相机权限被拒绝 | 用户拒绝相机权限(非「仅相册」场景) |
|
|||
|
|
| `9021003` | 用户取消扫码 | 点关闭、系统返回、未扫就退出 |
|
|||
|
|
| `9021004` | 未获取到有效扫码结果 | 成功关页但内容为空(少见) |
|
|||
|
|
| `9021005` | 相机被其他应用占用或当前不可用 | 相机被其他应用、系统功能或设备状态占用 |
|
|||
|
|
| `9021006` | 设备不支持手电筒 | 点击手电筒时提示;不结束当前扫码会话 |
|
|||
|
|
| `9021007` | 相机初始化失败 | CameraX 初始化、绑定预览或分析管线失败 |
|
|||
|
|
| `9021008` | 相册图片无法读取 | 所选 URI 无法读取、复制或解码图片 |
|
|||
|
|
| `9021009` | 图片中未识别到条码 | `fromAlbum: true` 且无可回退相机时,图片中不存在有效码 |
|
|||
|
|
| `9021010` | 指定码制不支持 | `formats` 非空但其中没有插件支持的码制 |
|
|||
|
|
| `9021011` | 扫码超时 | 单次扫码设置了有效 `scanTimeout` 且超时未成功;连续扫码不启用 |
|
|||
|
|
|
|||
|
|
说明:
|
|||
|
|
|
|||
|
|
- `9021001` ~ `9021004` 为已有错误码,含义保持不变;新增错误码从 `9021005` 开始。
|
|||
|
|
- `fail` 与 `complete` 在失败时都会触发(连续扫「已有命中后关闭」除外:只 `complete`)。
|
|||
|
|
- 打开即相册且无相机权限时,仍可用相册;点关闭且无命中 → 一般为 `9021003`。
|
|||
|
|
- 无闪光灯不影响扫码,因此 `9021006` 仅通过扫码页提示展示,不会触发 `fail`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 扫码页交互说明
|
|||
|
|
|
|||
|
|
| 控件 / 手势 | 说明 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 左上关闭 | 退出;连续扫时结束会话 |
|
|||
|
|
| 左下相册 | 选图识别;多码时同样可点选 |
|
|||
|
|
| 右下手电筒 | 有闪光灯时可用;可由 `torch` 设置初始策略,`auto` 会在低照度时自动开启 |
|
|||
|
|
| 右上暂停按钮 | 仅连续扫码且 `showPauseButton: true` 时显示,暂停/继续识别 |
|
|||
|
|
| 结果确认框 | `resultMode: 'confirm'` 或 `'pause'` 时,确认返回或继续扫描 |
|
|||
|
|
| 双指捏合 | 手动变焦 |
|
|||
|
|
| 多码绿点 | 同屏多个有效码时定格,显示绿色呼吸波纹,点击选中 |
|
|||
|
|
| 系统返回键 | 等同关闭 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 码制 formats 取值
|
|||
|
|
|
|||
|
|
传入字符串数组;空/缺省时按全格式。若 `formats` 非空但全部不受支持,调用失败并返回 `9021010`。
|
|||
|
|
|
|||
|
|
| 取值 | 说明 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| `QR_CODE` / `QR` | 二维码 |
|
|||
|
|
| `EAN_13` / `EAN_8` | 商品条码 |
|
|||
|
|
| `CODE_128` / `CODE_39` / `CODE_93` | 一维码 |
|
|||
|
|
| `CODABAR` | Codabar |
|
|||
|
|
| `DATA_MATRIX` | Data Matrix |
|
|||
|
|
| `UPC_A` / `UPC_E` | UPC |
|
|||
|
|
| `ITF` | ITF |
|
|||
|
|
| `PDF417` | PDF417 |
|
|||
|
|
| `AZTEC` | Aztec |
|
|||
|
|
| `ALL` | 全格式(与具体码制混用时按全格式处理) |
|
|||
|
|
|
|||
|
|
补充规则:插件会忽略未知 token;只要数组中存在一个有效 token 就使用有效 token。数组非空但全部无效时返回 `9021010`。`ALL` 与其他有效码制同时传入时最终使用全格式。
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
formats: ['QR_CODE', 'CODE_128', 'EAN_13']
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 常见问题
|
|||
|
|
|
|||
|
|
### 1. 点击没反应 / 报找不到类?
|
|||
|
|
|
|||
|
|
未使用自定义基座。请按上文制作并选择「自定义调试基座」再运行。
|
|||
|
|
|
|||
|
|
### 2. 提示相机权限被拒绝(9021002)?
|
|||
|
|
|
|||
|
|
到系统设置打开应用相机权限后重试。
|
|||
|
|
|
|||
|
|
### 3. 连续扫只回调一次?
|
|||
|
|
|
|||
|
|
确认传了 `continuous: true`。同一内容约 1.2 秒内会去重,避免狂刷;换个码或稍等再扫。
|
|||
|
|
|
|||
|
|
### 4. 相册选了图但提示未识别?
|
|||
|
|
|
|||
|
|
图中可能没有清晰条码,或被 `formats` 过滤掉。可先不传 `formats` 试全格式;打开即相册且没有可用相机时,会以 `9021009` 返回无条码错误。
|
|||
|
|
|
|||
|
|
### 5. 为什么会提示扫码超时(9021011)?
|
|||
|
|
|
|||
|
|
仅在单次扫码传入 `scanTimeout`(1000 ~ 600000 毫秒)后启用。超时不会影响连续扫码;不需要限制时不传该参数或传 `0`。
|
|||
|
|
|
|||
|
|
### 6. iOS / 鸿蒙能用吗?
|
|||
|
|
|
|||
|
|
当前版本仅 Android。目录未包含 iOS/鸿蒙实现。
|
|||
|
|
|
|||
|
|
### 7. 能否当组件标签用?
|
|||
|
|
|
|||
|
|
不能。这是 **UTS API**,请 `import { openCodeScan } from '@/uni_modules/mas-code-scan'` 后调用。
|
|||
|
|
|
|||
|
|
### 8. 演示页可以直接当业务页面使用吗?
|
|||
|
|
|
|||
|
|
`prototype.vue` 是能力验证页面,结果列表是本地状态,不包含业务接口提交、权限引导和业务字段转换。正式业务建议复制 `openScan` 调用方式,在自己的页面中处理 `success`、`fail` 和 `complete`,并在 `complete` 中恢复 loading、页面状态等资源。
|
|||
|
|
|
|||
|
|
### 9. `scanStyle` 不生效怎么办?
|
|||
|
|
|
|||
|
|
确认调用的是包含当前插件的自定义调试基座或正式打包应用。修改 UTS 原生代码后需要重新制作自定义基座或重新云打包,普通标准基座不会加载插件原生依赖和最新实现。
|
|||
|
|
|
|||
|
|
### 10. 扫描框显示了但框外条码没有识别?
|
|||
|
|
|
|||
|
|
这是 `scanRegion` 的预期行为。扫描框不仅是视觉提示,也会作为实时识别过滤区域;如需整屏识别,请不传 `scanRegion` 或传入 `fullScreen: true`。
|
|||
|
|
|
|||
|
|
### 11. `resultMode` 和 `continuous` 一起传会怎样?
|
|||
|
|
|
|||
|
|
连续扫码以连续模式为准,不显示单次扫码确认框,每次识别成功都会通过 `success` 上报;如需单个结果确认,请不要传 `continuous: true`。
|
|||
|
|
|
|||
|
|
### 12. `fromAlbum` 和相机权限有什么关系?
|
|||
|
|
|
|||
|
|
Android 13 及以上优先使用系统 Photo Picker;Android 12 及以下使用系统图片选择器,可能需要读取相册权限。打开即相册时,即使没有相机权限,也可以先完成相册识图;相册无结果且无法继续使用相机时返回 `9021009`。
|
|||
|
|
|
|||
|
|
### 13. 为什么识别框位置和相机画面看起来不完全一致?
|
|||
|
|
|
|||
|
|
预览采用 `FILL_CENTER`,相机画面会按比例填充并裁剪边缘;插件会在识别过滤时补偿裁剪和旋转,但不同设备的相机传感器比例、状态栏和厂商裁剪策略可能存在少量视觉差异。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. 仅 Android App-Plus;发布市场包请走正式打包,勿依赖标准基座调试结果。
|
|||
|
|
2. 首次使用实时相机需要相机权限;插件不会主动跳转系统设置,权限被拒绝后由业务决定是否引导用户开启。
|
|||
|
|
3. 震动权限由插件 Manifest 声明;设置 `vibrate: false` 可关闭成功震动。
|
|||
|
|
4. 相册选图走系统选择器;Android 12 及以下可能需要读取相册权限,Android 13 及以上优先使用 Photo Picker。
|
|||
|
|
5. `scanRegion` 只过滤实时相机,且按条码包围盒中心点判断;相册识图始终分析整张图片。
|
|||
|
|
6. 连续扫依赖原生命中队列和 UTS 桥接轮询,业务应在 `complete` 中收尾,不要只在 `success` 中恢复状态。
|
|||
|
|
7. 多码点选依赖 ML Kit 返回定位框;多码中只有一个可解码码时会直接返回,无法定位的结果不能作为可点击目标。
|
|||
|
|
8. 智能变焦和双指缩放共享相机变焦能力;不支持变焦的设备会保持原始比例。
|
|||
|
|
9. 大尺寸相册图片会先缩放到最长边约 1920 像素再识别,以降低内存压力。
|
|||
|
|
10. 文档与演示不保证覆盖所有机型相机差异,建议使用真实目标机型回归。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 目录结构
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
mas-code-scan/
|
|||
|
|
├── package.json
|
|||
|
|
├── readme.md
|
|||
|
|
├── changelog.md
|
|||
|
|
└── utssdk/
|
|||
|
|
├── interface.uts # 对外类型与 API 声明
|
|||
|
|
├── unierror.uts # 错误实现
|
|||
|
|
└── app-android/
|
|||
|
|
├── index.uts # JS/UTS 桥接
|
|||
|
|
├── config.json # 原生依赖
|
|||
|
|
├── AndroidManifest.xml
|
|||
|
|
├── CodeCaptureActivity.kt
|
|||
|
|
├── MultiCodePickView.kt
|
|||
|
|
├── ScanFrameDecorView.kt
|
|||
|
|
├── YuvFrames.kt
|
|||
|
|
├── SymbologyUtil.kt
|
|||
|
|
├── CodeScanHitBus.kt
|
|||
|
|
└── res/drawable/ # 关闭/相册等资源
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 版本
|
|||
|
|
|
|||
|
|
| 版本 | 说明 |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 1.3.0 | 新增 `rounded` 完整圆角边框、`rounded-corner` 四角圆角边框和自定义 `cornerRadius`,遮罩与扫描线同步适配圆角 |
|
|||
|
|
| 1.2.0 | 新增 `scanStyle` 扫描框视觉配置,支持遮罩、扫描线、颜色、动画周期、四角/完整边框;完善 `scanRegion` 与回调说明 |
|
|||
|
|
| 1.1.0 | 权限码区分、关闭/相册、码制过滤、震动、连续扫、Kotlin 拆分 |
|
|||
|
|
| 1.0.0 | Android 扫码初版 |
|
|||
|
|
|
|||
|
|
更多变更见 `changelog.md`。
|
|||
|
|
|
|||
|
|
## 相关链接
|
|||
|
|
|
|||
|
|
- [UTS 语法](https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html)
|
|||
|
|
- [UTS API 插件](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html)
|
|||
|
|
- [制作自定义调试基座](https://uniapp.dcloud.net.cn/tutorial/run/run-app.html#customplayground)
|