修改NFC读取
This commit is contained in:
Binary file not shown.
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2020] 雷龙飞[1102228556@qq.com]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"name": "Android-NFC读卡",
|
||||
"id": "LF-Sense-Card",
|
||||
"version": "0.0.2",
|
||||
"description": "uni-app android原生插件 用来使用nfc读取m1卡",
|
||||
"_dp_type": "nativeplugin",
|
||||
"_dp_nativeplugin": {
|
||||
"android": {
|
||||
"plugins": [
|
||||
{
|
||||
"type": "module",
|
||||
"name": "LF-Sense-Card-M1",
|
||||
"class": "com.clubank.uniplugin_m1card.M1CardModule"
|
||||
}
|
||||
],
|
||||
"hooksClass": "",
|
||||
"integrateType": "aar",
|
||||
"dependencies": [],
|
||||
"compileOptions": {
|
||||
"sourceCompatibility": "1.8",
|
||||
"targetCompatibility": "1.8"
|
||||
},
|
||||
"abis": [
|
||||
"armeabi-v7a",
|
||||
"arm64-v8a",
|
||||
"x86"
|
||||
],
|
||||
"minSdkVersion": 19,
|
||||
"useAndroidX": false,
|
||||
"permissions": [
|
||||
"<uses-permission android:name=\"android.permission.NFC\" />"
|
||||
],
|
||||
"parameters": {
|
||||
"插件需要配置的参数名称, 如appid": {
|
||||
"des": "参数描述",
|
||||
"key": "AndroidManifest.xml中添加meta-data节点!对应android:name属性值, 如GETUI_APPID",
|
||||
"placeholder": "build.gradle中添加到manifestPlaceholders中的字段名"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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,7 +158,9 @@
|
||||
</block>
|
||||
<view class="report-border" :style="{borderColor:index<optionObj.pointList.length-1?'#E7E7E7':'#fff'}"></view>
|
||||
</view>
|
||||
<view class="btn-submit">
|
||||
<!-- 状态是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>
|
||||
@@ -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'
|
||||
})
|
||||
}
|
||||
16
unpackage/resources/__UNI__4C459F4/www/__uniappautomator.js
Normal file
16
unpackage/resources/__UNI__4C459F4/www/__uniappautomator.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
unpackage/resources/__UNI__4C459F4/www/__uniapperror.png
Normal file
BIN
unpackage/resources/__UNI__4C459F4/www/__uniapperror.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
File diff suppressed because one or more lines are too long
33
unpackage/resources/__UNI__4C459F4/www/__uniapppicker.js
Normal file
33
unpackage/resources/__UNI__4C459F4/www/__uniapppicker.js
Normal file
File diff suppressed because one or more lines are too long
8
unpackage/resources/__UNI__4C459F4/www/__uniappquill.js
Normal file
8
unpackage/resources/__UNI__4C459F4/www/__uniappquill.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
32
unpackage/resources/__UNI__4C459F4/www/__uniappscan.js
Normal file
32
unpackage/resources/__UNI__4C459F4/www/__uniappscan.js
Normal file
File diff suppressed because one or more lines are too long
BIN
unpackage/resources/__UNI__4C459F4/www/__uniappsuccess.png
Normal file
BIN
unpackage/resources/__UNI__4C459F4/www/__uniappsuccess.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
24
unpackage/resources/__UNI__4C459F4/www/__uniappview.html
Normal file
24
unpackage/resources/__UNI__4C459F4/www/__uniappview.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>View</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<script>var __uniConfig = {"globalStyle":{},"darkmode":false}</script>
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="uni-app-view.umd.js"></script>
|
||||
<script src="app-wxs.js"></script>
|
||||
<script src="app-renderjs.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
11
unpackage/resources/__UNI__4C459F4/www/app-config-service.js
Normal file
11
unpackage/resources/__UNI__4C459F4/www/app-config-service.js
Normal file
File diff suppressed because one or more lines are too long
1
unpackage/resources/__UNI__4C459F4/www/app-config.js
Normal file
1
unpackage/resources/__UNI__4C459F4/www/app-config.js
Normal file
@@ -0,0 +1 @@
|
||||
(function(){})();
|
||||
2
unpackage/resources/__UNI__4C459F4/www/app-renderjs.js
Normal file
2
unpackage/resources/__UNI__4C459F4/www/app-renderjs.js
Normal file
@@ -0,0 +1,2 @@
|
||||
var __renderjsModules={};
|
||||
__renderjsModules["1e78dde8"]=(()=>{var a=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var p=(t,n)=>{for(var i in n)a(t,i,{get:n[i],enumerable:!0})},f=(t,n,i,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of u(n))!d.call(t,r)&&r!==i&&a(t,r,{get:()=>n[r],enumerable:!(o=l(n,r))||o.enumerable});return t};var g=t=>f(a({},"__esModule",{value:!0}),t);var m={};p(m,{default:()=>w});var e={};window&&!window.$mescrollRenderInit&&(window.$mescrollRenderInit=!0,window.addEventListener("touchstart",function(t){e.disabled()||(e.startPoint=e.getPoint(t))},{passive:!0}),window.addEventListener("touchmove",function(t){if(!e.disabled()&&!(e.getScrollTop()>0)){var n=e.getPoint(t),i=n.y-e.startPoint.y;if(i>0&&!e.isDownScrolling&&!e.optDown.isLock&&(!e.isUpScrolling||e.isUpScrolling&&e.isUpBoth)){for(var o=t.target,r=!1;o&&o.tagName&&o.tagName!=="UNI-PAGE-BODY"&&o.tagName!="BODY";){var s=o.classList;if(s&&s.contains("mescroll-render-touch")){r=!0;break}o=o.parentNode}r&&t.cancelable&&!t.defaultPrevented&&t.preventDefault()}}},{passive:!1}));e.getScrollTop=function(){return e.scrollTop||0};e.disabled=function(){return!e.optDown||!e.optDown.use||e.optDown.native};e.getPoint=function(t){return t?t.touches&&t.touches[0]?{x:t.touches[0].pageX,y:t.touches[0].pageY}:t.changedTouches&&t.changedTouches[0]?{x:t.changedTouches[0].pageX,y:t.changedTouches[0].pageY}:{x:t.clientX,y:t.clientY}:{x:0,y:0}};function h(t){e.optDown=t.optDown,e.scrollTop=t.scrollTop,e.isDownScrolling=t.isDownScrolling,e.isUpScrolling=t.isUpScrolling,e.isUpBoth=t.isUpBoth}var v={data(){return{propObserver:h}}},c=v;var w={mixins:[c]};return g(m);})();
|
||||
28
unpackage/resources/__UNI__4C459F4/www/app-service.js
Normal file
28
unpackage/resources/__UNI__4C459F4/www/app-service.js
Normal file
File diff suppressed because one or more lines are too long
2
unpackage/resources/__UNI__4C459F4/www/app-wxs.js
Normal file
2
unpackage/resources/__UNI__4C459F4/www/app-wxs.js
Normal file
@@ -0,0 +1,2 @@
|
||||
var __wxsModules={};
|
||||
__wxsModules["199dc8ed"]=(()=>{var f=(o,e)=>()=>(e||o((e={exports:{}}).exports,e),e.exports);var w=f((m,c)=>{var t={};t.onMoving=function(o,e,n){o.requestAnimationFrame(function(){o.selectComponent(".mescroll-wxs-content").setStyle({"will-change":"transform",transform:"translateY("+n+"px)",transition:""});var r=o.selectComponent(".mescroll-wxs-progress");r&&r.setStyle({transform:"rotate("+360*e+"deg)"})})};t.showLoading=function(o){t.downHight=t.optDown.offset,o.requestAnimationFrame(function(){o.selectComponent(".mescroll-wxs-content").setStyle({"will-change":"auto",transform:"translateY("+t.downHight+"px)",transition:"transform 300ms"})})};t.endDownScroll=function(o){t.downHight=0,t.isDownScrolling=!1,o.requestAnimationFrame(function(){o.selectComponent(".mescroll-wxs-content").setStyle({"will-change":"auto",transform:"translateY(0)",transition:"transform 300ms"})})};t.clearTransform=function(o){o.requestAnimationFrame(function(){o.selectComponent(".mescroll-wxs-content").setStyle({"will-change":"",transform:"",transition:""})})};function h(o){t.optDown=o.optDown,t.scrollTop=o.scrollTop,t.bodyHeight=o.bodyHeight,t.isDownScrolling=o.isDownScrolling,t.isUpScrolling=o.isUpScrolling,t.isUpBoth=o.isUpBoth,t.isScrollBody=o.isScrollBody,t.startTop=o.scrollTop}function u(o,e,n){t.disabled()||o.callType&&(o.callType==="showLoading"?t.showLoading(n):o.callType==="endDownScroll"?t.endDownScroll(n):o.callType==="clearTransform"&&t.clearTransform(n))}function p(o,e){t.downHight=0,t.startPoint=t.getPoint(o),t.startTop=t.getScrollTop(),t.startAngle=0,t.lastPoint=t.startPoint,t.maxTouchmoveY=t.getBodyHeight()-t.optDown.bottomOffset,t.inTouchend=!1,t.callMethod(e,{type:"setWxsProp"})}function d(o,e){var n=!0;if(t.disabled())return n;var r=t.getScrollTop(),l=t.getPoint(o),i=l.y-t.startPoint.y;if(i>0&&(t.isScrollBody&&r<=0||!t.isScrollBody&&(r<=0||r<=t.optDown.startTop&&r===t.startTop))&&!t.inTouchend&&!t.isDownScrolling&&!t.optDown.isLock&&(!t.isUpScrolling||t.isUpScrolling&&t.isUpBoth)){if(t.startAngle||(t.startAngle=t.getAngle(t.lastPoint,l)),t.startAngle<t.optDown.minAngle)return n;if(t.maxTouchmoveY>0&&l.y>=t.maxTouchmoveY)return t.inTouchend=!0,s(o,e),n;n=!1;var a=l.y-t.lastPoint.y;t.downHight<t.optDown.offset?(t.movetype!==1&&(t.movetype=1,t.callMethod(e,{type:"setLoadType",downLoadType:1}),t.isMoveDown=!0),t.downHight+=a*t.optDown.inOffsetRate):(t.movetype!==2&&(t.movetype=2,t.callMethod(e,{type:"setLoadType",downLoadType:2}),t.isMoveDown=!0),a>0?t.downHight+=a*t.optDown.outOffsetRate:t.downHight+=a),t.downHight=Math.round(t.downHight);var g=t.downHight/t.optDown.offset;t.onMoving(e,g,t.downHight)}return t.lastPoint=l,n}function s(o,e){if(t.isMoveDown)t.downHight>=t.optDown.offset?(t.downHight=t.optDown.offset,t.callMethod(e,{type:"triggerDownScroll"})):(t.downHight=0,t.callMethod(e,{type:"endDownScroll"})),t.movetype=0,t.isMoveDown=!1;else if(!t.isScrollBody&&t.getScrollTop()===t.startTop){var n=t.getPoint(o).y-t.startPoint.y<0;if(n){var r=t.getAngle(t.getPoint(o),t.startPoint);r>80&&t.callMethod(e,{type:"triggerUpScroll"})}}t.callMethod(e,{type:"setWxsProp"})}t.disabled=function(){return!t.optDown||!t.optDown.use||t.optDown.native};t.getPoint=function(o){return o?o.touches&&o.touches[0]?{x:o.touches[0].pageX,y:o.touches[0].pageY}:o.changedTouches&&o.changedTouches[0]?{x:o.changedTouches[0].pageX,y:o.changedTouches[0].pageY}:{x:o.clientX,y:o.clientY}:{x:0,y:0}};t.getAngle=function(o,e){var n=Math.abs(o.x-e.x),r=Math.abs(o.y-e.y),l=Math.sqrt(n*n+r*r),i=0;return l!==0&&(i=Math.asin(r/l)/Math.PI*180),i};t.getScrollTop=function(){return t.scrollTop||0};t.getBodyHeight=function(){return t.bodyHeight||0};t.callMethod=function(o,e){o&&o.callMethod("wxsCall",e)};c.exports={propObserver:h,callObserver:u,touchstartEvent:p,touchmoveEvent:d,touchendEvent:s}});return w();})();
|
||||
3
unpackage/resources/__UNI__4C459F4/www/app.css
Normal file
3
unpackage/resources/__UNI__4C459F4/www/app.css
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
1
unpackage/resources/__UNI__4C459F4/www/manifest.json
Normal file
1
unpackage/resources/__UNI__4C459F4/www/manifest.json
Normal file
@@ -0,0 +1 @@
|
||||
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__4C459F4","name":"718友晟","version":{"name":"1.0.0","code":"100"},"description":"","developer":{"name":"","email":"","url":""},"permissions":{"Push":{},"Camera":{},"Barcode":{},"VideoPlayer":{},"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"target":"id:1","autoclose":true,"waiting":false,"delay":0},"popGesture":"close","launchwebview":{"id":"1","kernel":"WKWebview"},"compatible":{"ignoreVersion":true},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"nativePlugins":{"Tm-TmSafeSaveFileModule":{"__plugin_info__":{"name":"TmSafeSaveFileModule隐私数据保存","description":"TmSafeSaveFileModule隐私数据保存","platforms":"Android","url":"","android_package_name":"","ios_bundle_id":"","isCloud":false,"bought":-1,"pid":"","parameters":{}}}},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"safearea":{"background":"#000000","bottom":{"offset":"auto"}},"uni-app":{"control":"uni-v3","vueVersion":"3","compilerVersion":"4.76","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal","webView":{"minUserAgentVersion":"49.0"}},"tabBar":{"position":"bottom","color":"#919191","selectedColor":"#ffffff","borderStyle":"#ffffff","blurEffect":"none","fontSize":"12px","iconWidth":"24px","spacing":"3px","height":"50px","list":[{"pagePath":"pages/home/home","iconPath":"/static/images/tabs/menu-home.png","selectedIconPath":"/static/images/tabs/menu-home-on.png","text":"首页"},{"pagePath":"pages/business/business","iconPath":"/static/images/tabs/menu-business.png","selectedIconPath":"/static/images/tabs/menu-business-on.png","text":"业务中心"},{"pagePath":"pages/notice/notice","iconPath":"/static/images/tabs/menu-info.png","selectedIconPath":"/static/images/tabs/menu-info-on.png","text":"消息"},{"pagePath":"pages/userinfo/userinfo","iconPath":"/static/images/tabs/menu-me.png","selectedIconPath":"/static/images/tabs/menu-me-on.png","text":"我的"}],"backgroundColor":"#000000","selectedIndex":0,"shown":true}},"app-harmony":{"useragent":{"value":"uni-app","concatenate":true},"uniStatistics":{"enable":false},"safearea":{"background":"#000000","bottom":{"offset":"auto"}}},"launch_path":"__uniappview.html"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user