更新记录

1.4.0(2026-08-26)

  • 新增 bottomButtons 自定义底部按钮,支持控制按钮顺序、内置手电筒/相册行为和自定义业务按钮
  • 新增 bottomButtonsHorizontalPadding,支持配置底部按钮等间隔布局的两侧留白
  • 优化自定义底部按钮的图标回退,未配置图标或加载失败时显示按钮文案首字符
  • 新增本地静态资源、本地文件、Base64 和 HTTPS 在线按钮图标,在线图标支持异步加载、缓存、超时及失败回退
  • 新增 onBottomButtonClick 点击事件,支持通过 closeOnClick 在原生扫码页关闭后执行页面跳转等业务操作
  • 自定义按钮关闭普通扫码时补充 reason: 'bottomButton'buttonId,方便区分用户取消与业务入口退出

1.3.0(2026-08-20)

  • 新增 autoZoomModeautoZoomDelayautoZoomRangeautoZoomStepautoZoomInterval 配置项,支持选择智能或渐进策略,并调整自动变焦开始时间、倍率范围、单次幅度和执行间隔
  • 优化 Android 和 iOS 自动变焦为延迟、平滑地逐步放大,并在识别到有效内容后立即停止变焦及过渡
  • 新增相册图片多码手动选择,识别到多个码时显示原图和绿色箭头
  • 新增 scanConfirmTimeout 配置项,支持按毫秒设置相机单码确认及多码收集等待时间,并兼容旧参数 multiCodeScanTimeout
  • 优化相机多码识别,聚合时间窗内不同帧识别到的候选码后再统一展示
  • 修复 Android 相册竖图未按 EXIF 方向转正,导致多码选择预览旋转的问题
  • 新增连续扫码 validateEachScanonScan 逐码校验能力,支持暂停扫码并等待业务异步校验结果
  • 新增结果 scanIdcompleteScan(scanId, feedback),用于提交逐码校验回执;只有校验成功的码会进入汇总结果并触发 success
  • 新增 processingTipText 配置项,支持自定义等待逐码校验时的提示文案
  • 新增 failureTipText 配置项,支持自定义校验失败提示;校验成功提示复用 successTipText
  • 新增 stripAimPrefix 配置项,默认过滤扫码结果开头的 AIM 码制标识
  • 新增 useSystemPhotoPicker 配置项,支持 Android 系统照片和文档选择器,并保留旧图库作为默认方式及兼容兜底

1.2.0(2026-07-14)

  • 新增 successTipText 配置项,用于自定义连续扫码成功提示文案
  • 新增 pinchZoom 配置项,用于控制扫码页面是否支持双指缩放
  • 修复连续扫码模式下,从相册识别成功后扫码页面会直接退出的问题
查看更多

平台兼容性

uni-app(3.8.2)

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

uni-app x(3.8.2)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

s-scan 原生扫码插件

s-scan 提供接近微信扫码的全屏体验,重点覆盖原生多码点选、远距自动变焦、相册多码识别、连续扫码逐码异步校验和可扩展底部操作,可用于设备绑定、商品条码识别、票券核销、批量盘点等场景。

重点提示:本插件是 UTS 原生插件,本地运行和真机调试前需要先制作并使用自定义调试基座。直接使用标准基座运行时,原生扫码能力不会被打包进 App,可能出现插件不存在、调用失败或无响应等情况。

功能特性

  • 支持二维码、常见条形码、DataMatrix、PDF417。
  • 支持 scanCode(options) 回调写法和 scanCodeAsync(options) Promise 写法。
  • 支持自定义标题、提示文案、提示背景色、扫描线颜色和手电筒激活色。
  • 支持相机扫码和相册图片识别。
  • 支持同时识别多个码时手动点选目标码。
  • 支持连续扫码和逐码异步校验,适合批量盘点、批量核销等场景。
  • 支持自定义底部按钮、按钮顺序和点击事件,可配置本地、Base64 或 HTTPS 在线图标。
  • 支持自定义按钮先关闭原生扫码页再回调,业务可安全跳转到手动输入等页面。
  • 支持业务代码主动关闭当前扫码页。
  • 支持扫码成功后的震动和 Beep 提示音。
  • 支持 onlyFromCamerasuccessfailcompleteuni.scanCode 常用字段。

