更新记录

1.0.0(2026-10-20) 下载此版本

首次发布:三端统一的原生扫码

  • 对外 API 两个,都是 uni 风格(options 入参带 success / fail / complete,返回 void,不返回 Promise):scan(options) 拉起当前端的原生扫码界面并回一个码值;getEnvInfo() 同步查询端标识、系统版本、相机可用性与授权状态。Web 与小程序走 utssdk/index.uts 兜底,getEnvInfo 照常降级返回、scan 回 9050009。
  • 三端实现:Android 走华为 ScanKit com.huawei.hms:scanplus:2.12.0.301(ScanUtil.startScan + Activity result 解析,原生细节留在混编 scankit.kt);iOS 走插件内置的 ScanKitFrameWork.framework(HmsDefaultScanViewController,桥接层 HmsScanKit.swift);HarmonyOS 走系统 @kit.ScanKit 的 scanBarcode.startScanForResult,零厂商配置。
  • 契约收敛为「一次交互一个码值」:连续扫码/多码结果在三端的界面与回调时序差异过大,强行对齐会让业务侧仍需判端,因此由业务侧循环调用,插件保证每次调用都是一个完整的用户交互。
  • 三端行为归一(跨端纯逻辑集中在 core.uts,无任何平台 API):三端之间只对接归一化码型名,scanTypeCode 用的是本插件统一编号(取自鸿蒙 scanCore.ScanType,与 iOS HmsScanFormat 位序 +1 一致;华为 Android 的 HmsScan.*_SCAN_TYPE 是另一套位掩码,换算全部封闭在 app-android/scankit.kt 内,入参走 setHmsScanTypes);鸿蒙侧因 ScanOptions.scanTypes 是名义枚举,另写「名字 ↔ 枚举成员」显式对照表;iOS 把框架短名映射回归一化名并按位掩码过滤。厂商原始码一律归类到 9050 段位、原值拼进 errMsg,业务侧可不分端地写 switch。
  • 权限最小化:Android 由 scan 内部按机型 API 与宿主 targetSdkVersion 的较低档分档申请(≥34 选择性媒体访问三件套、≥33 只要 READ_MEDIA_IMAGES、更低用 READ_EXTERNAL_STORAGE),enableAlbum: false 时只申请相机;取较低档是因为系统判定媒体权限能否申请看的是宿主 targetSdk,在 targetSdk < 33 的宿主上申请 READ_MEDIA_IMAGES 会抛 IllegalStateException 并被框架转成 JS 异常(fail 不回、单飞槽位卡死),所以低 target 的宿主自动走旧权限并对申请调用做了 try/catch 复位;iOS 同样由 scan 代管——notDetermined 时直接弹系统授权框(授权 API 走混编层,info.plist 随插件提供 NSCameraUsageDescription,文件名必须小写),被拒或 denied/restricted 才回 9050003 并给出去设置里的路径;HarmonyOS 的相机与相册权限由系统扫码界面自理,宿主无需声明 ohos.permission.*。
  • 错误码占 9050 段位,9050001–9050007 + 9050009 全部登记文案,序号 009 固定表示平台不支持。ScanFailImpl 的入参是 number 并在唯一收口点判段内/段外(段外归 9050006、原始数留在 errMsg),因为数字字面量联合类型只有 ArkTS 会硬判。
  • iOS 端两条框架级限制如实记录在 readme,不做假成功:① DefaultScanDelegate 无取消回调,用户返回/侧滑关闭界面时插件收不到任何信号,只能在下一次调用时用「上一会话仍挂着」回 9050005;② 内置框架版本不开放相册识别入口,enableAlbum 在 iOS 上不生效、getEnvInfo().albumSupported 固定 false。
  • 案例页 example/index.vue 首屏是「最近扫码内容」大字块:每次成功的码值原文直接显示在顶部,副标题带码型名、统一编号与长度;下面是环境检测块与按本端能力显示的扫码按钮(相册按钮只在 albumSupported 的 Android/HarmonyOS 出现,一码多物只在 HarmonyOS 出现,iOS 只有实时扫码)。本插件是原生模态「一次交互一个结果」,取消与失败由界面自己收,因此按规则 §7 的 scenario-exempt 豁免单独演示失败路径与生命周期,但每个按钮下方仍如实显示 fail 回来的 errCode/errMsg。
  • 补 license.txt:免费 + 插件市场广告解锁口径,写明使用权与源码权、禁止再分发、支持边界(宿主包名/签名/开放平台资质不在支持范围)与免责;条款内不写死任何价格数字。
  • 上架前需人工补齐:package.json 的 repository 与 contact.qq;Android 验收必须用云端自定义调试基座(本地标准基座不解析 Maven 依赖,报「找不到名称 com.huawei」属环境限制)。

