news 2026/7/2 11:40:19

uniapp APP端实现NFC读卡功能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
uniapp APP端实现NFC读卡功能
<template> <view class="main"> <u-button type="primary" text="开启NFC" @click="openNfc"></u-button> <!-- NFC读取弹窗 --> <u-popup :show="showNfcPopup" mode="center" :zoom="false" @close="showNfcPopup = false" closeOnClickOverlay > <view v-if="showNfcPopup" class="nfc-popup-content"> <view class="nfc-icon-wrapper"> <view class="nfc-icon"> <text class="iconfont icon-nfc"></text> <view class="pulse-ring"></view> <view class="pulse-ring delay-1"></view> <view class="pulse-ring delay-2"></view> </view> </view> <text class="nfc-title">NFC 识别中</text> <text class="nfc-desc">请将设备靠近感应区</text> <view class="scan-line"></view> </view> </u-popup> </view> </template> <script> import nfcReader from "@/utils/nfcReader" export default { data() { return { nfcData: null,//NFC数据 showNfcPopup:false } }, onLoad() { this.initNFCReader()//初始化NFC }, onUnload() { // 移除全局事件监听 nfcReader.destroy(); }, methods: { openNfc() { if (nfcReader.startRead()) { this.showNfcPopup=true } }, // NFC初始化 initNFCReader() { nfcReader.init({ onRead: result => this.handleNFCRead(result), onError: error => this.handleNFCError(error) }); }, // NFC读取到的数据 handleNFCRead(result) { this.nfcData = result; console.log('nfc_id:', result.nfcId); console.log('nfc_id_reverse:', result.reversedNfcId); console.log('NFC 数据:', result.nfcText); this.showNfcPopup = false; }, handleNFCError(e) { console.error('NFC读取失败', e); uni.showToast({ icon: 'none', title: 'NFC读取失败,请重试' }); }, } } </script> <style lang="scss" scoped> .nfc-popup-content { width: 500rpx; background: #1a2332; border-radius: 40rpx; padding: 60rpx 40rpx; display: flex; flex-direction: column; align-items: center; color: #fff; position: relative; overflow: hidden; } .nfc-icon-wrapper { position: relative; width: 160rpx; height: 160rpx; margin-bottom: 30rpx; } .pulse-ring { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 4rpx solid #00cfff; border-radius: 50%; animation: nfcPulse 2s ease-out infinite; opacity: 0; } .pulse-ring.delay-1 { animation-delay: 0.6s; } .pulse-ring.delay-2 { animation-delay: 1.2s; } @keyframes nfcPulse { 0% { transform: scale(0.8); opacity: 0.8; } 100% { transform: scale(1.8); opacity: 0; } } .scan-line { position: absolute; bottom: 0; left: 0; width: 100%; height: 4rpx; background: linear-gradient(90deg, transparent, #00cfff, transparent); box-shadow: 0 0 20rpx #00cfff; animation: nfcScan 1.5s linear infinite; } @keyframes nfcScan { 0% { bottom: 0%; opacity: 1; } 50% { bottom: 100%; opacity: 0.5; } 100% { bottom: 0%; opacity: 1; } } .nfc-icon { position: relative; z-index: 2; width: 160rpx; height: 160rpx; background: #2a3a4a; border-radius: 50%; display: flex; align-items: center; justify-content: center; animation: iconBreath 1.5s ease-in-out infinite; } @keyframes iconBreath { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.9); } } </style>

nfcReader.js

const TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED'; const TECH_LISTS = [ ['android.nfc.tech.IsoDep'], ['android.nfc.tech.NfcA'], ['android.nfc.tech.NfcB'], ['android.nfc.tech.NfcF'], ['android.nfc.tech.Nfcf'], ['android.nfc.tech.NfcV'], ['android.nfc.tech.NdefFormatable'], ['android.nfc.tech.MifareClassi'], ['android.nfc.tech.MifareUltralight'] ]; let NfcAdapter = null; let main = null; let nfcAdapter = null; let pendingIntent = null; let intentFiltersArray = null; let readyRead = false; let noNFC = false; let readDelay = 1000; let callbacks = { onRead: null, onError: null }; let handlers = { newintent: null, pause: null, resume: null }; function toast(content) { uni.showToast({ title: content, icon: 'none' }); } function normalizeNfcValue(value) { return String(value || '').replace(/[\s:-]/g, '').toUpperCase(); } function reverseNfcHex(value) { const normalizedValue = normalizeNfcValue(value); if (!normalizedValue || normalizedValue.length % 2 !== 0) return normalizedValue; return normalizedValue.match(/.{2}/g).reverse().join(''); } function byteArrayToHexString(inarray) { if (!inarray) return ''; const hex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']; let out = ''; for (let j = 0; j < inarray.length; ++j) { const inn = inarray[j] & 0xff; let i = (inn >>> 4) & 0x0f; out += hex[i]; i = inn & 0x0f; out += hex[i]; } return out; } function getNfcRecordText(record) { const payload = record.getPayload(); if (!payload || payload.length === 0) return ''; const text = plus.android.newObject('java.lang.String', payload, 'UTF-8').toString(); const type = plus.android.newObject('java.lang.String', record.getType(), 'UTF-8').toString(); if (type === 'T' && payload.length > 1) { const languageLength = payload[0] & 0x3f; return text.substring(languageLength + 1).trim(); } return text.trim(); } function readIntent(intent) { const bytesId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID); const nfcId = byteArrayToHexString(bytesId); const rawmsgs = intent.getParcelableArrayExtra('android.nfc.extra.NDEF_MESSAGES'); let nfcText = ''; if (rawmsgs != null && rawmsgs.length > 0) { const records = rawmsgs[0].getRecords(); if (records != null && records.length > 0) { nfcText = getNfcRecordText(records[0]); } } return { nfcId, reversedNfcId: reverseNfcHex(nfcId), nfcText }; } function runNfc() { try { const intent = main.getIntent(); console.log('action type:' + intent.getAction()); if (TECH_DISCOVERED === intent.getAction() && readyRead) { readyRead = false; const result = readIntent(intent); if (typeof callbacks.onRead === 'function') callbacks.onRead(result); } } catch (e) { readyRead = true; if (typeof callbacks.onError === 'function') callbacks.onError(e); } } function enableForegroundDispatch() { if (nfcAdapter && main) { nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, TECH_LISTS); } } function disableForegroundDispatch() { if (nfcAdapter && main) { nfcAdapter.disableForegroundDispatch(main); } } function bindEvents() { handlers.newintent = function() { console.log('newintent running'); setTimeout(runNfc, readDelay); }; handlers.pause = function() { console.log('pause running'); disableForegroundDispatch(); }; handlers.resume = function() { console.log('resume running'); enableForegroundDispatch(); }; plus.globalEvent.addEventListener('newintent', handlers.newintent); plus.globalEvent.addEventListener('pause', handlers.pause); plus.globalEvent.addEventListener('resume', handlers.resume); } function unbindEvents() { if (typeof plus === 'undefined' || !plus.globalEvent || !plus.globalEvent.removeEventListener) return; if (handlers.newintent) plus.globalEvent.removeEventListener('newintent', handlers.newintent); if (handlers.pause) plus.globalEvent.removeEventListener('pause', handlers.pause); if (handlers.resume) plus.globalEvent.removeEventListener('resume', handlers.resume); handlers.newintent = null; handlers.pause = null; handlers.resume = null; } export default { init(options = {}) { this.destroy(); callbacks.onRead = options.onRead || null; callbacks.onError = options.onError || null; readDelay = options.readDelay || 1000; try { if (typeof plus === 'undefined' || !plus.android) { noNFC = true; return false; } main = plus.android.runtimeMainActivity(); const Intent = plus.android.importClass('android.content.Intent'); const PendingIntent = plus.android.importClass('android.app.PendingIntent'); const IntentFilter = plus.android.importClass('android.content.IntentFilter'); NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter'); nfcAdapter = NfcAdapter.getDefaultAdapter(main); if (nfcAdapter == null) { noNFC = true; toast('设备不支持NFC!'); return false; } if (!nfcAdapter.isEnabled()) { noNFC = true; toast('请在系统设置中先启用NFC功能!'); return false; } noNFC = false; const intent = new Intent(main, main.getClass()); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); pendingIntent = PendingIntent.getActivity(main, 0, intent, 0); const ndef = new IntentFilter(TECH_DISCOVERED); ndef.addDataType('*/*'); intentFiltersArray = [ndef]; bindEvents(); enableForegroundDispatch(); return true; } catch (e) { noNFC = true; if (typeof callbacks.onError === 'function') callbacks.onError(e); return false; } }, startRead() { if (noNFC) { toast('请检查设备是否支持并开启NFC'); return false; } readyRead = true; // toast('请将NFC标签靠近'); return true; }, destroy() { try { unbindEvents(); disableForegroundDispatch(); } catch (e) { if (typeof callbacks.onError === 'function') callbacks.onError(e); } main = null; nfcAdapter = null; pendingIntent = null; intentFiltersArray = null; readyRead = false; }, isAvailable() { return !noNFC; }, normalizeNfcValue, reverseNfcHex };
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/2 11:38:53

掌握演讲时间的终极免费工具:PPTTimer 完全指南

掌握演讲时间的终极免费工具&#xff1a;PPTTimer 完全指南 【免费下载链接】ppttimer 一个简易的 PPT 计时器 项目地址: https://gitcode.com/gh_mirrors/pp/ppttimer 你是否曾在重要演讲中因时间失控而尴尬收场&#xff1f;PPTTimer 正是为解决这一痛点而生的智能演示…

作者头像 李华
网站建设 2026/7/2 11:38:06

OAuth2 认证全链路实现:客户端凭证流、服务端授权与令牌刷新机制源码剖析

一、引言:当认证成为架构瓶颈 你有没有遇到过这样的场景——微服务之间调用需要频繁传递用户凭证,每次都要重新登录;单页面应用(SPA)的Token过期后用户体验断崖式下跌;服务端守护进程需要访问受保护资源却找不到合适的认证方式? 在分布式系统架构下,用户认证授权面临…

作者头像 李华
网站建设 2026/7/2 11:35:25

终极Steam创意工坊下载指南:WorkshopDL轻松获取1000+游戏模组

终极Steam创意工坊下载指南&#xff1a;WorkshopDL轻松获取1000游戏模组 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否曾因没有Steam账号而无法下载《盖瑞的模组》的创…

作者头像 李华
网站建设 2026/7/2 11:34:55

抖音批量下载技术方案:从零构建高效内容管理工具

抖音批量下载技术方案&#xff1a;从零构建高效内容管理工具 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖…

作者头像 李华
网站建设 2026/7/2 11:34:52

【Claude】IDE 集成与编辑器配置全攻略 — 已解决

【Claude】IDE 集成与编辑器配置全攻略 — 已解决 适用版本&#xff1a;Claude Code v1.0.x 及以上受影响场景&#xff1a;VS Code、JetBrains、Neovim、Emacs 集成阅读时长&#xff1a;约 25 分钟 目录 问题现象原理深挖&#xff1a;IDE 集成架构根因分析&#xff1a;集成中的…

作者头像 李华