安装说明

  1. 在插件市场导入本插件到项目。
  2. 确认项目已包含 uni_modules/s-scan 目录。
  3. 在需要扫码的页面中按下面示例导入并调用。
import { scanCode, scanCodeAsync, completeScan, closeScan } from '@/uni_modules/s-scan'

基础用法

回调写法

import { scanCode } from '@/uni_modules/s-scan'

scanCode({
  title: '扫码',
  content: '将二维码或条形码放入屏幕内',
  scanType: ['qrCode', 'barCode'],
  success(res) {
    console.log('扫码结果', res.result)
  },
  fail(err) {
    console.log('扫码失败', err.errCode, err.errMsg)
  },
  complete(res) {
    console.log('扫码完成', res)
  },
})

Promise 写法

import { scanCodeAsync } from '@/uni_modules/s-scan'

try {
  const res = await scanCodeAsync({
    title: '扫码',
    content: '将二维码或条形码放入屏幕内',
  })
  console.log('扫码结果', res.result)
} catch (err) {
  console.log('扫码失败', err.errCode, err.errMsg)
}

完整配置

scanCode({
  title: '设备扫码',
  titleStyle: {
    color: '#FFFFFF',
    fontSize: 18,
  },
  content: '请扫描设备机身二维码',
  contentStyle: {
    color: '#FFFFFF',
    fontSize: 15,
    backgroundColor: '#66000000',
  },
  scanType: ['qrCode', 'barCode'],
  stripAimPrefix: true,
  vibrate: false,
  beep: false,
  manualSelect: true,
  scanConfirmTimeout: 420,
  scanLineColor: '#07C160',
  flashlightActiveColor: '#07C160',
  autoZoom: true,
  autoZoomMode: 'smart',
  autoZoomDelay: 2000,
  autoZoomRange: [1, 2],
  autoZoomStep: 0.2,
  autoZoomInterval: 500,
  pinchZoom: true,
  showFlashlight: true,
  showAlbum: true,
  bottomButtons: [
    { id: 'flashlight', type: 'flashlight', text: '手电筒' },
    {
      id: 'manualInput',
      type: 'custom',
      text: '手动输入',
      icon: 'https://img.icons8.com/ios-filled/100/ffffff/keyboard.png',
      closeOnClick: true,
    },
    { id: 'album', type: 'album', text: '相册' },
  ],
  bottomButtonsHorizontalPadding: 52,
  useSystemPhotoPicker: false,
  continuous: false,
  validateEachScan: false,
  deduplicate: false,
  showSuccessTip: true,
  successTipText: '扫描成功',
  failureTipText: '验证失败',
  processingTipText: '处理中…',
  scanInterval: 1200,
  onlyFromCamera: false,
  onBottomButtonClick(event) {},
  success(res) {},
  fail(err) {},
  complete(res) {},
})

自定义底部按钮

传入 bottomButtons 后,数组将替代 showFlashlightshowAlbum 生成的默认布局,并按照数组顺序显示最多 4 个有效按钮。多个按钮使用类似 CSS justify-content: space-between 的方式排列,bottomButtonsHorizontalPadding 用于设置两侧留白。flashlightalbum 使用插件内置行为,custom 通过 onBottomButtonClick 交给业务处理。