平台兼容性

uni-app(4.75)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ - - √ √ 9.0 √ 12
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × ×

uni-app x(4.75)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - 9.0 √ 12 ×

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

bear-scan

一行调用拉起原生扫码界面,回一个码值:Android 走华为 ScanKit(com.huawei.hms:scanplus)、 iOS 走内置的华为 ScanKit Framework、HarmonyOS 走系统 @kit.ScanKit,Web / 小程序返回「平台不支持」而不是崩。 是对华为统一扫码服务薄封装:界面、解码、权限全部由各端的 ScanKit 承担,插件只做统一契约 + 错误码归一 + 三端行为对齐。

本插件免费,以插件市场的「观看广告解锁下载」方式提供(广告位由市场承载,插件内不含任何广告 SDK 与请求); 权利边界、支持范围与免责见 license.txt。

兼容性

端 支持 实现方式 最低版本 相册选图 备注
Android √ 华为 ScanKit com.huawei.hms:scanplus:2.12.0.301 API 28(Android 9) √ 需云端自定义基座,见「使用前提」1
iOS √ 插件内置 ScanKitFrameWork.framework iOS 11 × 框架无取消回调,见「实现要点」2
HarmonyOS √ 系统 @kit.ScanKit startScanForResult API 12 √(系统界面自带) 无需任何厂商配置
Web x utssdk/index.uts 兜底 — — scan 返回 9050009,getEnvInfo 照常返回
各小程序 x 同上 — — 同上

版本数值与 package.json 的平台矩阵、各端 config.json 逐项一致;uni-app 与 uni-app-x 共用同一份原生实现,矩阵取值相同。

使用前提

  1. Android 必须用云端自定义调试基座验收。config.json 声明了 Maven 依赖(scanplus),本地标准基座不解析这类依赖, 直接「运行到手机或模拟器」会在生成的 index.kt 报 找不到名称“com.huawei” 并附官方 known-issue 链接——这是环境限制, 不要为此改动依赖声明(依据:DCloud 官方「带第三方依赖的 UTS 插件需云端编译」口径)。
  2. 包名与签名:Android 的 AndroidManifest.xml 里 package="com.bear.plugins.scan" 只是插件的清单片段名,会被合并进宿主清单, 不需要与宿主 applicationId 一致;ScanKit 的本地解码不校验应用身份,无需 agconnect-services.json。 如果你同时要接华为登录/支付等要求应用注册的服务,请在宿主工程侧配置,与本插件无关。
  3. 相机权限:Android 与 iOS 都由本插件在 scan() 内部申请,无需宿主重复申请。 Android 清单已声明、含相册选图所需的媒体权限分档;iOS 在「从未申请过」时直接弹系统授权框(info.plist 随插件提供 NSCameraUsageDescription),被拒后系统不会再弹,此时回 9050003 并在文案里给出去设置里开启的路径; HarmonyOS 的相机与相册权限由系统扫码界面自理,宿主无需声明任何 ohos.permission.*。
  4. iOS 模拟器不可用:内置 framework 只含真机架构,扫码这类相机能力本身也必须真机验收。
  5. Android 相册选图依赖媒体权限:enableAlbum 为 true(默认)时才会申请图片/视频读取权限;只用实时扫码时传 enableAlbum: false 可以只申请相机权限,做到权限最小化。 权限档位按机型 API 与宿主 targetSdkVersion 中较低的那一档取:宿主 targetSdkVersion ≥ 33 才申请 READ_MEDIA_IMAGES(≥ 34 再加 READ_MEDIA_VIDEO + READ_MEDIA_VISUAL_USER_SELECTED),低于 33 时自动退回 READ_EXTERNAL_STORAGE(系统按兼容模式授予媒体读取,相册选图仍可用)。因此低 targetSdk 的老宿主也能直接用, 不需要为接入本插件而升级;想让高版本机型走选择性媒体访问(用户可只勾选部分照片),就把宿主 targetSdkVersion 提到 34。

