<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 };