scanCode({
  bottomButtons: [
    { id: 'flashlight', type: 'flashlight', text: '手电筒' },
    {
      id: 'manualInput',
      type: 'custom',
      text: '手动输入',
      icon: 'https://img.icons8.com/ios-filled/100/ffffff/keyboard.png',
      closeOnClick: true,
    },
    { id: 'help', type: 'custom', icon: 'https://example.com/icons/help.png' },
    { id: 'album', type: 'album', text: '相册' },
  ],
  bottomButtonsHorizontalPadding: 40,
  onBottomButtonClick(event) {
    if (event.id === 'manualInput' && event.closed) {
      uni.navigateTo({ url: '/pages/manual-input/manual-input' })
    }
  },
  fail(err) {
    if (err.errCode === 1300004 && err.reason === 'bottomButton') return
    console.log('扫码失败', err)
  },
})

closeOnClick 仅作用于 custom 按钮,默认为 false。设为 true 时,插件会停止相机、关闭原生扫码页、结束本次扫码调用,最后触发 onBottomButtonClick;业务跳转不会被扫码页遮挡。普通扫码会以 1300004 结束,并附带 reason: 'bottomButton'buttonId;连续扫码则先返回当前汇总结果,再触发按钮事件。

图标支持 /static/...、App 本地文件路径、data:image/...;base64,... 和 HTTPS URL。推荐使用透明背景的 96 × 96 PNG,原生端会按约 28 dp/pt 等比显示。在线图标异步加载,不阻塞相机启动;custom 按钮未配置图标或图标加载失败时显示 text 的第一个字符,text 也为空时不绘制内容;flashlightalbum 未配置图标时仍使用内置图形。默认不加载 HTTP 明文地址。

底部按钮字段

字段 类型 默认值 说明
id String - 按钮唯一标识,通过 onBottomButtonClick 原样返回
type String custom flashlightalbumcustom
icon String 按类型回退 普通图标,支持静态资源、本地文件、Base64 和 HTTPS URL
activeIcon String 空字符串 激活图标,主要用于手电筒按钮
text String 空字符串 图标下方的短标签
iconColor String #FFFFFF 内置图标颜色;自定义图片仅在显式传入时着色
activeColor String 主题色 激活状态颜色
backgroundColor String 半透明黑色 圆形按钮背景色
closeOnClick Boolean false 自定义按钮点击后是否先关闭扫码页

onBottomButtonClick 返回 { id, type, closed, active? }closed 表示回调前扫码页是否已经关闭;手电筒按钮还会通过 active 返回当前开关状态。

连续扫码

开启 continuous 后,扫码页不会在识别成功后自动关闭。默认每次识别到结果都会触发 success;用户关闭扫码页时,会再返回一次当前已识别结果列表。配置 onScan 后,只有业务校验通过的单次结果才会触发 success

scanCode({
  continuous: true,
  success(res) {
    if (Array.isArray(res.result)) {
      console.log('本次连续扫码汇总', res.result)
      return
    }
    console.log('单次扫码结果', res.result)
  },
})

使用 scanCodeAsync 开启连续扫码时,Promise 会在用户关闭扫码页并返回汇总结果后 resolve。

如需在连续扫码中让相同内容只回调并汇总一次,可设置 deduplicate: true。默认 false,相同码再次识别时仍会触发回调,并保留在关闭页面时返回的汇总列表中。

连续扫码成功后默认会显示轻量的“扫描成功”提示,可通过 showSuccessTip: false 关闭,也可以通过 successTipText 自定义提示文案;scanInterval 用于设置两次自动连续扫码回调之间的最小间隔,单位毫秒,默认 1200。用户手动点选多码结果时不受该间隔限制。

连续扫码时从相册选取图片识别成功后,会触发一次 success 回调并继续停留在扫码页;用户关闭扫码页时才会返回已识别内容数组。

逐码异步校验

连续扫码需要先请求后端确认当前码时,需同时设置 validateEachScan: true 并传入 onScan。识别到码后,扫码页会暂停识别并显示“处理中…”,直到业务调用 completeScan;随后会显示业务返回的成功或失败提示,再恢复扫码。普通连续扫码不要开启 validateEachScan,识别后会直接触发 success 并继续扫描。