快速开始

<template>
    <button @click="doScan">扫码</button>
    <text>{{ text }}</text>
</template>

<script setup>
    import { ref } from 'vue'
    import { scan, getEnvInfo } from '@/uni_modules/bear-scan'

    const text = ref('未扫码')

    function doScan() {
        scan({
            enableAlbum: true,
            success: (res) => {
                text.value = `${res.data.scanType}|${res.data.originalValue}`
            },
            fail: (err) => {
                text.value = `code=${err.errCode}:${err.errMsg}`
            }
        })
    }

    // 进入页面先看清自己在哪个端、相机是否可用
    console.log(getEnvInfo())
</script>

import 只写到插件根目录(@/uni_modules/bear-scan),MUST NOT 指到 utssdk/ 内部文件,否则插件升级就会断。

API

scan(options)

拉起当前端的原生扫码界面,识别到一个码值后关闭界面并回调。无返回值(不返回 Promise),结果只通过回调给出。

参数 类型 默认值 说明
viewType number 0 仅 Android:取景框样式,透传 ScanKit 的 setViewType;iOS / 鸿蒙使用系统界面,本字段忽略
enableAlbum boolean true 是否允许从相册选图识别。Android 据此决定是否申请媒体权限;HarmonyOS 透传系统界面的 enableAlbum;iOS 当前内置框架版本未开放该入口,无论传什么都只有实时扫码
enableMultiMode boolean false 一码多物(同屏多个码)识别,仅 HarmonyOS 生效;Android 走 ScanUtil.startScan 单码路径,本字段被忽略
scanTypes Array\<string> | null null 只识别指定码型,元素取自下方码型清单;不传或传空数组表示不限;全部非法时回 9050001
success (res : ScanResult) => void — 识别成功
fail (err : ScanFail) => void — 失败,errCode 为 9050xxx
complete (res : any) => void — 成功回 ScanResult、失败回 ScanFail,两条路径都必定触发一次

成功回调的 ScanResult:

字段 类型 说明
code number 固定 0,与 fail 分支的 9050xxx 成对,便于业务统一判 code
msg string 结果描述
data ScanData | null 码值;success 分支必定非空

ScanData:

字段 类型 说明
originalValue string 码值原文
scanType string 三端归一化后的码型名(下表取值),识别不出时为 FORMAT_UNKNOWN
scanTypeCode number 统一口径的码型编号(取自鸿蒙 scanCore.ScanType,与 iOS 框架位序 +1 一致);识别不出码型时三端都回 -1,与华为 HmsScan 常量的数值无关

失败回调的 ScanFail 继承 UniError:errSubject 固定为 bear-scan,errCode 见「错误码」表, errMsg = 基础文案 + 厂商原始码/文案(Android 的 ScanUtil 结果码与鸿蒙的 BusinessError.code 都不占 本插件段位,只拼在文案里;iOS 内置框架不回错误码)。

getEnvInfo()

同步查询运行环境与相机状态,开销极小,可在页面 onLoad 里直接调用。返回 ScanEnv:

