# 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 ` ``` ### 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)