等待文案可通过 processingTipText 自定义。校验回执未传 message 时,成功提示使用 successTipText,失败提示使用 failureTipText。三个字段传入空字符串或纯空白时都会使用各自的默认文案。

import { scanCode, completeScan } from '@/uni_modules/s-scan'

scanCode({
  continuous: true,
  validateEachScan: true,
  successTipText: '核验成功',
  failureTipText: '核验失败',
  processingTipText: '正在核验…',
  onScan(res) {
    uni.request({
      url: 'https://example.com/api/verify-code',
      method: 'POST',
      data: { code: res.result },
      success(response) {
        const passed = response.data.code === 0
        completeScan(res.scanId, {
          success: passed,
          message: passed ? '核验成功' : response.data.message || '核验失败',
        })
      },
      fail() {
        completeScan(res.scanId, { success: false, message: '网络异常,请重试' })
      },
    })
  },
  success(res) {
    if (Array.isArray(res.result)) {
      console.log('校验通过的扫码汇总', res.result)
      return
    }
    console.log('本次校验通过', res.result)
  },
})

completeScan(res.scanId, { success, message }) 的规则:

  • success: true:显示绿色提示,将当前码加入连续扫码汇总,并触发一次 success
  • success: false:显示红色提示,不加入汇总,也不触发 success,提示后可重新扫描。
  • message 可省略;成功时使用 successTipText,失败时使用 failureTipText
  • showSuccessTip: false 时不显示逐码结果提示,收到回执后立即恢复扫码。

业务必须在接口成功、业务失败和网络异常分支中都调用一次 completeScan。未调用时扫码页会继续保持暂停,避免产生并发校验请求;相同 scanId 重复调用只处理第一次。等待校验时仍可关闭扫码页,尚未完成校验的码不会进入汇总结果。

主动关闭扫码页

import { closeScan } from '@/uni_modules/s-scan'

const closed = closeScan()
console.log('是否已发起关闭', closed)

closeScan() 会关闭当前正在运行的扫码页,并返回 boolean 表示是否存在可关闭的扫码页。普通扫码会触发取消失败回调,错误码为 1300004;连续扫码会和用户点关闭按钮一样,返回当前已识别结果数组。

参数说明