字段 类型 说明
platform string 分端文件里是常量 android / ios / harmony;Web 与小程序走兜底,回的是 uni.getSystemInfoSync().osName(如 ios / android / windows)而不是 web / mp,因此判「有没有原生扫码」请用 cameraAvailable(兜底端固定 false)
systemVersion string Android 为 API level,iOS 为系统版本,鸿蒙为 osFullName,兜底端为 osVersion
deviceBrand string 设备品牌:Android 取 Build.BRAND、鸿蒙取 deviceInfo.brand,取不到时为 '';iOS 走的是 UIDevice.current.model,回的是 iPhone / iPad 这类机型名而不是品牌
cameraAvailable boolean 相机硬件是否可用(Android 查 FEATURE_CAMERA_ANY;iOS / 鸿蒙固定按有相机处理)
cameraAuthorization string granted / denied / restricted / notDetermined / unknown。只有 iOS 会真实查询;Android 由 scan 内部按需申请、鸿蒙由系统界面自理,两端固定回 unknown
albumSupported boolean 本端是否支持相册选图:Android 与相机可用性同值,iOS 因内置框架未开放入口如实回 false,鸿蒙固定 true

码型清单

scanTypes 的入参取值与结果里的 scanType 使用同一套名字,三端一致:

FORMAT_UNKNOWN、AZTEC_CODE、CODABAR_CODE、CODE39_CODE、CODE93_CODE、CODE128_CODE、 DATAMATRIX_CODE、EAN8_CODE、EAN13_CODE、ITF14_CODE、PDF417_CODE、QR_CODE、 UPC_A_CODE、UPC_E_CODE、MULTIFUNCTIONAL_CODE、ONE_D_CODE、TWO_D_CODE

中间 13 个一维/二维码型(AZTEC_CODE … UPC_E_CODE)三端都能精确过滤;MULTIFUNCTIONAL_CODE 只有 Android 与 HarmonyOS 能过滤,ONE_D_CODE / TWO_D_CODE 只有 HarmonyOS 能,FORMAT_UNKNOWN 是结果侧 「码型未知」的占位名、不作为过滤条件。传了本端过滤不了的项时不会报 9050001(它仍是合法名字), 而是在换算各端掩码/枚举时被丢弃,全部丢弃后降级为「不限码型」;要按端精确过滤请只传单条码型名。

实现要点与注意事项

三端行为差异速查(细节见下面的编号条目):

