更新记录
1.0.66(2026-07-30)
- 修复 iOS 首次允许相机权限后,动态扫码页图片配置在生成 Swift 中发生强制类型转换并导致 App 闪退的问题;插件层改为安全读取动态字段并直接构造平台配置类型,原示例
uiImageConfig + startScan(options)调用保持不变。 - 修复 iPhone X / iOS 16 全屏扫码默认码制下同步 Vision 兜底占用主线程、导致相册/手电/连续模式/关闭按钮响应迟滞的问题;视频帧现在由专用串行队列识别,主线程只接收结果。
- 修复打开系统相册触发页面
onHide时,stopScan/destroyScanner可能从 Weex bridge 线程直接关闭 UIKit 页面并触发EXC_BREAKPOINT / SIGTRAP的问题;全屏关闭、状态清理和完成回调统一回到主线程。 - Android / iOS 全屏与嵌入式运行诊断标识统一升级到 1.0.66。本版包含 iOS 原生 UTS/Swift 生成变化,发布和真机验收必须重新制作并安装匹配的 iOS 自定义基座或正式包;仅更新页面资源不能替换旧 framework。
1.0.65(2026-07-28)
- 修复 Android 原生扫码六处将
Throwable直接传给仅接受Exception的Tasks.forException,导致 KotlincompileReleaseKotlin类型不匹配、安卓云打包失败的问题。 - 失败任务现在统一通过安全转换收敛异常类型:普通
Exception保持原对象,其他Throwable使用保留 cause 的RuntimeException包装,不改变扫码回调、错误码或业务行为。 - 用户已确认当前源码安卓云打包成功;Android / iOS 全屏与嵌入式运行诊断标识统一升级到 1.0.65。Android 原生 Kotlin 已变化,旧自定义基座需重新原生联编或重新制作。
1.0.64(2026-07-27)
- 修复 Android 工业平板或低清晰度相机中,长而细的 Code128 在实时画面占比较小时持续扫描却不返回结果的问题;整帧无结果后会对可见扫码区域执行节流的裁剪放大重试。
- 修复 Android 相册整图中一维码占比较小时返回
album decode failed: no code recognized的问题;首轮无结果后会对中心区域执行受尺寸上限保护的裁剪放大重试。 - 新兜底仅覆盖 Code128、Code39、Code93、Codabar、EAN、UPC、ITF 等一维码,二维码与既有 Data Matrix 中心裁剪、反色重试链路保持不变。
- Android / iOS 全屏与嵌入式运行诊断标识统一升级到 1.0.64;Android 原生 Kotlin 已变化,发布与客户复测必须重新原生联编或安装匹配的正式包/自定义基座。
平台兼容性
uni-app(4.84)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | - | - | √ | √ | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(4.84)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | √ | √ | √ | - |
lizhao-scan-pro
lizhao-scan-pro 是一个面向 uni-app / uni-app x 的原生扫码 UTS 插件。既可以通过 API 拉起原生全屏扫码页,也可以在 .nvue/.uvue 页面使用 <lizhao-scan-pro /> 原生预览组件,完全自由布局扫码框、按钮、提示和业务结果 UI。
插件底层按平台走原生能力:Android 使用 CameraX + ML Kit,iOS 使用 AVFoundation + Vision,Harmony 使用系统扫码能力。Web 和小程序当前不伪造成功结果,会返回明确的不支持错误。
适合哪些场景
| 场景 | 推荐能力 | 说明 |
|---|---|---|
| 普通扫码 | startScan |
打开全屏扫码页,识别成功后回调结果 |
| 商品条码/库存盘点 | formats、minBarcodeLength、continuous |
限制条码类型、过滤短码、连续返回结果 |
| 一屏多个码 | returnAllResults |
同一帧内返回多条识别结果,适合货架、票据、批量标签 |
| 相册识别 | enableAlbum、pickImageAndScan |
支持从图片中识别二维码或条形码 |
| 近距离扫码 | nearFocusLock、zoomRatio |
适合约 5cm 近距标签、设备码、细小条码 |
| 品牌化扫码页 | scanFrameStyle、uiTextConfig、uiImageConfig、uiLayoutConfig |
自定义扫码框、文案、图标、按钮位置和遮罩显示 |
| 完全自由布局 | <lizhao-scan-pro /> |
原生预览嵌入客户页面,扫码框、按钮与业务 UI 全部由页面决定 |
| 页面状态联动 | pauseScan、resumeScan、stopScan、destroyScanner |
页面隐藏、返回、销毁时控制相机资源 |
接入方式选择
| 接入方式 | 适合客户 | 推荐入口 |
|---|---|---|
| 只要快速扫码 | 想先跑通功能,界面使用默认样式 | 复制“5 分钟跑通”示例 |
| 需要业务规则 | 指定二维码/条码类型、连续扫码、过滤短码 | 查看“从基础到进阶” |
| 需要品牌化全屏扫码 | 调整扫码框、操作按钮、提示文案和图标 | 查看“扫码页样式配置” |
| 需要完全自由布局 | 自己编写页面结构、动画、按钮与结果区域 | 查看“完全自由布局组件” |
| 需要完整查询资料 | 已接入,正在查某个字段含义 | 直接跳到“参数说明(ScanOptions)” |
5 分钟跑通
1. 使用推荐版本
建议使用 HBuilderX 5.07 及以上版本进行测试、运行和打包。低版本 HBuilderX 的 UTS 编译器可能不支持当前插件使用的部分 UTS 语法或生成规则,容易出现 UTS 语法校验、编译或原生联编问题。
2. 引入插件
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
全屏扫码通过 script 中的 API 调用;完全自由布局通过 <lizhao-scan-pro /> 组件使用。页面 import 只写插件根目录,不要直接 import utssdk 内部文件。
3. 打开扫码页
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
LizhaoScanPro.startScan({
// 建议页面内固定一个扫描器 ID,后续停止、暂停、恢复都使用同一个 ID
scannerId: 'main',
// 显示相册入口,方便用户从图片中识别二维码或条形码
enableAlbum: true,
// 识别成功回调,单次扫码默认识别成功后返回结果
onResult(res) {
console.log('扫码结果:', res.value, res.format)
},
// 识别失败、权限异常或平台不支持时会进入这里
onError(err) {
console.error('扫码失败:', err)
}
})
4. 页面离开时释放资源
onHide() {
// 页面不可见时停止扫码,避免后台占用相机
LizhaoScanPro.stopScan({ scannerId: 'main' })
}
onUnload() {
// 页面销毁时彻底释放扫描器
LizhaoScanPro.destroyScanner({ scannerId: 'main' })
}
从基础到进阶
只识别指定码制
LizhaoScanPro.startScan({
scannerId: 'main',
// 只识别二维码和 Code128,减少无关码制干扰
formats: ['qrCode', 'code128'],
onResult(res) {
console.log('命中指定码制:', res.value)
}
})
连续扫码和批量识别
LizhaoScanPro.startScan({
scannerId: 'main',
// 开启连续扫码,适合仓储、盘点、核销等连续作业
continuous: true,
// 两次回调至少间隔 500ms,业务侧也建议继续做去重
continuousIntervalMs: 500,
// 同一帧出现多个码时返回全部识别结果
returnAllResults: true,
onResult(res) {
res.results.forEach((item) => {
console.log('批量结果:', item.value, item.format)
})
}
})
近距小码识别
LizhaoScanPro.startScan({
scannerId: 'main',
// 开启近距锁焦模式,适合设备铭牌、细小标签、约 5cm 近距扫码
nearFocusLock: true,
// 预设缩放倍率,帮助小码在画面中更清晰
zoomRatio: 1.8,
// 固定码长业务可过滤明显错误的短结果
minBarcodeLength: 8,
onResult(res) {
console.log('近距识别结果:', res.value)
}
})
品牌化扫码页
LizhaoScanPro.startScan({
scannerId: 'main',
scanFrameStyle: {
// 调整扫码框宽高和位置,适配业务视觉
widthRatio: 0.72,
heightRatio: 0.5,
topRatio: 0.2,
cornerRadius: 22,
borderColor: '#88FFFFFF',
cornerColor: '#FFFF6B6B'
},
uiTextConfig: {
// 自定义提示文案和按钮文案
tipText: '请对准二维码或条形码',
closeText: '关闭',
albumText: '相册',
torchOffText: '打开手电筒',
torchOnText: '关闭手电筒'
},
onResult(res) {
console.log('扫码结果:', res.value)
}
})
完全自由布局组件
当扫码页的结构、动画、按钮或业务信息需要由业务自己编写时,使用 <lizhao-scan-pro /> 把原生相机预览嵌入页面。组件只负责相机、识别和状态回调,扫码框、扫描线、按钮、提示、结果区等界面全部由页面实现。
接入前先确认
先按页面形态选择正确入口:
| 你的需求 | 正确接入方式 | 不要这样用 |
|---|---|---|
| 快速打开完整扫码页 | 调用 startScan(options) |
不需要为了改几段文案就改用组件 |
| 页面结构、按钮和业务区完全自定义 | 在 .nvue/.uvue 使用 <lizhao-scan-pro /> |
不要同时再调用同 ID 的全屏 startScan |
uni-app 普通 .vue 页面 |
继续使用全屏 startScan |
不要在普通 .vue 中放嵌入式组件 |
开始接入前必须满足以下条件:
- 只在 Android/iOS App 使用;Harmony、Web 和小程序当前不支持嵌入式预览。
- uni-app 页面文件必须是
.nvue,uni-app x 页面文件必须是.uvue,不能只把普通.vue改个组件标签。 - 必须重新制作并安装包含当前插件原生代码的 Android/iOS 自定义基座或正式包。只更新 wgt/appResource 不会更新旧基座中的相机组件。
- 给预览容器和组件设置明确的宽度、高度;高度为
0时不会显示相机画面。 scannerId必须是非空且当前页面唯一的固定字符串,组件挂载后不要动态修改。- 必须监听
error,权限被拒绝、参数错误或会话冲突都会通过该事件返回,不能只监听scan。 - 一个页面只保留一个活动相机会话;打开全屏扫码前先停止嵌入式组件。
最小正确接入
下面的 template 和 style 可用于 .nvue/.uvue;script 部分需要按 uni-app 或 uni-app x 选择对应版本。
<view class="scanner-shell">
<!-- 原生组件只负责相机预览与识别,不绘制固定扫码 UI -->
<lizhao-scan-pro
ref="scanner"
scanner-id="freeLayout"
class="camera-preview"
:formats="['all']"
:continuous="false"
@ready="onReady"
@scan="onScan"
@error="onError"
@statechange="onStateChange"
/>
<!-- 扫码框、扫描线、按钮和业务提示全部由客户页面自由布局 -->
<view class="custom-overlay">
<view class="custom-scan-frame"></view>
<text class="custom-tip">请将二维码或条形码放入扫码框</text>
</view>
</view>
.scanner-shell {
position: relative;
width: 750rpx;
height: 900rpx;
}
.camera-preview,
.custom-overlay {
position: absolute;
left: 0;
top: 0;
width: 750rpx;
height: 900rpx;
}
.camera-preview { z-index: 1; }
.custom-overlay { z-index: 10; }
uni-app .nvue 的最小生命周期代码:
<script>
export default {
data() {
return {
// ready 前不要在 onShow 中调用 resume,避免首次进入时重复启动。
scannerReady: false
}
},
onShow() {
const scanner = this.$refs.scanner
if (this.scannerReady && scanner != null && scanner.resume != null) {
scanner.resume()
}
},
onHide() {
const scanner = this.$refs.scanner
if (scanner != null && scanner.pause != null) scanner.pause()
},
onUnload() {
const scanner = this.$refs.scanner
if (scanner != null && scanner.dispose != null) scanner.dispose()
},
methods: {
eventData(event) {
// uni-app 原生组件事件数据可能位于 event.detail。
return event != null && event.detail != null ? event.detail : event
},
onReady(event) {
this.scannerReady = true
console.log('原生扫码预览已就绪:', this.eventData(event))
},
onScan(event) {
const frame = this.eventData(event)
console.log('本帧扫码结果:', frame.results)
},
onError(event) {
console.error('嵌入式扫码失败:', this.eventData(event))
},
onStateChange(event) {
console.log('扫码状态变化:', this.eventData(event))
}
}
}
</script>
uni-app x .uvue 的最小生命周期代码:
<script setup lang="uts">
import {
ScanEmbeddedEvent,
ScanEmbeddedState,
ScanFail,
ScanFrameResult
} from '@/uni_modules/lizhao-scan-pro'
// component-uts easycom 会生成该公开实例类型,无需手写组件声明。
const scanner = ref<LizhaoScanProComponentPublicInstance | null>(null)
const scannerReady = ref<boolean>(false)
const onReady = (state: ScanEmbeddedState): void => {
scannerReady.value = true
console.log('原生扫码预览已就绪:' + JSON.stringify(state))
}
const onScan = (frame: ScanFrameResult): void => {
console.log('本帧扫码结果:' + JSON.stringify(frame.results))
}
const onError = (error: ScanFail): void => {
console.error('嵌入式扫码失败:' + JSON.stringify(error))
}
const onStateChange = (event: ScanEmbeddedEvent): void => {
console.log('扫码状态变化:' + JSON.stringify(event))
}
onShow(() => {
if (scannerReady.value) scanner.value?.resume?.()
})
onHide(() => {
scanner.value?.pause?.()
})
onUnload(() => {
scanner.value?.dispose?.()
})
</script>
完整可运行页面:
- uni-app:
uni_modules/lizhao-scan-pro/example/uniapp/free-layout.nvue - uni-app x:
uni_modules/lizhao-scan-pro/example/uniappx/free-layout.uvue
建议先复制对应完整示例确认预览和回调正常,再逐步替换成业务 UI,不要从零拼装生命周期代码。
组件职责与扫码范围
| 能力 | 由谁负责 | 说明 |
|---|---|---|
| 相机预览、码识别、格式过滤、连续扫码 | <lizhao-scan-pro /> |
通过 props 配置,通过事件返回结果 |
| 扫码框、扫描线、遮罩、按钮、提示、动画、结果区 | 业务页面 | 使用 .nvue/.uvue 页面元素覆盖在组件上方 |
| 相册识别 | 业务页面调用 pickImageAndScan |
嵌入式组件不内置相册按钮 |
| 页面隐藏、恢复和销毁 | 业务页面生命周期 | 分别调用 pause()、resume()、dispose() |
自定义扫码框只是视觉引导,不是识别裁剪区域。组件会识别整个相机预览范围内的有效码;如业务只允许扫码框内的码,需要在业务层结合结果规则做校验,不能仅依赖页面上画出的方框。
showScanFrame、showMaskOverlay、scanFrameStyle、uiTextConfig、uiImageConfig、uiLayoutConfig、tipText 和 enableAlbum 属于全屏 startScan 的界面配置,不是嵌入式组件属性。自由布局模式下请直接在页面中实现这些 UI。
页面覆盖层如果拦截了点击事件,组件不会自动收到点击位置。需要点按聚焦时,在覆盖层的点击事件中取得组件局部坐标,再调用 focusAt(x, y)。
生命周期必须这样处理
| 页面时机/动作 | 应调用 | 说明 |
|---|---|---|
| 首次进入页面 | 通常无需手动调用 | 默认 autoStart=true,组件挂载后自动请求权限并启动;不要再在 onReady 中重复调用 start() |
| 页面进入前台 | resume() |
仅在已经收到 ready 且没有活动中的全屏会话时调用;首次 onShow 应跳过 |
| 页面进入后台 | pause() |
暂停识别但保留相机和组件实例,返回页面后可快速恢复 |
| 临时关闭相机 | stop() |
解绑相机但保留组件实例和 scannerId 注册,之后可调用 start() 再次启动 |
| Android 示例切换全屏扫码 | 先 stop(),再用不同 ID 调用 startScan() |
仅 Android 示例提供“嵌入 → 全屏 → 返回恢复”比较链;必须监听 onOverlayEvent,收到 closed/stopped 后再调用组件 start() |
| 页面卸载或永久关闭 | dispose() |
释放相机、线程、回调和注册关系;dispose() 后不能再次调用 start(),重新使用必须重新挂载组件 |
页面 ref 的最终释放方法是 dispose(),不要调用 scanner.destroy();destroy 与 component-uts 框架内部方法同名,会导致生成代码冲突。脚本层公共 API destroyScanner({ scannerId }) 仍然保留,它会按 scannerId 销毁对应扫码会话,不等同于页面 ref 方法名。
Android 从嵌入式组件切换到全屏扫码时,onResult 只表示已经返回识别结果,不表示全屏原生页面和相机已经释放。不要在 onResult/onError 中直接调用组件 start();示例会设置 overlayConfig.enabled=true,并在 onOverlayEvent 收到 closed/stopped 后恢复嵌入预览。iOS 支持嵌入式自由布局,但本示例不提供嵌入与全屏混合切换;iOS 客户应在同一页面只选择一种相机会话形态。
Android 全屏 Activity 返回时,页面 onShow 可能早于原生 closed/stopped 事件触发。fullScreenActive=true 时必须跳过页面 onShow 中的 resume();只能由 closed/stopped(或全屏启动前同步失败)调用统一恢复方法,避免两个相机会话同时抢占摄像头。
如果设置 autoStart=false,请在组件 ref 可用后主动调用 start();不要等待 ready 再启动,因为 ready 只有原生相机真正启动后才会触发。
scannerId 使用规则
- 每个已挂载组件使用一个非空、唯一且固定的
scannerId,推荐按页面或业务命名,例如warehouseInboundScanner。 - 通过公共 API 控制组件时,API 参数中的
scannerId必须与组件完全一致。 - 为保证 Android/iOS 行为一致,组件挂载后不要动态修改
scannerId;需要换 ID 时先卸载旧组件,再挂载新组件。 - 同一个
scannerId不能同时属于全屏扫码和嵌入式组件,否则返回9010011 scanner id occupied。stop()不会释放组件注册;必须复用同 ID 时需要先dispose()或卸载组件。 - 即使使用不同 ID,设备通常也只能稳定占用一个相机会话,因此打开全屏扫码前仍应先调用组件
stop()。
常见错误与处理
| 现象/错误 | 常见原因 | 正确处理 |
|---|---|---|
| 组件不显示或 easycom 无法识别 | uni-app 使用了普通 .vue |
改用 .nvue;uni-app x 使用 .uvue |
| 页面有位置但相机黑屏 | 组件宽高为 0、相机权限未授权或仍在使用旧基座 |
明确设置宽高,处理 error,重新制作并安装当前插件自定义基座/正式包 |
| 只更新 wgt 后仍没有组件能力 | 原生代码已经编译进旧基座 | 重新原生联编或重新云打包,wgt/appResource 不能替换原生组件 |
9010011 scanner id occupied |
同 ID 已被另一个组件或全屏扫码占用 | 改用新的唯一 ID;如必须复用原 ID,先 dispose() 或卸载旧组件,单独调用 stop() 不会释放 ID |
9010012 scanner component unavailable |
ref 尚未就绪、组件已卸载或已执行 dispose() |
等待组件挂载;dispose() 后重新挂载组件,不要复用旧 ref |
调用 scanner.destroy() 编译失败或方法不存在 |
使用了旧示例或把页面 ref 方法与公共 API 混用 | 页面 ref 改用 scanner.dispose();脚本 API 使用 destroyScanner({ scannerId }) |
| 首次进入页面重复启动 | autoStart=true,同时又在首次 onShow/onReady 调用了 start/resume |
使用 scannerReady 标记,首次 onShow 跳过,收到 ready 后再允许恢复 |
| 连续收到相同结果 | 开启了 continuous,但没有设置间隔或业务去重 |
设置 continuousIntervalMs,并按业务主键做二次去重 |
| Android 全屏扫码无法打开或返回后黑屏 | 嵌入式组件仍占用相机,或在 onResult/onError 中过早恢复 |
先 stop() 再调用不同 ID 的 startScan();设置 overlayConfig.enabled=true,只在 closed/stopped 后调用组件 start() |
| 扫码框外的码也被识别 | 误以为自定义扫码框会裁剪识别区域 | 扫码框只是视觉提示;在业务层校验结果或调整相机预览范围 |
组件属性
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| scannerId | string | 否 | 扫描器唯一标识,用于组件注册与公共 API 控制;挂载后不要修改 | embedded |
任意非空且当前页面唯一的字符串 |
| formats | Array |
否 | 允许识别的码制 | ["all"] |
all / qrCode / aztec / codabar / code39 / code93 / code128 / dataMatrix / ean8 / ean13 / itf / pdf417 / upcA / upcE |
| minBarcodeLength | number | 否 | 一维码最小长度过滤 | 0 |
大于等于 0 |
| returnAllResults | boolean | 否 | 是否返回同一帧的全部有效结果 | false |
true / false |
| continuous | boolean | 否 | 是否持续识别 | false |
true / false |
| continuousIntervalMs | number | 否 | 连续结果最小间隔,单位毫秒;仅 continuous=true 生效 |
0 |
大于等于 0 |
| autoStart | boolean | 否 | 原生 View 挂载后是否自动启动;设为 false 后需在 ref 可用时调用 start() |
true |
true / false |
| zoomRatio | number | 否 | 初始缩放倍率 | 1 |
大于 0 |
| torchEnabled | boolean | 否 | 初始手电状态 | false |
true / false |
| nearFocusLock | boolean | 否 | 是否启用近距辅助锁焦 | false |
true / false |
| beepOnScan | boolean | 否 | 识别成功后是否播放提示音 | false |
true / false |
| vibrateOnScan | boolean | 否 | 识别成功后是否短震动 | false |
true / false |
组件事件
| 事件 | 数据类型 | 说明 |
|---|---|---|
ready |
ScanEmbeddedState | 原生预览与识别链路准备完成;收到后才可安全执行前后台恢复控制 |
scan |
ScanFrameResult | 返回单码或同帧多码结果,业务数据读取 results 数组,结构与 startScan 一致 |
error |
ScanFail | 权限、初始化、参数或会话冲突错误;必须监听并记录 errCode/errMsg |
statechange |
ScanEmbeddedEvent | 返回 starting/running/paused/stopped/destroyed 状态变化 |
focus |
ScanEmbeddedEvent | 返回点按聚焦坐标与执行状态 |
uni-app .nvue 的原生组件事件数据可能包装在 event.detail 中,建议像完整示例一样先取 event.detail,不存在时再使用事件对象本身;uni-app x .uvue 按表格中的强类型直接接收。
组件方法
| 方法 | 说明 |
|---|---|
start() |
请求权限并启动或重新绑定相机;适用于 autoStart=false 或 stop() 后重启 |
stop() |
停止识别并解绑相机,保留组件实例和 scannerId 注册;打开不同 ID 的全屏扫码前必须先调用 |
pause() |
暂停分析,不派发识别结果 |
resume() |
恢复分析;尚未启动时会启动会话 |
applyTorchEnabled(enabled) |
设置手电状态 |
applyZoomRatio(zoomRatio) |
设置缩放倍率 |
setScanFormats(formats) |
动态更新码制 |
focusAt(x, y) |
按组件局部坐标执行聚焦 |
getState() |
返回当前组件状态快照 |
dispose() |
永久释放相机、识别器、线程、回调和注册表引用;调用后必须重新挂载组件才能再次使用 |
组件与现有 pauseScan/resumeScan/setTorchEnabled/setZoomRatio/setScanFormats/stopScan/destroyScanner 共用 scannerId。公共 API 会优先控制同 ID 的组件;若同 ID 已被组件占用,再调用全屏 startScan 会返回 9010011 scanner id occupied,不会隐式关闭客户页面中的相机。
自由布局平台边界
| 页面/平台 | 是否支持 | 说明 |
|---|---|---|
uni-app App .nvue |
是 | Android / iOS 可嵌入原生预览 |
uni-app x App .uvue |
是 | Android / iOS 可嵌入原生预览 |
| Android 嵌入/全屏比较链 | 是 | 示例提供切换按钮,并在全屏 closed/stopped 后恢复嵌入预览 |
| iOS 嵌入/全屏比较链 | 否 | iOS 仍支持嵌入式自由布局;本示例不提供两种相机会话混合切换 |
uni-app 普通 .vue |
否 | 普通 Vue 页面不承诺原生 View 层级与覆盖层稳定性,请使用 .nvue |
| Harmony | 否 | 保留系统全屏 startScan,不伪造嵌入式组件成功 |
| Web / 小程序 | 否 | 不具备本插件的原生嵌入式预览能力 |
页面隐藏时必须调用 pause(),再次显示时在已收到 ready 的前提下调用 resume(),页面卸载时调用 dispose()。Android/iOS 原生组件代码会编译进基座或安装包,仅更新 wgt/appResource 不能替换旧基座里的原生实现。
特色功能一览
| 特色功能 | 能解决什么问题 | 相关配置 |
|---|---|---|
| 原生全屏扫码 | 不依赖页面组件拼装,直接使用平台原生相机能力 | startScan |
| 多码制识别 | 支持二维码、Data Matrix、PDF417、Code39、Code128、EAN、UPC 等常见码制 | formats |
| 同帧多码返回 | 一张画面中多个码可一次性回传 | returnAllResults |
| 连续扫码 | 适合盘点、核销、批量入库等高频扫码场景 | continuous、continuousIntervalMs |
| 近距聚焦增强 | 改善细小码、设备铭牌、近距离扫码体验 | nearFocusLock、zoomRatio |
| 相册识别 | 支持从本地图片中识别码 | enableAlbum、pickImageAndScan |
| 扫码页自定义 | 支持扫码框、遮罩、扫描线、文案、图片图标和布局偏移配置 | scanFrameStyle、uiTextConfig、uiImageConfig、uiLayoutConfig |
| 完全自由布局 | 原生预览嵌入 .nvue/.uvue,页面自由编写扫码框、按钮和业务区 |
<lizhao-scan-pro /> |
| 手电与缩放控制 | 扫码过程中动态调整设备能力 | setTorchEnabled、setZoomRatio |
| 明确平台边界 | 不支持平台返回错误,不伪造成功 | 9010001 |
支持平台
| 平台 | 是否支持 | 说明 |
|---|---|---|
| uni-app | 是 | Vue2/Vue3 可用 |
| uni-app x | 是 | App 侧可用 |
| Android | 是 | 原生依赖入口已配置,扫码链路基于 CameraX + ML Kit |
| iOS | 是 | 原生扫码链路已实现(AVFoundation + Vision,实时扫码含 Vision 视频帧兜底) |
| Harmony | 是 | 基础扫码能力可用(基于系统 scanCode,手电/缩放为状态兼容) |
| Web | 否 | 返回 9010001 |
| 微信小程序 | 否 | 返回 9010001 |
| 支付宝小程序 | 否 | 返回 9010001 |
API 列表
| API | 用途 |
|---|---|
startScan(options) |
打开原生全屏扫码页 |
stopScan(options) |
停止当前扫码 |
pauseScan(options) |
暂停当前扫码 |
resumeScan(options) |
恢复当前扫码 |
setTorchEnabled(options) |
动态打开或关闭手电筒 |
setZoomRatio(options) |
动态设置缩放倍率 |
pickImageAndScan(options) |
从图片中识别二维码或条形码 |
setScanFormats(options) |
动态设置识别码制 |
postOverlayMessage(options) |
向扫码遮罩事件链路发送业务消息 |
destroyScanner(options) |
销毁扫描器并释放资源 |
Harmony 能力边界
Harmony 端已补齐与 Android / iOS 同名 API 与回调结构,但受系统扫码页能力限制,存在以下边界:
setTorchEnabled/setZoomRatio:保持状态兼容并返回成功回调,当前不直接驱动系统扫码页的手电筒和缩放。startScan/pickImageAndScan:基于系统uni.scanCode能力,UI 形态以系统页面为准,不保证与 Android/iOS 自定义扫码页完全一致。continuous=true:通过循环拉起系统扫码能力实现连续扫描,支持continuousIntervalMs回调间隔控制;建议业务侧继续做去重与节流。showContinuousToggle:参数可接收但受系统扫码页能力限制,Harmony 当前不展示页内“单次/连续”切换按钮。returnAllResults:参数可接收但受系统扫码能力限制,Harmony 当前仍按单条结果回调。formats:Harmony 仅能按系统扫码大类限制(如barCode / qrCode / datamatrix / pdf417),code39/code128/ean13等一维细分格式会映射到barCode。minBarcodeLength:Harmony 端按识别结果长度做回调前过滤,可过滤短于阈值的一维码结果,但不能提升系统扫码页本身的识别精度。beepOnScan / vibrateOnScan:Android / iOS 按插件原生链路处理;Harmony 当前暂不承诺震动/提示音生效,以系统扫码页行为为准。
参数说明(ScanOptions)
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| scannerId | string | 否 | 扫描器 ID | default |
任意非空字符串 |
| formats | Array |
否 | 识别格式列表 | ["all"] |
all / qrCode / aztec / codabar / code39 / code93 / code128 / dataMatrix / ean8 / ean13 / itf / pdf417 / upcA / upcE |
| minBarcodeLength | number | 否 | 一维条码最小长度过滤(Android 相机扫码 / 相册识别、iOS 相机扫码;Harmony 回调前过滤);固定码长业务建议传实际位数 | 0 |
大于 0 的数值 |
| returnAllResults | boolean | 否 | 是否返回同一帧内全部识别结果(默认保持历史行为:每帧仅返回一个优先结果) | false |
true / false |
| continuous | boolean | 否 | 是否连续扫码 | false |
true / false |
| continuousIntervalMs | number | 否 | 连续扫码最小回调间隔(ms);仅 continuous=true 生效 |
0 |
大于等于 0 的数值 |
| showContinuousToggle | boolean | 否 | 是否在原生扫码全屏界面显示“单次/连续”切换按钮(Harmony 当前仅接收参数,不展示) | false |
true / false |
| enableAlbum | boolean | 否 | 是否在原生扫码全屏界面右上角显示相册入口(仅 startScan 生效) |
false |
true / false |
| enableTorch | boolean | 否 | 是否启用闪光灯 | false |
true / false |
| zoomRatio | number | 否 | 缩放倍率 | 1 |
大于 0 的数值 |
| nearFocusLock | boolean | 否 | 是否启用近距锁焦模式(Android / iOS);开启后点击聚焦会优先应用近距辅助缩放,适合约 5cm 场景 |
false |
true / false |
| showScanFrame | boolean | 否 | 是否显示红色扫码角框与中间透明框遮罩;传 false 时隐藏角框和遮罩仅保留扫描线 |
true |
true / false |
| showMaskOverlay | boolean | 否 | 是否显示扫码页黑色遮罩层;仅控制黑色遮罩显隐,不影响扫码框、扫描线、提示文案与操作按钮 | true |
true / false |
| scanFrameStyle | ScanFrameStyle | 否 | 扫码框样式配置(仅 Android / iOS);支持区域大小、圆角、顶角显隐与颜色定制 | null |
widthRatio / heightRatio / topRatio / cornerRadius / showCorners / borderColor / cornerColor |
| scanLineColor | string | 否 | 扫描线颜色 | #FF3B30 |
#RRGGBB / #AARRGGBB |
| tipText | string | 否 | 提示文案 | 请将二维码/条形码放入框内 |
任意字符串 |
| uiTextConfig | ScanUiTextConfig | 否 | 扫码页文案/图标配置,支持提示文案与“关闭/相册/手电”按钮图标文案自定义 | null |
tipIcon / tipText / closeIcon / closeText / albumIcon / albumText / torchIcon / torchOnText / torchOffText / torchUnsupportedText |
| uiImageConfig | ScanUiImageConfig | 否 | 扫码页图片图标配置(提示区/关闭/相册/手电),可与 uiTextConfig 组合使用 |
null |
tip / close / album / torchOn / torchOff / torchUnsupported |
| uiLayoutConfig | ScanUiLayoutConfig | 否 | 扫码页布局偏移配置,支持 close/album/continuous/torch/tip 的容器+图标+文字三层独立偏移 |
null |
close / album / continuous / torch / tip |
| showActionCapsuleBackground | boolean | 否 | 是否显示顶部/底部操作按钮胶囊背景(关闭/相册/单次连续/手电) | true |
true / false |
| beepOnScan | boolean | 否 | 成功识别时播放提示音;对相机扫码与相册识别都生效 | false |
true / false |
| vibrateOnScan | boolean | 否 | 成功识别时触发短震动;对相机扫码与相册识别都生效 | false |
true / false |
| overlayConfig | OverlayConfig | 否 | 遮罩事件透传配置(不加载 webview) | null |
enabled / htmlUrl / bridgeName / userAgentSuffix |
| permissionRationale | PermissionRationale | 否 | 权限说明文案 | null |
cameraTitle / cameraMessage / albumTitle / albumMessage |
扫码框样式配置(scanFrameStyle)
scanFrameStyle 仅作用于 Android / iOS 全屏原生扫码页:
- 不传时保持历史默认扫码框样式(零破坏兼容)。
- 比例参数使用 0~1 坐标系:
widthRatio / heightRatio / topRatio。 cornerRadius单位:Android 按dp、iOS 按pt。showScanFrame=false时,scanFrameStyle整体忽略。showCorners=false时隐藏四个顶角,仅保留边框与扫描线。- 字段缺失或非法值自动回退默认值,不抛错、不影响扫码链路。
参数结构:
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| scanFrameStyle.widthRatio | number | 否 | 扫码框宽度占屏宽比例 | 0.68 |
推荐 0.3~0.9 |
| scanFrameStyle.heightRatio | number | 否 | 扫码框高度占屏高比例 | 0.44 |
推荐 0.25~0.85 |
| scanFrameStyle.topRatio | number | 否 | 扫码框顶部占屏高比例 | 0.24 |
推荐 0.05~0.7 |
| scanFrameStyle.cornerRadius | number | 否 | 扫码框圆角(Android=dp,iOS=pt) | 14(Android)/18(iOS) |
大于等于 0 |
| scanFrameStyle.showCorners | boolean | 否 | 是否显示四个顶角;false 时仅显示边框与扫描线 |
true |
true / false |
| scanFrameStyle.borderColor | string | 否 | 框体描边色 | 平台默认色 | #RRGGBB / #AARRGGBB |
| scanFrameStyle.cornerColor | string | 否 | 四角线条色(仅影响四角,不影响边框) | 平台默认色 | #RRGGBB / #AARRGGBB |
默认行为示例(不传 scanFrameStyle):
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
LizhaoScanPro.startScan({
// 扫描会话 ID;后续 stop/pause/resume/destroy 需传同一个值
scannerId: 'main',
// 是否显示默认扫码框与遮罩层
showScanFrame: true,
// 识别成功回调(单次/连续结果都从这里返回)
onResult(res) {
console.log(res)
}
})
自定义样式示例:
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
LizhaoScanPro.startScan({
// 扫描会话 ID
scannerId: 'main',
// 启用扫码框渲染(scanFrameStyle 仅在此为 true 时生效)
showScanFrame: true,
scanFrameStyle: {
// 扫码框宽度占屏宽比例(0~1)
widthRatio: 0.72,
// 扫码框高度占屏高比例(0~1)
heightRatio: 0.5,
// 扫码框顶部距屏幕顶部比例(0~1)
topRatio: 0.2,
// 圆角:Android=dp,iOS=pt
cornerRadius: 22,
// 是否显示四个顶角
showCorners: true,
// 边框颜色(支持 #RRGGBB / #AARRGGBB)
borderColor: '#88FFFFFF',
// 顶角颜色(仅影响顶角,不影响边框)
cornerColor: '#FFFF6B6B'
},
// 识别成功回调
onResult(res) {
console.log(res)
}
})
黑色遮罩独立开关示例(仅隐藏黑色遮罩,保留扫码框/扫描线):
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
LizhaoScanPro.startScan({
scannerId: 'main',
// 继续显示扫码框与扫描线
showScanFrame: true,
// 仅隐藏黑色遮罩层
showMaskOverlay: false,
onResult(res) {
console.log(res)
}
})
扫码页文案/图标配置(uiTextConfig)
uiTextConfig 仅作用于 Android / iOS 全屏原生扫码页,支持以下三种配置方式:
- 仅图标:
{ closeIcon: "✕" } - 仅文案:
{ closeText: "返回" } - 图标 + 文案:
{ closeIcon: "✕", closeText: "关闭" }
提示:
tipText(旧字段)继续可用,若同时传uiTextConfig.tipText,优先使用uiTextConfig.tipText。torchIcon会同时作用于手电按钮的开/关/不支持文案。- 未配置的字段自动回退到默认文案,不影响历史行为。
扫码页图片图标配置(uiImageConfig)
uiImageConfig 仅作用于 Android / iOS 全屏原生扫码页,推荐配合 uiTextConfig 使用:
- 仅图标:只传图片图标,文本字段留空(例如
close只传src)。 - 仅文案:不传
uiImageConfig,只传uiTextConfig文案字段。 - 图标 + 文案:同时传
uiImageConfig与uiTextConfig对应字段。
图标来源支持:
- 本地文件路径(如
/storage/...、应用沙盒路径) file:///content://URIdata:image/*;base64,...- 应用静态资源路径(如
static/xxx.png、_www/static/xxx.png)
静态资源路径建议:
- 优先传
static/xxx.png或/static/xxx.png(推荐)。 _www/static/xxx.png也兼容,但通常不需要业务层手动加_www前缀。- 若业务需要固定为绝对路径,可在页面侧用
plus.io.convertLocalFileSystemURL('_www/static/xxx.png')转换后再传入。
本地 static 图片示例(Android / iOS):
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
function resolveUiImagePath(path: string): string {
// 统一路径分隔符并去除首尾空白
const normalized = String(path || '').replace(/\\/g, '/').trim()
if (!normalized) {
return normalized
}
// #ifdef APP-PLUS
try {
// 兼容传入 static/xxx.png 或 /static/xxx.png 的写法
const plusLocalPath = normalized.startsWith('_www/')
? normalized
: `_www/${normalized.replace(/^\/+/, '')}`
return plus.io.convertLocalFileSystemURL(plusLocalPath) || normalized
} catch (_) {
// plus 不可用时回退原始路径(例如非 App 环境文档预览)
return normalized
}
// #endif
return normalized
}
LizhaoScanPro.startScan({
// 扫描会话 ID
scannerId: 'main',
// 关闭按钮/相册/手电等按钮胶囊背景(适合 icon-only 风格)
showActionCapsuleBackground: false,
uiTextConfig: {
// icon-only 场景:文本置空
closeText: '',
albumText: '',
torchOnText: '',
torchOffText: '',
// 底部提示文案
tipText: '请对准二维码'
},
uiImageConfig: {
// 左上角关闭图标(src 支持 static/file/content/data URI)
close: { src: resolveUiImagePath('/static/logo.png'), width: 18, height: 18 },
// 右上角相册图标(需同时 enableAlbum=true 才会显示按钮)
album: { src: resolveUiImagePath('/static/logo.png'), width: 18, height: 18 },
// 手电开启态图标
torchOn: { src: resolveUiImagePath('/static/logo.png'), width: 16, height: 16 },
// 手电关闭态图标
torchOff: { src: resolveUiImagePath('/static/logo.png'), width: 16, height: 16 },
// 底部提示区图标
tip: { src: resolveUiImagePath('/static/logo.png'), width: 18, height: 18 }
}
})
tintColor 使用建议:
- 彩色图标建议不传
tintColor,保持原图颜色(iOS 默认alwaysOriginal渲染)。 - 单色图标需要统一着色时再传
tintColor(如#FFFFFF)。
参数结构:
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| uiImageConfig.tip | ScanUiImageItem | 否 | 提示区图标(底部提示) | null |
src / width / height / tintColor |
| uiImageConfig.close | ScanUiImageItem | 否 | 左上角关闭按钮图标 | null |
src / width / height / tintColor |
| uiImageConfig.album | ScanUiImageItem | 否 | 右上角相册按钮图标 | null |
src / width / height / tintColor |
| uiImageConfig.torchOn | ScanUiImageItem | 否 | 手电开启态图标 | null |
src / width / height / tintColor |
| uiImageConfig.torchOff | ScanUiImageItem | 否 | 手电关闭态图标 | null |
src / width / height / tintColor |
| uiImageConfig.torchUnsupported | ScanUiImageItem | 否 | 设备无手电筒能力时图标 | null |
src / width / height / tintColor |
ScanUiImageItem 字段说明:
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| src | string | 是 | 图标来源(建议 png/jpg/webp 或 data URI) | 无 | 无 |
| width | number | 否 | 图标宽度(Android 按 dp,iOS 按 pt) | 16~18(按位置默认) |
大于 0 的数值 |
| height | number | 否 | 图标高度(Android 按 dp,iOS 按 pt) | 16~18(按位置默认) |
大于 0 的数值 |
| tintColor | string | 否 | 图标染色 | null |
#RRGGBB / #AARRGGBB |
扫码页偏移配置(uiLayoutConfig)
uiLayoutConfig 仅作用于 Android / iOS 全屏原生扫码页,用于解决“隐藏胶囊后图标离边太远”场景:
- 坐标语义统一:
x > 0向右,y > 0向下;支持负值。 - 单位:Android 按
dp、iOS 按pt。 - 未传字段按
0处理,不影响旧布局。
参数结构:
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| uiLayoutConfig.close | ScanUiElementLayout | 否 | 左上角关闭按钮偏移配置 | null |
containerOffset / iconOffset / textOffset |
| uiLayoutConfig.album | ScanUiElementLayout | 否 | 右上角相册按钮偏移配置 | null |
containerOffset / iconOffset / textOffset |
| uiLayoutConfig.continuous | ScanUiElementLayout | 否 | 底部“单次/连续”按钮偏移配置 | null |
containerOffset / iconOffset / textOffset |
| uiLayoutConfig.torch | ScanUiElementLayout | 否 | 底部手电按钮偏移配置 | null |
containerOffset / iconOffset / textOffset |
| uiLayoutConfig.tip | ScanUiElementLayout | 否 | 底部提示区偏移配置 | null |
containerOffset / iconOffset / textOffset |
ScanUiElementLayout 字段说明:
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| containerOffset | ScanUiOffset | 否 | 元素整体偏移(容器位置) | { x: 0, y: 0 } |
x / y |
| iconOffset | ScanUiOffset | 否 | 图标相对容器偏移(无图标时忽略) | { x: 0, y: 0 } |
x / y |
| textOffset | ScanUiOffset | 否 | 文案相对容器偏移(无文案时忽略) | { x: 0, y: 0 } |
x / y |
顶栏 icon-only 贴边示例:
LizhaoScanPro.startScan({
// 建议固定 scannerId,便于在生命周期中释放资源
scannerId: 'main',
// 关闭按钮胶囊背景,配合 icon-only 更简洁
showActionCapsuleBackground: false,
// 文本置空,仅展示图标
uiTextConfig: {
closeText: '',
albumText: ''
},
// 细调顶部按钮位置(Android=dp,iOS=pt)
uiLayoutConfig: {
close: {
// 整个关闭按钮容器向左偏移
containerOffset: { x: -12, y: 0 },
// 图标相对容器再微调
iconOffset: { x: -2, y: 0 }
},
album: {
// 整个相册按钮容器向右偏移
containerOffset: { x: 12, y: 0 },
// 图标相对容器再微调
iconOffset: { x: 2, y: 0 }
}
}
})
胶囊背景控制:
- 默认
showActionCapsuleBackground=true,保持历史胶囊样式。 - 传
showActionCapsuleBackground=false时,顶部/底部操作按钮不渲染胶囊底和描边。 - 该开关对“关闭/相册/单次连续/手电”统一生效,适合纯图标视觉风格。
底层实现说明:
- Android:原生页按钮使用“容器 + 独立 icon/text 子视图”渲染,支持
containerOffset / iconOffset / textOffset三层偏移。 - iOS:原生页按钮使用
UIButton + UIImage渲染图标(支持尺寸缩放与tintColor,默认保持原图色彩,不受系统蓝色模板渲染影响)。 - 传参方式:统一通过
startScan({ uiImageConfig: {...} })传入,不需要额外调用其他 API。
回调说明(startScan)
| 参数 | 类型 | 必填 | 说明 | 默认值 | 可选参数 |
|---|---|---|---|---|---|
| options.onResult | function | 否 | 单次或连续扫码结果回调 | null |
无 |
| options.onError | function | 否 | 扫码失败回调 | null |
无 |
| options.onOverlayEvent | function | 否 | 原生遮罩事件回调(仅当 overlayConfig.enabled=true 时触发) |
null |
无 |
说明:startScan 结果流默认通过 options.onResult/options.onError 回调返回。options.onOverlayEvent 属于高级通道,需同时配置 overlayConfig.enabled=true 才会触发。
说明:不使用遮罩事件时,建议不要传 options.onOverlayEvent,可减少页面销毁后回调释放日志。
说明:returnAllResults=true 时,onResult(res) 的 res.results 会携带同帧多码结果;默认 false 保持单码回调行为。
说明:Android 相机扫码 / 相册识别、iOS 相机扫码支持 minBarcodeLength 一维条码最小长度过滤;Harmony 端按识别结果长度做回调前过滤。固定长度业务可直接过滤近距失焦、倾斜或轻微拖影时出现的短码片段;过滤后会优先返回码值更长的一维码,长度一致时再按识别框面积选择;Android 相机扫码还会在前 8 帧或约 1.2s 内合并一维码候选择长,避免 minBarcodeLength 小于实际码长时首帧短码立即回调;二维码等二维格式不受该字段影响。
说明:iOS 实时扫码默认先走 AVCaptureMetadataOutput 快速识别;当竖向、反光或轻微虚焦的一维条码没有 metadata 回调时,会低频抽取视频帧交给 Vision 兜底识别,成功后仍沿用原有 onResult、去重与 minBarcodeLength 过滤逻辑。
说明:连续扫码中同一数字 Code39/Code128 已返回较长结果后,后续短码子序列会被过滤,减少同一标签结果从 2000000031 回退到 20000031。
建议:业务已知码制时,优先通过 formats 只保留需要的格式;若码值固定长度,务必同时传 minBarcodeLength,例如本场景应传 formats: ['code39'], minBarcodeLength: 10,可过滤 20000031 这类短码片段且不会额外等待冷启动窗口。
建议:15 位纯数字 ITF / Interleaved 2 of 5 设备码场景应传 formats: ['itf'], minBarcodeLength: 15;iOS 会同时启用 itf14 与 interleaved2of5,避免只支持 ITF-14 导致普通 ITF 无法识别。
Android 对长而细的一维码提供小占比识别兜底:当 Code128、Code39、EAN、UPC、ITF 等条码占画面比例较小、整帧首轮没有结果时,插件会在后台对可见扫码区域做节流的裁剪放大重试;相册整图首轮无结果时也会对中心区域有限重试。该兜底不会替代清晰成像,工业平板仍建议让条码横向占扫码框宽度约 60%~80%、避免反光,并在已知码制时只传目标 formats。
Data Matrix / 反色 DM 码说明
识别 DM 码时建议显式传 formats: ['dataMatrix'],减少 Android ML Kit 与 iOS Vision 的无关码制候选。
Android Data Matrix 识别链包含中心裁剪与反色兜底,并已升级 Android 原生依赖以兼容 16 KB page size;ML Kit 帧回调在 cameraExecutor 执行并对 DM Bitmap 兜底节流,避免进入预览后出现预览卡顿,同时修复扫码成功后退出全屏界面时晚到回调触发 RejectedExecutionException 的闪退问题:
- 常规整帧识别无结果时,会按可视扫码框做中心裁剪后再次识别,让 DM 码更接近 ML Kit 输入图中心。
- 中心裁剪仍无结果时,会对裁剪图做反色重试,用于黑底白点这类反色 DM 码。
- 该增强仅在请求包含
dataMatrix或未限制码制时启用;未限制码制时已做低频兜底节流,不影响普通二维码/条形码主链路的实时预览。 - 反色 DM 码仍受清晰度、反光、焦距、打标质量影响;建议客户提供实物样张做 Android 真机确认。
示例:
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
LizhaoScanPro.startScan({
scannerId: 'dm-main',
// DM / Data Matrix 码制固定这样传,大小写需保持 dataMatrix
formats: ['dataMatrix'],
// 近距离小码建议开启近距锁焦
nearFocusLock: true,
// 现场较暗时可打开手电
enableTorch: true,
onResult(res) {
console.log('DM扫码结果', res.results[0].value)
},
onError(err) {
console.error('DM扫码失败', err)
},
onOverlayEvent(event) {
console.log('扫码诊断事件', event)
},
overlayConfig: {
enabled: true
}
})
连续扫描说明
continuous=true 时,扫码界面保持打开并持续返回 onResult,适用于批量入库、流水线录入等场景。
可选传 continuousIntervalMs 控制连续回调最小间隔(默认 0,即不额外延迟)。
当 continuousIntervalMs 传入负数或无效值时,按 0 处理。
建议:
- 业务侧对
onResult做去重或节流(插件内部会对相同码值做约900ms短窗口去重,但建议业务侧保底)。 - 在页面
onHide/onUnload中调用stopScan/destroyScanner主动释放会话。 - 若业务需要同帧多码,请传
returnAllResults=true并按res.results批量处理。
相册识别说明(Android / iOS / Harmony)
在 startScan(options) 中设置 enableAlbum=true 后,原生扫码界面右上角会显示相册按钮。
点击该按钮后会拉起系统图片选择器,识别成功后复用 startScan 的 onResult 回调返回结果。
Android / iOS 扫码页面按钮文案(Harmony 以系统页面为准):
- 左上角:
关闭 - 右上角(启用相册时):
相册 - 底部操作区(
showContinuousToggle=true时):单次扫码 / 连续扫码与打开手电筒 / 关闭手电同一行左右并排、整体居中 - 底部中间(
showContinuousToggle=false且设备支持闪光灯时):打开手电筒 / 关闭手电 - Android / iOS 支持点击扫码画面触发重新聚焦(启动时也会自动执行一次近景优先聚焦)
- Android / iOS 点击对焦时会显示短暂聚焦提示框,便于确认“已触发对焦”
- Android / iOS 传
nearFocusLock=true时,会保持近距辅助缩放策略,提升近距离(约5cm)对焦稳定性 - Android / iOS 传
showActionCapsuleBackground=false时,可关闭“关闭/相册/单次连续/手电”按钮胶囊背景
pickImageAndScan(options) 会拉起系统图片选择器,用户选图后由插件原生链路解析条码/二维码。
说明:
- Android 一般无需额外存储权限(使用系统 Picker 返回授权 URI);iOS 走系统相册选择器并受
NSPhotoLibraryUsageDescription约束;Harmony 走系统扫码页相册入口,权限与系统实现保持一致。 - 识别成功时会触发
options.onResult,并回调options.success/complete。 - 用户取消选择时返回
9010008。 - 图片中无可识别码时返回
9010006。 - 如需识别成功提示音/短震动,请在
startScan或pickImageAndScan中传beepOnScan/vibrateOnScan=true(默认false);Harmony 当前暂不承诺震动/提示音生效,以系统扫码页行为为准。 returnAllResults=true在 Android 相册识别生效;iOS/Harmony 相册识别当前仍返回单条结果。
示例:
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
LizhaoScanPro.pickImageAndScan({
// 扫描会话 ID(用于区分不同业务会话)
scannerId: 'main',
// 指定相册识别格式,减少无关格式扫描开销
formats: ['qrCode', 'code128', 'ean13'],
// 识别成功时播放提示音
beepOnScan: true,
// 识别成功时触发短震动
vibrateOnScan: true,
// 识别成功回调
onResult(res) {
console.log('album scan result', res)
},
// 识别失败回调(如 9010006 / 9010008)
onError(err) {
console.error('album scan error', err)
}
})
Overlay 事件类型
onOverlayEvent 回调事件结构:
{
type: string,
scannerId: string,
payload: any
}
| 事件 type | payload 类型 | 说明 |
|---|---|---|
| overlayReady | string | null | 遮罩事件通道初始化完成,payload 为 overlayConfig.htmlUrl(如未配置则为 null) |
| shown | null | 原生扫码全屏界面已显示 |
| cameraReady | null | 相机预览与分析链路已绑定完成 |
| albumClick | null | 点击右上角相册按钮后触发(需 enableAlbum=true) |
| albumRecognized | string | 通过右上角相册入口识别成功,payload 为识别字符串 |
| focusTapped | string | object | 点击预览触发聚焦后回传;Android 为 "x,y",iOS 为坐标对象 |
| recognized | string | 识别到码内容,payload 为识别字符串 |
| continuousChanged | object | 原生页模式切换事件,payload 固定为 { continuous: boolean, source: "nativeToggle" } |
| paused | null | 调用 pauseScan 后触发 |
| resumed | null | 调用 resumeScan 后触发 |
| buildInfo | string | Android / iOS 原生扫码页当前构建标识(用于确认真机运行到的插件版本) |
| error | string | 原生扫码过程异常,payload 为错误文本 |
| closed | null | 原生扫码界面已关闭并释放 |
| nativeMessage | any | 调用 postOverlayMessage 后回传的消息内容 |
说明:上述事件仅在 overlayConfig.enabled=true 时派发。
Overlay 事件处理示例
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
LizhaoScanPro.startScan({
// 扫描会话 ID
scannerId: 'main',
// 限定识别格式,提升识别稳定性和效率
formats: ['qrCode', 'code128'],
// 连续扫码模式(扫码页不会自动关闭)
continuous: true,
// 启用右上角相册入口
enableAlbum: true,
// 近距锁焦(Android/iOS)
nearFocusLock: true,
// 隐藏默认扫码框,仅保留扫描线
showScanFrame: false,
// 开启 overlay 事件通道(onOverlayEvent 才会触发)
overlayConfig: {
enabled: true
},
// 识别成功回调
onResult(res) {
console.log('scan result', res)
},
// 识别失败回调
onError(err) {
console.error('scan error', err)
},
// 原生层事件回调(shown/cameraReady/recognized/...)
onOverlayEvent(event) {
switch (event.type) {
case 'shown':
console.log('scanner shown')
break
case 'cameraReady':
console.log('camera ready')
break
case 'recognized':
console.log('recognized payload:', event.payload)
break
case 'continuousChanged':
console.log('mode changed:', event.payload)
break
case 'error':
console.warn('overlay error:', event.payload)
break
case 'closed':
console.log('scanner closed')
break
case 'nativeMessage':
console.log('native message:', event.payload)
break
default:
console.log('overlay event:', event)
break
}
}
})
返回值说明(ScanFrameResult)
| 字段 | 类型 | 说明 |
|---|---|---|
| scannerId | string | 扫描器 ID |
| timestamp | number | 时间戳 |
| frameIndex | number | 帧序号 |
| results | Array |
当前帧结果列表(默认 1 条;returnAllResults=true 时可返回多条) |
错误码说明
| 错误码 | 含义 | 说明 |
|---|---|---|
| 9010001 | platform unsupported | 当前平台不支持 |
| 9010002 | camera permission denied | 相机权限不足 |
| 9010003 | album permission denied | 相册权限不足 |
| 9010004 | camera init failed | 相机流程失败 |
| 9010005 | format not supported | 格式配置不合法 |
| 9010006 | album decode failed | 相册图片识别失败(无码或解码异常) |
| 9010007 | overlay reserved | 预留错误码,当前版本不触发 |
| 9010008 | scan aborted | 扫码被中止 |
| 9010009 | invalid options | 参数不合法 |
| 9010010 | system error | 系统异常 |
| 9010011 | scanner id occupied | scannerId 已被其他全屏或嵌入式组件会话占用 |
| 9010012 | scanner component unavailable | 组件未就绪、已销毁或目标组件不存在 |
权限说明
- Android:
CAMERA、FLASHLIGHT、VIBRATE(相册识别走系统 Picker,通常不需要额外存储权限) - iOS:
NSCameraUsageDescription、NSPhotoLibraryUsageDescription(扫码与相册识别) - Harmony:依赖系统扫码能力与系统权限弹窗策略
自定义基座说明
新增以下配置时需要自定义基座:
- Android CameraX / ML Kit 依赖
- AndroidManifest 权限
- Android
utssdk/app-android/ScanCaptureNative.kt原生链路变更 - iOS
Info.plist/PrivacyInfo.xcprivacy - iOS
utssdk/app-ios/index.uts、ScanIosBridge.swift或ScanEmbeddedNative.swift原生链路变更 - Android/iOS
utssdk/*/index.vuecomponent-uts 原生组件入口变更
本插件 Android 端严格使用自研链路(UTS + CameraX + ML Kit),不依赖 Barcode/uni-barcode-scanning 模块兜底。
如果直接使用普通调试基座运行,Android 侧可能出现 androidx.camera.* 或 com.google.android.gms.tasks.* 类缺失,表现为无法打开扫码界面。此时必须重新编译并安装自定义基座或原生安装包。
当前发布包会通过 buildInfo 返回运行诊断标识。完全自由布局原生扫码组件保留 Android 全屏关闭与迟到回调隔离、JS 回调桥接、组件事件主线程派发,以及“嵌入式自由布局 → 原生全屏 → 返回恢复”示例;当前发布同时包含 iOS 聚焦、Vision 视频帧和 Swift 类型桥接兼容修复。Android 16 KB page size、预览卡顿、扫码成功后退出闪退、HarmonyOS 4.2 Android 兼容层桥接修复、参数强转修复与本次组件会话均涉及自定义基座内的原生/平台代码;Android/iOS 发布或真机验证前需要重新原生联编、重新云打包或制作并安装对应平台当前插件自定义基座/正式包。全屏链路可通过 onOverlayEvent 的 buildInfo 确认真机运行构建,组件可通过 ready/getState() 的 buildInfo 确认嵌入式实现构建。
Android 16 KB page size 说明:
- 旧版
androidx.camera:camera-core:1.3.3会带入libimage_processing_util_jni.so,旧版com.google.mlkit:barcode-scanning:17.2.0会带入libbarhopper_v3.so;这两个库在旧调试包中均可能表现为 4 KB ELF 对齐,从而触发 Google Play 的 16 KB page size 检查。 - 当前版本已将 CameraX 升级到
1.4.2,将 ML Kit Barcode Scanning 升级到17.3.0;重新打包后应以最终 AAB/APK 中的.so为准再次验证。 - 只替换
uni_modules文件但不重新制作 Android 自定义基座,设备和上架包仍可能使用旧.so,无法解决 Google Play 拦截。
建议步骤:
- 在 HBuilderX 执行「运行 -> 运行到手机或模拟器 -> 制作自定义基座」。
- 卸载设备上旧基座后安装新基座(避免继续复用旧依赖)。
- 清理项目编译缓存后重新运行。
完整组合示例(uni-app)
如果已经跑通前面的最小示例,可以参考下面的组合写法一次性启用多码识别、连续扫码、相册入口、自定义文案和图片图标。完整页面示例可查看 uni_modules/lizhao-scan-pro/example/index.vue 或项目页面 uni_modules/lizhao-scan-pro/example/uniapp/scan.vue。
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
const info = uni.getSystemInfoSync()
const platform = String(info.platform || '').toLowerCase()
if (platform !== 'android' && platform !== 'ios' && platform !== 'harmony') {
console.warn('lizhao-scan-pro 当前仅支持 Android/iOS/Harmony')
return
}
LizhaoScanPro.startScan({
// 扫描会话 ID;建议页面内固定值
scannerId: 'main',
// 仅识别二维码和 code128
formats: ['qrCode', 'code128'],
// 同帧多码回传(默认 false)
returnAllResults: true,
// 连续扫码模式
continuous: true,
// 连续回调最小间隔(ms)
continuousIntervalMs: 500,
// 显示“单次/连续”切换按钮
showContinuousToggle: true,
// 隐藏操作胶囊背景(保留按钮能力)
showActionCapsuleBackground: false,
// 显示相册入口
enableAlbum: true,
uiTextConfig: {
// 底部提示图标(emoji 或文本图标)
tipIcon: '📷',
// 底部提示文案
tipText: '请对准二维码',
// 左上角关闭按钮图标/文案
closeIcon: '✕',
closeText: '关闭',
// 右上角相册按钮图标/文案
albumIcon: '🖼',
albumText: '相册',
// 底部手电按钮图标/开关文案
torchIcon: '🔦',
torchOnText: '关闭手电',
torchOffText: '打开手电筒'
},
uiImageConfig: {
// 可传本地路径、file://、content://、data URI、static 资源路径
// 彩色图标建议不传 tintColor;需要统一单色时再传 tintColor(如 #FFFFFF)。
close: { src: 'static/scan-icons/close.png', width: 18, height: 18 },
album: { src: 'static/scan-icons/album.png', width: 18, height: 18 },
torchOn: { src: 'static/scan-icons/torch-on.png', width: 16, height: 16 },
torchOff: { src: 'static/scan-icons/torch-off.png', width: 16, height: 16 },
tip: { src: 'static/scan-icons/tip.png', width: 18, height: 18 }
},
// 识别成功回调(returnAllResults=true 时 res.results 可能有多条)
onResult(res) {
res.results.forEach((item) => {
console.log(item.value, item.format)
})
},
// 识别失败回调
onError(err) {
console.error(err)
}
})
页面生命周期建议
onHide() {
// 页面不可见时先停扫码(避免后台占用相机)
LizhaoScanPro.stopScan({ scannerId: 'main' })
}
onUnload() {
// 页面销毁时彻底释放扫描器资源
LizhaoScanPro.destroyScanner({ scannerId: 'main' })
}
完整组合示例(uni-app x)
uni-app x 同样通过插件根目录导入 API。下面示例展示扫码启动后再动态控制手电和缩放,适合需要在页面按钮中调整扫码状态的场景。
import * as LizhaoScanPro from '@/uni_modules/lizhao-scan-pro'
const info = uni.getSystemInfoSync()
const platform = String(info.platform || '').toLowerCase()
if (platform !== 'android' && platform !== 'ios' && platform !== 'harmony') {
console.warn('lizhao-scan-pro 当前仅支持 Android/iOS/Harmony')
return
}
LizhaoScanPro.startScan({
// 扫描会话 ID(需与后续 setTorch/setZoom 一致)
scannerId: 'main',
// 开启连续扫码,便于演示动态调参
continuous: true,
// 显示手电按钮(部分业务可选)
enableTorch: true,
onResult(res) {
console.log('uni-app x scan result', res)
},
onError(err) {
console.error('uni-app x scan error', err)
}
})
// 动态打开手电(仅设备支持时生效)
LizhaoScanPro.setTorchEnabled({
scannerId: 'main',
enabled: true
})
// 动态设置缩放倍率(>0)
LizhaoScanPro.setZoomRatio({
scannerId: 'main',
zoomRatio: 1.8
})
// 页面离开时释放资源(示例)
// onHide() {
// LizhaoScanPro.stopScan({ scannerId: 'main' })
// }
// onUnload() {
// LizhaoScanPro.destroyScanner({ scannerId: 'main' })
// }
联系方式
信-微:l-z-1-8-7-1512-5421(-去掉,不这样写会被和谐)
发布前自检清单
- 确认插件仅通过
@/uni_modules/lizhao-scan-proAPI 入口调用。 - 确认未使用
<lizhao-scan-pro />模板标签或common/*.js旧入口。 - 确认 Android 自定义基座已重建并重装(含 CameraX + ML Kit 依赖)。
- 确认扫码页在 Android 真机可拉起全屏原生界面并返回结果。
- 确认 iOS 与 Harmony 真机均可拉起扫码并返回结果;Web/小程序路径返回
9010001且文档说明一致。
作者系列UTS插件
以下为已在 DCloud 插件市场上架的作者系列 UTS 插件,可按业务场景组合使用。未列出的插件表示当前未确认公开市场页,后续上架后再补充。
| 插件 | 能力方向 | 插件市场 |
|---|---|---|
lizhao-nfc-pro |
NFC 标签读写、NDEF、IsoDep 与诊断 | 查看插件 |
lizhao-float-window |
悬浮窗、画中画、权限与诊断 | 查看插件 |
lizhao-device-id |
设备标识、隐私策略与诊断 | 查看插件 |
lizhao-scan-pro |
原生扫码、连续扫码、相册识别 | 查看插件 |
lizhao-choose-file |
原生文件选择、上传、进度与取消 | 查看插件 |
lizhao-bg-audio |
背景音频播放、队列、倍速与事件 | 查看插件 |
lizhao-smart-tts |
系统 TTS、云端合成、听书方案 | 查看插件 |
lizhao-share-plus |
系统分享、远程文件下载后分享 | 查看插件 |
lizhao-sqlite-pro |
原生 SQLite、迁移、备份与诊断 | 查看插件 |
lizhao-icon-pro |
SVG 图标组件、多主题与缓存 | 查看插件 |
lizhao-cast-screen |
DLNA 投屏、AirPlay 路由入口 | 查看插件 |
lizhao-call-kit |
电话、短信、通讯录原生能力 | 查看插件 |
lizhao-app-keepalive |
应用保活、唤醒、自愈与报告 | 查看插件 |
lizhao-doc-corrector |
文档扫描、矫正、增强与识别 | 查看插件 |
lizhao-emu-detect |
模拟器环境检测、风险评分与证据 | 查看插件 |
lizhao-gallery-pro |
相册媒体分页、筛选、缩略图与导出 | 查看插件 |
lizhao-video-thumb |
视频封面、批量取帧与 Base64 返回 | 查看插件 |
lizhao-ble |
BLE 扫描、连接、读写、通知与自动重连 | 查看插件 |
lizhao-sse-pro |
SSE、Line、JSONL 与 Raw 流式请求 | 查看插件 |

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 6485
赞赏 5
下载 12494566
赞赏 1939
赞赏
京公网安备:11010802035340号