参数 类型 默认值 说明
scanType Array<string> ['qrCode', 'barCode'] 识别类型,支持 qrCodebarCodedatamatrixpdf417
stripAimPrefix Boolean true 是否移除开头且与识别类型匹配的 AIM 码制标识
onlyFromCamera Boolean false 兼容 uni.scanCode 字段。为 true 时隐藏相册入口,只允许相机扫码
title String 扫码 扫码页标题
titleStyle.color String #FFFFFF 标题颜色
titleStyle.fontSize Number 18 标题字号,Android 为 sp,iOS 为 pt
content String 将二维码或条形码放入屏幕内 扫码提示内容
prompt String - 兼容旧参数,content 优先级更高
contentStyle.color String #FFFFFF 提示文字颜色
contentStyle.fontSize Number 15 提示文字字号
contentStyle.backgroundColor String 空字符串 提示背景色,例如 #66000000
vibrate Boolean false 扫码成功后是否震动
beep Boolean false 扫码成功后是否播放 Beep 音
manualSelect Boolean true 相机或相册识别到多个码时,是否显示箭头让用户手动选择
scanConfirmTimeout Number 420 相机扫码确认等待时间,单位毫秒。单码用于跨帧一致性确认,多码用于候选收集;设置为 0 表示立即处理当前帧
multiCodeScanTimeout Number - scanConfirmTimeout 的兼容旧别名;同时传入时以新参数为准
scanLineColor String #07C160 扫描线颜色
flashlightActiveColor String scanLineColor 手电筒开启时的图标颜色
autoZoom Boolean true 是否启用温和自动缩放,便于识别远距离或较小的码
autoZoomMode String smart 自动缩放策略:smart 由 Android ML Kit 判断是否缩放及目标倍率;progressive 主动渐进到最大倍率
autoZoomDelay Number 2000 开始自动缩放前的等待时间,单位毫秒
autoZoomRange Array<number> [1, 2] 自动缩放的起始倍率和最大倍率;超出设备支持范围时会自动限制
autoZoomStep Number 0.2 每次自动缩放增加的倍率;设置为 0 时不会改变倍率
autoZoomInterval Number 500 两次自动缩放之间的时间间隔,单位毫秒,最小值为 100
pinchZoom Boolean true 是否允许双指捏合手势手动放大或缩小扫码画面
showFlashlight Boolean true 是否显示手电筒按钮
showAlbum Boolean true 是否显示相册按钮
bottomButtons Array - 自定义底部按钮;传入后替代默认手电筒/相册布局,最多显示 4 个
bottomButtonsHorizontalPadding Number 52 多个底部按钮使用等间隔排列时的左右留白,Android 单位为 dp,iOS 单位为 pt
useSystemPhotoPicker Boolean false Android 是否优先使用系统照片/文档选择器;iOS 忽略该配置
continuous Boolean false 是否连续扫码。扫码页不自动关闭;普通模式不会等待逐码回执
validateEachScan Boolean false 是否逐码校验。开启后每次识别会暂停并等待 completeScan
deduplicate Boolean false 连续扫码时是否按内容去重。为 true 时相同内容只回调并汇总一次
showSuccessTip Boolean true 是否显示连续扫码提示;使用 onScan 时同时控制成功和失败提示
successTipText String 扫描成功 连续扫码及逐码校验成功提示。传空字符串或纯空白时使用默认文案
failureTipText String 验证失败 逐码校验失败提示。传空字符串或纯空白时使用默认文案
processingTipText String 处理中… 等待逐码校验回执时的提示文案。传空字符串或纯空白时使用默认文案
scanInterval Number 1200 自动连续扫码两次回调之间的最小间隔,单位毫秒。设置为 0 表示不限制;手动点选多码结果不受该间隔限制
onScan Function - validateEachScan 开启后的逐码回调;使用 scanId 回执后恢复扫码
onBottomButtonClick Function - 底部按钮点击回调,返回按钮标识、类型、关闭状态及可选激活状态
success Function - 扫码成功回调
fail Function - 扫码失败回调
complete Function - 调用结束回调,成功或失败都会执行

AIM 码制前缀

stripAimPrefix 默认为 true,相机扫码和相册识别都会移除结果开头且与实际识别类型匹配的 AIM 标识。设为 false 时返回解码器的原始内容。插件支持过滤的前缀家族如下,其中 n 表示一位数字修饰符:

前缀 识别类型
]Qn QR Code,例如 GS1 QR 的 ]Q3
]Cn Code 128,例如 GS1-128 的 ]C1
]An Code 39
]Gn Code 93
]Fn Codabar
]dn Data Matrix,例如 ]d2
]En EAN-13、UPC-A
]En]en EAN-8、UPC-E,例如 ]e0
]In ITF
]Ln PDF417

过滤只处理结果开头的 3 个字符,不会替换正文中出现的相似文本;前缀家族与原生识别类型不匹配时也会保留原内容。

返回值

type ScanCodeSuccess = {
  scanId: number
  result: string | string[]
  scanType: string
  rawScanType?: string
  fromAlbum?: boolean
  charSet?: string
}
字段 说明
scanId 当前逐码结果编号,传给 completeScan;普通结果和最终汇总为 0
result 扫码内容。普通扫码为 string;连续扫码关闭页面时为已识别内容的 string[]
scanType 规范化后的码类型,例如 QR_CODEEAN_13
rawScanType 原生平台返回的码类型
fromAlbum 是否来自相册
charSet 固定为 UTF-8

错误码

错误码 说明
1300001 相机权限被拒绝
1300002 相机不可用
1300003 已经有扫码页正在运行
1300004 用户取消扫码
1300005 扫码失败

