更新记录
1.0.0(2026-05-18)
- 支持安卓、iOS、鸿蒙NFC基础功能
- 调试NDEF读写卡,完善功能
平台兼容性
uni-app(4.45)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 鸿蒙插件版本 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | × | × | √ | 1.0.0 | √ | 1.0.0 | 5.0 | 1.0.0 | 13 | 1.0.0 | √ | 1.0.0 |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(4.45)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 鸿蒙插件版本 | 微信小程序 |
|---|---|---|---|---|---|---|---|---|
| × | × | 5.0 | 1.0.0 | 13 | 1.0.0 | √ | 1.0.0 | × |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
NFC读写卡能力 支持安卓 iOS 鸿蒙 UTS插件
介绍
- 支持
Android、iOS、HarmonyOS的 NFC 读卡能力封装,可用于读取卡片基础信息、NDEF 记录、透传 APDU 以及部分 Mifare 操作。 - 适用于
uni-app与uni-app xApp 平台,不支持 Web 和各类小程序平台。 - 读卡流程统一为:先
initNFC初始化,再readCard建立卡片会话,之后调用transceive、readNDEFRecords、readBlock等方法,结束后调用closeNFC关闭会话。
插件试用
平台支持
| 平台 | 支持情况 |
|---|---|
| Android | 支持 |
| iOS | 支持 |
| HarmonyOS | 支持 |
| Web | 不支持 |
| 小程序 | 不支持 |
数据结构说明
所有 API 均通过回调中的 ResponseEntity 返回结果,各业务数据类型定义如下。
ResponseEntity
通用响应体,所有方法回调均使用此类型。
type ResponseEntity = {
success: boolean // 操作是否成功
data?: any | null // 业务数据,具体类型取决于调用的方法
message?: string | null // 错误信息,success 为 false 时说明失败原因
}
NfcTag
readCard 成功后 data 字段返回的卡片信息对象。
type NfcTag = {
type?: string | null // 卡片类型,如 "iso7816"、"mifare_classic"、"mifare_ultralight"、"iso15693"、"iso18092"
id?: string | null // 卡片 ID(UID),十六进制字符串
standard?: string | null // 卡片协议标准描述
atqa?: string | null // ATQA(ISO14443A 应答),十六进制字符串
sak?: string | null // SAK(Select Acknowledge),十六进制字符串
historicalBytes?: string | null // 历史字节(ISO7816),十六进制字符串
protocolInfo?: string | null // 协议信息,十六进制字符串
applicationData?: string | null // 应用数据,十六进制字符串
hiLayerResponse?: string | null // 高层响应,十六进制字符串
manufacturer?: string | null // 制造商信息
systemCode?: string | null // 系统码(FeliCa),十六进制字符串
dsfId?: string | null // DSF ID(ISO15693),十六进制字符串
ndefAvailable?: boolean | null // 是否支持 NDEF
ndefType?: string | null // NDEF 标签类型,如 "TYPE1"、"TYPE2"、"TYPE3"、"TYPE4"、"MIFARE_CLASSIC"
ndefWritable?: boolean | null // NDEF 是否可写
ndefCanMakeReadOnly?: boolean | null // 是否可将 NDEF 设置为只读
ndefCapacity?: number | null // NDEF 容量(字节)
mifareInfo?: NfcMifareInfo | null // Mifare 卡片详细参数,非 Mifare 卡为 null
}
NfcMifareInfo
NfcTag.mifareInfo 的子结构,描述 Mifare 卡片的物理参数。
type NfcMifareInfo = {
type?: string | null // Mifare 子类型,如 "Classic"、"Ultralight"、"Ultralight C"
size?: number | null // 卡片总容量(字节)
blockSize?: number | null // 单块/单页大小(字节)
blockCount?: number | null // 块/页总数
sectorCount?: number | null // 扇区总数(Mifare Classic 为 16 或 40)
}
NdefInfo
readNDEFRecords 成功后 data 字段返回的 NDEF 记录列表元素。所有字段均为十六进制字符串,业务层可按需转为文本。
type NdefInfo = {
identifier: string // 记录标识符,十六进制字符串
payload: string // NDEF 消息有效负载,十六进制字符串
type: string // NDEF 记录类型,十六进制字符串(如 "54" 表示 Text、"55" 表示 URI)
typeNameFormat: string // TNF(类型名称格式),取值见下表
}
TNF(typeNameFormat)常见值:
| 值 | 含义 |
|---|---|
"0" |
空(Empty) |
"1" |
NFC Forum 已知类型(Well Known) |
"2" |
MIME 媒体类型 |
"3" |
绝对 URI |
"4" |
NFC Forum 外部类型 |
"5" |
未知类型 |
AndroidReaderModeFlags
initNFC 的 androidReaderModeFlags 参数类型,用于控制 Android Reader Mode 的附加行为,可按位组合(| 运算符)。
type AndroidReaderModeFlags = 0x80 | 0x100
| 常量 | 十六进制 | 说明 |
|---|---|---|
| 跳过 NDEF 自动发现 | 0x80 |
跳过系统自动 NDEF 检测,可将标签发现速度提升约 50ms |
| 禁用系统音效/振动 | 0x100 |
发现标签时不播放系统提示音也不振动 |
组合示例:0x80 | 0x100 表示既跳过 NDEF 发现又不播放音效。
API 参考
API 一览
| 方法名称 | 参数类型 | 回调 data 类型 |
说明 |
|---|---|---|---|
| initNFC | InitNFCParams |
无 | 初始化 NFC 读卡配置,检查权限和设备能力 |
| readCard | ReadCardParams |
NfcTag |
开始轮询卡片并返回卡片信息 |
| transceive | TransceiveParams |
string(十六进制) |
与卡片进行 APDU 或原始指令透传 |
| readNDEFRecords | ReadNDEFParams |
NdefInfo[](JSON) |
读取当前卡片上的 NDEF 记录 |
| writeNDEFRecords | WriteNDEFParams |
无 | 写入 NDEF 记录到卡片 |
| makeNdefReadOnly | MakeNDEFReadOnlyParams |
无 | 将当前 NDEF 标签设置为只读,操作不可逆 |
| authenticateSector | AuthenticateSectorParams |
boolean |
对 Mifare Classic 扇区进行 KeyA 或 KeyB 认证 |
| readSector | ReadSectorParams |
string(十六进制) |
读取 Mifare Classic 整个扇区 |
| readBlock | ReadBlockParams |
string(十六进制) |
读取 Mifare Classic 块或 Mifare Ultralight 页 |
| writeBlock | WriteBlockParams |
boolean |
写入 Mifare Classic 块或 Mifare Ultralight 页 |
| closeNFC | CloseNFCParams |
无 | 关闭当前 NFC 会话 |
initNFC
初始化 NFC 能力和读卡配置。所有 NFC 操作的第一步,调用成功后才能进行读卡。
参数:InitNFCParams
| 参数 | 类型 | 默认值 | 平台 | 说明 |
|---|---|---|---|---|
| timeout | number |
30000 |
Android / HarmonyOS | 读卡超时时间,单位毫秒。iOS 该参数无效 |
| readIso14443A | boolean |
true |
全部 | 是否启用 ISO14443A 类型卡片(Mifare Classic / Ultralight 等) |
| readIso14443B | boolean |
true |
全部 | 是否启用 ISO14443B 类型卡片 |
| readIso18092 | boolean |
false |
全部 | 是否启用 ISO18092 / FeliCa 类型卡片 |
| readIso15693 | boolean |
true |
全部 | 是否启用 ISO15693 类型卡片 |
| androidSound | boolean |
true |
仅 Android | 发现标签时是否播放系统提示音 |
| androidCheckNDEF | boolean |
true |
仅 Android | 是否启用系统自动 NDEF 检查 |
| androidReaderModeFlags | AndroidReaderModeFlags |
无 | 仅 Android | Reader Mode 附加标志,见上方 AndroidReaderModeFlags 说明 |
| extraReaderPresenceCheckDelay | number |
无 | 仅 Android | Presence Check 额外延迟(毫秒),用于兼容部分响应慢的卡片 |
| iosAlertMessage | string |
无 | 仅 iOS | 会话开始时系统弹窗显示的提示文字,如 "请将手机靠近 NFC 标签" |
| iosMultipleTagMessage | string |
无 | 仅 iOS | 检测到多张标签时系统弹窗显示的提示文字 |
| completeListener | (res: ResponseEntity) => void |
- | 全部 | 操作完成回调 |
示例:
reader.initNFC({
timeout: 30000,
readIso14443A: true,
readIso14443B: true,
readIso15693: true,
readIso18092: false,
androidSound: true,
iosAlertMessage: "请将手机靠近 NFC 标签",
completeListener(res) {
if (res.success) {
console.log("NFC 初始化成功")
} else {
console.log(`NFC 初始化失败: ${res.message}`)
}
}
})
readCard
启动一次读卡轮询,靠近卡片后自动识别并返回卡片信息。后续如果要继续调用 transceive、readNDEFRecords、writeNDEFRecords、readBlock 等方法,必须在 readCard 回调成功后保持会话,不要提前 closeNFC。
参数:ReadCardParams
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| completeListener | (res: ResponseEntity) => void |
- | 操作完成回调,res.data 为 NfcTag |
回调 data: NfcTag — 详见上方「数据结构说明」。
示例:
reader.readCard({
completeListener(res) {
if (!res.success || res.data == null) {
console.log(`读卡失败: ${res.message}`)
return
}
const tag = res.data as reader.NfcTag
console.log(`卡号: ${tag.id}`)
console.log(`类型: ${tag.type}`)
console.log(`标准: ${tag.standard}`)
console.log(`支持NDEF: ${tag.ndefAvailable}`)
console.log(`NDEF可写: ${tag.ndefWritable}`)
console.log(`NDEF容量: ${tag.ndefCapacity}`)
if (tag.mifareInfo != null) {
console.log(`Mifare类型: ${tag.mifareInfo.type}`)
console.log(`容量: ${tag.mifareInfo.size} 字节`)
console.log(`扇区数: ${tag.mifareInfo.sectorCount}`)
console.log(`块数: ${tag.mifareInfo.blockCount}`)
}
}
})
transceive
向当前卡片发送十六进制指令字符串并接收响应。调用前必须先成功执行 readCard。
- ISO7816(NfcA / IsoDep)卡片:发送标准 APDU 指令
- ISO15693(NfcV)卡片:发送原始指令
- FeliCa(NfcF)卡片:发送原始指令
参数:TransceiveParams
| 参数 | 类型 | 默认值 | 平台 | 说明 |
|---|---|---|---|---|
| capdu | string |
- | 全部 | APDU 或原始指令,十六进制字符串,如 "00A4040009A00000000386980701" |
| timeout | number |
3000 |
Android / HarmonyOS | 指令超时时间,单位毫秒。iOS 该参数无效 |
| completeListener | (res: ResponseEntity) => void |
- | 全部 | 操作完成回调,res.data 为十六进制响应字符串 |
示例:
// 选择 PPSE(支付环境)
reader.transceive({
capdu: "00A404000E325041592E5359532E444446303100",
timeout: 5000,
completeListener(res) {
if (res.success) {
console.log(`响应: ${res.data}`)
} else {
console.log(`透传失败: ${res.message}`)
}
}
})
readNDEFRecords
读取当前卡片上的 NDEF 记录。
调用前必须先成功执行 readCard,建议先通过 tag.ndefAvailable 判断卡片是否支持 NDEF。
参数:ReadNDEFParams
| 参数 | 类型 | 默认值 | 平台 | 说明 |
|---|---|---|---|---|
| cached | boolean |
false |
仅 Android | 是否允许读取缓存中的 NDEF 消息(首次扫描时系统可能已读取) |
| completeListener | (res: ResponseEntity) => void |
- | 全部 | 操作完成回调,res.data 为 NdefInfo[] 的 JSON 字符串 |
回调 data: NdefInfo[] — 详见上方「数据结构说明」。注意实际返回为 JSON 字符串格式,可用 JSON.parse 解析。
示例:
reader.readNDEFRecords({
cached: false,
completeListener(res) {
if (!res.success || res.data == null) {
console.log(`读取 NDEF 失败: ${res.message}`)
return
}
const records: reader.NdefInfo[] = JSON.parse(res.data)
records.forEach((record, index) => {
console.log(`记录 ${index}:`)
console.log(` TNF: ${record.typeNameFormat}`)
console.log(` 类型: ${record.type}`)
console.log(` 标识符: ${record.identifier}`)
console.log(` 负载: ${record.payload}`)
})
}
})
writeNDEFRecords
将 NDEF 记录写入卡片。调用前必须先成功执行 readCard,建议先通过 tag.ndefWritable 确认卡片可写。
参数:WriteNDEFParams
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | string |
- | NDEF 记录列表的 JSON 字符串,格式为 NdefInfo[] |
| completeListener | (res: ResponseEntity) => void |
- | 操作完成回调 |
注意:
data参数中的identifier、payload、type字段均需为十六进制字符串,不是纯文本。例如 Text 记录的 payload 需要先将文本转 hex。
示例:写入一条 Text 记录和一条 URI 记录
// 构建 NDEF 记录
const records: reader.NdefInfo[] = [
{
identifier: "", // 无标识符
payload: "02656E48656C6C6F", // Text: "en" + "Hello" 的 hex
type: "54", // "T" = Text 类型
typeNameFormat: "1" // Well Known
},
{
identifier: "",
payload: "0368747470733A2F2F7777772E6578616D706C652E636F6D", // URI: "https://www.example.com"
type: "55", // "U" = URI 类型
typeNameFormat: "1"
}
]
reader.writeNDEFRecords({
data: JSON.stringify(records),
completeListener(res) {
if (res.success) {
console.log("NDEF 写入成功")
} else {
console.log(`NDEF 写入失败: ${res.message}`)
}
}
})
makeNdefReadOnly
将当前 NDEF 标签设置为只读。该操作不可逆,调用前请确认卡片支持(tag.ndefCanMakeReadOnly 为 true)且业务允许。
参数:MakeNDEFReadOnlyParams
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| completeListener | (res: ResponseEntity) => void |
- | 操作完成回调 |
示例:
reader.makeNdefReadOnly({
completeListener(res) {
if (res.success) {
console.log("已将标签设为只读")
} else {
console.log(`设只读失败: ${res.message}`)
}
}
})
authenticateSector
对 Mifare Classic 卡片的指定扇区进行密钥认证。使用 KeyA 或 KeyB 认证后,才能对该扇区的块进行读写操作。
iOS 平台: CoreNFC 不支持直接操作 Mifare Classic 扇区认证,iOS 侧固定回调
res.success = true、res.data = true,以避免影响业务调用链。
参数:AuthenticateSectorParams
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| index | number |
- | 扇区索引,从 0 开始。Mifare Classic 1K 为 0–15,4K 为 0–39 |
| keyA | string |
无 | 6 字节 KeyA,十六进制字符串(12 个字符),如 "FFFFFFFFFFFF"。与 keyB 二选一 |
| keyB | string |
无 | 6 字节 KeyB,十六进制字符串(12 个字符),如 "FFFFFFFFFFFF"。与 keyA 二选一 |
| completeListener | (res: ResponseEntity) => void |
- | 操作完成回调,res.data 为 boolean 表示认证是否成功 |
示例:
// 用默认 KeyA 认证第 1 扇区
reader.authenticateSector({
index: 1,
keyA: "FFFFFFFFFFFF",
completeListener(res) {
if (res.success && res.data == true) {
console.log("扇区 1 认证成功,可以读写")
} else {
console.log(`认证失败: ${res.message}`)
}
}
})
readSector
读取 Mifare Classic 卡片的整个扇区(包含所有块的数据)。
iOS 平台: 不支持此操作,回调返回失败。
参数:ReadSectorParams
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| index | number |
- | 扇区索引,从 0 开始 |
| keyA | string |
无 | 6 字节 KeyA,十六进制字符串。与 keyB 二选一 |
| keyB | string |
无 | 6 字节 KeyB,十六进制字符串。与 keyA 二选一 |
| completeListener | (res: ResponseEntity) => void |
- | 操作完成回调,res.data 为整个扇区数据的十六进制字符串 |
注意: 插件内部会自动先执行认证再读取,无需手动调用
authenticateSector。但如果扇区密钥不是默认值,需要通过keyA或keyB传入正确的密钥。
示例:
// 读取扇区 0(通常包含厂商数据)
reader.readSector({
index: 0,
keyA: "FFFFFFFFFFFF",
completeListener(res) {
if (res.success) {
const sectorData = res.data as string
console.log(`扇区 0 数据: ${sectorData}`)
// 扇区 0 共 4 个块(Mifare Classic 1K),每块 32 个 hex 字符(16 字节)
// 块 0: sectorData.substring(0, 32)
// 块 1: sectorData.substring(32, 64)
// 块 2: sectorData.substring(64, 96)
// 块 3: sectorData.substring(96, 128) — 密钥区
} else {
console.log(`读取扇区失败: ${res.message}`)
}
}
})
readBlock
读取 Mifare Classic 的一个块(16 字节)或 Mifare Ultralight 的一个页(4 字节)。也可用于读取 ISO15693 卡片块数据。
iOS 平台: 主要支持
MiFare和ISO15693类型,不支持 Mifare Classic 原生扇区认证。 HarmonyOS 平台:ISO15693类型建议通过transceive兜底。
参数:ReadBlockParams
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| block | number |
- | 块/页索引,从 0 开始 |
| index | number |
无 | 扇区索引,从 0 开始。Mifare Classic 必填,Mifare Ultralight 可不传 |
| keyA | string |
无 | 6 字节 KeyA,十六进制字符串。与 keyB 二选一 |
| keyB | string |
无 | 6 字节 KeyB,十六进制字符串。与 keyA 二选一 |
| iso15693Flags | number |
无 | ISO15693 请求标志位(仅 ISO15693 卡片有效) |
| iso15693ExtendedMode | boolean |
无 | 是否使用扩展模式,支持块编号 0–255(仅 ISO15693 卡片有效) |
| completeListener | (res: ResponseEntity) => void |
- | 操作完成回调,res.data 为十六进制字符串 |
注意: 对于 Mifare Classic,插件内部会自动先对
index指定扇区执行认证再读取块,无需手动调用authenticateSector。Mifare Ultralight 无需认证,不传index/keyA/keyB即可。
示例 1:读取 Mifare Classic 块
// 读取扇区 1 的块 0(即绝对块号 4)
reader.readBlock({
block: 0,
index: 1,
keyA: "FFFFFFFFFFFF",
completeListener(res) {
if (res.success) {
console.log(`块数据: ${res.data}`) // 32 个 hex 字符 = 16 字节
} else {
console.log(`读块失败: ${res.message}`)
}
}
})
示例 2:读取 Mifare Ultralight 页
// 读取第 4 页
reader.readBlock({
block: 4,
completeListener(res) {
if (res.success) {
console.log(`页数据: ${res.data}`) // 8 个 hex 字符 = 4 字节
} else {
console.log(`读页失败: ${res.message}`)
}
}
})
示例 3:读取 ISO15693 块
reader.readBlock({
block: 0,
iso15693Flags: 0x20, // 高位地址模式
iso15693ExtendedMode: false,
completeListener(res) {
if (res.success) {
console.log(`ISO15693 块数据: ${res.data}`)
} else {
console.log(`读块失败: ${res.message}`)
}
}
})
writeBlock
写入 Mifare Classic 的一个块(16 字节)或 Mifare Ultralight 的一个页(4 字节)。也可用于写入 ISO15693 卡片块数据。
平台注意: 同
readBlock。
参数:WriteBlockParams
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| block | number |
- | 块/页索引,从 0 开始 |
| index | number |
无 | 扇区索引,从 0 开始。Mifare Classic 必填,Mifare Ultralight 可不传 |
| keyA | string |
无 | 6 字节 KeyA,十六进制字符串。与 keyB 二选一 |
| keyB | string |
无 | 6 字节 KeyB,十六进制字符串。与 keyA 二选一 |
| iso15693Flags | number |
无 | ISO15693 请求标志位(仅 ISO15693 卡片有效) |
| iso15693ExtendedMode | boolean |
无 | 是否使用扩展模式(仅 ISO15693 卡片有效) |
| data | string |
- | 要写入的数据,十六进制字符串 |
| completeListener | (res: ResponseEntity) => void |
- | 操作完成回调,res.data 为 boolean 表示是否写入成功 |
数据长度要求:
- Mifare Classic 单块:32 个 hex 字符(16 字节)
- Mifare Ultralight 单页:8 个 hex 字符(4 字节)
示例 1:写入 Mifare Classic 块
// 向扇区 1 的块 1 写入 16 字节数据
reader.writeBlock({
block: 1,
index: 1,
keyA: "FFFFFFFFFFFF",
data: "48656C6C6F20576F726C6421212121", // "Hello World!!!" 的 hex(16 字节)
completeListener(res) {
if (res.success && res.data == true) {
console.log("写块成功")
} else {
console.log(`写块失败: ${res.message}`)
}
}
})
示例 2:写入 Mifare Ultralight 页
// 向第 6 页写入 4 字节数据
reader.writeBlock({
block: 6,
data: "DEADBEEF",
completeListener(res) {
if (res.success && res.data == true) {
console.log("写页成功")
} else {
console.log(`写页失败: ${res.message}`)
}
}
})
示例 3:写入 ISO15693 块
reader.writeBlock({
block: 2,
iso15693Flags: 0x20,
iso15693ExtendedMode: false,
data: "AABBCCDD",
completeListener(res) {
if (res.success) {
console.log("ISO15693 写块成功")
} else {
console.log(`写块失败: ${res.message}`)
}
}
})
closeNFC
结束当前 NFC 会话,释放资源。建议每次完成读卡、透传、NDEF 读写或块操作后调用,避免会话残留影响下次使用。
参数:CloseNFCParams
| 参数 | 类型 | 默认值 | 平台 | 说明 |
|---|---|---|---|---|
| iosAlertMessage | string |
无 | 仅 iOS | 会话结束时系统弹窗显示的提示文字 |
| completeListener | (res: ResponseEntity) => void |
- | 全部 | 操作完成回调 |
示例:
reader.closeNFC({
iosAlertMessage: "读取完成",
completeListener(res) {
console.log("NFC 会话已关闭")
}
})
平台差异说明
不同平台底层 NFC 能力不同,部分 API 在三端的支持范围并不完全一致,接入前建议先阅读本节。
| API | Android | iOS | HarmonyOS | 说明 |
|---|---|---|---|---|
| initNFC | 支持 | 支持 | 支持 | 三端都支持初始化,但 timeout/androidSound 等参数仅在特定平台生效 |
| readCard | 支持 | 支持 | 支持 | 三端都支持读卡与返回基础卡信息 |
| transceive | 支持 | 支持 | 支持 | 三端都支持,但不同卡型支持范围取决于系统底层能力 |
| readNDEFRecords | 支持 | 支持 | 支持 | 三端实际返回的 data 为 JSON 字符串格式的记录列表 |
| writeNDEFRecords | 支持 | 支持 | 支持 | 三端都支持 NDEF 写入,但实际可写能力取决于卡片类型与系统支持 |
| makeNdefReadOnly | 支持 | 支持 | 支持 | 三端都支持,但是否允许设只读取决于标签本身 |
| authenticateSector | 支持 | 不支持(固定返回 true) |
支持 | iOS 无 Mifare Classic 扇区认证能力;为兼容前置调用链,默认返回成功 |
| readSector | 支持 | 不支持 | 支持 | iOS 当前未实现整扇区读取 |
| readBlock | 支持 | 部分支持 | 部分支持 | iOS 主要支持 MiFare/ISO15693;鸿蒙当前 ISO15693 需走 transceive 兜底 |
| writeBlock | 支持 | 部分支持 | 部分支持 | iOS 主要支持 MiFare/ISO15693;鸿蒙当前 ISO15693 需走 transceive 兜底 |
| closeNFC | 支持 | 支持 | 支持 | 三端都支持关闭当前会话 |
iOS 特别说明
authenticateSector在 iOS 中不支持真实的 Mifare Classic 扇区认证。- 由于很多业务会把
authenticateSector作为后续读写流程的前置 API,为避免直接中断调用链,iOS 侧当前默认返回true。 readSector在 iOS 中不支持,当前会直接返回失败。readBlock/writeBlock在 iOS 中并不是完整对齐 Android 的 Mifare Classic 语义,主要依赖 CoreNFC 对MiFare与ISO15693的能力支持。
HarmonyOS 特别说明
readBlock/writeBlock当前对ISO15693没有直接暴露独立块读写封装,建议通过transceive作为兜底方案。- HarmonyOS 当前实现中,卡片技术类型筛选依赖
readIso14443A、readIso14443B、readIso18092、readIso15693的组合;如果全部关闭,不会自动回退为全卡型扫描。
Android 特别说明
androidSound、androidCheckNDEF、androidReaderModeFlags、extraReaderPresenceCheckDelay仅 Android 平台生效。readBlock/writeBlock在 Android 侧对Mifare Classic、Mifare Ultralight、ISO15693的支持相对更完整。
快速开始
1. 引入插件
import * as reader from "@/uni_modules/cz-nfc-reader"
2. 初始化并读卡
reader.initNFC({
readIso14443A: true,
readIso14443B: true,
readIso15693: true,
readIso18092: true,
completeListener(res) {
if (res.success) {
console.log("NFC 初始化成功")
} else {
console.log(`NFC 初始化失败: ${res.message}`)
}
}
})
3. 读卡并获取卡片信息
reader.readCard({
completeListener(res) {
if (!res.success || res.data == null) {
console.log(`读卡失败: ${res.message}`)
return
}
const tag = res.data as reader.NfcTag
console.log(`卡号: ${tag.id}`)
console.log(`标准: ${tag.standard}`)
console.log(`类型: ${tag.type}`)
console.log(`支持NDEF: ${tag.ndefAvailable}`)
}
})
4. 读取 NDEF 记录
reader.readCard({
completeListener(card) {
if (!card.success || card.data == null) {
return
}
const tag = card.data as reader.NfcTag
if (tag.ndefAvailable != true) {
console.log("当前卡片不支持 NDEF")
reader.closeNFC({})
return
}
reader.readNDEFRecords({
cached: true,
completeListener(res) {
if (res.success) {
console.log(`NDEF 结果: ${JSON.stringify(res.data)}`)
}
reader.closeNFC({})
}
})
}
})
5. APDU 透传
reader.readCard({
completeListener(card) {
if (!card.success || card.data == null) {
return
}
reader.transceive({
capdu: "00A4040009A00000000386980701",
timeout: 5000,
completeListener(res) {
console.log(`透传结果: ${res.success} ${res.data}`)
reader.closeNFC({})
}
})
}
})
完整示例
以下是一个完整的读卡流程示例,包含初始化 → 读卡 → 判断卡类型 → 按类型执行不同操作的完整调用链。
import * as reader from "@/uni_modules/cz-nfc-reader"
export default {
methods: {
startRead() {
reader.initNFC({
readIso14443A: true,
readIso14443B: true,
readIso15693: true,
readIso18092: true,
completeListener: (initRes) => {
if (!initRes.success) {
uni.showModal({
content: initRes.message ?? "初始化失败",
showCancel: false
})
return
}
reader.readCard({
completeListener: (cardRes) => {
if (!cardRes.success || cardRes.data == null) {
uni.showModal({
content: cardRes.message ?? "读卡失败",
showCancel: false
})
return
}
const tag = cardRes.data as reader.NfcTag
console.log(`卡片信息: ${JSON.stringify(tag)}`)
if (tag.ndefAvailable == true) {
reader.readNDEFRecords({
cached: true,
completeListener: (ndefRes) => {
console.log(`NDEF: ${JSON.stringify(ndefRes.data)}`)
reader.closeNFC({})
}
})
} else {
reader.closeNFC({})
}
}
})
}
})
}
}
}
常用场景示例
场景:Mifare Classic 扇区读写
function readAndWriteSector() {
reader.readCard({
completeListener(cardRes) {
if (!cardRes.success || cardRes.data == null) return
const tag = cardRes.data as reader.NfcTag
if (tag.type != "mifare_classic") {
console.log("非 Mifare Classic 卡片")
reader.closeNFC({})
return
}
// 步骤1:认证扇区 1
reader.authenticateSector({
index: 1,
keyA: "FFFFFFFFFFFF",
completeListener(authRes) {
if (!authRes.success || authRes.data != true) {
console.log(`认证失败: ${authRes.message}`)
reader.closeNFC({})
return
}
// 步骤2:读取扇区 1 的块 0
reader.readBlock({
block: 0,
index: 1,
keyA: "FFFFFFFFFFFF",
completeListener(readRes) {
console.log(`块数据: ${readRes.data}`)
// 步骤3:写入数据到扇区 1 的块 1
reader.writeBlock({
block: 1,
index: 1,
keyA: "FFFFFFFFFFFF",
data: "00112233445566778899AABBCCDDEEFF", // 16 字节
completeListener(writeRes) {
console.log(`写入结果: ${writeRes.success}`)
reader.closeNFC({})
}
})
}
})
}
})
}
})
}
场景:NDEF 写入并设为只读
function writeNdefAndMakeReadOnly() {
reader.readCard({
completeListener(cardRes) {
if (!cardRes.success || cardRes.data == null) return
const tag = cardRes.data as reader.NfcTag
if (!tag.ndefWritable) {
console.log("卡片不支持 NDEF 写入")
reader.closeNFC({})
return
}
// 构建一条 Text 记录
const records: reader.NdefInfo[] = [{
identifier: "",
payload: "02656E48656C6C6F", // "en" + "Hello"
type: "54", // Text
typeNameFormat: "1" // Well Known
}]
reader.writeNDEFRecords({
data: JSON.stringify(records),
completeListener(writeRes) {
if (!writeRes.success) {
console.log(`写入失败: ${writeRes.message}`)
reader.closeNFC({})
return
}
console.log("NDEF 写入成功")
// 设为只读(不可逆)
reader.makeNdefReadOnly({
completeListener(roRes) {
if (roRes.success) {
console.log("已设为只读")
} else {
console.log(`设只读失败: ${roRes.message}`)
}
reader.closeNFC({})
}
})
}
})
}
})
}
权限说明
Android
插件已包含 NFC 权限声明:
<uses-permission android:name="android.permission.NFC"/>
iOS
插件已包含以下配置:
/// Info.plist 参考配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NFCReaderUsageDescription</key>
<string>需要使用NFC读取卡片信息</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>nfc</string>
</array>
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
<string>88B4</string>
</array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A00000000386980701</string>
</array>
</dict>
</plist>
/// UTS.entitlements 参考配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
<string>NDEF</string>
</array>
</dict>
</plist>
同时已预置 FeliCa 与 ISO7816 相关会话配置。
iOS 侧还需要注意以下事项:
- 插件要求
iOS 13+。 - 需要在项目的
UTS.entitlements中添加Near Field Communication Tag Reader Session Formats能力。 - 需要在
Info.plist中添加NFCReaderUsageDescription。 - 根据业务需要,在
Info.plist中添加:com.apple.developer.nfc.readersession.felica.systemcodescom.apple.developer.nfc.readersession.iso7816.select-identifiers
- 特别注意:在
iOS 14.5及更早版本上,如果调用轮询前启用了readIso18092或readIso15693,那么必须先正确配置上面的 FeliCa / ISO7816 相关项。否则受CoreNFC已知问题影响,设备 NFC 可能在重启前都不可用。 - 如果是原生工程联调,打开
Runner.xcworkspace,进入 Xcode 的项目设置页面,在Signing & Capabilities中为目标应用添加Near Field Communication Tag Reading能力。
HarmonyOS
插件已声明:
{
"requestPermissions": [
{
"name": "ohos.permission.NFC_TAG"
}
]
}
注意事项
- 仅支持手机真机调试,不支持模拟器。
- 使用
transceive、readNDEFRecords、writeNDEFRecords、readBlock、writeBlock前,必须先成功调用readCard。 - 调用完成后建议执行
closeNFC关闭会话,避免影响下次读卡。 makeNdefReadOnly为不可逆操作,请谨慎使用。- 不同平台、不同卡型支持能力不同,业务接入前建议先通过
readCard返回的type、standard、ndefAvailable等字段进行判断。 - 所有 API 的
data字段中涉及二进制数据的(如 APDU 指令、块数据、NDEF 负载等),均使用十六进制字符串表示,业务层需自行处理 hex ↔ 文本/字节的转换。 - Mifare Classic 密钥
keyA/keyB必须是 12 个十六进制字符(6 字节),如"FFFFFFFFFFFF"表示全 F 的默认密钥。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 778
赞赏 0
下载 12512793
赞赏 1943
赞赏
京公网安备:11010802035340号