修改NFC读取
This commit is contained in:
421
src/components/NFCTemplate.vue
Normal file
421
src/components/NFCTemplate.vue
Normal file
@@ -0,0 +1,421 @@
|
||||
<template>
|
||||
<view class="nfc-con">
|
||||
<view class="nfc-bg">
|
||||
<view class="nfc-title">
|
||||
<uni-icons type="closeempty" size="20" class="nfc-close" @click="handleClose"></uni-icons>NFC识别
|
||||
</view>
|
||||
<block v-if="!nfcResult">
|
||||
<view class="nfc-pic" @click="readData" v-if="!isReading">
|
||||
<img :src="'static/images/polling/nfc-logo.png'" />
|
||||
</view>
|
||||
<view class="nfc-pic" v-else>
|
||||
<img class="nfc-pic-animal" :src="'static/images/polling/nfc-logo.png'" />
|
||||
</view>
|
||||
<view class="nfc-desc">请将设备靠近NFC识别</view>
|
||||
</block>
|
||||
|
||||
<!-- 识别成功 -->
|
||||
<view class="result-section" v-else>
|
||||
<block v-if="readStatus">
|
||||
<view class="result-header">
|
||||
<img class="result-img" :src="'static/images/polling/icon-success.png'" />
|
||||
<view class="result-label">识别成功</view>
|
||||
</view>
|
||||
<view class="result-value">
|
||||
<view>识别时间</view>
|
||||
<view>{{parseTime(new Date().getTime(),'{y}年{m}月{d}日 星期{a} {h}:{i}')}}</view>
|
||||
</view>
|
||||
<view class="btn-blue" @click="handleConfirm">确定</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
<view class="result-header">
|
||||
<img class="result-img" :src="'static/images/polling/icon-Alert.png'" />
|
||||
<view class="result-label result-err">识别失败</view>
|
||||
</view>
|
||||
<view class="result-value">
|
||||
可能的失败原因:手机距离NFC设备较远、手机未联网、NFC设备故障……
|
||||
</view>
|
||||
<view class="btn-blue" @click="readData">重新识别</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref,watch } from 'vue'
|
||||
import { onLoad,onHide} from '@dcloudio/uni-app';
|
||||
import { parseTime } from '@/utils/datetime.js';
|
||||
|
||||
// 包路径
|
||||
const package_NdefRecord = 'android.nfc.NdefRecord';
|
||||
const package_NdefMessage = 'android.nfc.NdefMessage';
|
||||
const package_TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED';
|
||||
const package_Intent = 'android.content.Intent';
|
||||
const package_Activity = 'android.app.Activity';
|
||||
const package_PendingIntent = 'android.app.PendingIntent';
|
||||
const package_IntentFilter = 'android.content.IntentFilter';
|
||||
const package_NfcAdapter = 'android.nfc.NfcAdapter';
|
||||
const package_Ndef = 'android.nfc.tech.Ndef';
|
||||
const package_NdefFormatable = 'android.nfc.tech.NdefFormatable';
|
||||
const package_Parcelable = 'android.os.Parcelable';
|
||||
const package_String = 'java.lang.String';
|
||||
|
||||
let NfcAdapter;
|
||||
let NdefRecord;
|
||||
let NdefMessage;
|
||||
let Uri;
|
||||
let readyWriteData = false;
|
||||
let readyRead = false;
|
||||
let noNFC = false;
|
||||
let techListsArray = [
|
||||
['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.net.Uri'],
|
||||
['android.nfc.tech.NdefFormatable'],
|
||||
['android.nfc.tech.MifareClassi'],
|
||||
['android.nfc.tech.MifareUltralight']
|
||||
];
|
||||
|
||||
let text = '';// 要写入的数据
|
||||
let readResult = '';
|
||||
|
||||
const isReading=ref(false)
|
||||
const readStatus=ref(false);//读取状态 true 成功
|
||||
const nfcResult=ref('')
|
||||
|
||||
// onLoad(() => {
|
||||
// listenNFCStatus();
|
||||
// })
|
||||
|
||||
// 监听NFC状态
|
||||
const listenNFCStatus=()=>{
|
||||
console.log("listenNFCStatus=====begin")
|
||||
try {
|
||||
let main = plus.android.runtimeMainActivity();
|
||||
let Intent = plus.android.importClass('android.content.Intent');
|
||||
let Activity = plus.android.importClass('android.app.Activity');
|
||||
let PendingIntent = plus.android.importClass('android.app.PendingIntent');
|
||||
let IntentFilter = plus.android.importClass('android.content.IntentFilter');
|
||||
Uri = plus.android.importClass('android.net.Uri');
|
||||
NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
|
||||
let nfcAdapter = NfcAdapter.getDefaultAdapter(main);
|
||||
|
||||
if(nfcAdapter == null){
|
||||
uni.showToast({
|
||||
title: '设备不支持NFC!',
|
||||
icon: 'none'
|
||||
})
|
||||
noNFC = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nfcAdapter.isEnabled()) {
|
||||
uni.showToast({
|
||||
title: '请在系统设置中先启用NFC功能!',
|
||||
icon: 'none'
|
||||
});
|
||||
noNFC = true;
|
||||
return;
|
||||
}else{
|
||||
noNFC = false;
|
||||
}
|
||||
|
||||
let intent = new Intent(main, main.getClass());
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
let pendingIntent = PendingIntent.getActivity(main, 0, intent, 0);
|
||||
let ndef = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
|
||||
ndef.addDataType("*/*");
|
||||
let intentFiltersArray = [ndef];
|
||||
|
||||
plus.globalEvent.addEventListener('newintent',function() {
|
||||
// console.log('newintent running');
|
||||
// 监听 NFC
|
||||
setTimeout(nfcRuning(), 1000);
|
||||
});
|
||||
plus.globalEvent.addEventListener('pause',function(e) {
|
||||
// console.log('pause running');
|
||||
if (nfcAdapter) {
|
||||
//关闭前台调度系统
|
||||
//恢复默认状态
|
||||
nfcAdapter.disableForegroundDispatch(main);
|
||||
}
|
||||
});
|
||||
plus.globalEvent.addEventListener('resume',function(e) {
|
||||
// console.log('resume running');
|
||||
if (nfcAdapter) {
|
||||
//开启前台调度系统
|
||||
nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
|
||||
}
|
||||
});
|
||||
nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 运行nfc
|
||||
const nfcRuning=()=>{
|
||||
NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
|
||||
NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
|
||||
let main = plus.android.runtimeMainActivity();
|
||||
let intent = main.getIntent();
|
||||
console.log("action type:" + intent.getAction());
|
||||
if (package_TECH_DISCOVERED == intent.getAction()) {
|
||||
if (readyWriteData) {
|
||||
write(intent);
|
||||
readyWriteData = false;
|
||||
} else if (readyRead) {
|
||||
read(intent);
|
||||
readyRead = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 写入nfc
|
||||
const write=(intent)=>{
|
||||
try {
|
||||
toast('请勿移开标签 正在写入...');
|
||||
console.log("text=" + text);
|
||||
let textBytes = plus.android.invoke(text, "getBytes");
|
||||
// image/jpeg text/plain
|
||||
let uri = Uri.parse(text)
|
||||
let message = new NdefMessage([new NdefRecord.createUri(uri)]);
|
||||
console.log("====message===>", message)
|
||||
let Ndef = plus.android.importClass('android.nfc.tech.Ndef');
|
||||
let NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
|
||||
let tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||
let ndef = Ndef.get(tag);
|
||||
if (ndef != null) {
|
||||
// 待写入的数据长度
|
||||
let size = message.toByteArray().length;
|
||||
ndef.connect();
|
||||
if (!ndef.isWritable()) {
|
||||
toast('tag不允许写入!');
|
||||
return;
|
||||
}
|
||||
if (ndef.getMaxSize() < size) {
|
||||
toast('文件大小超出容量!');
|
||||
return;
|
||||
}
|
||||
ndef.writeNdefMessage(message);
|
||||
toast('写入数据成功!');
|
||||
return;
|
||||
} else {
|
||||
let format = NdefFormatable.get(tag);
|
||||
if (format != null) {
|
||||
try {
|
||||
format.connect();
|
||||
format.format(message);
|
||||
toast('格式化tag并且写入message');
|
||||
return;
|
||||
} catch (e) {
|
||||
toast('格式化tag失败.');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
toast('Tag不支持NDEF');
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
toast('写入失败');
|
||||
console.log("error=" + e);
|
||||
}
|
||||
|
||||
}
|
||||
// 读取nfc
|
||||
const read=(intent)=>{
|
||||
toast('请勿移开标签正在读取数据');
|
||||
// NFC id
|
||||
let bytesId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
|
||||
let nfc_id = byteArrayToHexString(bytesId);
|
||||
console.log('nfc_id:', nfc_id);
|
||||
let Parcelable = plus.android.importClass("android.os.Parcelable");
|
||||
let rawmsgs = intent.getParcelableArrayExtra("android.nfc.extra.NDEF_MESSAGES");
|
||||
//let rawmsgs = intent.getParcelableArrayExtra();
|
||||
|
||||
// console.log('rawmsgs:', rawmsgs);
|
||||
if(rawmsgs != null && rawmsgs.length > 0) {
|
||||
let records = rawmsgs[0].getRecords();
|
||||
let result = records[0].getPayload();
|
||||
let data = plus.android.newObject("java.lang.String", result);
|
||||
console.log('data=>'+data);
|
||||
readResult = data;
|
||||
readStatus.value = true;
|
||||
nfcResult.value = data;
|
||||
toast('NFC 数据:' + data);
|
||||
// plus.runtime.openURL(data, function(res) {
|
||||
// console.log("NFC 数据----", res);
|
||||
// });
|
||||
// return data;
|
||||
}else{
|
||||
toast('没有读取到数据');
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// 字节转换
|
||||
const byteArrayToHexString=(inarray)=>{ // converts byte arrays to string
|
||||
let i, j, inn;
|
||||
let hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
|
||||
let out = "";
|
||||
|
||||
for(j = 0; j < inarray.length; ++j) {
|
||||
inn = inarray[j] & 0xff;
|
||||
i = (inn >>> 4) & 0x0f;
|
||||
out += hex[i];
|
||||
i = inn & 0x0f;
|
||||
out += hex[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// 正式调用写入
|
||||
const writeData= ()=>{
|
||||
if(noNFC){
|
||||
toast('请检查设备是否支持并开启 NFC 功能!');
|
||||
return;
|
||||
}
|
||||
// 监听事件,触发条件
|
||||
readyWriteData = true;
|
||||
toast('请将NFC标签靠近!');
|
||||
}
|
||||
|
||||
// 正式调用读
|
||||
const readData= ()=>{
|
||||
if(noNFC){
|
||||
toast('请检查设备是否支持并开启 NFC 功能!');
|
||||
return;
|
||||
}
|
||||
// 监听事件,触发条件
|
||||
readyRead = true;
|
||||
isReading.value = true;
|
||||
readStatus.value = false;
|
||||
// toast('请将NFC标签靠近!');
|
||||
}
|
||||
|
||||
// 提示
|
||||
const toast = (content)=>{
|
||||
uni.showToast({
|
||||
title: content,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 回调父组件
|
||||
const emit = defineEmits(['close','confirm'])
|
||||
const handleClose = ()=>{
|
||||
emit('close');
|
||||
}
|
||||
const handleConfirm= ()=>{
|
||||
emit('confirm',nfcResult.value);
|
||||
}
|
||||
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
listenNFCStatus
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nfc-con {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 9999;
|
||||
}
|
||||
.nfc-con .nfc-bg{
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
left:0;
|
||||
background-color:#fff;
|
||||
border-radius:18rpx 18rpx 0 0;
|
||||
text-align:center;
|
||||
width:650rpx;
|
||||
padding:30rpx 50rpx 130rpx;
|
||||
}
|
||||
.nfc-bg .nfc-title{
|
||||
text-align:center;
|
||||
color:#333333;
|
||||
font-size:32rpx;
|
||||
font-weight:bold;
|
||||
position:relative;
|
||||
}
|
||||
.nfc-bg .nfc-title .nfc-close{
|
||||
position:absolute;
|
||||
top:0rpx;
|
||||
left:-20rpx
|
||||
}
|
||||
.nfc-con .nfc-bg .nfc-pic{
|
||||
padding:126rpx 0 86rpx;
|
||||
}
|
||||
.nfc-con .nfc-bg .nfc-pic img{
|
||||
width:260rpx;
|
||||
height:208rpx;
|
||||
|
||||
}
|
||||
.nfc-con .nfc-bg .nfc-desc{
|
||||
text-align:center;
|
||||
color:#0395E0;
|
||||
font-size:36rpx;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.nfc-pic-animal{
|
||||
animation: elasticLoop 3s ease-in-out infinite;
|
||||
}
|
||||
@keyframes elasticLoop {
|
||||
0%, 100% { transform: scale(1); }
|
||||
25% { transform: scale(1.2); }
|
||||
50% { transform: scale(0.95); }
|
||||
75% { transform: scale(1.1); }
|
||||
}
|
||||
|
||||
|
||||
.result-section{
|
||||
.result-header{
|
||||
padding:70rpx 0 0;
|
||||
.result-img{
|
||||
width:120rpx;
|
||||
height:120rpx;
|
||||
}
|
||||
.result-label,.result-err{
|
||||
text-align:center;
|
||||
color:#00BF5A;
|
||||
font-size:36rpx;
|
||||
font-weight:bold;
|
||||
padding:20rpx 0;
|
||||
}
|
||||
.result-err{
|
||||
color:#FF9638;
|
||||
}
|
||||
}
|
||||
.result-value{
|
||||
color:#333;
|
||||
text-align:center;
|
||||
font-size:30rpx;
|
||||
margin-top:20rpx;
|
||||
}
|
||||
.btn-blue{
|
||||
background-color:#05A3F4;
|
||||
border-radius:40rpx;
|
||||
width:380rpx;
|
||||
height:80rpx;
|
||||
line-height:80rpx;
|
||||
text-align:center;
|
||||
color:#fff;
|
||||
font-size:36rpx;
|
||||
margin:50rpx auto 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -112,27 +112,6 @@
|
||||
"pid" : "",
|
||||
"parameters" : {}
|
||||
}
|
||||
},
|
||||
"LF-Sense-Card" : {
|
||||
"插件需要配置的参数名称, 如appid" : "",
|
||||
"__plugin_info__" : {
|
||||
"name" : "Android-NFC读卡",
|
||||
"description" : "uni-app android原生插件 用来使用nfc读取m1卡",
|
||||
"platforms" : "Android",
|
||||
"url" : "",
|
||||
"android_package_name" : "",
|
||||
"ios_bundle_id" : "",
|
||||
"isCloud" : false,
|
||||
"bought" : -1,
|
||||
"pid" : "",
|
||||
"parameters" : {
|
||||
"插件需要配置的参数名称, 如appid" : {
|
||||
"des" : "参数描述",
|
||||
"key" : "AndroidManifest.xml中添加meta-data节点!对应android:name属性值, 如GETUI_APPID",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -662,6 +662,18 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/polling/nfcTest/nfcTemplate",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/business/polling/nfcTest/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
78
src/pages/business/polling/nfcTest/index.vue
Normal file
78
src/pages/business/polling/nfcTest/index.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<input class="input-box" v-model="inputText" @input="input" type="text" placeholder="请输入内容"/>
|
||||
<view class="btn-box">
|
||||
<button class="btn-write" @click="writeData()">写信息</button>
|
||||
<button class="btn-read" @click="readData()">读信息</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import nfc from '@/utils/ouu-nfc.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
inputText: 'https://baidu.com'
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
nfc.listenNFCStatus();
|
||||
},
|
||||
methods: {
|
||||
input(e) {
|
||||
nfc.inputChanage(e.detail.value)
|
||||
},
|
||||
writeData() {
|
||||
nfc.writeData()
|
||||
},
|
||||
readData() {
|
||||
nfc.readData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
.iframe {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
.input-box {
|
||||
border: 1px solid #aaa;
|
||||
margin: 30px 0;
|
||||
padding: 10px;
|
||||
border-radius: 100px;
|
||||
}
|
||||
.btn-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.btn-write {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
.btn-read {
|
||||
width: 100%;
|
||||
margin-left: 20px;
|
||||
height: 100px;
|
||||
background-color: #1ee176;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
784
src/pages/business/polling/nfcTest/nfcTemplate.vue
Normal file
784
src/pages/business/polling/nfcTest/nfcTemplate.vue
Normal file
@@ -0,0 +1,784 @@
|
||||
<template>
|
||||
<view class="container" @touchstart="touchStart" @touchend="touchEnd">
|
||||
<view class="header">
|
||||
<text class="title">NFC卡片读取器</text>
|
||||
</view>
|
||||
|
||||
<view class="content" :class="{ 'landscape-layout': isLandscape }">
|
||||
<!-- NFC读取按钮 - 平板适配:按钮尺寸不小于48px×48px -->
|
||||
<button class="nfc-button" @click="startNFCReading" :disabled="isReading">
|
||||
<text v-if="!isReading">开始NFC读取</text>
|
||||
<view v-else class="loading-wrapper">
|
||||
<view class="loading-spinner"></view>
|
||||
<text>正在读取中...</text>
|
||||
</view>
|
||||
</button>
|
||||
|
||||
<!-- 读取状态提示 -->
|
||||
<view class="status-section">
|
||||
<text class="status-text" :class="statusClass">{{ statusMessage }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 读取结果显示区域 -->
|
||||
<view class="result-section" v-if="nfcResult">
|
||||
<view class="result-header">
|
||||
<text class="result-label">NFC ID:</text>
|
||||
<button class="copy-button" @click="copyToClipboard">复制</button>
|
||||
</view>
|
||||
<text class="result-value">{{ nfcResult }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 操作提示 -->
|
||||
<view class="instruction">
|
||||
<text class="instruction-title">操作指南:</text>
|
||||
<text class="instruction-text">1. 确保设备NFC功能已开启</text>
|
||||
<text class="instruction-text">2. 将NFC卡片贴近设备感应区域</text>
|
||||
<text class="instruction-text">3. 听到提示音或感受到震动时,表示读取成功</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<view class="footer">
|
||||
<text class="footer-text">支持卡片类型: ISO14443A/B, Mifare, NFC-V, FeliCa</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import nfcUtil from "@/utils/hexiii-nfc.js"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isReading: false,
|
||||
statusMessage: '准备就绪',
|
||||
statusClass: 'status-ready',
|
||||
nfcResult: '',
|
||||
statusHistory: [], // 记录状态历史
|
||||
isLandscape: false, // 是否横屏
|
||||
screenWidth: 0, // 屏幕宽度
|
||||
screenHeight: 0, // 屏幕高度
|
||||
deviceInfo: null, // 设备信息
|
||||
touchStartTime: 0, // 触摸开始时间
|
||||
touchStartX: 0, // 触摸开始X坐标
|
||||
touchStartY: 0 // 触摸开始Y坐标
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 获取设备信息
|
||||
this._getDeviceInfo();
|
||||
// 页面加载时检查NFC状态
|
||||
this._checkNFCStatus();
|
||||
// 添加屏幕旋转监听
|
||||
uni.onWindowResize(this._onWindowResize);
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 页面显示时重新检查NFC状态
|
||||
this._checkNFCStatus();
|
||||
},
|
||||
|
||||
onHide() {
|
||||
// 页面隐藏时暂停NFC监听
|
||||
if (this.isReading) {
|
||||
nfcUtil.stopNFCListening();
|
||||
this.isReading = false;
|
||||
}
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
// 页面卸载时清理NFC资源
|
||||
nfcUtil.cleanupNFC();
|
||||
// 移除屏幕旋转监听
|
||||
uni.offWindowResize(this._onWindowResize);
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 获取设备信息
|
||||
* @private
|
||||
*/
|
||||
_getDeviceInfo() {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
this.deviceInfo = res;
|
||||
this.screenWidth = res.screenWidth;
|
||||
this.screenHeight = res.screenHeight;
|
||||
// 判断是否为横屏
|
||||
this.isLandscape = res.windowWidth > res.windowHeight;
|
||||
console.log('设备信息:', res);
|
||||
console.log('当前屏幕方向:', this.isLandscape ? '横屏' : '竖屏');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听屏幕旋转
|
||||
* @private
|
||||
*/
|
||||
_onWindowResize(res) {
|
||||
if (res) {
|
||||
const isLandscape = res.windowWidth > res.windowHeight;
|
||||
// 屏幕方向改变时更新状态
|
||||
if (this.isLandscape !== isLandscape) {
|
||||
this.isLandscape = isLandscape;
|
||||
console.log('屏幕旋转为:', isLandscape ? '横屏' : '竖屏');
|
||||
// 屏幕旋转时重新调整布局
|
||||
this._adjustLayoutForOrientation();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据屏幕方向调整布局
|
||||
* @private
|
||||
*/
|
||||
_adjustLayoutForOrientation() {
|
||||
// 可以在这里进行特定的布局调整
|
||||
// 例如:横屏时调整按钮位置、改变元素大小等
|
||||
// 目前主要通过CSS媒体查询实现,这里预留接口
|
||||
console.log('调整布局适应屏幕方向');
|
||||
},
|
||||
|
||||
/**
|
||||
* 开始NFC读取
|
||||
*/
|
||||
async startNFCReading() {
|
||||
// 提供触觉反馈 - 平板用户体验增强
|
||||
this._provideHapticFeedback();
|
||||
|
||||
// 设置读取状态
|
||||
this.isReading = true;
|
||||
this.statusMessage = '等待NFC卡片...';
|
||||
this.statusClass = 'status-waiting';
|
||||
this._addStatusHistory('开始读取NFC');
|
||||
|
||||
try {
|
||||
// 调用NFC读取功能
|
||||
this._addStatusHistory('正在监听NFC信号...');
|
||||
const nfcId = await nfcUtil.listenNFCStatus();
|
||||
|
||||
// 确保ID有效
|
||||
if (nfcId) {
|
||||
// 成功读取,提供触觉反馈
|
||||
this._provideHapticFeedback('success');
|
||||
|
||||
// 播放成功提示音
|
||||
this._playBeepSound();
|
||||
|
||||
// 显示结果并输出到控制台
|
||||
this.nfcResult = nfcId;
|
||||
console.log('NFC读取结果:', nfcId);
|
||||
|
||||
// 更新状态
|
||||
this.statusMessage = '读取成功';
|
||||
this.statusClass = 'status-success';
|
||||
this._addStatusHistory(`成功读取ID: ${nfcId}`);
|
||||
|
||||
// 显示成功提示框
|
||||
uni.showModal({
|
||||
title: '读取成功',
|
||||
content: `NFC ID: ${nfcId}\n\n是否复制到剪贴板?`,
|
||||
showCancel: true,
|
||||
cancelText: '取消',
|
||||
confirmText: '复制',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.copyToClipboard();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.statusMessage = '未读取到NFC数据';
|
||||
this.statusClass = 'status-error';
|
||||
this._addStatusHistory('未读取到NFC数据');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('NFC读取错误:', error);
|
||||
|
||||
// 错误情况,提供触觉反馈
|
||||
this._provideHapticFeedback('error');
|
||||
|
||||
// 根据错误类型显示不同的提示
|
||||
let errorMsg = '读取失败';
|
||||
if (error.message.includes('不支持NFC')) {
|
||||
errorMsg = '设备不支持NFC功能';
|
||||
} else if (error.message.includes('未开启')) {
|
||||
errorMsg = '请先在系统设置中开启NFC功能';
|
||||
} else if (error.message.includes('超时')) {
|
||||
errorMsg = '读取超时,请重试';
|
||||
} else {
|
||||
errorMsg = '读取失败: ' + error.message;
|
||||
}
|
||||
|
||||
this.statusMessage = errorMsg;
|
||||
this.statusClass = 'status-error';
|
||||
this._addStatusHistory(`错误: ${error.message}`);
|
||||
|
||||
// 显示错误提示
|
||||
uni.showModal({
|
||||
title: '读取失败',
|
||||
content: errorMsg,
|
||||
showCancel: false,
|
||||
confirmText: '确定'
|
||||
});
|
||||
} finally {
|
||||
// 重置读取状态
|
||||
this.isReading = false;
|
||||
|
||||
// 3秒后重置状态消息
|
||||
setTimeout(() => {
|
||||
if (!this.isReading) {
|
||||
this.statusMessage = '准备就绪';
|
||||
this.statusClass = 'status-ready';
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 复制NFC ID到剪贴板
|
||||
*/
|
||||
copyToClipboard() {
|
||||
if (this.nfcResult) {
|
||||
uni.setClipboardData({
|
||||
data: this.nfcResult,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '已复制到剪贴板',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查NFC状态
|
||||
* @private
|
||||
*/
|
||||
_checkNFCStatus() {
|
||||
try {
|
||||
// 尝试初始化NFC适配器检查状态
|
||||
const main = plus.android.runtimeMainActivity();
|
||||
const NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
|
||||
const nfcAdapter = NfcAdapter.getDefaultAdapter(main);
|
||||
|
||||
if (nfcAdapter == null) {
|
||||
this.statusMessage = '设备不支持NFC功能';
|
||||
this.statusClass = 'status-error';
|
||||
} else if (!nfcAdapter.isEnabled()) {
|
||||
this.statusMessage = 'NFC功能未开启,请在系统设置中开启';
|
||||
this.statusClass = 'status-warning';
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('NFC状态检查失败,可能在非Android环境');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 提供触觉反馈
|
||||
* @private
|
||||
* @param {string} type - 反馈类型: 'success', 'error'或默认
|
||||
*/
|
||||
_provideHapticFeedback(type = 'default') {
|
||||
try {
|
||||
// 在Android设备上提供震动反馈
|
||||
const VIBRATOR_SERVICE = 'vibrator';
|
||||
const Context = plus.android.importClass('android.content.Context');
|
||||
const main = plus.android.runtimeMainActivity();
|
||||
const vibrator = main.getSystemService(VIBRATOR_SERVICE);
|
||||
|
||||
// 根据类型设置不同的震动模式
|
||||
if (vibrator) {
|
||||
if (type === 'success') {
|
||||
// 成功震动模式: 短震动
|
||||
vibrator.vibrate(100);
|
||||
} else if (type === 'error') {
|
||||
// 错误震动模式: 长震动
|
||||
vibrator.vibrate(300);
|
||||
} else {
|
||||
// 默认震动模式: 轻微震动
|
||||
vibrator.vibrate(50);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 如果设备不支持震动或没有权限,忽略错误
|
||||
console.log('无法提供震动反馈:', e.message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 播放提示音
|
||||
* @private
|
||||
*/
|
||||
_playBeepSound() {
|
||||
try {
|
||||
// 使用系统默认提示音
|
||||
const AudioManager = plus.android.importClass('android.media.AudioManager');
|
||||
const ToneGenerator = plus.android.importClass('android.media.ToneGenerator');
|
||||
const main = plus.android.runtimeMainActivity();
|
||||
|
||||
const toneGen = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
|
||||
toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
|
||||
} catch (e) {
|
||||
// 如果无法播放提示音,忽略错误
|
||||
console.log('无法播放提示音:', e.message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加状态历史记录
|
||||
* @private
|
||||
* @param {string} message - 状态消息
|
||||
*/
|
||||
_addStatusHistory(message) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
this.statusHistory.push(`[${timestamp}] ${message}`);
|
||||
// 只保留最近10条记录
|
||||
if (this.statusHistory.length > 10) {
|
||||
this.statusHistory.shift();
|
||||
}
|
||||
console.log(`[NFC状态] ${message}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 触摸开始事件处理(用于长按等手势识别)
|
||||
* @private
|
||||
*/
|
||||
touchStart(event) {
|
||||
this.touchStartTime = Date.now();
|
||||
this.touchStartX = event.touches[0].clientX;
|
||||
this.touchStartY = event.touches[0].clientY;
|
||||
},
|
||||
|
||||
/**
|
||||
* 触摸结束事件处理(用于长按等手势识别)
|
||||
* @private
|
||||
*/
|
||||
touchEnd(event) {
|
||||
// 可以在这里添加手势识别逻辑
|
||||
// 例如:长按识别、滑动检测等
|
||||
const touchEndTime = Date.now();
|
||||
const touchEndX = event.changedTouches[0].clientX;
|
||||
const touchEndY = event.changedTouches[0].clientY;
|
||||
|
||||
// 计算触摸时间和移动距离
|
||||
const touchDuration = touchEndTime - this.touchStartTime;
|
||||
const distanceX = Math.abs(touchEndX - this.touchStartX);
|
||||
const distanceY = Math.abs(touchEndY - this.touchStartY);
|
||||
|
||||
// 长按检测(超过500ms且移动距离小于10px)
|
||||
if (touchDuration > 500 && distanceX < 10 && distanceY < 10) {
|
||||
console.log('长按操作');
|
||||
// 长按可以触发特殊功能,如显示详细信息
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
padding: 32rpx;
|
||||
background-color: #f5f5f5;
|
||||
/* 增加背景纹理,提升平板视觉体验 */
|
||||
background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.02) 25%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.02) 75%, rgba(0, 0, 0, 0.02)),
|
||||
linear-gradient(45deg, rgba(0, 0, 0, 0.02) 25%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.02) 75%, rgba(0, 0, 0, 0.02));
|
||||
background-size: 200rpx 200rpx;
|
||||
background-position: 0 0, 100rpx 100rpx;
|
||||
}
|
||||
|
||||
/* 横屏布局 */
|
||||
.content.landscape-layout {
|
||||
/* 横屏时优化内容区布局 */
|
||||
max-width: 960rpx;
|
||||
margin: 0 auto;
|
||||
/* 为横屏模式添加微妙的阴影效果 */
|
||||
box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.05);
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
border-radius: 16rpx;
|
||||
padding: 40rpx;
|
||||
backdrop-filter: blur(5rpx);
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 48rpx;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #212121;
|
||||
/* 平板适配:增加文字阴影提升可读性 */
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
/* NFC按钮 - 平板适配:按钮高度不小于60px */
|
||||
.nfc-button {
|
||||
width: 600rpx;
|
||||
height: 120rpx;
|
||||
line-height: 120rpx;
|
||||
font-size: 36rpx;
|
||||
background-color: #1976D2;
|
||||
color: white;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* 平板适配:增加按钮阴影和触摸反馈 */
|
||||
box-shadow: 0 4rpx 12rpx rgba(25, 118, 210, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 水波纹效果 - 符合Android设计规范 */
|
||||
.nfc-button::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
}
|
||||
|
||||
.nfc-button:active::after {
|
||||
width: 120%;
|
||||
height: 120%;
|
||||
}
|
||||
|
||||
.nfc-button:active {
|
||||
background-color: #1565C0;
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 6rpx rgba(25, 118, 210, 0.3);
|
||||
}
|
||||
|
||||
.nfc-button[disabled] {
|
||||
background-color: #90CAF9;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 4rpx solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.status-section {
|
||||
margin-bottom: 32rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
border-radius: 8rpx;
|
||||
background-color: white;
|
||||
min-width: 500rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.status-ready {
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
.status-waiting {
|
||||
color: #1976D2;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
.status-success {
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
background-color: white;
|
||||
padding: 32rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 32rpx;
|
||||
min-width: 500rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.result-section:hover {
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.result-label {
|
||||
font-size: 28rpx;
|
||||
color: #757575;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
min-width: 120rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
font-size: 24rpx;
|
||||
background-color: #E3F2FD;
|
||||
color: #1976D2;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.copy-button:active {
|
||||
background-color: #BBDEFB;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
font-size: 36rpx;
|
||||
font-family: monospace;
|
||||
color: #212121;
|
||||
word-break: break-all;
|
||||
line-height: 1.5;
|
||||
padding: 16rpx;
|
||||
background-color: #FAFAFA;
|
||||
border-radius: 6rpx;
|
||||
border: 1rpx solid #EEEEEE;
|
||||
}
|
||||
|
||||
.instruction {
|
||||
margin-top: 32rpx;
|
||||
background-color: white;
|
||||
padding: 32rpx;
|
||||
border-radius: 8rpx;
|
||||
min-width: 500rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.instruction-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #212121;
|
||||
margin-bottom: 16rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.instruction-text {
|
||||
font-size: 28rpx;
|
||||
color: #757575;
|
||||
line-height: 1.6;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 40rpx;
|
||||
padding: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 24rpx;
|
||||
color: #9E9E9E;
|
||||
}
|
||||
|
||||
/* 平板横屏适配 */
|
||||
@media screen and (orientation: landscape) {
|
||||
/* 横屏时优化元素排布 */
|
||||
.container {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
/* 在横屏模式下,核心操作区放在右侧,便于右手操作 */
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-top: 40rpx;
|
||||
min-height: calc(100vh - 100rpx);
|
||||
}
|
||||
|
||||
/* 横屏时增大按钮点击区域 */
|
||||
.nfc-button {
|
||||
width: 500rpx;
|
||||
height: 130rpx;
|
||||
line-height: 130rpx;
|
||||
/* 放在屏幕右侧,便于右手操作 */
|
||||
align-self: flex-end;
|
||||
margin-right: 60rpx;
|
||||
}
|
||||
|
||||
/* 状态区域也放在右侧 */
|
||||
.status-section {
|
||||
min-width: 600rpx;
|
||||
align-self: flex-end;
|
||||
margin-right: 60rpx;
|
||||
}
|
||||
|
||||
/* 结果区域在横屏时更宽,方便查看完整信息 */
|
||||
.result-section {
|
||||
min-width: 800rpx;
|
||||
margin: 32rpx auto;
|
||||
}
|
||||
|
||||
/* 操作指南放在左侧,不干扰核心操作区 */
|
||||
.instruction {
|
||||
position: absolute;
|
||||
left: 40rpx;
|
||||
top: 180rpx;
|
||||
min-width: 400rpx;
|
||||
max-width: 450rpx;
|
||||
padding: 24rpx;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
/* 底部信息在横屏时放在左侧底部 */
|
||||
.footer {
|
||||
position: absolute;
|
||||
left: 40rpx;
|
||||
bottom: 30rpx;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* 安卓平板特殊适配 */
|
||||
@media (min-width: 768px) and (min-height: 500px) {
|
||||
/* 优化触控体验:确保所有可点击元素至少有48px x 48px的点击区域 */
|
||||
.nfc-button {
|
||||
min-height: 120rpx; /* 约等于48px在rpx中的值 */
|
||||
min-width: 300rpx;
|
||||
touch-action: manipulation; /* 防止双击缩放,提高点击响应速度 */
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
min-height: 80rpx;
|
||||
min-width: 160rpx;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
/* 平板上增大字体和间距,提升可读性 */
|
||||
.title {
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
letter-spacing: 2rpx;
|
||||
font-size: 42rpx; /* 在平板上进一步增大ID显示字体 */
|
||||
padding: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 大型平板适配(10英寸以上) */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding: 48rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 64rpx;
|
||||
}
|
||||
|
||||
.nfc-button {
|
||||
width: 700rpx;
|
||||
height: 150rpx;
|
||||
line-height: 150rpx;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.status-section,
|
||||
.result-section {
|
||||
min-width: 900rpx;
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
/* 大型平板上,优化内容区的最大宽度,避免内容过宽影响阅读 */
|
||||
.content {
|
||||
max-width: 70vw;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 适配不同尺寸的平板 */
|
||||
@media screen and (min-width: 768px) {
|
||||
.title {
|
||||
font-size: 56rpx;
|
||||
}
|
||||
|
||||
.nfc-button {
|
||||
width: 700rpx;
|
||||
height: 140rpx;
|
||||
line-height: 140rpx;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -6,7 +6,9 @@
|
||||
:leftFlag="true" :rightFlag="true"
|
||||
>
|
||||
<template #right>
|
||||
<view class="head-right" @click="handleQuestion">
|
||||
<!-- 状态是4-已完成 5-已过期的不能再问题上报了 -->
|
||||
<block v-if="optionObj.taskStatus==5||optionObj.taskStatus==4"></block>
|
||||
<view v-else class="head-right" @click="handleQuestion">
|
||||
<view class="btn-yellow">
|
||||
<img :src="'static/images/polling/icon-repair.png'" class="img-repair" />新建问题上报
|
||||
</view>
|
||||
@@ -92,7 +94,7 @@
|
||||
<view>{{ item.pointName }}</view>
|
||||
</view>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="r-right" @click.stop="initNFC">
|
||||
<view class="r-right" @click.stop="initNFC(item,index)">
|
||||
<img :src="'static/images/polling/icon-NFCcode.png'" class="img-nfc" /> 开始识别
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
@@ -156,8 +158,10 @@
|
||||
</block>
|
||||
<view class="report-border" :style="{borderColor:index<optionObj.pointList.length-1?'#E7E7E7':'#fff'}"></view>
|
||||
</view>
|
||||
<view class="btn-submit">
|
||||
<button type="primary" @click="handleSubmit" :loading="submitLoading">提交</button>
|
||||
<!-- 状态是4-已完成 5-已过期的不能再修改了 -->
|
||||
<block v-if="optionObj.taskStatus==5||optionObj.taskStatus==4"></block>
|
||||
<view v-else class="btn-submit">
|
||||
<button type="primary" @click="handleSubmit" :loading="submitLoading" >提交</button>
|
||||
</view>
|
||||
</block>
|
||||
<view v-else class="no-data">
|
||||
@@ -195,11 +199,20 @@
|
||||
></customShowModal>
|
||||
|
||||
<!-- 图片放大 -->
|
||||
<mediaPreview :visible="isVisible" :url="mediaUrl" @close="handlePreviewClose"></mediaPreview>
|
||||
<mediaPreview :visible="isVisible"
|
||||
:url="mediaUrl"
|
||||
@close="handlePreviewClose"
|
||||
></mediaPreview>
|
||||
|
||||
<!-- NFC 弹窗 -->
|
||||
<NFCTemplate v-if="nfcShow" ref="nfcTemplateRef"
|
||||
@close="nfcClose"
|
||||
@confirm="nfcConfirm"
|
||||
></NFCTemplate>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref,onMounted,onUnmounted,nextTick,computed,reactive } from 'vue'
|
||||
import { ref,onMounted,onUnmounted,nextTick,computed,reactive,getCurrentInstance } from 'vue'
|
||||
import { onLoad,onHide} from '@dcloudio/uni-app';
|
||||
import { MINIO_KEY } from '@/enums/cacheEnums';
|
||||
import customHeader from '@/components/customHeader.vue';
|
||||
@@ -208,14 +221,13 @@ import multipleSelect from "@/components/multipleSelect.vue";
|
||||
import pollingShowModal from "@/components/pollingShowModal.vue";
|
||||
import customShowModal from "@/components/customShowModal.vue"
|
||||
import mediaPreview from "@/components/mediaPreview.vue"
|
||||
import NFCTemplate from "@/components/NFCTemplate.vue"
|
||||
import { parseTime } from '@/utils/datetime.js';
|
||||
import { formatTaskStatus } from '@/utils/status.js';
|
||||
import { taskGroupDetail,submitResult,minioUpload } from '@/api/polling.js'
|
||||
import {compressImageUni} from '@/utils/common.js'
|
||||
// import {uploadFileMinio} from '@/utils/minio.js'
|
||||
// #ifdef APP-PLUS
|
||||
let m1CardModule = uni.requireNativePlugin("LF-Sense-Card-M1")
|
||||
// #endif
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
let taskId = ref(undefined);
|
||||
let groupId = ref(undefined);
|
||||
@@ -227,6 +239,8 @@ onLoad(option => {
|
||||
|
||||
minioObj = JSON.parse(uni.getStorageSync(MINIO_KEY) || "\{\}")
|
||||
// console.log(minioObj)
|
||||
|
||||
|
||||
})
|
||||
|
||||
// 下拉刷新
|
||||
@@ -431,15 +445,29 @@ const handlePreviewClose=()=>{
|
||||
// nfc 处理
|
||||
let cardUID = ref('');
|
||||
let cardValue = ref('');
|
||||
const initNFC = async(item) => {
|
||||
m1CardModule.openNativeSenseCard({
|
||||
'keyA': 'TestKey',
|
||||
'sector': 0,
|
||||
'block': 0
|
||||
},ret => {
|
||||
cardUID.value = ret.uid
|
||||
cardValue.value = ret.code
|
||||
})
|
||||
let nfcShow = ref(false);
|
||||
const nfcTemplateRef = ref(null);
|
||||
// optionObj.pointList
|
||||
let nfcIndex = ref(0);
|
||||
const initNFC = async(item,index) => {
|
||||
nfcShow.value = true;
|
||||
nfcIndex.value=index;
|
||||
// uni.navigateTo({url:'/pages/business/polling/nfcTest/index'})
|
||||
// #ifdef APP-PLUS
|
||||
setTimeout(()=>{
|
||||
// console.log("nfcTemplateRef==",nfcTemplateRef.value)
|
||||
if (nfcTemplateRef.value) {
|
||||
nfcTemplateRef.value.listenNFCStatus();
|
||||
}
|
||||
},500)
|
||||
// #endif
|
||||
}
|
||||
const nfcClose = async(item) => {
|
||||
nfcShow.value = false;
|
||||
}
|
||||
const nfcConfirm=(result)=>{
|
||||
console.log("confirm=>",result)
|
||||
optionObj.pointList[nfcIndex].resultContent = result
|
||||
}
|
||||
|
||||
// 扫二维码
|
||||
@@ -576,6 +604,7 @@ onUnmounted(() => {
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
.scroll-h{
|
||||
/* #ifdef APP-PLUS */
|
||||
height:calc(100vh - 78px) !important;
|
||||
|
||||
BIN
src/static/images/polling/nfc-logo.png
Normal file
BIN
src/static/images/polling/nfc-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
4
src/uni_modules/ohyes-nfc/changelog.md
Normal file
4
src/uni_modules/ohyes-nfc/changelog.md
Normal file
@@ -0,0 +1,4 @@
|
||||
## 1.0.2(2025-08-29)
|
||||
api 变更注意查看最新文档
|
||||
## 1.0.0(2025-07-30)
|
||||
无
|
||||
BIN
src/uni_modules/ohyes-nfc/encrypt
Normal file
BIN
src/uni_modules/ohyes-nfc/encrypt
Normal file
Binary file not shown.
105
src/uni_modules/ohyes-nfc/package.json
Normal file
105
src/uni_modules/ohyes-nfc/package.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"id": "ohyes-nfc",
|
||||
"displayName": "android NFC ",
|
||||
"version": "1.0.2",
|
||||
"description": "ohyes-nfc",
|
||||
"keywords": [
|
||||
"android",
|
||||
"uts",
|
||||
"nfc"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^4.66",
|
||||
"uni-app": "^4.27",
|
||||
"uni-app-x": "^4.27"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "uts",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "1.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "3000.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": "1209808782"
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "android.permission.NFC\nandroid.permission.VIBRATE"
|
||||
},
|
||||
"npmurl": "",
|
||||
"darkmode": "x",
|
||||
"i18n": "x",
|
||||
"widescreen": "x"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "√",
|
||||
"aliyun": "√",
|
||||
"alipay": "√"
|
||||
},
|
||||
"client": {
|
||||
"uni-app": {
|
||||
"vue": {
|
||||
"vue2": {
|
||||
"extVersion": "1.0.1",
|
||||
"minVersion": ""
|
||||
},
|
||||
"vue3": {
|
||||
"extVersion": "1.0.1",
|
||||
"minVersion": ""
|
||||
}
|
||||
},
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"vue": "-",
|
||||
"nvue": "-",
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-",
|
||||
"alipay": "-",
|
||||
"toutiao": "-",
|
||||
"baidu": "-",
|
||||
"kuaishou": "-",
|
||||
"jd": "-",
|
||||
"harmony": "-",
|
||||
"qq": "-",
|
||||
"lark": "-"
|
||||
},
|
||||
"quickapp": {
|
||||
"huawei": "-",
|
||||
"union": "-"
|
||||
}
|
||||
},
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
174
src/uni_modules/ohyes-nfc/readme.md
Normal file
174
src/uni_modules/ohyes-nfc/readme.md
Normal file
@@ -0,0 +1,174 @@
|
||||
*注意:iOS平台的NFC功能暂无支持计划,当前版本仅支持Android平台。*
|
||||
|
||||
# ohyes-nfc
|
||||
|
||||
一个用于uni-app的NFC读取插件,支持Android的NFC标签读取功能。
|
||||
|
||||
## 免责声明
|
||||
- 源码无后门代码。
|
||||
- 由第三方SDK而导致的任何损失,与作者无关。
|
||||
- 使用本插件而导致的任何损失,与作者无关。
|
||||
- 使用该插件即代表同意免责声明。
|
||||
|
||||
## 是否使用第三方SDK?
|
||||
- 无
|
||||
|
||||
## 我有需求怎么办?
|
||||
发送邮件到 helloword202507@163.com 感谢支持
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🔍 **NFC标签读取** - 支持多种NFC标签类型的读取
|
||||
- 🏷️ **多种标签格式** - 支持NDEF、Mifare Classic、Mifare Ultralight、ISO-DEP等
|
||||
- 📊 **详细信息获取** - 获取标签ID、技术列表、内容、内存使用情况等
|
||||
- 🔒 **安全验证** - 支持标签签名验证和密码保护检测
|
||||
|
||||
## 支持的标签类型
|
||||
|
||||
- **NDEF标签** - NFC Forum Type 1-5
|
||||
- **Mifare Classic** - 1K/4K标签
|
||||
- **Mifare Ultralight** - 轻量级标签
|
||||
- **ISO-DEP** - ISO14443-4标准标签
|
||||
- **NFC-A/B/F/V** - 各种NFC技术标准
|
||||
|
||||
## 安装
|
||||
|
||||
将插件导入到你的uni-app项目中:
|
||||
|
||||
1. 下载插件包
|
||||
2. 将插件放入项目的`uni_modules`目录
|
||||
3. 重新编译项目
|
||||
|
||||
## 权限配置
|
||||
|
||||
### Android权限
|
||||
|
||||
插件会自动添加以下权限到AndroidManifest.xml:
|
||||
|
||||
```xml
|
||||
<uses-feature android:name="android.hardware.nfc" android:required="true" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 基本用法
|
||||
|
||||
参考示例工程
|
||||
|
||||
## API文档
|
||||
|
||||
### enableNFCRead()
|
||||
|
||||
启用NFC读取模式
|
||||
|
||||
### disableNFCRead()
|
||||
|
||||
停用NFC读取模式
|
||||
|
||||
### isNFCSupported()
|
||||
|
||||
检查当前设备是否支持NFC功能
|
||||
|
||||
**返回值:**
|
||||
- `boolean` - true表示设备支持NFC,false表示不支持
|
||||
|
||||
### isNFCEnabled()
|
||||
|
||||
检查当前设备NFC功能是否已打开
|
||||
|
||||
**返回值:**
|
||||
- `boolean` - true表示NFC已启用,false表示NFC未启用
|
||||
|
||||
### openNFCSettings()
|
||||
|
||||
跳转到系统NFC设置页面,用户可以手动打开NFC开关
|
||||
|
||||
### onNFCListener(callback:(res:NFCInfo)=>void)
|
||||
|
||||
监听NFC返回数据,全局唯一,重复调用会覆盖
|
||||
|
||||
## NFCInfo数据结构
|
||||
|
||||
读取成功后返回的NFC信息对象包含以下字段:
|
||||
|
||||
```typescript
|
||||
interface NFCInfo {
|
||||
id: string // 标签唯一ID
|
||||
techList: string[] // 支持的技术列表
|
||||
type: string // 标签类型
|
||||
size: number // 标签容量(字节)
|
||||
isWritable: boolean // 是否可写
|
||||
content: string // 标签内容
|
||||
atqa: string // ATQA值(NFC-A技术)
|
||||
sak: string // SAK值(NFC-A技术)
|
||||
signature: string // 签名状态
|
||||
passwordProtected: boolean // 是否密码保护
|
||||
dataFormat: string // 数据格式
|
||||
recordCount: number // NDEF记录数量
|
||||
usedBytes: number // 已使用字节数
|
||||
freeBytes: number // 剩余字节数
|
||||
}
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
|
||||
- **id**: 标签的唯一标识符,通常为十六进制字符串
|
||||
- **techList**: 标签支持的NFC技术,如["NfcA", "Ndef", "MifareUltralight"]
|
||||
- **type**: 标签类型描述,如"Type 2 (Ultralight)"
|
||||
- **size**: 标签总容量,单位为字节
|
||||
- **isWritable**: 标签是否支持写入操作
|
||||
- **content**: 解析后的标签内容,包括文本、URL等
|
||||
- **atqa/sak**: NFC-A技术的技术参数
|
||||
- **signature**: 标签签名验证状态
|
||||
- **dataFormat**: 数据格式,如"NFC Forum Type 2"
|
||||
- **recordCount**: NDEF消息中的记录数量
|
||||
- **usedBytes/freeBytes**: 内存使用情况
|
||||
|
||||
## 支持的内容类型
|
||||
|
||||
插件能够解析以下类型的NFC内容:
|
||||
|
||||
### NDEF记录类型
|
||||
- **文本记录** - 纯文本内容,支持多语言
|
||||
- **URI记录** - 网址、电话、邮箱等链接
|
||||
- **MIME记录** - 多媒体内容
|
||||
- **外部类型** - 自定义格式
|
||||
|
||||
### 非NDEF标签
|
||||
- **Mifare Classic** - 读取扇区和块数据
|
||||
- **Mifare Ultralight** - 读取页面数据
|
||||
- **ISO-DEP** - 读取历史字节和高层响应
|
||||
|
||||
## 平台支持
|
||||
|
||||
| 平台 | 支持状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| Android | ✅ 完全支持 | 支持所有NFC功能,包括多种标签类型 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **设备要求**: 设备必须支持NFC功能
|
||||
2. **权限**: Android需要NFC权限,iOS需要在设置中启用NFC
|
||||
3. **距离**: NFC读取需要设备与标签距离很近(通常<4cm)
|
||||
4. **性能**: 读取大容量标签时可能需要较长时间
|
||||
5. **兼容性**: 不同厂商的NFC标签可能有细微差异
|
||||
|
||||
## 错误处理
|
||||
|
||||
常见错误及处理方法:
|
||||
|
||||
- **"NFC不可用"** - 检查设备是否支持NFC并已启用
|
||||
- **"读取超时"** - 确保标签与设备距离足够近
|
||||
- **"标签格式不支持"** - 标签可能损坏或使用了不支持的格式
|
||||
- **"权限被拒绝"** - 检查应用是否有NFC权限
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.0.0
|
||||
- 初始版本发布
|
||||
- 支持Android平台NFC读取
|
||||
- 支持多种标签类型和格式
|
||||
- 提供详细的标签信息
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.ohyes.nfc">
|
||||
<uses-feature android:name="android.hardware.nfc" android:required="true" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
</manifest>
|
||||
BIN
src/uni_modules/ohyes-nfc/utssdk/app-android/NFCUtil.kt
Normal file
BIN
src/uni_modules/ohyes-nfc/utssdk/app-android/NFCUtil.kt
Normal file
Binary file not shown.
3
src/uni_modules/ohyes-nfc/utssdk/app-android/config.json
Normal file
3
src/uni_modules/ohyes-nfc/utssdk/app-android/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
BIN
src/uni_modules/ohyes-nfc/utssdk/app-android/index.uts
Normal file
BIN
src/uni_modules/ohyes-nfc/utssdk/app-android/index.uts
Normal file
Binary file not shown.
3
src/uni_modules/ohyes-nfc/utssdk/app-ios/config.json
Normal file
3
src/uni_modules/ohyes-nfc/utssdk/app-ios/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"deploymentTarget": "12"
|
||||
}
|
||||
BIN
src/uni_modules/ohyes-nfc/utssdk/app-ios/index.uts
Normal file
BIN
src/uni_modules/ohyes-nfc/utssdk/app-ios/index.uts
Normal file
Binary file not shown.
29
src/uni_modules/ohyes-nfc/utssdk/interface.uts
Normal file
29
src/uni_modules/ohyes-nfc/utssdk/interface.uts
Normal file
@@ -0,0 +1,29 @@
|
||||
export type NFCEnableReadMode = () => void
|
||||
export type NFCDisableReadMode = () => void
|
||||
export type NFCIsSupported = () => boolean
|
||||
export type NFCIsEnabled = () => boolean
|
||||
export type NFCOpenSettings = () => void
|
||||
|
||||
export type NFCListener = (info : NFCInfo) => void
|
||||
|
||||
export type NFCReadOptions = {
|
||||
resolve : (res : NFCInfo) => void
|
||||
reject : (e : String) => void
|
||||
}
|
||||
|
||||
export type NFCInfo = {
|
||||
id : string,
|
||||
techList : Array<string>,
|
||||
type : string,
|
||||
size : number,
|
||||
isWritable : boolean,
|
||||
content : string,
|
||||
atqa : string,
|
||||
sak : string,
|
||||
signature : string,
|
||||
passwordProtected : boolean,
|
||||
dataFormat : string,
|
||||
recordCount : number,
|
||||
usedBytes : number,
|
||||
freeBytes : number
|
||||
}
|
||||
18
src/uni_modules/read-nfc/changelog.md
Normal file
18
src/uni_modules/read-nfc/changelog.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## 1.0.8(2025-07-22)
|
||||
修复uniapp x打包报错,AndroidManifest.xml 需要显式声明 android:exported="true"
|
||||
## 1.0.7(2025-06-26)
|
||||
修改BUG
|
||||
## 1.0.6(2024-07-24)
|
||||
更新刷卡监听界面背景透明化,提高界面友好度
|
||||
## 1.0.5(2024-07-24)
|
||||
更新刷新提示窗体
|
||||
## 1.0.4(2024-05-14)
|
||||
修改BUG
|
||||
## 1.0.3(2024-05-11)
|
||||
优化
|
||||
## 1.0.2(2024-05-11)
|
||||
更换图标
|
||||
## 1.0.1(2024-04-12)
|
||||
增加设备支持与NFC启用判断
|
||||
## 1.0.0(2024-04-12)
|
||||
提交插件
|
||||
1
src/uni_modules/read-nfc/encrypt
Normal file
1
src/uni_modules/read-nfc/encrypt
Normal file
@@ -0,0 +1 @@
|
||||
<EFBFBD>,<2C>P<EFBFBD><50>=<3D>[&<26>۸<>6<EFBFBD><36>z<EFBFBD>s<EFBFBD>+<2B>g<02><>_<EFBFBD><5F>ϭ<EFBFBD><12><><EFBFBD>륀<EFBFBD><EBA580>e<EFBFBD>$<24>,<14><><EFBFBD><EFBFBD>s`<60>_<><5F><07>B<EFBFBD><42><EFBFBD><EFBFBD>Wa<57><61>8W<0C><><EFBFBD>t<EFBFBD>O<EFBFBD><4F>{_Hf<48><66>ڎ@<15>-u+UX<55><02>*<2A><><EFBFBD>zl0f<30>U8K^G<><47><EFBFBD>0<14><><17><><EFBFBD>hpj<70><6A><EFBFBD><EFBFBD><EFBFBD>]^?
|
||||
102
src/uni_modules/read-nfc/package.json
Normal file
102
src/uni_modules/read-nfc/package.json
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"id": "read-nfc",
|
||||
"displayName": "read-nfc",
|
||||
"version": "1.0.8",
|
||||
"description": "读取NFC卡号 read-nfc",
|
||||
"keywords": [
|
||||
"read-nfc"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"uni-app": "^4.06",
|
||||
"uni-app-x": "^4.07"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "uts",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "50.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "200.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "<uses-permission android:name=\"android.permission.NFC\" /> \r\n<uses-feature android:name=\"android.hardware.nfc\" android:required=\"true\" />"
|
||||
},
|
||||
"npmurl": "",
|
||||
"darkmode": "x",
|
||||
"i18n": "x",
|
||||
"widescreen": "x"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "√",
|
||||
"aliyun": "√",
|
||||
"alipay": "√"
|
||||
},
|
||||
"client": {
|
||||
"uni-app": {
|
||||
"vue": {
|
||||
"vue2": "√",
|
||||
"vue3": "√"
|
||||
},
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"vue": "√",
|
||||
"nvue": "-",
|
||||
"android": {
|
||||
"extVersion": "1.0.0",
|
||||
"minVersion": "21"
|
||||
},
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-",
|
||||
"alipay": "-",
|
||||
"toutiao": "-",
|
||||
"baidu": "-",
|
||||
"kuaishou": "-",
|
||||
"jd": "-",
|
||||
"harmony": "-",
|
||||
"qq": "-",
|
||||
"lark": "-"
|
||||
},
|
||||
"quickapp": {
|
||||
"huawei": "-",
|
||||
"union": "-"
|
||||
}
|
||||
},
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"android": {
|
||||
"extVersion": "1.0.0",
|
||||
"minVersion": "21"
|
||||
},
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/uni_modules/read-nfc/readme.md
Normal file
52
src/uni_modules/read-nfc/readme.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# read-nfc
|
||||
## 插件示例
|
||||
|
||||
1、通过插件市场“使用 HBuilderX 导入示例项目”下载插件示例项目
|
||||
|
||||
2、通过插件市场“试用”导入插件到示例项目中
|
||||
|
||||
3、打包并运行自定义基座
|
||||
|
||||
使用了NFC的Android原生库需要打包使用自定义基座
|
||||
|
||||
## 引入插件对象
|
||||
```
|
||||
import * as nfc from "@/uni_modules/read-nfc";
|
||||
```
|
||||
|
||||
## 调起NFC识别
|
||||
|
||||
```
|
||||
data() {
|
||||
return {
|
||||
title: '读取NFC监听',
|
||||
nfcCode:''
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.gotoNfcActivity();//调起NFC识别,并监听回调
|
||||
},
|
||||
methods: {
|
||||
gotoNfcActivity() {
|
||||
let that=this;
|
||||
nfc.gotoNfcActivity(function(result){
|
||||
that.nfcCode=result.message;
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: result.message,
|
||||
confirmText:'继续',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
that.gotoNfcActivity();//继续识别
|
||||
console.log('用户点击继续!');
|
||||
} else if (res.cancel) {
|
||||
console.log('用户点击取消!');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('NFC',result);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.dcloud.uni_modules.read_nfc">
|
||||
<!--获取NFC权限-->
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-feature android:name="android.hardware.nfc" android:required="true" />
|
||||
|
||||
<application>
|
||||
<activity android:name="uts.sdk.modules.readNfc.NfcActivity" android:exported="true" android:launchMode="singleTop" android:theme="@android:style/Theme.Translucent">
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.TAG_DISCOVERED" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
3
src/uni_modules/read-nfc/utssdk/app-android/config.json
Normal file
3
src/uni_modules/read-nfc/utssdk/app-android/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
BIN
src/uni_modules/read-nfc/utssdk/app-android/index.uts
Normal file
BIN
src/uni_modules/read-nfc/utssdk/app-android/index.uts
Normal file
Binary file not shown.
BIN
src/uni_modules/read-nfc/utssdk/app-android/res/drawable/nfc.png
Normal file
BIN
src/uni_modules/read-nfc/utssdk/app-android/res/drawable/nfc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:orientation="vertical">
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="请靠近NFC卡"
|
||||
android:gravity="center_horizontal"
|
||||
android:src="@drawable/nfc" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="请靠近NFC卡"
|
||||
android:textColor="#000"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
45
src/uni_modules/read-nfc/utssdk/interface.uts
Normal file
45
src/uni_modules/read-nfc/utssdk/interface.uts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* interface.uts
|
||||
* uts插件接口定义文件,按规范定义接口文件可以在HBuilderX中更好的做到语法提示
|
||||
*/
|
||||
|
||||
/**
|
||||
* myApi 异步函数的参数,在type里定义函数需要的参数以及api成功、失败的相关回调函数。
|
||||
*/
|
||||
export type MyApiOptions = {
|
||||
paramA : boolean
|
||||
success ?: (res : MyApiResult) => void
|
||||
fail ?: (res : MyApiFail) => void
|
||||
complete ?: (res : any) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* 函数返回结果
|
||||
* 可以是void, 基本数据类型,自定义type, 或者其他类型。
|
||||
* [可选实现]
|
||||
*/
|
||||
export type MyApiResult = {
|
||||
fieldA : number,
|
||||
fieldB : boolean,
|
||||
fieldC : string
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
* 根据uni错误码规范要求,建议错误码以90开头,以下是错误码示例:
|
||||
* - 9010001 错误信息1
|
||||
* - 9010002 错误信息2
|
||||
*/
|
||||
export type MyApiErrorCode = 9010001 | 9010002;
|
||||
/**
|
||||
* myApi 的错误回调参数
|
||||
*/
|
||||
export interface MyApiFail extends IUniError {
|
||||
errCode : MyApiErrorCode
|
||||
};
|
||||
|
||||
/* 异步函数定义 */
|
||||
export type MyApi = (options : MyApiOptions) => void
|
||||
|
||||
/* 同步函数定义 */
|
||||
export type MyApiSync = (paramA : boolean) => MyApiResult
|
||||
BIN
src/uni_modules/read-nfc/utssdk/unierror.uts
Normal file
BIN
src/uni_modules/read-nfc/utssdk/unierror.uts
Normal file
Binary file not shown.
431
src/utils/hexiii-nfc.js
Normal file
431
src/utils/hexiii-nfc.js
Normal file
@@ -0,0 +1,431 @@
|
||||
// NFC工具类 - 优化版,适配安卓平板环境
|
||||
// 包路径定义
|
||||
const package_TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED';
|
||||
const package_TAG_DISCOVERED = 'android.nfc.action.TAG_DISCOVERED';
|
||||
const package_NDEF_DISCOVERED = 'android.nfc.action.NDEF_DISCOVERED';
|
||||
|
||||
// 全局变量
|
||||
let nfcAdapter = null; // NFC适配器实例
|
||||
let mainActivity = null; // 主Activity实例
|
||||
let pendingIntent = null; // 用于NFC前台调度的PendingIntent
|
||||
let currentPromiseResolve = null; // 当前Promise的resolve函数引用
|
||||
let currentPromiseReject = null; // 当前Promise的reject函数引用
|
||||
let isNFCInitialized = false; // NFC初始化状态
|
||||
let noNFC = false; // 无NFC功能标识
|
||||
let timeoutId = null; // 超时定时器ID
|
||||
let isListening = false; // 是否正在监听状态
|
||||
|
||||
// 支持的NFC技术列表 - 优化以适配更多平板设备
|
||||
let techListsArray = [
|
||||
['android.nfc.tech.IsoDep'],
|
||||
['android.nfc.tech.NfcA'],
|
||||
['android.nfc.tech.NfcB'],
|
||||
['android.nfc.tech.NfcF'],
|
||||
['android.nfc.tech.NfcV'],
|
||||
['android.nfc.tech.NdefFormatable'],
|
||||
['android.nfc.tech.MifareClassic'],
|
||||
['android.nfc.tech.MifareUltralight']
|
||||
];
|
||||
|
||||
/**
|
||||
* NFC工具类 - 处理安卓平台NFC卡片读取
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* 初始化NFC功能并监听NFC状态
|
||||
* @param {number} timeoutMs - 超时时间(毫秒),默认30秒
|
||||
* @returns {Promise<string>} 返回读取到的NFC卡片ID
|
||||
*/
|
||||
listenNFCStatus: function(timeoutMs = 30000) {
|
||||
console.log("NFC: 开始监听NFC状态,超时时间:" + timeoutMs + "ms");
|
||||
|
||||
// 先停止之前的监听(如果存在)
|
||||
this.stopNFCListening();
|
||||
|
||||
// 使用Promise封装NFC读取过程
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
// 保存当前Promise的引用
|
||||
currentPromiseResolve = resolve;
|
||||
currentPromiseReject = reject;
|
||||
isListening = true;
|
||||
|
||||
// 获取主Activity
|
||||
if (!mainActivity) {
|
||||
mainActivity = plus.android.runtimeMainActivity();
|
||||
}
|
||||
|
||||
// 初始化NFC适配器
|
||||
if (!this._initializeNFC()) {
|
||||
// 如果初始化失败且是因为设备不支持NFC或NFC未开启,则直接reject
|
||||
if (noNFC) {
|
||||
reject(new Error('设备不支持NFC或NFC功能未开启'));
|
||||
} else {
|
||||
reject(new Error('NFC初始化失败'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置NFC前台调度
|
||||
if (!this._setupForegroundDispatch()) {
|
||||
reject(new Error('NFC前台调度设置失败'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置超时
|
||||
timeoutId = setTimeout(() => {
|
||||
if (isListening && currentPromiseReject === reject) {
|
||||
console.log('NFC: 读取超时');
|
||||
const errorMsg = 'NFC读取超时,请重试';
|
||||
this._cleanupResources();
|
||||
reject(new Error(errorMsg));
|
||||
}
|
||||
}, timeoutMs);
|
||||
|
||||
} catch (error) {
|
||||
console.error('NFC: 监听NFC状态时发生错误:', error);
|
||||
this._cleanupResources();
|
||||
reject(new Error('NFC操作异常: ' + error.message));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止NFC监听
|
||||
*/
|
||||
stopNFCListening: function() {
|
||||
console.log('NFC: 停止监听NFC状态');
|
||||
this._cleanupResources();
|
||||
|
||||
if (currentPromiseReject) {
|
||||
const reject = currentPromiseReject;
|
||||
currentPromiseReject = null;
|
||||
// 不抛出错误,而是安静地停止监听
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清理NFC资源
|
||||
* @private
|
||||
*/
|
||||
_cleanupResources: function() {
|
||||
try {
|
||||
// 清除超时定时器
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}
|
||||
|
||||
// 禁用前台调度
|
||||
if (nfcAdapter && mainActivity) {
|
||||
try {
|
||||
nfcAdapter.disableForegroundDispatch(mainActivity);
|
||||
console.log('NFC: 已禁用前台调度');
|
||||
} catch (e) {
|
||||
console.warn('NFC: 禁用前台调度时出错:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
isListening = false;
|
||||
currentPromiseResolve = null;
|
||||
currentPromiseReject = null;
|
||||
} catch (error) {
|
||||
console.error('NFC: 清理资源时发生错误:', error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化NFC适配器
|
||||
* @private
|
||||
* @returns {boolean} 初始化是否成功
|
||||
*/
|
||||
_initializeNFC: function() {
|
||||
try {
|
||||
// 如果已经初始化,则直接返回成功
|
||||
if (isNFCInitialized && nfcAdapter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 导入必要的类
|
||||
const NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
|
||||
|
||||
// 获取NFC适配器实例
|
||||
nfcAdapter = NfcAdapter.getDefaultAdapter(mainActivity);
|
||||
|
||||
// 检查设备是否支持NFC
|
||||
if (nfcAdapter == null) {
|
||||
console.warn('NFC: 设备不支持NFC');
|
||||
noNFC = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查NFC是否已启用
|
||||
if (!nfcAdapter.isEnabled()) {
|
||||
console.warn('NFC: NFC功能未启用');
|
||||
noNFC = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置初始化状态为成功
|
||||
isNFCInitialized = true;
|
||||
noNFC = false;
|
||||
console.log('NFC: 初始化成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('NFC: 初始化失败:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置NFC前台调度 - 优化以适配平板大屏和不同Android版本
|
||||
* @private
|
||||
* @returns {boolean} 设置是否成功
|
||||
*/
|
||||
_setupForegroundDispatch: function() {
|
||||
try {
|
||||
// 导入必要的类
|
||||
const Intent = plus.android.importClass('android.content.Intent');
|
||||
const PendingIntent = plus.android.importClass('android.app.PendingIntent');
|
||||
const IntentFilter = plus.android.importClass('android.content.IntentFilter');
|
||||
const NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
|
||||
|
||||
// 创建Intent和PendingIntent
|
||||
const intent = new Intent(mainActivity, mainActivity.getClass());
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
// 适配不同Android版本的PendingIntent标志
|
||||
let flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
|
||||
// 检查Android版本,适配Android 12+
|
||||
try {
|
||||
const Build = plus.android.importClass('android.os.Build');
|
||||
if (Build.VERSION.SDK_INT >= 31) { // Android 12 (API 31)
|
||||
flags = PendingIntent.FLAG_MUTABLE;
|
||||
console.log('NFC: 检测到Android 12+,使用FLAG_MUTABLE标志');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('NFC: 无法检测Android版本,使用默认标志');
|
||||
}
|
||||
|
||||
pendingIntent = PendingIntent.getActivity(mainActivity, 0, intent, flags);
|
||||
|
||||
// 创建多个IntentFilter以提高平板设备上的识别率
|
||||
const ndef = new IntentFilter(package_TECH_DISCOVERED);
|
||||
const tag = new IntentFilter(package_TAG_DISCOVERED);
|
||||
const ndefDiscovered = new IntentFilter(package_NDEF_DISCOVERED);
|
||||
|
||||
try {
|
||||
ndef.addDataType('*/*');
|
||||
ndefDiscovered.addDataType('*/*');
|
||||
} catch (e) {
|
||||
console.warn('NFC: 添加数据类型失败:', e);
|
||||
}
|
||||
|
||||
const intentFiltersArray = [ndef, tag, ndefDiscovered];
|
||||
|
||||
// 移除旧的监听器,避免重复添加
|
||||
try {
|
||||
plus.globalEvent.removeEventListener('newintent', this._handleNewIntent);
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
// 设置全局事件监听器
|
||||
plus.globalEvent.addEventListener('newintent', this._handleNewIntent.bind(this));
|
||||
|
||||
// 启用前台调度 - 优化平板设备上的NFC识别
|
||||
nfcAdapter.enableForegroundDispatch(mainActivity, pendingIntent, intentFiltersArray, techListsArray);
|
||||
console.log('NFC: 前台调度设置成功,已添加多种Intent过滤器');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('NFC: 前台调度设置失败:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理新的NFC Intent - 优化以支持多种NFC Intent类型
|
||||
* @private
|
||||
*/
|
||||
_handleNewIntent: function() {
|
||||
try {
|
||||
console.log('NFC: 接收到新的Intent');
|
||||
|
||||
// 导入必要的类
|
||||
const NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
|
||||
|
||||
// 获取当前Intent
|
||||
const intent = mainActivity.getIntent();
|
||||
const action = intent.getAction();
|
||||
|
||||
// 检查是否是NFC相关的Intent - 支持多种NFC Intent类型
|
||||
if (action === package_TECH_DISCOVERED ||
|
||||
action === package_TAG_DISCOVERED ||
|
||||
action === package_NDEF_DISCOVERED) {
|
||||
|
||||
console.log('NFC: 检测到NFC卡片,动作类型:', action);
|
||||
|
||||
// 读取NFC ID
|
||||
const nfcId = this._readNFCId(intent, NfcAdapter);
|
||||
|
||||
// 如果成功读取到ID且Promise仍在等待,则解析Promise
|
||||
if (nfcId && currentPromiseResolve && isListening) {
|
||||
console.log('NFC: 成功读取到ID:', nfcId);
|
||||
const resolve = currentPromiseResolve;
|
||||
|
||||
// 清理资源
|
||||
this._cleanupResources();
|
||||
|
||||
// 延迟解析Promise,确保资源完全清理
|
||||
setTimeout(() => {
|
||||
resolve(nfcId);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('NFC: 处理Intent时发生错误:', error);
|
||||
if (currentPromiseReject && isListening) {
|
||||
const reject = currentPromiseReject;
|
||||
this._cleanupResources();
|
||||
reject(new Error('处理NFC数据时出错: ' + error.message));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 读取NFC卡片ID - 优化以适配安卓平板
|
||||
* @private
|
||||
* @param {android.content.Intent} intent - Intent对象
|
||||
* @param {android.nfc.NfcAdapter} adapter - NfcAdapter类
|
||||
* @returns {string} NFC卡片ID的十六进制字符串
|
||||
*/
|
||||
_readNFCId: function(intent, adapter) {
|
||||
try {
|
||||
// 获取NFC ID字节数组 - 尝试多种方式获取
|
||||
let bytesId = null;
|
||||
|
||||
// 主要方式
|
||||
bytesId = intent.getByteArrayExtra(adapter.EXTRA_ID);
|
||||
|
||||
// 如果失败,尝试其他方式
|
||||
if (!bytesId || bytesId.length === 0) {
|
||||
try {
|
||||
// 获取标签对象
|
||||
const tag = intent.getParcelableExtra(adapter.EXTRA_TAG);
|
||||
if (tag) {
|
||||
bytesId = tag.getId();
|
||||
console.log('NFC: 通过Tag对象获取ID');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('NFC: 尝试通过Tag对象获取ID失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bytesId || bytesId.length === 0) {
|
||||
console.warn('NFC: 未读取到ID字节数组');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 转换为十六进制字符串
|
||||
const hexId = this.byteArrayToHexString(bytesId);
|
||||
console.log('NFC: 读取到ID:', hexId, '长度:', bytesId.length);
|
||||
|
||||
// 尝试获取更多卡片信息,增强平板使用体验
|
||||
try {
|
||||
const tag = intent.getParcelableExtra(adapter.EXTRA_TAG);
|
||||
if (tag) {
|
||||
const Tag = plus.android.importClass('android.nfc.Tag');
|
||||
const techList = tag.getTechList();
|
||||
console.log('NFC: 卡片支持的技术:', techList);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('NFC: 获取卡片技术信息失败:', e);
|
||||
}
|
||||
|
||||
return hexId;
|
||||
} catch (error) {
|
||||
console.error('NFC: 读取ID时发生错误:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查NFC是否可用
|
||||
* @returns {Object} 包含支持状态和启用状态的对象
|
||||
*/
|
||||
checkNfcAvailability: function() {
|
||||
try {
|
||||
const main = plus.android.runtimeMainActivity();
|
||||
const NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
|
||||
const adapter = NfcAdapter.getDefaultAdapter(main);
|
||||
|
||||
if (adapter === null) {
|
||||
return {
|
||||
supported: false,
|
||||
enabled: false,
|
||||
error: '设备不支持NFC'
|
||||
};
|
||||
}
|
||||
|
||||
const isEnabled = adapter.isEnabled();
|
||||
return {
|
||||
supported: true,
|
||||
enabled: isEnabled,
|
||||
error: isEnabled ? null : 'NFC未启用'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('NFC: 检查可用性失败:', error);
|
||||
return {
|
||||
supported: false,
|
||||
enabled: false,
|
||||
error: error.message || '检查失败'
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 将字节数组转换为十六进制字符串
|
||||
* @param {Uint8Array|Array} inarray - 字节数组
|
||||
* @returns {string} 十六进制字符串
|
||||
*/
|
||||
byteArrayToHexString: function(inarray) {
|
||||
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;
|
||||
const i = (inn >>> 4) & 0x0f;
|
||||
out += hex[i];
|
||||
const k = inn & 0x0f;
|
||||
out += hex[k];
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
/**
|
||||
* 清理NFC资源
|
||||
*/
|
||||
cleanupNFC: function() {
|
||||
try {
|
||||
// 使用私有清理方法
|
||||
this._cleanupResources();
|
||||
|
||||
// 移除事件监听器
|
||||
try {
|
||||
plus.globalEvent.removeEventListener('newintent', this._handleNewIntent);
|
||||
console.log('NFC: 已移除newintent事件监听器');
|
||||
} catch (e) {
|
||||
console.warn('NFC: 移除事件监听器时出错:', e);
|
||||
}
|
||||
|
||||
// 重置初始化状态
|
||||
isNFCInitialized = false;
|
||||
console.log('NFC: 资源已完全清理');
|
||||
} catch (error) {
|
||||
console.error('NFC: 清理资源时发生错误:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
244
src/utils/ouu-nfc.js
Normal file
244
src/utils/ouu-nfc.js
Normal file
@@ -0,0 +1,244 @@
|
||||
// 包路径
|
||||
const package_NdefRecord = 'android.nfc.NdefRecord';
|
||||
const package_NdefMessage = 'android.nfc.NdefMessage';
|
||||
const package_TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED';
|
||||
const package_Intent = 'android.content.Intent';
|
||||
const package_Activity = 'android.app.Activity';
|
||||
const package_PendingIntent = 'android.app.PendingIntent';
|
||||
const package_IntentFilter = 'android.content.IntentFilter';
|
||||
const package_NfcAdapter = 'android.nfc.NfcAdapter';
|
||||
const package_Ndef = 'android.nfc.tech.Ndef';
|
||||
const package_NdefFormatable = 'android.nfc.tech.NdefFormatable';
|
||||
const package_Parcelable = 'android.os.Parcelable';
|
||||
const package_String = 'java.lang.String';
|
||||
|
||||
let NfcAdapter;
|
||||
let NdefRecord;
|
||||
let NdefMessage;
|
||||
let Uri;
|
||||
let readyWriteData = false;
|
||||
let readyRead = false;
|
||||
let noNFC = false;
|
||||
let techListsArray = [
|
||||
['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.net.Uri'],
|
||||
['android.nfc.tech.NdefFormatable'],
|
||||
['android.nfc.tech.MifareClassi'],
|
||||
['android.nfc.tech.MifareUltralight']
|
||||
];
|
||||
// 要写入的数据
|
||||
let text = '';
|
||||
let readResult = '';
|
||||
|
||||
export default {
|
||||
|
||||
listenNFCStatus: function () {
|
||||
let that = this;
|
||||
try {
|
||||
let main = plus.android.runtimeMainActivity();
|
||||
let Intent = plus.android.importClass('android.content.Intent');
|
||||
let Activity = plus.android.importClass('android.app.Activity');
|
||||
let PendingIntent = plus.android.importClass('android.app.PendingIntent');
|
||||
let IntentFilter = plus.android.importClass('android.content.IntentFilter');
|
||||
Uri = plus.android.importClass('android.net.Uri');
|
||||
NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
|
||||
let nfcAdapter = NfcAdapter.getDefaultAdapter(main);
|
||||
|
||||
if(nfcAdapter == null){
|
||||
uni.showToast({
|
||||
title: '设备不支持NFC!',
|
||||
icon: 'none'
|
||||
})
|
||||
noNFC = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nfcAdapter.isEnabled()) {
|
||||
uni.showToast({
|
||||
title: '请在系统设置中先启用NFC功能!',
|
||||
icon: 'none'
|
||||
});
|
||||
noNFC = true;
|
||||
return;
|
||||
}else{
|
||||
noNFC = false;
|
||||
}
|
||||
|
||||
let intent = new Intent(main, main.getClass());
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
let pendingIntent = PendingIntent.getActivity(main, 0, intent, 0);
|
||||
let ndef = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
|
||||
ndef.addDataType("*/*");
|
||||
let intentFiltersArray = [ndef];
|
||||
|
||||
plus.globalEvent.addEventListener('newintent',function() {
|
||||
console.log('newintent running');
|
||||
// 监听 NFC
|
||||
setTimeout(that.nfcRuning(), 1000);
|
||||
});
|
||||
plus.globalEvent.addEventListener('pause',function(e) {
|
||||
console.log('pause running');
|
||||
if (nfcAdapter) {
|
||||
//关闭前台调度系统
|
||||
//恢复默认状态
|
||||
nfcAdapter.disableForegroundDispatch(main);
|
||||
}
|
||||
});
|
||||
plus.globalEvent.addEventListener('resume',function(e) {
|
||||
console.log('resume running');
|
||||
if (nfcAdapter) {
|
||||
//开启前台调度系统
|
||||
nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
|
||||
}
|
||||
});
|
||||
nfcAdapter.enableForegroundDispatch(main, pendingIntent, intentFiltersArray, techListsArray);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
nfcRuning: function () {
|
||||
NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
|
||||
NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
|
||||
let main = plus.android.runtimeMainActivity();
|
||||
let intent = main.getIntent();
|
||||
let that = this;
|
||||
|
||||
console.log("action type:" + intent.getAction());
|
||||
|
||||
if (package_TECH_DISCOVERED == intent.getAction()) {
|
||||
if (readyWriteData) {
|
||||
that.write(intent);
|
||||
readyWriteData = false;
|
||||
} else if (readyRead) {
|
||||
that.read(intent);
|
||||
readyRead = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
write(intent) {
|
||||
try {
|
||||
toast('请勿移开标签 正在写入...');
|
||||
console.log("text=" + text);
|
||||
let textBytes = plus.android.invoke(text, "getBytes");
|
||||
// image/jpeg text/plain
|
||||
let uri = Uri.parse(text)
|
||||
let message = new NdefMessage([new NdefRecord.createUri(uri)]);
|
||||
console.log("====message===>", message)
|
||||
let Ndef = plus.android.importClass('android.nfc.tech.Ndef');
|
||||
let NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
|
||||
let tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||
let ndef = Ndef.get(tag);
|
||||
if (ndef != null) {
|
||||
// 待写入的数据长度
|
||||
let size = message.toByteArray().length;
|
||||
ndef.connect();
|
||||
if (!ndef.isWritable()) {
|
||||
toast('tag不允许写入!');
|
||||
return;
|
||||
}
|
||||
if (ndef.getMaxSize() < size) {
|
||||
toast('文件大小超出容量!');
|
||||
return;
|
||||
}
|
||||
ndef.writeNdefMessage(message);
|
||||
toast('写入数据成功!');
|
||||
return;
|
||||
} else {
|
||||
let format = NdefFormatable.get(tag);
|
||||
if (format != null) {
|
||||
try {
|
||||
format.connect();
|
||||
format.format(message);
|
||||
toast('格式化tag并且写入message');
|
||||
return;
|
||||
} catch (e) {
|
||||
toast('格式化tag失败.');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
toast('Tag不支持NDEF');
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
toast('写入失败');
|
||||
console.log("error=" + e);
|
||||
}
|
||||
|
||||
},
|
||||
read(intent) {
|
||||
toast('请勿移开标签正在读取数据');
|
||||
let that = this;
|
||||
// NFC id
|
||||
let bytesId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
|
||||
let nfc_id = that.byteArrayToHexString(bytesId);
|
||||
console.log('nfc_id:', nfc_id);
|
||||
let Parcelable = plus.android.importClass("android.os.Parcelable");
|
||||
let rawmsgs = intent.getParcelableArrayExtra("android.nfc.extra.NDEF_MESSAGES");
|
||||
//let rawmsgs = intent.getParcelableArrayExtra();
|
||||
|
||||
if(rawmsgs != null && rawmsgs.length > 0) {
|
||||
let records = rawmsgs[0].getRecords();
|
||||
let result = records[0].getPayload();
|
||||
let data = plus.android.newObject("java.lang.String", result);
|
||||
toast('NFC 数据:' + data);
|
||||
plus.runtime.openURL(data, function(res) {
|
||||
console.log("NFC 数据----", res);
|
||||
});
|
||||
console.log('NFC 数据:',data);
|
||||
readResult = data;
|
||||
// return data;
|
||||
}else{
|
||||
toast('没有读取到数据');
|
||||
return ""
|
||||
}
|
||||
},
|
||||
byteArrayToHexString: function (inarray) { // converts byte arrays to string
|
||||
let i, j, inn;
|
||||
let hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
|
||||
let out = "";
|
||||
|
||||
for(j = 0; j < inarray.length; ++j) {
|
||||
inn = inarray[j] & 0xff;
|
||||
i = (inn >>> 4) & 0x0f;
|
||||
out += hex[i];
|
||||
i = inn & 0x0f;
|
||||
out += hex[i];
|
||||
}
|
||||
return out;
|
||||
},
|
||||
writeData: function () {
|
||||
if(noNFC){
|
||||
toast('请检查设备是否支持并开启 NFC 功能!');
|
||||
return;
|
||||
}
|
||||
// 监听事件,触发条件
|
||||
readyWriteData = true;
|
||||
toast('请将NFC标签靠近!');
|
||||
},
|
||||
readData: function () {
|
||||
if(noNFC){
|
||||
toast('请检查设备是否支持并开启 NFC 功能!');
|
||||
return;
|
||||
}
|
||||
// 监听事件,触发条件
|
||||
readyRead = true;
|
||||
toast('请将NFC标签靠近!');
|
||||
},
|
||||
// 输入文本改变
|
||||
inputChanage: function (res) {
|
||||
console.log("正在编辑写入数据...", res)
|
||||
text = res
|
||||
}
|
||||
}
|
||||
function toast(content){
|
||||
uni.showToast({
|
||||
title: content,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user