行为 Android iOS HarmonyOS
用户取消 9050004(RESULT_CANCELED 或无结果数据) 无任何回调,下一次 scan 才回 9050005 9050006(系统只给 BusinessError,不与失败区分)
相册选图 enableAlbum 同时控制入口与媒体权限申请 忽略,恒实时扫码 透传系统界面的 enableAlbum
一码多物 忽略(单码路径) 忽略 enableMultiMode 生效
取景框样式 viewType 透传 ScanKit setViewType 忽略(框架界面) 忽略(系统界面)
相机权限 scan 内部申请 scan 首次弹系统授权框 系统扫码界面自理
码型过滤入参 名字 → HmsScan 常量 名字 → 统一编号 → 位掩码 名字 → scanCore.ScanType 成员
  1. 三端只做「一个码值」。华为 ScanKit 支持连续扫码与多码结果,但三端的连续模式在界面能力、 生命周期与回调时序上差异很大(Android 是 Activity result、iOS 是代理回调、鸿蒙是系统页面), 强行对齐会让业务代码仍需判端。因此本插件把契约收敛成「拉起一次 → 回一个码值」, 连续扫码请由业务侧循环调用(每次都是一个完整的用户交互)。
  2. iOS 没有「取消」回调(本插件最容易踩的一条)。内置 ScanKitFrameWork 的 DefaultScanDelegate 只有「识别到码」与「相册图片结果」两个方法,头文件里没有取消通道,用户按返回/侧滑关闭界面时 插件收不到任何信号,complete 也不会触发。插件的应对是:把当前会话对象持在单例的一个槽位里, 下一次 scan 发现上一会话仍挂着时回 9050005 并在文案里提示重新发起——业务侧 MUST 把 「iOS 上用户可能取消且没有任何回调」当成正常情况,不要靠超时自己造取消。
  3. iOS 的对象持有走单例槽位而不是 Map。原实现用 Map<string, any> 缓存扫码对象、结算后不清理, 每扫一次泄漏一个实例;现在结算路径(成功、初始化失败、下一次调用前的挂起检查)都会把槽位置空。
  4. iOS 的相机授权由插件在首次扫码时代为申请。scan() 先读授权状态:granted 直接扫, notDetermined 调 AVCaptureDevice.requestAccess 弹系统框(回调在系统子线程,混编层里已统一切回主线程), 被拒或 denied/restricted 才回 9050003。授权 API 必须放在混编层 HmsScanKit.swift: UTS 转译器会把 AVCaptureDevice.authorizationStatus(for:) 这类 Swift API 原样透传,写在 index.uts 里就是非法调用。 另外 NSCameraUsageDescription 缺失时系统不是报错而是直接杀进程,所以 utssdk/app-ios/info.plist 随插件提供,不可删; 文件名必须是小写 info.plist(云端按字面名读取,Info.plist 不会被合并进宿主工程)。
  5. Android 的结果解析留在 Kotlin。data.getParcelableExtra(ScanUtil.RESULT) 返回的是 Parcelable, UTS 侧无法安全地转成 HmsScan,因此解析放在 scankit.kt,与 UTS 之间只走定长字符串数组: 第 2 位回的是归一化码型名(不是 HmsScan 的常量数值,理由见第 11 条), 码值不能带 \0,且 UTS→Kotlin 的 Number(str) 会按「位累加」而不是十进制解析(编译器已知坑), 所以所有数值都在 Kotlin 侧用 String.toInt() 转好后以字符串传回。
  6. Activity result 监听按会话登记、结算即注销。原实现在 handleActivityResult 内部无条件 offAppActivityResult,会把别的插件正在等的 Activity result 一起摘掉;现在只有 requestCode 属于本插件时才注销,并且登记/注销都收敛在桥接层的一对私有函数里。
  7. 权限清单按「机型 API 与宿主 targetSdkVersion 的较低档」分档,且随 enableAlbum 收缩。 Android 34+ 走选择性媒体访问(READ_MEDIA_IMAGES + READ_MEDIA_VIDEO + READ_MEDIA_VISUAL_USER_SELECTED 三件套,只申请 IMAGES 时相册面板看不到可选内容),33 只要 IMAGES,≤32 用 READ_EXTERNAL_STORAGE; 不使用相册时只申请相机权限。分档不能只看机型:系统判定某个媒体权限能不能申请看的是宿主 targetSdkVersion,在 targetSdk < 33 的宿主上申请 READ_MEDIA_IMAGES 会抛 IllegalStateException: Request android.permission.READ_MEDIA_IMAGES permission, The targetSdkVersion SDK must be 33 or more, 而这个异常由 UTSAndroid.requestSystemPermission 抛出后被 uni 框架转成 JS 异常——插件的 fail 一个都不回, 业务侧只看到控制台报 targetMethod error,且单飞槽位会永久占住(之后每次都回 9050005)。 现在取 min(机型, targetSdk) 选档,低 target 的宿主自动退回 READ_EXTERNAL_STORAGE, 另外把申请调用包在 try/catch 里兜底复位。原实现漏掉了 34+ 的 VIDEO,并存在「权限被拒时既不 fail 也不 complete」 的分支,现已全部走统一收口。
  8. 厂商错误码一律归类,不占用段位。Android ScanUtil 回传的 RESULT_CODE 原值、鸿蒙 BusinessError.code(100270xxxx 一类)等只拼进 errMsg,对外只暴露 9050 段位的 8 个码, 业务侧可以不分端地写 switch(iOS 内置框架只有结果回调、不回错误码,因此本端不产生这类原始码)。 归类只承认官方 SDK 中确认存在的三个常量(SUCCESS / RESULT_CODE / ERROR_NO_READ_PERMISSION), 其余按语义标签走,避免猜常量名导致云端编译失败。
  9. 鸿蒙不区分「取消」与「识别失败」。系统 startScanForResult 只回一个 BusinessError, 厂商码里取消与失败混在一起,本端不猜常量名、不硬判取消,一律归 9050006 并把鸿蒙原始码留在 errMsg。 业务侧若要区分「用户放弃」与「真的扫不出」,在 Android 判 9050004,在鸿蒙只能按「没有结果」统一处理。
  10. 数字字面量联合类型只有 ArkTS 会硬判(type ScanErrorCode = 9050001 | …)。厂商原始码与 options 里回填的数字一律先经 core.uts 的 toScanErrorCode / ScanFailImpl 收口(段内保留、段外归 9050006 并把原数留在文案里),三端才能同时编译通过。
  11. 跨端只对接码型名,编号是本插件自定义口径。scanTypeCodeOf 在 core.uts 只写一遍:编号取自鸿蒙 scanCore.ScanType(API 10 起的命名:1–14 各码型、100 一维集合、101 二维集合),iOS 内置框架的 HMSScanFormatTypeCode 是位掩码、位序 +1 后与这套编号逐项一致。Android 的 HmsScan.*_SCAN_TYPE 属于另一套位掩码(QR=1、AZTEC=2、DATAMATRIX=4 … CODABAR=4096、ALL=8191),与上面两个编号体系都不能 按数值对接,否则 QR 会被读成 AZTEC;因此跨桥只传码型名,掩码换算与结果反查全部留在 app-android/scankit.kt,入参用 HmsScanAnalyzerOptions.Creator.setHmsScanTypes(int, int...)(ScanKit 无 setScanType)。 鸿蒙的 ScanOptions.scanTypes 声明为 Array<scanCore.ScanType>,枚举是名义类型、也不能按字符串动态取成员, 所以 app-harmony/index.uts 里额外写了「名字 ↔ 枚举成员」两张显式对照表,两端都不做枚举与 number 的隐式转换。 注意 API 9 的鸿蒙枚举是另一套命名(BARCODE_128、PDF417_2D…),本插件最低到 API 12,不兼容那套。 scanTypeCode 只作辅助,识别不出码型时三端一致回 -1,业务判据请用 scanType。
  12. 体积与来源:Android 依赖由云端从 Maven 拉取(scanplus 2.12.0.301,非华为设备可用的完整解码版本, 不含基础版的机型限制);iOS 的二进制随插件提供——Frameworks/ScanKitFrameWork.framework + Resources/ScanKitFrameWorkBundle.bundle(约 4.5 MB,来源为华为 ScanKit iOS 官方 SDK 发布包, LICENSE 随 framework 目录保留)。插件不采集任何数据、不含任何网络请求,也不会从远端拉取原生代码。

