更新记录
1.0.0(2026-09-05)
- 首个版本
- 支持居民身份证、港澳台居民居住证、外国人永久居留身份证(2017/2023 版)阅读
- 支持 USB(默认 vid=1024/pid=50010)与串口两种传输协议
- 支持单次读卡、自动读卡、USB 插拔事件监听
平台兼容性
uni-app(3.8.3)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | √ | 6.0 | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | - | × | × |
uni-app x(3.8.3)
| Chrome | Safari | Android | Android插件版本 | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|
| × | × | 6.0 | 1.0.0 | × | × | × |
zy-zkidcardreader
身份证阅读器 UTS 插件(Android)新中新 华视 神盾 神思 熵基等
简介
离线身份证阅读器UTS插件,支持居民身份证、港澳台居民居住证、外国人永久居留身份证阅读,支持USB和串口两种传输协议,可读取文字、照片(base64)及指纹特征。
支持读取:
- 居民身份证(二代证)
- 港澳台居民居住证
- 外国人永久居留身份证(2017 版 / 2023 版)
支持获取:文字信息、照片(base64)、指纹特征(需授权 SAM 及卡片含指纹)。
支持两种传输协议(initModule 的 mode 参数):
| mode | 说明 | 参数 |
|---|---|---|
usb(默认) |
USB HID,需系统 USB 授权 | vid(默认 1024)/ pid(默认 50010) |
serial |
串口,无需 USB 授权 | serialName(默认 /dev/ttyS1)/ baudRate(默认 115200) |
平台兼容
| Android | iOS | HarmonyOS | Web | 小程序 |
|---|---|---|---|---|
| ✓ | x | x | x | x |
- Android 最低版本:21(minSdkVersion 21)
- 支持 armeabi-v7a / arm64-v8a
使用前提
- 本插件依赖中控智慧官方 SDK 的
.so库(libwlt2bmp / libzkserialport / libzkwltdecode),必须使用自定义基座或云打包生效,标准基座无法运行; - USB 模式下首次初始化会弹出系统 USB 权限弹窗,请选择"允许";
- 插件根目录
utssdk/app-android/libs已内置 SDK 的 jar 与 so,无需额外配置。
API
initModule(options: InitOptions)
设备初始化。
import { initModule } from '@/uni_modules/zy-zkidcardreader'
// USB 模式(默认)
initModule({
mode: 'usb', // 可省略,默认 usb
success: (res) => {
// res.code == 0 初始化成功,res.samId 为安全模块号
},
fail: (err) => {
// err.errCode / err.errMsg
}
})
// 串口模式
initModule({
mode: 'serial',
serialName: '/dev/ttyS1',
baudRate: 115200,
success: (res) => { }
})
readCard(options: ReadCardOptions)
单次读卡:在 timeout(默认 20000ms)时间内轮询寻卡,读到后立即回调。
import { readCard } from '@/uni_modules/zy-zkidcardreader'
readCard({
timeout: 20000,
success: (res) => {
// res.name 姓名 / res.sex 性别 / res.nation 民族
// res.birth 出生日期(yyyyMMdd) / res.address 住址
// res.idCard 身份证号 / res.depart 签发机关 / res.validityTime 有效期限
// res.photo 照片 base64(data:image/png;base64,xxx)
// res.type 证件类型代码 / res.typeName 证件类型名称
},
fail: (err) => { }
})
autoReadCard(options: AutoReadCardOptions)
自动读卡:启动后台轮询线程,每读到一张卡通过 onEvent 回调。
import { autoReadCard, stopAutoRead } from '@/uni_modules/zy-zkidcardreader'
autoReadCard({
interval: 500, // 轮询间隔,默认 500ms,最小 100
dedup: true, // 同卡去重,默认 true(同一张卡只回调一次)
success: (res) => {
// res.event == 'started' 启动成功
},
onEvent: (res) => {
if (res.event == 'card') {
// 读到卡,字段同 readCard 的 success
} else if (res.event == 'stopped') {
// 自动读卡已停止
}
}
})
// 停止
stopAutoRead((res) => { })
close(callback)
关闭设备并释放资源。
import { close } from '@/uni_modules/zy-zkidcardreader'
close((res) => { })
setEventListener(callback)
USB 插拔事件监听(仅 usb 模式有事件):res.event 为 usbAttached / usbDetached。
import { setEventListener } from '@/uni_modules/zy-zkidcardreader'
setEventListener((res) => {
if (res.event == 'usbDetached') {
// 阅读器被拔出,可提示用户重新插拔后调用 initModule
}
})
错误码
| errCode | 说明 |
|---|---|
| 5010001 | 参数错误 |
| 5010002 | 未检测到身份证阅读器 |
| 5010003 | USB 权限被拒绝 |
| 5010004 | 设备未初始化 |
| 5010005 | 初始化超时 |
| 5010006 | 打开设备失败 |
| 5010007 | 读卡超时 |
| 5010008 | 解析证件信息失败 |
| 5010009 | 设备已关闭 |
| 5010010 | 上下文为空 |
示例代码
<template>
<view class="container">
<view class="header">
<text class="title">身份证阅读器</text>
<text class="subtitle">身份证阅读器(新中新/华视/神盾/神思/熵基等)</text>
</view>
<!-- 设备状态 -->
<view class="section">
<text class="section-title">设备状态</text>
<view class="status-row">
<text class="status-label">连接状态:</text>
<text class="status-value" :class="opened ? 'status-on' : 'status-off'">{{ statusText }}</text>
</view>
<view class="status-row" v-if="samId">
<text class="status-label">SAM卡号:</text>
<text class="status-value">{{ samId }}</text>
</view>
<view class="status-row" v-if="usbEvent">
<text class="status-label">USB事件:</text>
<text class="status-value">{{ usbEvent }}</text>
</view>
</view>
<!-- 初始化 -->
<view class="section">
<text class="section-title">设备初始化</text>
<radio-group class="mode-group" @change="onModeChange">
<label class="mode-item">
<radio value="usb" :checked="mode === 'usb'" color="#007aff" />
<text>USB(默认 1024/50010)</text>
</label>
<label class="mode-item">
<radio value="serial" :checked="mode === 'serial'" color="#007aff" />
<text>串口(/dev/ttyS1)</text>
</label>
</radio-group>
<view class="button-group">
<button type="primary" size="mini" @click="doInit" :loading="initializing">初始化</button>
<button type="warn" size="mini" @click="doClose">关闭设备</button>
</view>
</view>
<!-- 读卡 -->
<view class="section">
<text class="section-title">读卡操作</text>
<view class="option-row">
<checkbox :checked="readFingerprint" color="#007aff" @tap="readFingerprint = !readFingerprint" />
<text class="option-text">读取指纹特征(需授权SAM)</text>
</view>
<view class="button-group">
<button type="primary" size="mini" @click="doReadCard" :disabled="!opened || autoRunning">单次读卡</button>
<button v-if="!autoRunning" type="primary" size="mini" @click="doAutoRead" :disabled="!opened">自动读卡</button>
<button v-else type="warn" size="mini" @click="doStopAuto">停止自动</button>
</view>
</view>
<!-- 读卡结果 -->
<view class="section" v-if="card">
<text class="section-title">读卡结果({{ card.typeName || '证件' }})</text>
<view class="photo-wrap" v-if="card.photo">
<image class="photo" :src="card.photo" mode="aspectFit" />
</view>
<view class="result-item">
<text class="label">姓名:</text>
<text class="value">{{ card.name || '-' }}</text>
</view>
<view class="result-item">
<text class="label">性别:</text>
<text class="value">{{ card.sex || '-' }}</text>
</view>
<view class="result-item" v-if="card.nation">
<text class="label">民族:</text>
<text class="value">{{ card.nation }}</text>
</view>
<view class="result-item" v-if="card.country">
<text class="label">国籍:</text>
<text class="value">{{ card.country }}</text>
</view>
<view class="result-item">
<text class="label">出生:</text>
<text class="value">{{ card.birth || '-' }}</text>
</view>
<view class="result-item" v-if="card.address">
<text class="label">住址:</text>
<text class="value">{{ card.address }}</text>
</view>
<view class="result-item">
<text class="label">证件号:</text>
<text class="value">{{ card.idCard || '-' }}</text>
</view>
<view class="result-item" v-if="card.depart">
<text class="label">签发机关:</text>
<text class="value">{{ card.depart }}</text>
</view>
<view class="result-item" v-if="card.validityTime">
<text class="label">有效期:</text>
<text class="value">{{ card.validityTime }}</text>
</view>
<view class="result-item" v-if="card.fingerprintLength > 0">
<text class="label">指纹:</text>
<text class="value">{{ card.fingerprintLength }} 字节特征数据</text>
</view>
</view>
<!-- 日志 -->
<view class="section">
<text class="section-title">操作日志</text>
<view class="log-box">
<text class="log-line" v-for="(log, index) in logs" :key="logIndex - index">{{ log }}</text>
</view>
<view class="button-group" style="margin-top: 16rpx;">
<button type="default" size="mini" @click="logs = []">清空日志</button>
</view>
</view>
</view>
</template>
<script>
import { initModule, readCard, autoReadCard, stopAutoRead, close, setEventListener } from '@/uni_modules/zy-zkidcardreader'
export default {
data() {
return {
mode: 'usb',
readFingerprint: false,
initializing: false,
opened: false,
autoRunning: false,
samId: '',
usbEvent: '',
card: null,
logs: [],
logIndex: 0
}
},
computed: {
statusText() {
return this.opened ? '已连接' : (this.initializing ? '初始化中...' : '未连接')
}
},
onUnload() {
// 页面卸载时停止自动读卡并关闭设备
stopAutoRead()
close()
},
methods: {
addLog(msg) {
const now = new Date()
const pad = n => (n < 10 ? '0' + n : '' + n)
const time = pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds())
this.logs.unshift('[' + time + '] ' + msg)
this.logIndex++
if (this.logs.length > 100) {
this.logs.length = 100
}
},
onModeChange(e) {
this.mode = e.detail.value
},
doInit() {
if (this.initializing) return
this.initializing = true
this.addLog('开始初始化(' + this.mode + ' 模式)...')
initModule({
mode: this.mode,
readFingerprint: this.readFingerprint,
success: (res) => {
this.initializing = false
this.opened = true
this.samId = res.samId || ''
this.addLog('初始化成功: ' + (res.msg || '') + (res.samId ? (',SAM=' + res.samId) : ''))
// 注册 USB 插拔事件(仅 usb 模式有事件)
setEventListener((evt) => {
this.usbEvent = evt.event
if (evt.event === 'usbDetached') {
this.opened = false
this.addLog('阅读器已拔出,请重新初始化')
} else if (evt.event === 'usbAttached') {
this.addLog('阅读器已接入')
}
})
},
fail: (err) => {
this.initializing = false
this.opened = false
this.addLog('初始化失败: [' + err.errCode + '] ' + err.errMsg)
uni.showToast({ title: err.errMsg, icon: 'none' })
}
})
},
doClose() {
stopAutoRead()
this.autoRunning = false
close((res) => {
this.opened = false
this.card = null
this.addLog('设备已关闭')
})
},
doReadCard() {
this.addLog('开始读卡(请放置卡片)...')
readCard({
timeout: 20000,
readFingerprint: this.readFingerprint,
success: (res) => {
this.card = res
this.addLog('读卡成功: ' + (res.name || '') + ' ' + (res.idCard || ''))
},
fail: (err) => {
this.addLog('读卡失败: [' + err.errCode + '] ' + err.errMsg)
}
})
},
doAutoRead() {
autoReadCard({
interval: 500,
dedup: true,
readFingerprint: this.readFingerprint,
success: (res) => {
this.autoRunning = true
this.addLog('自动读卡已启动(同卡去重),取走卡片后重新放卡会再次回调')
},
fail: (err) => {
this.addLog('启动失败: [' + err.errCode + '] ' + err.errMsg)
},
onEvent: (res) => {
if (res.event === 'card') {
this.card = res
this.addLog('读到卡片: ' + (res.name || '') + ' ' + (res.idCard || ''))
} else if (res.event === 'stopped') {
this.autoRunning = false
this.addLog('自动读卡已停止')
}
}
})
},
doStopAuto() {
stopAutoRead((res) => {
this.addLog('请求停止自动读卡')
})
}
}
}
</script>
<style>.container {padding: 24rpx;}.header {margin-bottom: 24rpx;}.title {font-size: 40rpx;font-weight: bold;color: #333;}.subtitle {display: block;margin-top: 8rpx;font-size: 26rpx;color: #999;}.section {background: #fff;border-radius: 16rpx;padding: 24rpx;margin-bottom: 24rpx;}.section-title {display: block;font-size: 30rpx;font-weight: bold;color: #333;margin-bottom: 16rpx;}.status-row {display: flex;margin-bottom: 8rpx;}.status-label {width: 160rpx;font-size: 26rpx;color: #666;}.status-value {flex: 1;font-size: 26rpx;color: #333;word-break: break-all;}.status-on {color: #07c160;}.status-off {color: #fa5151;}.mode-group {margin-bottom: 16rpx;}.mode-item {display: flex;align-items: center;margin-bottom: 12rpx;}.mode-item text {margin-left: 8rpx;font-size: 26rpx;color: #333;}.option-row {display: flex;align-items: center;margin-bottom: 16rpx;}.option-text {margin-left: 8rpx;font-size: 26rpx;color: #333;}.button-group {display: flex;flex-direction: row;gap: 16rpx;}.photo-wrap {display: flex;justify-content: center;margin-bottom: 16rpx;}.photo {width: 204rpx;height: 264rpx;border: 1rpx solid #eee;border-radius: 8rpx;}.result-item {display: flex;margin-bottom: 10rpx;}.result-item .label {width: 180rpx;font-size: 26rpx;color: #666;}.result-item .value {flex: 1;font-size: 26rpx;color: #333;word-break: break-all;}.log-box {background: #f7f7f7;border-radius: 8rpx;padding: 16rpx;max-height: 400rpx;overflow-y: scroll;}.log-line {display: block;font-size: 22rpx;color: #666;line-height: 36rpx;word-break: break-all;}</style>
注意事项
- 指纹读取:需同时满足 ①
readFingerprint: true;② 卡片含指纹(2013 年后采集过指纹的二代证);③ SAM 模块具备指纹解密授权,否则fingerprintLength恒为 0; - 单次读卡与自动读卡互斥:调用
readCard会自动停止自动读卡; - 重复调用
initModule会先释放上一次的资源再重新初始化; - 阅读器 USB 拔出后需重新插拔并再次调用
initModule; - 项目中含
.so的 UTS 插件需使用自定义基座运行或云打包。

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