权限配置

Android 插件已声明以下权限:

  • android.permission.CAMERA
  • android.permission.INTERNET
  • android.permission.VIBRATE
  • android.permission.READ_EXTERNAL_STORAGE
  • android.permission.READ_MEDIA_IMAGES

Android 默认使用与旧版本一致的厂商图库选择方式,并按系统版本申请相册读取权限。设置 useSystemPhotoPicker: true 后,会按能力依次尝试系统照片选择器和标准文档选择器,这两种方式无需运行时相册读取授权;均不可用时回退到旧图库。个别定制 ROM 在旧图库模式下仍可能显示应用选择器。

iOS 需要在 manifest.json 中配置相机和相册用途说明:

{
  "NSCameraUsageDescription": "需要使用相机扫描二维码或条形码",
  "NSPhotoLibraryUsageDescription": "需要访问相册以识别图片中的二维码或条形码"
}

示例工程已内置以上配置,接入业务项目时请按项目实际文案调整。

注意事项

  • 本插件仅支持 App Android 和 App iOS,H5 和小程序端不会启用原生扫码能力。
  • content 为空字符串时,扫码页不会显示提示文字,也不会显示提示背景。
  • onlyFromCameratrue 时会隐藏相册入口;如果同时设置了 showAlbum: true,仍以 onlyFromCamera 为准。
  • bottomButtons 一旦传入就接管底部布局;显式传入空数组会隐藏全部底部按钮。onlyFromCamera: true 仍会过滤数组中的 album 按钮。
  • 在线按钮图标仅默认支持 HTTPS,使用 Android 明文 HTTP 或放宽 iOS ATS 需要由业务项目自行承担并配置安全策略。
  • useSystemPhotoPicker 仅影响 Android 相册入口,默认为 false;设为 true 可优先使用系统照片/文档选择器,降低厂商图库应用选择弹窗的出现概率。
  • Android 的 smart 模式保留 ML Kit 智能缩放判断:没有智能建议时不会主动放大;收到建议后,插件会使用 autoZoomDelayautoZoomRangeautoZoomStepautoZoomInterval 约束执行时机、倍率范围和过渡速度。progressive 模式不等待智能建议,会在延迟结束后主动渐进到最大倍率。iOS 没有 ML Kit 同等能力,smart 会回退到 progressive
  • autoZoom 开启时会先保持原始倍率识别 autoZoomDelay 毫秒。识别到有效内容或用户开始双指缩放时,会立刻停止本次扫码会话的自动变焦及正在执行的过渡。
  • pinchZoom 开启后,用户双指缩放会优先使用手动倍率,本次扫码中 autoZoom 不会继续覆盖用户设置。
  • 相机扫码会在 scanConfirmTimeout 时间窗内进行跨帧确认;只有一个候选时自动返回,开启 manualSelect 且收集到多个候选时显示多码箭头。EAN、UPC、ITF 等数字一维码使用更严格的确认次数,以降低暗光、模糊和反光导致的瞬时误码。
  • 多码选择的箭头位置来自原生识别框坐标。相机识别会冻结摄像头画面;相册图片识别到多个码时会显示原图。两种来源都会将左上角按钮变为“取消”,由用户点选目标码。
  • 连续扫码模式下,插件不会自动关闭扫码页;相册识别成功后会继续停留在扫码页,用户点左上角关闭时会返回已识别内容数组。
  • Android 相册识别依赖系统照片选择器、文档提供方或兼容图库返回图片 URI;部分定制 ROM 的选择器如果不返回可读取的标准 URI,可能无法识别。

隐私、权限声明

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

android.permission.CAMERA, android.permission.INTERNET, android.permission.VIBRATE, android.permission.READ_EXTERNAL_STORAGE, android.permission.READ_MEDIA_IMAGES

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

插件不采集用户数据;扫码结果仅通过 success / Promise 回调返回给业务代码。

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