错误码

段位 9050,序号 009 固定表示「当前平台不支持」;与 utssdk/unierror.uts 逐项一致。

errCode 说明
9050001 扫码参数错误:scanTypes 全部不是本插件支持的码型名(三端都在申请权限、拉起界面之前先校验,不会为了报参数错而先弹授权框)
9050002 扫码界面初始化失败:无相机、无可用 Activity/VC,或原生界面拉不起来
9050003 相机权限被拒绝:Android 申请被拒/永久拒绝,或权限清单与宿主 targetSdkVersion 不匹配导致申请失败;iOS 首次由插件弹框被拒,或此前已拒绝(系统不再弹框,需去设置里开启)
9050004 用户取消扫码:仅 Android(返回键/侧滑关闭扫码界面);iOS 因框架无取消回调不会产生此码,HarmonyOS 的取消按 9050006 回
9050005 扫码被中断:上一次扫码尚未结束(iOS 含「上一会话仍挂着」)
9050006 二维码识别失败:取到画面但解不出码,或厂商未归类错误(原始码拼在 errMsg 里);HarmonyOS 的用户取消也归这一码
9050007 相机或扫码界面被占用:预留语义,当前三端都不会主动回此码(Android ScanUtil.startScan 的非 0 返回值不猜厂商常量,统一按 9050002 回并把原值留在 errMsg)
9050009 当前平台不支持:Web、小程序等没有原生实现的端

目录结构

bear-scan/                          # 独立工程(HBuilderX 直接导入即可跑案例,不参与发布)
├── pages.json                      # 首屏即案例页 uni_modules/bear-scan/example/index
├── manifest.json                   # 案例工程配置:name 必须等于插件 id,工程侧只声明相机+震动(插件所需媒体权限在 app-android/AndroidManifest.xml)
├── main.js / App.vue / uni.scss    # 标准 uni-app(vue3)工程骨架
└── uni_modules/bear-scan/          # ★ 插件本体:上传插件市场时只上传这一个目录
    ├── package.json                # 元数据与平台矩阵(与 config.json 版本逐项一致)
    ├── readme.md                   # 本文件
    ├── changelog.md                # 版本记录
    ├── license.txt                 # 授权条款(免费 + 广告解锁)
    ├── example/index.vue           # 案例页:环境检测 + 按端能力显示的扫码按钮,码值进顶部「最近扫码内容」
    └── utssdk/
        ├── interface.uts           # 三端共享契约:ScanOptions / ScanResult / ScanErrorCode(9050xxx),只有声明
        ├── unierror.uts            # 错误码表 + ScanFailImpl extends UniError,段内/段外收口
        ├── core.uts                # 跨端纯逻辑:码型归一、统一码型编号、原生错误归类,无任何平台 API
        ├── index.uts               # Web / 小程序兜底:getEnvInfo 降级返回、scan 回 9050009
        ├── app-android/
        │   ├── index.uts           # BearScanAndroid 单例 + 薄导出,权限申请与错误归类
        │   ├── scankit.kt          # 华为 ScanKit 桥接:startScan、Activity result 解析、权限分档、码型名 ↔ HmsScan 常量
        │   ├── config.json         # minSdkVersion 28 + scanplus 依赖 + agconnect Gradle 插件声明
        │   └── AndroidManifest.xml # 相机 / 震动 / 媒体读取权限
        ├── app-ios/
        │   ├── index.uts           # BearScanIOS 单例:授权状态校验、位掩码换算、会话槽位
        │   ├── HmsScanKit.swift    # 框架桥接:present 扫码 VC、结果去重
        │   ├── config.json         # deploymentTarget 11
        │   ├── info.plist          # 相机 / 相册用途文案(文件名小写)
        │   ├── Frameworks/         # ScanKitFrameWork.framework(华为官方二进制)
        │   └── Resources/          # ScanKitFrameWorkBundle.bundle(模型资源与多语言文案)
        └── app-harmony/
            ├── index.uts           # BearScanHarmony 单例:startScanForResult 调用与错误归类
            └── config.json         # minSdkVersion 12(系统 ScanKit,无 har 依赖)

docs/rules/uts-plugin.md 是本仓库的 UTS 插件规范,改本插件前先看 §3(四层分工)与 §6(编码规范)。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

相机(用于实时取景扫码);震动(扫码命中反馈,华为 ScanKit 内部使用);图片/视频读取(仅 Android,用于从相册选一张图识别二维码,按「机型 API 与宿主 targetSdkVersion 中较低的一档」分档申请:34+ 申请 READ_MEDIA_IMAGES/READ_MEDIA_VIDEO/READ_MEDIA_VISUAL_USER_SELECTED,33 申请 READ_MEDIA_IMAGES,≤32 申请 READ_EXTERNAL_STORAGE,不用相册时只申请相机)。iOS 由本插件在首次扫码时弹系统授权框申请(小写 info.plist 已给 NSCameraUsageDescription 文案),HarmonyOS 的相机与相册权限由系统扫码界面自理。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据:相机画面、相册图片与解析出的码值只在设备本地处理,不会上传到任何服务端,也不写入插件自己的文件。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

本插件在插件市场以上架时配置的「观看广告解锁」方式提供,广告位由市场平台承载,插件代码内不含任何广告 SDK 与广告请求。

许可协议

bear-scan 插件许可协议

本协议约束你在 DCloud 插件市场获取并使用 bear-scan(下称「本插件」)的行为。 本插件不收取费用,以「观看广告解锁下载」的方式提供;解锁动作由插件市场平台完成, 本插件代码内不含任何广告 SDK、不发起广告请求,也不因广告的投放情况影响功能。 下载或使用本插件即表示你已阅读并同意以下条款;如不同意请立即停止使用并删除相关文件。

一、授予的权利

  1. 使用权:授予你一项非独占、不可转让、不可再分发的许可,你可以把本插件集成到任意数量的 uni-app / uni-app-x 项目中,用于个人或商业产品的发布与运营。
  2. 源码权:本插件以源码形态发布,你可以阅读、修改、按需裁剪其中的 UTS 与混编源码 (含 app-android/scankit.kt、app-ios/HmsScanKit.swift 与内置的华为 ScanKit 二进制), 并可把修改后的实现闭源地集成到你自己的产品里,以你的产品形态分发。
  3. 上述权利不因为你使用了哪一档下载方式而有所不同;本文件不写死任何价格或档位名称, 交易与售后规则以插件市场订单页面为准。

二、禁止事项

  1. 不得把本插件(含修改后的源码)以插件、模板、SDK、示例工程或任何形式再次分发、公开、 转售或上传到任何代码托管平台、网盘与第三方市场。
  2. 不得移除或替换本插件的版权与许可声明,不得以「bear-scan」或作者的名义对外发布产品、 宣称作者背书。
  3. 不得移除 AndroidManifest.xml / Info.plist 中的权限用途文案后另行改写为误导性描述, 不得利用本插件在用户不知情的情况下调用相机或读取相册内容。
  4. 不得对内置的 ScanKitFrameWork.framework 做逆向、重签名或重新分发;该二进制的权利归 华为技术有限公司,随本插件提供的部分仅在你已同意本协议的前提下许可。

三、支持边界

  1. 作者负责修复本插件在声明支持的端(Android minSdk 28、iOS deploymentTarget 11、 HarmonyOS API 12)与 readme 所述能力范围内的缺陷,并保证接口、错误码与文档三者一致。
  2. 下列内容不在支持范围内,由使用者自己承担:宿主应用的包名与签名、iOS 的 Bundle Identifier 与描述文件、华为/荣耀开放平台的资质与审核结果,以及华为 ScanKit(Android scanplus 与 iOS Framework)自身版本升级或策略变更导致的行为变化。
  3. iOS 端内置的 ScanKit Framework 不提供「用户取消」回调,界面被返回手势关闭时不会有任何结果, 这是框架限制而非缺陷;readme「实现要点与注意事项」已如实记录,作者不承诺在该框架版本上补齐。

四、免责

  1. 本插件按「现状」提供,作者不对适用性、无中断、无错误及所解析码值的正确性作任何明示或默示的担保。
  2. 扫码结果可能被用于支付、跳转等敏感场景,使用者 MUST 自行校验码值来源与合法性, 并遵守所在地区的法律法规与应用商店审核政策;因业务侧直接使用码值而不校验所引发的损失,由使用者自行承担。
  3. 在法律允许的最大范围内,作者不因使用或无法使用本插件造成的任何间接、附带或后果性损失承担责任。

五、协议变更与终止

  1. 作者保留在新版本中调整许可条款的权利;调整后的条款对新版本生效,已获取的版本沿用获取时的条款。
  2. 你违反本协议时,授权自动终止,并应立即停止使用与删除本插件相关文件。
  3. 对本协议的任何疑问或超出上述范围的授权需求(例如多主体、白标),请先与作者联系协商。