vite-uni-dev-tool 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +46 -0
  2. package/dist/const.cjs +1 -1
  3. package/dist/const.d.ts +12 -0
  4. package/dist/const.d.ts.map +1 -1
  5. package/dist/const.js +1 -1
  6. package/dist/core-shared.d.ts +1 -1
  7. package/dist/core-shared.d.ts.map +1 -1
  8. package/dist/core-shared.js +1 -1
  9. package/dist/core.d.ts +10 -3
  10. package/dist/core.d.ts.map +1 -1
  11. package/dist/core.js +2 -2
  12. package/dist/i18n/locales/en.cjs +1 -1
  13. package/dist/i18n/locales/en.d.ts +81 -0
  14. package/dist/i18n/locales/en.d.ts.map +1 -1
  15. package/dist/i18n/locales/en.js +1 -1
  16. package/dist/i18n/locales/zh-Hans.cjs +1 -1
  17. package/dist/i18n/locales/zh-Hans.d.ts +82 -1
  18. package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
  19. package/dist/i18n/locales/zh-Hans.js +1 -1
  20. package/dist/modules/devConsole/index.cjs +1 -1
  21. package/dist/modules/devConsole/index.js +3 -3
  22. package/dist/modules/devEvent/index.cjs +3 -3
  23. package/dist/modules/devEvent/index.d.ts +1 -0
  24. package/dist/modules/devEvent/index.d.ts.map +1 -1
  25. package/dist/modules/devEvent/index.js +3 -3
  26. package/dist/modules/devIntercept/index.cjs +14 -13
  27. package/dist/modules/devIntercept/index.d.ts +19 -0
  28. package/dist/modules/devIntercept/index.d.ts.map +1 -1
  29. package/dist/modules/devIntercept/index.js +14 -13
  30. package/dist/modules/devStore/index.cjs +1 -1
  31. package/dist/modules/devStore/index.d.ts +21 -0
  32. package/dist/modules/devStore/index.d.ts.map +1 -1
  33. package/dist/modules/devStore/index.js +1 -1
  34. package/dist/plugins/uniDevTool/transform/transformMain.cjs +1 -1
  35. package/dist/plugins/uniDevTool/transform/transformMain.js +1 -1
  36. package/dist/type.d.ts +47 -2
  37. package/dist/type.d.ts.map +1 -1
  38. package/dist/utils/language.cjs +1 -1
  39. package/dist/utils/language.d.ts.map +1 -1
  40. package/dist/utils/language.js +1 -1
  41. package/dist/utils/object.cjs +1 -1
  42. package/dist/utils/object.d.ts.map +1 -1
  43. package/dist/utils/object.js +1 -1
  44. package/dist/v3/DevTool/components/BluetoothList/BluetoothItem.vue +199 -0
  45. package/dist/v3/DevTool/components/BluetoothList/BluetoothTool.vue +730 -0
  46. package/dist/v3/DevTool/components/BluetoothList/index.vue +167 -0
  47. package/dist/v3/{CaptureScreen → DevTool/components/CaptureScreen}/index.vue +109 -109
  48. package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/ConsoleItem.vue +225 -224
  49. package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/RunJSInput.vue +247 -249
  50. package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/index.vue +171 -160
  51. package/dist/v3/{ConsoleList → DevTool/components/ConsoleList}/staticTips.ts +1145 -1145
  52. package/dist/v3/{DevToolButton → DevTool/components/DevToolButton}/index.vue +7 -4
  53. package/dist/v3/{DevToolTitle → DevTool/components/DevToolTitle}/index.vue +24 -24
  54. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/DevToolOverlay.vue +197 -182
  55. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/const.ts +28 -5
  56. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/dataUtils.ts +48 -48
  57. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/useDevToolData.ts +387 -338
  58. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/useDevToolHandlers.ts +629 -549
  59. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/hooks/useDevToolOverlay.ts +197 -184
  60. package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/index.vue +67 -16
  61. package/dist/v3/{ElEvent → DevTool/components/ElEvent}/ElEventItem.vue +105 -105
  62. package/dist/v3/{ElEvent → DevTool/components/ElEvent}/index.vue +106 -109
  63. package/dist/v3/{Instance → DevTool/components/Instance}/components/InstanceTreeNode.vue +265 -265
  64. package/dist/v3/{Instance → DevTool/components/Instance}/flatten.ts +226 -226
  65. package/dist/v3/{Instance → DevTool/components/Instance}/index.vue +94 -94
  66. package/dist/v3/{Instance → DevTool/components/Instance}/registry.ts +49 -49
  67. package/dist/v3/{Instance → DevTool/components/Instance}/transformTree.ts +375 -375
  68. package/dist/v3/{Instance → DevTool/components/Instance}/transformTreeCtx.ts +268 -268
  69. package/dist/v3/{Instance → DevTool/components/Instance}/typing.d.ts +43 -43
  70. package/dist/v3/{InstanceDetail → DevTool/components/InstanceDetail}/index.vue +485 -485
  71. package/dist/v3/{JsonDetail → DevTool/components/JsonDetail}/index.vue +70 -70
  72. package/dist/v3/{NFCList → DevTool/components/NFCList}/NFCItem.vue +112 -113
  73. package/dist/v3/{NFCList → DevTool/components/NFCList}/NFCTool.vue +454 -478
  74. package/dist/v3/{NFCList → DevTool/components/NFCList}/const.ts +56 -56
  75. package/dist/v3/{NFCList → DevTool/components/NFCList}/index.vue +94 -98
  76. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/InterceptConfig.vue +624 -608
  77. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/InterceptItem.vue +140 -140
  78. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkDetail.vue +287 -296
  79. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkIntercept.vue +88 -93
  80. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkItem.vue +163 -167
  81. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/NetworkSend.vue +589 -556
  82. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/const.ts +4 -4
  83. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/hooks/useNetworkForm.ts +86 -86
  84. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/index.vue +160 -160
  85. package/dist/v3/{NetworkList → DevTool/components/NetworkList}/utils.ts +101 -101
  86. package/dist/v3/{Performance → DevTool/components/Performance}/index.vue +498 -495
  87. package/dist/v3/{Performance → DevTool/components/Performance}/modules/PerformanceMetrics.vue +153 -153
  88. package/dist/v3/{Performance → DevTool/components/Performance}/modules/PerformanceWidget.vue +12 -9
  89. package/dist/v3/{Performance → DevTool/components/Performance}/modules/usePerformanceChart.ts +460 -460
  90. package/dist/v3/{Performance → DevTool/components/Performance}/modules/usePerformanceData.ts +258 -258
  91. package/dist/v3/{PiniaList → DevTool/components/PiniaList}/index.vue +93 -94
  92. package/dist/v3/{RouteList → DevTool/components/RouteList}/index.vue +21 -24
  93. package/dist/v3/{RunJS → DevTool/components/RunJS}/index.vue +148 -148
  94. package/dist/v3/{ScanCodeList → DevTool/components/ScanCodeList}/ScanCodeItem.vue +97 -98
  95. package/dist/v3/{ScanCodeList → DevTool/components/ScanCodeList}/index.vue +100 -104
  96. package/dist/v3/{SettingButton → DevTool/components/SettingButton}/index.vue +45 -45
  97. package/dist/v3/{SettingList → DevTool/components/SettingList}/index.vue +218 -150
  98. package/dist/v3/DevTool/components/SettingList/modules/SettingBarrage.vue +304 -0
  99. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingDevTool.vue +212 -208
  100. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingInfo.vue +157 -119
  101. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingLanguage.vue +74 -74
  102. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingLog.vue +230 -230
  103. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingNetwork.vue +3 -3
  104. package/dist/v3/{SettingList → DevTool/components/SettingList}/modules/SettingTheme.vue +37 -7
  105. package/dist/v3/{SettingList → DevTool/components/SettingList}/typing.d.ts +2 -2
  106. package/dist/v3/{SourceCode → DevTool/components/SourceCode}/Line.vue +127 -116
  107. package/dist/v3/{SourceCode → DevTool/components/SourceCode}/index.vue +8 -8
  108. package/dist/v3/{SourceCode → DevTool/components/SourceCode}/parseCode.ts +609 -701
  109. package/dist/v3/{StorageList → DevTool/components/StorageList}/index.vue +174 -174
  110. package/dist/v3/{TransferList → DevTool/components/TransferList}/TransferDetail.vue +268 -268
  111. package/dist/v3/{TransferList → DevTool/components/TransferList}/TransferItem.vue +4 -4
  112. package/dist/v3/{TransferList → DevTool/components/TransferList}/index.vue +8 -8
  113. package/dist/v3/{UniEvent → DevTool/components/UniEvent}/UniEventItem.vue +6 -7
  114. package/dist/v3/{UniEvent → DevTool/components/UniEvent}/index.vue +6 -6
  115. package/dist/v3/{VuexList → DevTool/components/VuexList}/index.vue +84 -84
  116. package/dist/v3/{WebSocket → DevTool/components/WebSocket}/WebSocketDetail.vue +8 -8
  117. package/dist/v3/{WebSocket → DevTool/components/WebSocket}/WebSocketItem.vue +4 -4
  118. package/dist/v3/{WebSocket → DevTool/components/WebSocket}/index.vue +8 -8
  119. package/dist/v3/DevTool/index.vue +179 -5
  120. package/dist/v3/{AppTransition → components/AppTransition}/index.vue +176 -170
  121. package/dist/v3/{AutoSizer → components/AutoSizer}/index.vue +192 -192
  122. package/dist/v3/{AutoSizer → components/AutoSizer}/index1.vue +184 -184
  123. package/dist/v3/{AutoSizer → components/AutoSizer}/utils.ts +49 -49
  124. package/dist/v3/components/Barrage/BarrageItem.vue +137 -0
  125. package/dist/v3/components/Barrage/index.vue +202 -0
  126. package/dist/v3/{CircularButton → components/CircularButton}/index.vue +84 -84
  127. package/dist/v3/{CustomSwiper → components/CustomSwiper}/CustomSwiperItem.vue +49 -49
  128. package/dist/v3/{CustomSwiper → components/CustomSwiper}/index.vue +104 -104
  129. package/dist/v3/{DraggableContainer → components/DraggableContainer}/index.vue +1 -1
  130. package/dist/v3/{Empty → components/Empty}/index.vue +29 -29
  131. package/dist/v3/{FilterInput → components/FilterInput}/index.vue +1 -1
  132. package/dist/v3/{FilterSelect → components/FilterSelect}/index.vue +179 -179
  133. package/dist/v3/{JsonPretty → components/JsonPretty}/components/Brackets/index.vue +27 -27
  134. package/dist/v3/{JsonPretty → components/JsonPretty}/components/Carets/index.vue +59 -59
  135. package/dist/v3/{JsonPretty → components/JsonPretty}/components/CheckController/index.vue +136 -136
  136. package/dist/v3/{JsonPretty → components/JsonPretty}/components/TreeNode/index.vue +387 -381
  137. package/dist/v3/{JsonPretty → components/JsonPretty}/hooks/useClipboard.ts +21 -21
  138. package/dist/v3/{JsonPretty → components/JsonPretty}/hooks/useError.ts +21 -21
  139. package/dist/v3/{JsonPretty → components/JsonPretty}/index.vue +16 -13
  140. package/dist/v3/{JsonPretty → components/JsonPretty}/type.ts +127 -126
  141. package/dist/v3/{JsonPretty → components/JsonPretty}/utils/index.ts +169 -169
  142. package/dist/v3/{MovableContainer → components/MovableContainer}/index.vue +1 -1
  143. package/dist/v3/{Pick → components/Pick}/index.vue +322 -322
  144. package/dist/v3/{Tabs → components/Tabs}/index.vue +30 -4
  145. package/dist/v3/{Tag → components/Tag}/index.vue +113 -113
  146. package/dist/v3/{VirtualList → components/VirtualList}/AutoSize.vue +40 -40
  147. package/dist/v3/{VirtualList → components/VirtualList}/index.vue +416 -412
  148. package/dist/v3/hooks/useBluetooth/index.ts +561 -0
  149. package/dist/v3/hooks/useContainerStyle.ts +153 -153
  150. package/dist/v3/hooks/useNFC/index.ts +107 -107
  151. package/dist/v3/hooks/useNFC/typing.d.ts +396 -396
  152. package/dist/v3/hooks/useNFC/useNFCAndroid.ts +966 -966
  153. package/dist/v3/hooks/useNFC/useNFCMpWeiXin.ts +812 -812
  154. package/dist/v3/hooks/useNFC/utils.ts +754 -754
  155. package/dist/v3/hooks/useRequest/index.ts +586 -573
  156. package/dist/v3/hooks/useRequest/utils.ts +267 -267
  157. package/dist/v3/hooks/useScanCode/index.ts +206 -206
  158. package/dist/v3/hooks/useWebsocket/README.md +79 -0
  159. package/dist/v3/hooks/useWebsocket/index.ts +253 -0
  160. package/dist/v3/styles/theme.css +17 -10
  161. package/dist/v3/styles/theme.ts +12 -12
  162. package/package.json +59 -64
  163. package/dist/plugins/uniParseStock/index.d.ts +0 -10
  164. package/dist/plugins/uniParseStock/index.d.ts.map +0 -1
  165. /package/dist/v3/{DevToolWindow → DevTool/components/DevToolWindow}/index.css +0 -0
  166. /package/dist/v3/{SettingList → DevTool/components/SettingList}/index.css +0 -0
  167. /package/dist/v3/{Empty → components/Empty}/empty.png +0 -0
  168. /package/dist/v3/{VirtualList → components/VirtualList}/readme.md +0 -0
@@ -1,966 +1,966 @@
1
- import { onMounted, onUnmounted, ref } from 'vue';
2
- import {
3
- formatCardInfo,
4
- getAndroidTechs,
5
- getReadWriteStandard,
6
- getSakHex,
7
- getSakType,
8
- getTnfDescription,
9
- getTypeDescription,
10
- parseAtqaAndroid,
11
- parseUniversalTLV,
12
- parseVCardPayload,
13
- stringToBytes,
14
- transformArrayBuffer,
15
- wifiTLVConfig,
16
- generateWSCBytesForAndroid,
17
- } from './utils';
18
-
19
- // 声明变量,但不立即初始化
20
- let NfcAdapter: any;
21
- let _Context: any;
22
- let _Tag: any;
23
- let Ndef: any;
24
- let NfcA: any;
25
- let NfcB: any;
26
- let NfcF: any;
27
- let NfcV: any;
28
- let _NdefFormat: any;
29
- let NdefFormatable: any;
30
- let MifareClassic: any;
31
- let MifareUltralight: any;
32
- let IsoDep: any;
33
- let Intent: any;
34
- let PendingIntent: any;
35
- let IntentFilter: any;
36
- let Settings: any;
37
- let NdefRecord: any;
38
- let NdefMessage: any;
39
-
40
- let INTENT_FLAGS_SINGLE_TOP: any;
41
- let PENDING_INTENT_FLAGS: any;
42
- let EXTRA_NDEF_MESSAGES: any;
43
- let EXTRA_ID: any;
44
- let EXTRA_TAG: any;
45
- let _TNF_MIME_MEDIA: any;
46
- let _ACTION_NFC_SETTINGS: any;
47
-
48
- let NFC_INTENT_FILTERS: any[] = [];
49
-
50
- const NFC_TECH_LISTS = [
51
- ['android.nfc.tech.Ndef'],
52
- ['android.nfc.tech.NdefFormatable'],
53
- ['android.nfc.tech.IsoDep'],
54
- ['android.nfc.tech.NfcA'],
55
- ['android.nfc.tech.NfcB'],
56
- ['android.nfc.tech.NfcF'],
57
- ['android.nfc.tech.NfcV'],
58
- ['android.nfc.tech.MifareClassic'],
59
- ['android.nfc.tech.MifareUltralight'],
60
- ];
61
-
62
- // 初始化 Android 类
63
- function initAndroidClasses() {
64
- try {
65
- if (NfcAdapter) return; // 避免重复初始化
66
-
67
- NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
68
- _Context = plus.android.importClass('android.content.Context');
69
- _Tag = plus.android.importClass('android.nfc.Tag');
70
- Ndef = plus.android.importClass('android.nfc.tech.Ndef');
71
- NfcA = plus.android.importClass('android.nfc.tech.NfcA');
72
- NfcB = plus.android.importClass('android.nfc.tech.NfcB');
73
- NfcF = plus.android.importClass('android.nfc.tech.NfcF');
74
- NfcV = plus.android.importClass('android.nfc.tech.NfcV');
75
- _NdefFormat = plus.android.importClass('android.nfc.tech.NdefFormat');
76
- NdefFormatable = plus.android.importClass(
77
- 'android.nfc.tech.NdefFormatable',
78
- );
79
- MifareClassic = plus.android.importClass('android.nfc.tech.MifareClassic');
80
- MifareUltralight = plus.android.importClass(
81
- 'android.nfc.tech.MifareUltralight',
82
- );
83
- IsoDep = plus.android.importClass('android.nfc.tech.IsoDep');
84
- Intent = plus.android.importClass('android.content.Intent');
85
- PendingIntent = plus.android.importClass('android.app.PendingIntent');
86
- IntentFilter = plus.android.importClass('android.content.IntentFilter');
87
- Settings = plus.android.importClass('android.provider.Settings');
88
- NdefRecord = plus.android.importClass('android.nfc.NdefRecord');
89
- NdefMessage = plus.android.importClass('android.nfc.NdefMessage');
90
-
91
- INTENT_FLAGS_SINGLE_TOP = Intent.FLAG_ACTIVITY_SINGLE_TOP;
92
- PENDING_INTENT_FLAGS = PendingIntent.FLAG_MUTABLE;
93
-
94
- EXTRA_NDEF_MESSAGES = NfcAdapter.EXTRA_NDEF_MESSAGES;
95
- EXTRA_ID = NfcAdapter.EXTRA_ID;
96
- EXTRA_TAG = NfcAdapter.EXTRA_TAG;
97
-
98
- _TNF_MIME_MEDIA = NdefRecord.TNF_MIME_MEDIA;
99
- _ACTION_NFC_SETTINGS = Settings.ACTION_NFC_SETTINGS;
100
-
101
- NFC_INTENT_FILTERS = [
102
- NfcAdapter.ACTION_NDEF_DISCOVERED,
103
- NfcAdapter.ACTION_TECH_DISCOVERED,
104
- NfcAdapter.ACTION_TAG_DISCOVERED,
105
- ];
106
- } catch (e) {
107
- console.error('Core: initAndroidClasses error', e);
108
- }
109
- }
110
-
111
- export default function useNFCAndroid(
112
- listener: NFC.Listener,
113
- options?: NFC.NfcOption,
114
- ): NFC.NfcResult {
115
- /** 是否支持 nfc */
116
- const isSupportNFC = ref<boolean | undefined>(false);
117
- /** 是否监听中 */
118
- const isListening = ref<boolean>(false);
119
- /** 是否已连接 */
120
- const isConnected = ref<boolean>(false);
121
- /** nfc 数据 */
122
- const data = ref<NFC.Result>({
123
- id: new ArrayBuffer(0),
124
- techs: [],
125
- byteArray: [],
126
- hexArray: [],
127
- hexNumberArray: [],
128
- hexString: '',
129
- });
130
-
131
- /** ndef 写入方式比较特殊,需要特殊处理 */
132
- const isNdef = ref(false);
133
-
134
- // 延迟获取 mainActivity,确保 plus 已就绪
135
- let mainActivity: any = null;
136
-
137
- onMounted(() => {
138
- initAndroidClasses();
139
- try {
140
- mainActivity = plus.android.runtimeMainActivity();
141
- initNfcAdapter();
142
- } catch (e) {
143
- console.error('Core: failed to init NFC adapter', e);
144
- }
145
- });
146
-
147
- let checkInterval: any = null;
148
-
149
- let nfcAdapter: NFC.Adapter | null = null;
150
- let nfcPendingIntent: any = null;
151
- let nfcTag: any = null;
152
- let nfcInstance: any = null;
153
-
154
- let maxTransceiveLength = 0;
155
-
156
- function checkConnected() {
157
- checkInterval = setInterval(() => {
158
- if (!nfcAdapter) {
159
- isConnected.value = false;
160
- clearInterval(checkInterval);
161
- return;
162
- }
163
-
164
- if (!nfcAdapter.isEnabled()) {
165
- isConnected.value = false;
166
- clearInterval(checkInterval);
167
- return;
168
- }
169
- if (!nfcInstance) {
170
- isConnected.value = false;
171
- clearInterval(checkInterval);
172
- return;
173
- }
174
-
175
- if (nfcInstance.isConnected()) {
176
- isConnected.value = true;
177
- } else {
178
- isConnected.value = false;
179
- clearInterval(checkInterval);
180
- }
181
- }, options?.checkConnectedDelay ?? 500);
182
- }
183
-
184
- function getInstance(techs: NFC.NfcType[], needNdef?: boolean) {
185
- let type = 'Ndef';
186
- if (needNdef) {
187
- type = 'Ndef';
188
- } else {
189
- type = techs.filter((tech) => tech != 'Ndef' && tech != 'NDEF')?.[0];
190
- }
191
-
192
- switch (type) {
193
- case 'NFC-A':
194
- return NfcA.get(nfcTag);
195
- case 'NFC-B':
196
- return NfcB.get(nfcTag);
197
- case 'NFC-F':
198
- return NfcF.get(nfcTag);
199
- case 'NFC-V':
200
- return NfcV.get(nfcTag);
201
- case 'MIFARE Classic':
202
- return MifareClassic.get(nfcTag);
203
- case 'MIFARE Ultralight':
204
- return MifareUltralight.get(nfcTag);
205
- case 'Ndef':
206
- isNdef.value = true;
207
- return Ndef.get(nfcTag);
208
- case 'Ndef Formatable':
209
- return NdefFormatable.get(nfcTag);
210
- case 'ISO-DEP':
211
- return IsoDep.get(nfcTag);
212
- default:
213
- return undefined;
214
- }
215
- }
216
-
217
- function innerListener() {
218
- try {
219
- const intent = mainActivity.getIntent();
220
- if (!intent) {
221
- uni.showToast({ title: 'NFC 数据解析失败', icon: 'none' });
222
- return;
223
- }
224
-
225
- // 获取id
226
- const id = intent.getByteArrayExtra(EXTRA_ID);
227
-
228
- const transform = transformArrayBuffer(id);
229
-
230
- let techs: NFC.NfcType[] = [];
231
-
232
- let sak = 0;
233
- let sakHex = '';
234
- let sakType = undefined;
235
- let atqaInfo = {
236
- uidType: 0,
237
- storageSize: 0,
238
- manufacturer: 0,
239
- cardFeatures: 0,
240
- reserved: 0,
241
- };
242
- let readWriteStandard: string[] = [];
243
- let sectorCount = 0;
244
- let blockCount = 0;
245
- let messages: any = [];
246
- nfcTag = intent.getParcelableExtra(EXTRA_TAG); // 先获取 Tag 实例
247
-
248
- if (nfcTag) {
249
- // 核心方法:通过 Tag.getTechList() 获取原生技术列表数组
250
- const nativeTechList = nfcTag.getTechList();
251
- // 转换为可直接使用的 string 数组(兼容原生返回格式)
252
- techs = Array.isArray(nativeTechList) ? nativeTechList : [];
253
-
254
- techs = getAndroidTechs(techs);
255
-
256
- nfcInstance = getInstance(techs, true);
257
-
258
- if (!nfcInstance) {
259
- uni.showToast({ title: 'NFC 实例获取失败', icon: 'none' });
260
- return;
261
- }
262
-
263
- // 一定要连接
264
- nfcInstance?.connect?.();
265
-
266
- if (isNdef.value) {
267
- const otherInstance = getInstance(techs);
268
- maxTransceiveLength = otherInstance?.getMaxTransceiveLength?.() ?? 0;
269
- } else {
270
- maxTransceiveLength = nfcInstance?.getMaxTransceiveLength?.() ?? 0;
271
- }
272
-
273
- readWriteStandard = getReadWriteStandard(techs);
274
-
275
- sectorCount = nfcInstance?.getSectorCount?.();
276
-
277
- blockCount = nfcInstance?.getBlockCount?.();
278
-
279
- const ndefMessage = intent.getParcelableArrayExtra(EXTRA_NDEF_MESSAGES);
280
-
281
- if (ndefMessage && ndefMessage.length > 0) {
282
- let records = ndefMessage?.[0]?.getRecords?.();
283
-
284
- records = records.map((record: any) => {
285
- const idObject = record?.getId?.();
286
-
287
- const id = idObject
288
- ? plus.android.invoke(idObject, 'toString')
289
- : '';
290
-
291
- const tnfObject = record?.getTnf?.();
292
- const tnf = tnfObject
293
- ? plus.android.invoke(tnfObject, 'toString')
294
- : '';
295
- const tnfDescription = getTnfDescription(tnf);
296
-
297
- const payloadByte = record?.getPayload?.();
298
-
299
- const typeObject = record?.getType?.();
300
- const type = typeObject
301
- ? (plus.android.newObject('java.lang.String', typeObject) as any)
302
- : '';
303
- const typeDescription = getTypeDescription(
304
- type as string,
305
- payloadByte,
306
- );
307
-
308
- let payload: any = payloadByte
309
- ? plus.android.newObject('java.lang.String', payloadByte)
310
- : '';
311
- if (type === 'application/vnd.wfa.wsc') {
312
- payload = parseUniversalTLV(payloadByte, wifiTLVConfig);
313
- } else if (type === 'text/vcard') {
314
- payload = parseVCardPayload(payload);
315
- }
316
-
317
- return {
318
- id,
319
- tnf,
320
- tnfDescription,
321
- type,
322
- typeDescription,
323
- payload,
324
- };
325
- });
326
- messages = [
327
- {
328
- records,
329
- },
330
- ];
331
- } else {
332
- messages = [];
333
- }
334
-
335
- if (techs.includes('NFC-A')) {
336
- // NFC-A 标签的 SAK 信息
337
- const nfcAInstance = NfcA.get(nfcTag);
338
- const sakShortObject =
339
- plus.android.invoke(nfcAInstance, 'getSak') ?? 0;
340
-
341
- const sakStr = plus.android.invoke(sakShortObject, 'toString');
342
-
343
- sak = Number(sakStr) || 0;
344
-
345
- sakType = getSakType(sak);
346
-
347
- sakHex = getSakHex(sak);
348
-
349
- const atqa = plus.android.invoke(nfcAInstance, 'getAtqa');
350
-
351
- atqaInfo = parseAtqaAndroid(atqa);
352
- }
353
- }
354
-
355
- // 返回数据
356
- data.value = {
357
- ...data.value,
358
- ...transform,
359
- ...atqaInfo,
360
- id,
361
- sak,
362
- sakHex,
363
- sakType,
364
- techs,
365
- messages,
366
- maxTransceiveLength,
367
- readWriteStandard,
368
- sectorCount,
369
- blockCount,
370
- };
371
- // 回调通知外部
372
- listener(data.value);
373
- // 检查链接状态
374
- checkConnected();
375
-
376
- // 可将改行去除
377
- uni?.__dev_tool_nfc_collection__?.(data.value);
378
- } catch (_error) {
379
- console.log('error: ', _error);
380
- }
381
- }
382
-
383
- function initNfcAdapter() {
384
- try {
385
- if (!mainActivity) {
386
- uni.showToast({ title: '无法获取主 Activity 实例', icon: 'none' });
387
- return;
388
- }
389
-
390
- nfcAdapter = NfcAdapter.getDefaultAdapter(mainActivity);
391
-
392
- // 1. 设备不支持 NFC
393
- if (!nfcAdapter) {
394
- isSupportNFC.value = false;
395
- return;
396
- }
397
-
398
- // 2. NFC 功能未开启
399
- if (!nfcAdapter.isEnabled()) {
400
- isSupportNFC.value = false;
401
- return;
402
- }
403
-
404
- // 3. 初始化成功,注册监听
405
- isSupportNFC.value = true;
406
- } catch (_error) {
407
- isSupportNFC.value = false;
408
- }
409
- }
410
-
411
- function startHCE() {
412
- if (!isSupportNFC.value) {
413
- uni.showToast({
414
- title: '设备不支持 NFC',
415
- icon: 'none',
416
- });
417
- return Promise.reject({
418
- errMsg: '设备不支持 NFC',
419
- });
420
- }
421
- // 前置校验
422
- if (!nfcAdapter) {
423
- uni.showToast({ title: '请先初始化 NFC 适配器', icon: 'none' });
424
- return Promise.reject({
425
- errMsg: '请先初始化 NFC 适配器 NFC',
426
- });
427
- }
428
- //
429
- if (isListening.value) {
430
- uni.showToast({ title: 'NFC 监听已开启,无需重复操作', icon: 'none' });
431
- return Promise.reject({
432
- errMsg: 'NFC 监听已开启,无需重复操作',
433
- });
434
- }
435
-
436
- try {
437
- // 1. 注册 newintent 事件监听
438
- plus.globalEvent.addEventListener('newintent', innerListener);
439
-
440
- plus.globalEvent.addEventListener('resume', () => {
441
- if (nfcAdapter) {
442
- const filters = NFC_INTENT_FILTERS.map(
443
- (filter) => new IntentFilter(filter),
444
- );
445
- nfcAdapter?.enableForegroundDispatch(
446
- mainActivity,
447
- nfcPendingIntent,
448
- filters,
449
- NFC_TECH_LISTS,
450
- );
451
- }
452
- });
453
-
454
- plus.globalEvent.addEventListener('pause', () => {
455
- if (nfcAdapter) {
456
- nfcAdapter?.disableForegroundDispatch(mainActivity);
457
- }
458
- });
459
-
460
- // 2. 构建前台调度 PendingIntent(缓存实例,避免重复创建)
461
- if (!nfcPendingIntent) {
462
- const intent = new Intent(mainActivity, mainActivity.getClass());
463
- intent.addFlags(INTENT_FLAGS_SINGLE_TOP);
464
- nfcPendingIntent = PendingIntent.getActivity(
465
- mainActivity,
466
- 0,
467
- intent,
468
- PENDING_INTENT_FLAGS,
469
- );
470
- }
471
-
472
- // 3. 构建 Intent 过滤器
473
- const filters = NFC_INTENT_FILTERS.map(
474
- (filter) => new IntentFilter(filter),
475
- );
476
-
477
- // 4. 启用前台调度(核心:App 前台优先捕获 NFC 事件)
478
- nfcAdapter?.enableForegroundDispatch(
479
- mainActivity,
480
- nfcPendingIntent,
481
- filters,
482
- NFC_TECH_LISTS,
483
- );
484
-
485
- // 5. 更新监听状态
486
- isListening.value = true;
487
- uni.showToast({
488
- title: '开始监听NFC',
489
- icon: 'none',
490
- });
491
- return Promise.reject({
492
- errMsg: '开始监听NFC',
493
- });
494
- } catch (_error) {
495
- uni.showToast({ title: 'NFC 监听开启失败', icon: 'none' });
496
- isListening.value = false;
497
- return Promise.reject({
498
- errMsg: 'NFC 监听开启失败',
499
- });
500
- }
501
- }
502
-
503
- function stopHCE() {
504
- // 前置校验
505
- if (!isSupportNFC.value) {
506
- uni.showToast({
507
- title: '设备不支持 NFC',
508
- icon: 'none',
509
- });
510
- return Promise.reject({
511
- errMsg: '设备不支持 NFC',
512
- });
513
- }
514
- // 前置校验
515
- if (!nfcAdapter) {
516
- uni.showToast({ title: '请先初始化 NFC 适配器', icon: 'none' });
517
- return Promise.reject({
518
- errMsg: '请先初始化 NFC 适配器',
519
- });
520
- }
521
- //
522
- if (!isListening.value) {
523
- uni.showToast({ title: 'NFC 监听已关闭,无需重复操作', icon: 'none' });
524
-
525
- return Promise.reject({
526
- errMsg: 'NFC 监听已关闭,无需重复操作',
527
- });
528
- }
529
-
530
- try {
531
- // 1. 禁用前台调度
532
- nfcAdapter.disableForegroundDispatch(mainActivity);
533
- // 2. 更新监听状态
534
- isListening.value = false;
535
- isConnected.value = false;
536
- uni.showToast({
537
- title: '停止监听NFC',
538
- icon: 'none',
539
- });
540
- clearInterval(checkInterval);
541
- return Promise.resolve({
542
- errMsg: '停止监听NFC',
543
- });
544
- } catch (_error) {
545
- uni.showToast({ title: 'NFC 监听停止失败', icon: 'error' });
546
- return Promise.resolve({
547
- errMsg: 'NFC 监听停止失败',
548
- error: _error,
549
- });
550
- }
551
- }
552
-
553
- function connect() {
554
- if (!isSupportNFC.value) {
555
- uni.showToast({ title: '设备不支持 NFC', icon: 'none' });
556
- return Promise.reject({
557
- errMsg: '设备不支持 NFC',
558
- });
559
- }
560
-
561
- if (!nfcInstance) {
562
- uni.showToast({ title: '请先靠近 NFC 标签', icon: 'none' });
563
- return Promise.reject({
564
- errMsg: '请先靠近 NFC 标签',
565
- });
566
- }
567
-
568
- if (!isListening.value) {
569
- uni.showToast({ title: '请先开启前台调度监听', icon: 'none' });
570
- return Promise.reject({
571
- errMsg: '请先开启前台调度监听',
572
- });
573
- }
574
-
575
- try {
576
- checkConnected();
577
- if (!nfcInstance.isConnected()) {
578
- // 补充连接状态判断
579
- nfcInstance.connect();
580
- isConnected.value = true;
581
- uni.showToast({ title: 'NFC 连接成功', icon: 'none' });
582
- return Promise.resolve({
583
- errMsg: 'NFC 连接成功',
584
- });
585
- } else {
586
- isConnected.value = true;
587
- uni.showToast({ title: 'NFC 已处于连接状态', icon: 'none' });
588
- return Promise.reject({
589
- errMsg: 'NFC 已处于连接状态',
590
- });
591
- }
592
- } catch (error) {
593
- uni.showToast({ title: 'NFC 连接失败', icon: 'none' });
594
- isConnected.value = false;
595
- return Promise.reject({
596
- errMsg: 'NFC 连接失败',
597
- error,
598
- });
599
- }
600
- }
601
-
602
- function close() {
603
- if (!isSupportNFC.value) {
604
- uni.showToast({ title: '设备不支持 NFC', icon: 'none' });
605
- return Promise.reject({
606
- errMsg: '设备不支持 NFC',
607
- });
608
- }
609
-
610
- if (!nfcInstance) {
611
- uni.showToast({ title: '无连接中的 NFC 标签', icon: 'none' });
612
- return Promise.reject({
613
- errMsg: '无连接中的 NFC 标签',
614
- });
615
- }
616
-
617
- try {
618
- if (nfcInstance.isConnected()) {
619
- nfcInstance.close();
620
- }
621
- // 清空缓存,重置状态
622
- nfcTag = null;
623
- nfcInstance = null;
624
- isConnected.value = false;
625
- isNdef.value = false;
626
- clearInterval(checkInterval);
627
- uni.showToast({ title: 'NFC 连接已关闭', icon: 'none' });
628
- return Promise.resolve({
629
- errMsg: 'NFC 连接已关闭',
630
- });
631
- } catch (error) {
632
- uni.showToast({ title: 'NFC 连接关闭失败', icon: 'none' });
633
- return Promise.reject({
634
- errMsg: 'NFC 连接关闭失败',
635
- error,
636
- });
637
- }
638
- }
639
-
640
- function offHCEMessage() {
641
- try {
642
- if (!isSupportNFC.value) {
643
- uni.showToast({ title: '设备不支持 NFC,无需移除监听', icon: 'none' });
644
- return true;
645
- }
646
-
647
- // 1. 移除 newintent 事件监听
648
- plus.globalEvent.removeEventListener('newintent', innerListener);
649
-
650
- // 2. 停止前台调度,关闭连接
651
- if (nfcAdapter && isListening.value) {
652
- nfcAdapter.disableForegroundDispatch(mainActivity);
653
- }
654
- if (nfcInstance && nfcInstance.isConnected()) {
655
- nfcInstance.close();
656
- }
657
-
658
- // 3. 清空所有缓存,重置状态
659
- nfcPendingIntent = null;
660
- nfcTag = null;
661
- nfcInstance = null;
662
- isListening.value = false;
663
- isConnected.value = false;
664
- isNdef.value = false;
665
- clearInterval(checkInterval);
666
- uni.showToast({ title: 'NFC 监听已移除', icon: 'none' });
667
- return true;
668
- } catch (_error) {
669
- uni.showToast({ title: 'NFC 监听移除失败', icon: 'none' });
670
-
671
- return false;
672
- }
673
- }
674
-
675
- /**
676
- * 确保 NFC 已连接
677
- * @param checkNdef 是否检查 NDEF 支持
678
- */
679
- function ensureConnected(checkNdef = false) {
680
- if (checkNdef && !isNdef.value) {
681
- uni.showToast({ title: 'NFC 标签不支持 NDEF', icon: 'none' });
682
- throw { errMsg: 'NFC 标签不支持 NDEF' };
683
- }
684
- if (!nfcInstance) {
685
- uni.showToast({ title: '请先建立连接', icon: 'none' });
686
- throw { errMsg: '请先建立连接' };
687
- }
688
- if (!isConnected.value) {
689
- uni.showToast({ title: '请先建立连接', icon: 'none' });
690
- throw { errMsg: '请先建立连接' };
691
- }
692
- }
693
-
694
- /**
695
- * 执行 NDEF 写入操作的通用流程
696
- * @param createMessage 创建 NdefMessage 的函数
697
- * @returns Promise
698
- */
699
- async function executeNdefWrite(createMessage: () => any) {
700
- try {
701
- ensureConnected(true);
702
-
703
- uni.showToast({
704
- title: '开始写入数据,请勿断开连接',
705
- icon: 'none',
706
- duration: 3000,
707
- });
708
-
709
- const message = createMessage();
710
-
711
- // 容量检查
712
- if (maxTransceiveLength < message.getByteArrayLength()) {
713
- uni.showToast({
714
- title: '写入数据超出标签容量',
715
- icon: 'none',
716
- duration: 3000,
717
- });
718
- try {
719
- nfcInstance.close();
720
- } catch (_e) {
721
- // ignore error
722
- }
723
- return Promise.reject({ errMsg: '写入数据超出标签容量' });
724
- }
725
-
726
- if (nfcInstance.isWritable()) {
727
- nfcInstance.writeNdefMessage(message);
728
- nfcInstance.close();
729
-
730
- uni.showToast({
731
- title: '数据写入成功',
732
- icon: 'none',
733
- duration: 3000,
734
- });
735
- return Promise.resolve({ errMsg: '数据写入成功' });
736
- } else {
737
- uni.showToast({
738
- title: 'NFC 标签不允许写入',
739
- icon: 'none',
740
- duration: 3000,
741
- });
742
- nfcInstance.close();
743
- return Promise.reject({ errMsg: 'NFC 标签不允许写入' });
744
- }
745
- } catch (_error) {
746
- uni.showToast({
747
- title: '数据写入失败',
748
- icon: 'none',
749
- duration: 3000,
750
- });
751
- try {
752
- nfcInstance?.close();
753
- } catch (_e) {
754
- // ignore error
755
- }
756
- return Promise.reject({ errMsg: '数据写入失败', error: _error });
757
- }
758
- }
759
-
760
- /**
761
- * 非 ndef 数据写入
762
- *
763
- * TODO: 暂时无法提供通用的写入方法
764
- *
765
- * @param {ArrayBuffer} writeData
766
- * @return {*}
767
- */
768
- function write(writeData: ArrayBuffer) {
769
- if (isNdef.value) {
770
- uni.showToast({
771
- title: 'NDEF 请勿使用该方法',
772
- icon: 'none',
773
- });
774
- return Promise.reject({
775
- errMsg: 'NDEF 请勿使用该方法',
776
- });
777
- }
778
-
779
- if (!nfcInstance) {
780
- uni.showToast({
781
- title: '请先建立连接',
782
- icon: 'none',
783
- });
784
- return Promise.reject({
785
- errMsg: '请先建立连接',
786
- });
787
- }
788
-
789
- if (!isConnected.value) {
790
- uni.showToast({
791
- title: '请先建立连接',
792
- icon: 'none',
793
- });
794
- return Promise.reject({
795
- errMsg: '请先建立连接',
796
- });
797
- }
798
-
799
- uni.showToast({
800
- title: '开始写入数据,请勿断开连接',
801
- icon: 'none',
802
- });
803
-
804
- try {
805
- nfcInstance.connect();
806
- if (!nfcInstance.isWritable()) {
807
- uni.showToast({ title: 'NFC 标签不允许写入', icon: 'none' });
808
- nfcInstance.close();
809
- return Promise.reject({
810
- errMsg: 'NFC 标签不允许写入',
811
- });
812
- }
813
- if (nfcInstance.getMaxTransceiveLength() < writeData.byteLength) {
814
- uni.showToast({ title: '写入数据超出标签容量', icon: 'none' });
815
- nfcInstance.close();
816
- return Promise.reject({
817
- errMsg: '写入数据超出标签容量',
818
- });
819
- }
820
- const result = nfcInstance.transceive(writeData);
821
- nfcInstance.close();
822
- uni.showToast({ title: '数据写入成功', icon: 'none' });
823
- return Promise.resolve({
824
- errMsg: '数据写入成功',
825
- data: result,
826
- });
827
- } catch (error) {
828
- uni.showToast({ title: '数据写入失败', icon: 'none' });
829
- return Promise.reject({
830
- errMsg: '数据写入失败',
831
- error,
832
- });
833
- }
834
- }
835
-
836
- /**
837
- * 通用的 ndef 写入方法
838
- *
839
- * @param {NFC.NdefData} data
840
- */
841
- function writeNdef(data: NFC.NdefRecord[]) {
842
- return executeNdefWrite(() => {
843
- const records = data?.map((item) => {
844
- const tnf = stringToBytes(item.tnf + '');
845
- const id = item.id ? stringToBytes(item.id) : null;
846
- const type = stringToBytes(item.type);
847
-
848
- return new NdefRecord(tnf, type, id, item.payload);
849
- });
850
- return new NdefMessage(records);
851
- });
852
- }
853
-
854
- /**
855
- * 写入 NDEF 文本数据
856
- *
857
- * https://developer.android.google.cn/reference/kotlin/android/nfc/NdefRecord?hl=en#createtextrecord
858
- *
859
- * @param {string} text
860
- * @param {string} [language='zh']
861
- * @return {*}
862
- */
863
- function writeNdefText(text: string, language: string = 'zh') {
864
- return executeNdefWrite(() => {
865
- return new NdefMessage([NdefRecord.createTextRecord(language, text)]);
866
- });
867
- }
868
-
869
- /**
870
- * 写入 NDEF URI 数据
871
- * https://developer.android.google.cn/reference/kotlin/android/nfc/NdefRecord?hl=en#createuri
872
- *
873
- * 会自动根据
874
- * https://
875
- * tel:
876
- * ...
877
- * 进行区分
878
- *
879
- * @param {string} uri
880
- * @return {*}
881
- */
882
- function writeNdefUri(uri: string) {
883
- return executeNdefWrite(() => {
884
- return new NdefMessage([NdefRecord.createUri(uri)]);
885
- });
886
- }
887
-
888
- /**
889
- * Android 应用包名 写入
890
- *
891
- * @param {string} packageName - 数据
892
- * @param {string} [typeName] - 类型名称,默认为 "a" (Android Application Record)
893
- * @return {*}
894
- */
895
- function writeNdefAndroidApp(packageName: string) {
896
- return executeNdefWrite(() => {
897
- // 创建 Android 应用记录 (AAR - Android Application Record)
898
- const appRecord = NdefRecord.createApplicationRecord(packageName);
899
- return new NdefMessage([appRecord]);
900
- });
901
- }
902
-
903
- /**
904
- * 写入 WiFi 配置到 NFC 标签
905
- *
906
- * @param {NFC.WSCConfig} { ssid, password, authType }
907
- * @return {*}
908
- */
909
- function writeNdefWifi({ ssid, password, authType }: NFC.WSCConfig) {
910
- return executeNdefWrite(() => {
911
- const mimeType = 'application/vnd.wfa.wsc';
912
- const wifiData = generateWSCBytesForAndroid({
913
- ssid,
914
- password,
915
- authType,
916
- });
917
- const wifiRecord = NdefRecord.createMime(mimeType, wifiData);
918
- return new NdefMessage([wifiRecord]);
919
- });
920
- }
921
-
922
- /**
923
- * 写入 vCard 数据到 NFC 标签
924
- *
925
- * @param {NFC.VCardContactInfo} cardInfo
926
- * @return {*}
927
- */
928
- function writeNdefVcard(cardInfo: NFC.VCardContactInfo) {
929
- return executeNdefWrite(() => {
930
- const mimeType = 'text/vcard';
931
- const cardString = formatCardInfo(cardInfo);
932
- const cardData = stringToBytes(cardString);
933
- const cardRecord = NdefRecord.createMime(mimeType, cardData);
934
- return new NdefMessage([cardRecord]);
935
- });
936
- }
937
-
938
- onMounted(() => {
939
- initNfcAdapter();
940
- });
941
-
942
- onUnmounted(() => {
943
- stopHCE();
944
- offHCEMessage();
945
- });
946
-
947
- return {
948
- data,
949
- isNdef,
950
- isSupportNFC,
951
- isListening,
952
- isConnected,
953
- nfcInstance,
954
- startHCE,
955
- stopHCE,
956
- connect,
957
- close,
958
- write,
959
- writeNdef,
960
- writeNdefText,
961
- writeNdefUri,
962
- writeNdefAndroidApp,
963
- writeNdefWifi,
964
- writeNdefVcard,
965
- };
966
- }
1
+ import { onMounted, onUnmounted, ref } from 'vue';
2
+ import {
3
+ formatCardInfo,
4
+ getAndroidTechs,
5
+ getReadWriteStandard,
6
+ getSakHex,
7
+ getSakType,
8
+ getTnfDescription,
9
+ getTypeDescription,
10
+ parseAtqaAndroid,
11
+ parseUniversalTLV,
12
+ parseVCardPayload,
13
+ stringToBytes,
14
+ transformArrayBuffer,
15
+ wifiTLVConfig,
16
+ generateWSCBytesForAndroid,
17
+ } from './utils';
18
+
19
+ // 声明变量,但不立即初始化
20
+ let NfcAdapter: any;
21
+ let _Context: any;
22
+ let _Tag: any;
23
+ let Ndef: any;
24
+ let NfcA: any;
25
+ let NfcB: any;
26
+ let NfcF: any;
27
+ let NfcV: any;
28
+ let _NdefFormat: any;
29
+ let NdefFormatable: any;
30
+ let MifareClassic: any;
31
+ let MifareUltralight: any;
32
+ let IsoDep: any;
33
+ let Intent: any;
34
+ let PendingIntent: any;
35
+ let IntentFilter: any;
36
+ let Settings: any;
37
+ let NdefRecord: any;
38
+ let NdefMessage: any;
39
+
40
+ let INTENT_FLAGS_SINGLE_TOP: any;
41
+ let PENDING_INTENT_FLAGS: any;
42
+ let EXTRA_NDEF_MESSAGES: any;
43
+ let EXTRA_ID: any;
44
+ let EXTRA_TAG: any;
45
+ let _TNF_MIME_MEDIA: any;
46
+ let _ACTION_NFC_SETTINGS: any;
47
+
48
+ let NFC_INTENT_FILTERS: any[] = [];
49
+
50
+ const NFC_TECH_LISTS = [
51
+ ['android.nfc.tech.Ndef'],
52
+ ['android.nfc.tech.NdefFormatable'],
53
+ ['android.nfc.tech.IsoDep'],
54
+ ['android.nfc.tech.NfcA'],
55
+ ['android.nfc.tech.NfcB'],
56
+ ['android.nfc.tech.NfcF'],
57
+ ['android.nfc.tech.NfcV'],
58
+ ['android.nfc.tech.MifareClassic'],
59
+ ['android.nfc.tech.MifareUltralight'],
60
+ ];
61
+
62
+ // 初始化 Android 类
63
+ function initAndroidClasses() {
64
+ try {
65
+ if (NfcAdapter) return; // 避免重复初始化
66
+
67
+ NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
68
+ _Context = plus.android.importClass('android.content.Context');
69
+ _Tag = plus.android.importClass('android.nfc.Tag');
70
+ Ndef = plus.android.importClass('android.nfc.tech.Ndef');
71
+ NfcA = plus.android.importClass('android.nfc.tech.NfcA');
72
+ NfcB = plus.android.importClass('android.nfc.tech.NfcB');
73
+ NfcF = plus.android.importClass('android.nfc.tech.NfcF');
74
+ NfcV = plus.android.importClass('android.nfc.tech.NfcV');
75
+ _NdefFormat = plus.android.importClass('android.nfc.tech.NdefFormat');
76
+ NdefFormatable = plus.android.importClass(
77
+ 'android.nfc.tech.NdefFormatable',
78
+ );
79
+ MifareClassic = plus.android.importClass('android.nfc.tech.MifareClassic');
80
+ MifareUltralight = plus.android.importClass(
81
+ 'android.nfc.tech.MifareUltralight',
82
+ );
83
+ IsoDep = plus.android.importClass('android.nfc.tech.IsoDep');
84
+ Intent = plus.android.importClass('android.content.Intent');
85
+ PendingIntent = plus.android.importClass('android.app.PendingIntent');
86
+ IntentFilter = plus.android.importClass('android.content.IntentFilter');
87
+ Settings = plus.android.importClass('android.provider.Settings');
88
+ NdefRecord = plus.android.importClass('android.nfc.NdefRecord');
89
+ NdefMessage = plus.android.importClass('android.nfc.NdefMessage');
90
+
91
+ INTENT_FLAGS_SINGLE_TOP = Intent.FLAG_ACTIVITY_SINGLE_TOP;
92
+ PENDING_INTENT_FLAGS = PendingIntent.FLAG_MUTABLE;
93
+
94
+ EXTRA_NDEF_MESSAGES = NfcAdapter.EXTRA_NDEF_MESSAGES;
95
+ EXTRA_ID = NfcAdapter.EXTRA_ID;
96
+ EXTRA_TAG = NfcAdapter.EXTRA_TAG;
97
+
98
+ _TNF_MIME_MEDIA = NdefRecord.TNF_MIME_MEDIA;
99
+ _ACTION_NFC_SETTINGS = Settings.ACTION_NFC_SETTINGS;
100
+
101
+ NFC_INTENT_FILTERS = [
102
+ NfcAdapter.ACTION_NDEF_DISCOVERED,
103
+ NfcAdapter.ACTION_TECH_DISCOVERED,
104
+ NfcAdapter.ACTION_TAG_DISCOVERED,
105
+ ];
106
+ } catch (e) {
107
+ console.error('Core: initAndroidClasses error', e);
108
+ }
109
+ }
110
+
111
+ export default function useNFCAndroid(
112
+ listener: NFC.Listener,
113
+ options?: NFC.NfcOption,
114
+ ): NFC.NfcResult {
115
+ /** 是否支持 nfc */
116
+ const isSupportNFC = ref<boolean | undefined>(false);
117
+ /** 是否监听中 */
118
+ const isListening = ref<boolean>(false);
119
+ /** 是否已连接 */
120
+ const isConnected = ref<boolean>(false);
121
+ /** nfc 数据 */
122
+ const data = ref<NFC.Result>({
123
+ id: new ArrayBuffer(0),
124
+ techs: [],
125
+ byteArray: [],
126
+ hexArray: [],
127
+ hexNumberArray: [],
128
+ hexString: '',
129
+ });
130
+
131
+ /** ndef 写入方式比较特殊,需要特殊处理 */
132
+ const isNdef = ref(false);
133
+
134
+ // 延迟获取 mainActivity,确保 plus 已就绪
135
+ let mainActivity: any = null;
136
+
137
+ onMounted(() => {
138
+ initAndroidClasses();
139
+ try {
140
+ mainActivity = plus.android.runtimeMainActivity();
141
+ initNfcAdapter();
142
+ } catch (e) {
143
+ console.error('Core: failed to init NFC adapter', e);
144
+ }
145
+ });
146
+
147
+ let checkInterval: any = null;
148
+
149
+ let nfcAdapter: NFC.Adapter | null = null;
150
+ let nfcPendingIntent: any = null;
151
+ let nfcTag: any = null;
152
+ let nfcInstance: any = null;
153
+
154
+ let maxTransceiveLength = 0;
155
+
156
+ function checkConnected() {
157
+ checkInterval = setInterval(() => {
158
+ if (!nfcAdapter) {
159
+ isConnected.value = false;
160
+ clearInterval(checkInterval);
161
+ return;
162
+ }
163
+
164
+ if (!nfcAdapter.isEnabled()) {
165
+ isConnected.value = false;
166
+ clearInterval(checkInterval);
167
+ return;
168
+ }
169
+ if (!nfcInstance) {
170
+ isConnected.value = false;
171
+ clearInterval(checkInterval);
172
+ return;
173
+ }
174
+
175
+ if (nfcInstance.isConnected()) {
176
+ isConnected.value = true;
177
+ } else {
178
+ isConnected.value = false;
179
+ clearInterval(checkInterval);
180
+ }
181
+ }, options?.checkConnectedDelay ?? 500);
182
+ }
183
+
184
+ function getInstance(techs: NFC.NfcType[], needNdef?: boolean) {
185
+ let type = 'Ndef';
186
+ if (needNdef) {
187
+ type = 'Ndef';
188
+ } else {
189
+ type = techs.filter((tech) => tech != 'Ndef' && tech != 'NDEF')?.[0];
190
+ }
191
+
192
+ switch (type) {
193
+ case 'NFC-A':
194
+ return NfcA.get(nfcTag);
195
+ case 'NFC-B':
196
+ return NfcB.get(nfcTag);
197
+ case 'NFC-F':
198
+ return NfcF.get(nfcTag);
199
+ case 'NFC-V':
200
+ return NfcV.get(nfcTag);
201
+ case 'MIFARE Classic':
202
+ return MifareClassic.get(nfcTag);
203
+ case 'MIFARE Ultralight':
204
+ return MifareUltralight.get(nfcTag);
205
+ case 'Ndef':
206
+ isNdef.value = true;
207
+ return Ndef.get(nfcTag);
208
+ case 'Ndef Formatable':
209
+ return NdefFormatable.get(nfcTag);
210
+ case 'ISO-DEP':
211
+ return IsoDep.get(nfcTag);
212
+ default:
213
+ return undefined;
214
+ }
215
+ }
216
+
217
+ function innerListener() {
218
+ try {
219
+ const intent = mainActivity.getIntent();
220
+ if (!intent) {
221
+ uni.showToast({ title: 'NFC 数据解析失败', icon: 'none' });
222
+ return;
223
+ }
224
+
225
+ // 获取id
226
+ const id = intent.getByteArrayExtra(EXTRA_ID);
227
+
228
+ const transform = transformArrayBuffer(id);
229
+
230
+ let techs: NFC.NfcType[] = [];
231
+
232
+ let sak = 0;
233
+ let sakHex = '';
234
+ let sakType = undefined;
235
+ let atqaInfo = {
236
+ uidType: 0,
237
+ storageSize: 0,
238
+ manufacturer: 0,
239
+ cardFeatures: 0,
240
+ reserved: 0,
241
+ };
242
+ let readWriteStandard: string[] = [];
243
+ let sectorCount = 0;
244
+ let blockCount = 0;
245
+ let messages: any = [];
246
+ nfcTag = intent.getParcelableExtra(EXTRA_TAG); // 先获取 Tag 实例
247
+
248
+ if (nfcTag) {
249
+ // 核心方法:通过 Tag.getTechList() 获取原生技术列表数组
250
+ const nativeTechList = nfcTag.getTechList();
251
+ // 转换为可直接使用的 string 数组(兼容原生返回格式)
252
+ techs = Array.isArray(nativeTechList) ? nativeTechList : [];
253
+
254
+ techs = getAndroidTechs(techs);
255
+
256
+ nfcInstance = getInstance(techs, true);
257
+
258
+ if (!nfcInstance) {
259
+ uni.showToast({ title: 'NFC 实例获取失败', icon: 'none' });
260
+ return;
261
+ }
262
+
263
+ // 一定要连接
264
+ nfcInstance?.connect?.();
265
+
266
+ if (isNdef.value) {
267
+ const otherInstance = getInstance(techs);
268
+ maxTransceiveLength = otherInstance?.getMaxTransceiveLength?.() ?? 0;
269
+ } else {
270
+ maxTransceiveLength = nfcInstance?.getMaxTransceiveLength?.() ?? 0;
271
+ }
272
+
273
+ readWriteStandard = getReadWriteStandard(techs);
274
+
275
+ sectorCount = nfcInstance?.getSectorCount?.();
276
+
277
+ blockCount = nfcInstance?.getBlockCount?.();
278
+
279
+ const ndefMessage = intent.getParcelableArrayExtra(EXTRA_NDEF_MESSAGES);
280
+
281
+ if (ndefMessage && ndefMessage.length > 0) {
282
+ let records = ndefMessage?.[0]?.getRecords?.();
283
+
284
+ records = records.map((record: any) => {
285
+ const idObject = record?.getId?.();
286
+
287
+ const id = idObject
288
+ ? plus.android.invoke(idObject, 'toString')
289
+ : '';
290
+
291
+ const tnfObject = record?.getTnf?.();
292
+ const tnf = tnfObject
293
+ ? plus.android.invoke(tnfObject, 'toString')
294
+ : '';
295
+ const tnfDescription = getTnfDescription(tnf);
296
+
297
+ const payloadByte = record?.getPayload?.();
298
+
299
+ const typeObject = record?.getType?.();
300
+ const type = typeObject
301
+ ? (plus.android.newObject('java.lang.String', typeObject) as any)
302
+ : '';
303
+ const typeDescription = getTypeDescription(
304
+ type as string,
305
+ payloadByte,
306
+ );
307
+
308
+ let payload: any = payloadByte
309
+ ? plus.android.newObject('java.lang.String', payloadByte)
310
+ : '';
311
+ if (type === 'application/vnd.wfa.wsc') {
312
+ payload = parseUniversalTLV(payloadByte, wifiTLVConfig);
313
+ } else if (type === 'text/vcard') {
314
+ payload = parseVCardPayload(payload);
315
+ }
316
+
317
+ return {
318
+ id,
319
+ tnf,
320
+ tnfDescription,
321
+ type,
322
+ typeDescription,
323
+ payload,
324
+ };
325
+ });
326
+ messages = [
327
+ {
328
+ records,
329
+ },
330
+ ];
331
+ } else {
332
+ messages = [];
333
+ }
334
+
335
+ if (techs.includes('NFC-A')) {
336
+ // NFC-A 标签的 SAK 信息
337
+ const nfcAInstance = NfcA.get(nfcTag);
338
+ const sakShortObject =
339
+ plus.android.invoke(nfcAInstance, 'getSak') ?? 0;
340
+
341
+ const sakStr = plus.android.invoke(sakShortObject, 'toString');
342
+
343
+ sak = Number(sakStr) || 0;
344
+
345
+ sakType = getSakType(sak);
346
+
347
+ sakHex = getSakHex(sak);
348
+
349
+ const atqa = plus.android.invoke(nfcAInstance, 'getAtqa');
350
+
351
+ atqaInfo = parseAtqaAndroid(atqa);
352
+ }
353
+ }
354
+
355
+ // 返回数据
356
+ data.value = {
357
+ ...data.value,
358
+ ...transform,
359
+ ...atqaInfo,
360
+ id,
361
+ sak,
362
+ sakHex,
363
+ sakType,
364
+ techs,
365
+ messages,
366
+ maxTransceiveLength,
367
+ readWriteStandard,
368
+ sectorCount,
369
+ blockCount,
370
+ };
371
+ // 回调通知外部
372
+ listener(data.value);
373
+ // 检查链接状态
374
+ checkConnected();
375
+
376
+ // 可将改行去除
377
+ uni?.__dev_tool_nfc_collection__?.(data.value);
378
+ } catch (_error) {
379
+ console.log('error: ', _error);
380
+ }
381
+ }
382
+
383
+ function initNfcAdapter() {
384
+ try {
385
+ if (!mainActivity) {
386
+ uni.showToast({ title: '无法获取主 Activity 实例', icon: 'none' });
387
+ return;
388
+ }
389
+
390
+ nfcAdapter = NfcAdapter.getDefaultAdapter(mainActivity);
391
+
392
+ // 1. 设备不支持 NFC
393
+ if (!nfcAdapter) {
394
+ isSupportNFC.value = false;
395
+ return;
396
+ }
397
+
398
+ // 2. NFC 功能未开启
399
+ if (!nfcAdapter.isEnabled()) {
400
+ isSupportNFC.value = false;
401
+ return;
402
+ }
403
+
404
+ // 3. 初始化成功,注册监听
405
+ isSupportNFC.value = true;
406
+ } catch (_error) {
407
+ isSupportNFC.value = false;
408
+ }
409
+ }
410
+
411
+ function startHCE() {
412
+ if (!isSupportNFC.value) {
413
+ uni.showToast({
414
+ title: '设备不支持 NFC',
415
+ icon: 'none',
416
+ });
417
+ return Promise.reject({
418
+ errMsg: '设备不支持 NFC',
419
+ });
420
+ }
421
+ // 前置校验
422
+ if (!nfcAdapter) {
423
+ uni.showToast({ title: '请先初始化 NFC 适配器', icon: 'none' });
424
+ return Promise.reject({
425
+ errMsg: '请先初始化 NFC 适配器 NFC',
426
+ });
427
+ }
428
+ //
429
+ if (isListening.value) {
430
+ uni.showToast({ title: 'NFC 监听已开启,无需重复操作', icon: 'none' });
431
+ return Promise.reject({
432
+ errMsg: 'NFC 监听已开启,无需重复操作',
433
+ });
434
+ }
435
+
436
+ try {
437
+ // 1. 注册 newintent 事件监听
438
+ plus.globalEvent.addEventListener('newintent', innerListener);
439
+
440
+ plus.globalEvent.addEventListener('resume', () => {
441
+ if (nfcAdapter) {
442
+ const filters = NFC_INTENT_FILTERS.map(
443
+ (filter) => new IntentFilter(filter),
444
+ );
445
+ nfcAdapter?.enableForegroundDispatch(
446
+ mainActivity,
447
+ nfcPendingIntent,
448
+ filters,
449
+ NFC_TECH_LISTS,
450
+ );
451
+ }
452
+ });
453
+
454
+ plus.globalEvent.addEventListener('pause', () => {
455
+ if (nfcAdapter) {
456
+ nfcAdapter?.disableForegroundDispatch(mainActivity);
457
+ }
458
+ });
459
+
460
+ // 2. 构建前台调度 PendingIntent(缓存实例,避免重复创建)
461
+ if (!nfcPendingIntent) {
462
+ const intent = new Intent(mainActivity, mainActivity.getClass());
463
+ intent.addFlags(INTENT_FLAGS_SINGLE_TOP);
464
+ nfcPendingIntent = PendingIntent.getActivity(
465
+ mainActivity,
466
+ 0,
467
+ intent,
468
+ PENDING_INTENT_FLAGS,
469
+ );
470
+ }
471
+
472
+ // 3. 构建 Intent 过滤器
473
+ const filters = NFC_INTENT_FILTERS.map(
474
+ (filter) => new IntentFilter(filter),
475
+ );
476
+
477
+ // 4. 启用前台调度(核心:App 前台优先捕获 NFC 事件)
478
+ nfcAdapter?.enableForegroundDispatch(
479
+ mainActivity,
480
+ nfcPendingIntent,
481
+ filters,
482
+ NFC_TECH_LISTS,
483
+ );
484
+
485
+ // 5. 更新监听状态
486
+ isListening.value = true;
487
+ uni.showToast({
488
+ title: '开始监听NFC',
489
+ icon: 'none',
490
+ });
491
+ return Promise.reject({
492
+ errMsg: '开始监听NFC',
493
+ });
494
+ } catch (_error) {
495
+ uni.showToast({ title: 'NFC 监听开启失败', icon: 'none' });
496
+ isListening.value = false;
497
+ return Promise.reject({
498
+ errMsg: 'NFC 监听开启失败',
499
+ });
500
+ }
501
+ }
502
+
503
+ function stopHCE() {
504
+ // 前置校验
505
+ if (!isSupportNFC.value) {
506
+ uni.showToast({
507
+ title: '设备不支持 NFC',
508
+ icon: 'none',
509
+ });
510
+ return Promise.reject({
511
+ errMsg: '设备不支持 NFC',
512
+ });
513
+ }
514
+ // 前置校验
515
+ if (!nfcAdapter) {
516
+ uni.showToast({ title: '请先初始化 NFC 适配器', icon: 'none' });
517
+ return Promise.reject({
518
+ errMsg: '请先初始化 NFC 适配器',
519
+ });
520
+ }
521
+ //
522
+ if (!isListening.value) {
523
+ uni.showToast({ title: 'NFC 监听已关闭,无需重复操作', icon: 'none' });
524
+
525
+ return Promise.reject({
526
+ errMsg: 'NFC 监听已关闭,无需重复操作',
527
+ });
528
+ }
529
+
530
+ try {
531
+ // 1. 禁用前台调度
532
+ nfcAdapter.disableForegroundDispatch(mainActivity);
533
+ // 2. 更新监听状态
534
+ isListening.value = false;
535
+ isConnected.value = false;
536
+ uni.showToast({
537
+ title: '停止监听NFC',
538
+ icon: 'none',
539
+ });
540
+ clearInterval(checkInterval);
541
+ return Promise.resolve({
542
+ errMsg: '停止监听NFC',
543
+ });
544
+ } catch (_error) {
545
+ uni.showToast({ title: 'NFC 监听停止失败', icon: 'error' });
546
+ return Promise.resolve({
547
+ errMsg: 'NFC 监听停止失败',
548
+ error: _error,
549
+ });
550
+ }
551
+ }
552
+
553
+ function connect() {
554
+ if (!isSupportNFC.value) {
555
+ uni.showToast({ title: '设备不支持 NFC', icon: 'none' });
556
+ return Promise.reject({
557
+ errMsg: '设备不支持 NFC',
558
+ });
559
+ }
560
+
561
+ if (!nfcInstance) {
562
+ uni.showToast({ title: '请先靠近 NFC 标签', icon: 'none' });
563
+ return Promise.reject({
564
+ errMsg: '请先靠近 NFC 标签',
565
+ });
566
+ }
567
+
568
+ if (!isListening.value) {
569
+ uni.showToast({ title: '请先开启前台调度监听', icon: 'none' });
570
+ return Promise.reject({
571
+ errMsg: '请先开启前台调度监听',
572
+ });
573
+ }
574
+
575
+ try {
576
+ checkConnected();
577
+ if (!nfcInstance.isConnected()) {
578
+ // 补充连接状态判断
579
+ nfcInstance.connect();
580
+ isConnected.value = true;
581
+ uni.showToast({ title: 'NFC 连接成功', icon: 'none' });
582
+ return Promise.resolve({
583
+ errMsg: 'NFC 连接成功',
584
+ });
585
+ } else {
586
+ isConnected.value = true;
587
+ uni.showToast({ title: 'NFC 已处于连接状态', icon: 'none' });
588
+ return Promise.reject({
589
+ errMsg: 'NFC 已处于连接状态',
590
+ });
591
+ }
592
+ } catch (error) {
593
+ uni.showToast({ title: 'NFC 连接失败', icon: 'none' });
594
+ isConnected.value = false;
595
+ return Promise.reject({
596
+ errMsg: 'NFC 连接失败',
597
+ error,
598
+ });
599
+ }
600
+ }
601
+
602
+ function close() {
603
+ if (!isSupportNFC.value) {
604
+ uni.showToast({ title: '设备不支持 NFC', icon: 'none' });
605
+ return Promise.reject({
606
+ errMsg: '设备不支持 NFC',
607
+ });
608
+ }
609
+
610
+ if (!nfcInstance) {
611
+ uni.showToast({ title: '无连接中的 NFC 标签', icon: 'none' });
612
+ return Promise.reject({
613
+ errMsg: '无连接中的 NFC 标签',
614
+ });
615
+ }
616
+
617
+ try {
618
+ if (nfcInstance.isConnected()) {
619
+ nfcInstance.close();
620
+ }
621
+ // 清空缓存,重置状态
622
+ nfcTag = null;
623
+ nfcInstance = null;
624
+ isConnected.value = false;
625
+ isNdef.value = false;
626
+ clearInterval(checkInterval);
627
+ uni.showToast({ title: 'NFC 连接已关闭', icon: 'none' });
628
+ return Promise.resolve({
629
+ errMsg: 'NFC 连接已关闭',
630
+ });
631
+ } catch (error) {
632
+ uni.showToast({ title: 'NFC 连接关闭失败', icon: 'none' });
633
+ return Promise.reject({
634
+ errMsg: 'NFC 连接关闭失败',
635
+ error,
636
+ });
637
+ }
638
+ }
639
+
640
+ function offHCEMessage() {
641
+ try {
642
+ if (!isSupportNFC.value) {
643
+ uni.showToast({ title: '设备不支持 NFC,无需移除监听', icon: 'none' });
644
+ return true;
645
+ }
646
+
647
+ // 1. 移除 newintent 事件监听
648
+ plus.globalEvent.removeEventListener('newintent', innerListener);
649
+
650
+ // 2. 停止前台调度,关闭连接
651
+ if (nfcAdapter && isListening.value) {
652
+ nfcAdapter.disableForegroundDispatch(mainActivity);
653
+ }
654
+ if (nfcInstance && nfcInstance.isConnected()) {
655
+ nfcInstance.close();
656
+ }
657
+
658
+ // 3. 清空所有缓存,重置状态
659
+ nfcPendingIntent = null;
660
+ nfcTag = null;
661
+ nfcInstance = null;
662
+ isListening.value = false;
663
+ isConnected.value = false;
664
+ isNdef.value = false;
665
+ clearInterval(checkInterval);
666
+ uni.showToast({ title: 'NFC 监听已移除', icon: 'none' });
667
+ return true;
668
+ } catch (_error) {
669
+ uni.showToast({ title: 'NFC 监听移除失败', icon: 'none' });
670
+
671
+ return false;
672
+ }
673
+ }
674
+
675
+ /**
676
+ * 确保 NFC 已连接
677
+ * @param checkNdef 是否检查 NDEF 支持
678
+ */
679
+ function ensureConnected(checkNdef = false) {
680
+ if (checkNdef && !isNdef.value) {
681
+ uni.showToast({ title: 'NFC 标签不支持 NDEF', icon: 'none' });
682
+ throw { errMsg: 'NFC 标签不支持 NDEF' };
683
+ }
684
+ if (!nfcInstance) {
685
+ uni.showToast({ title: '请先建立连接', icon: 'none' });
686
+ throw { errMsg: '请先建立连接' };
687
+ }
688
+ if (!isConnected.value) {
689
+ uni.showToast({ title: '请先建立连接', icon: 'none' });
690
+ throw { errMsg: '请先建立连接' };
691
+ }
692
+ }
693
+
694
+ /**
695
+ * 执行 NDEF 写入操作的通用流程
696
+ * @param createMessage 创建 NdefMessage 的函数
697
+ * @returns Promise
698
+ */
699
+ async function executeNdefWrite(createMessage: () => any) {
700
+ try {
701
+ ensureConnected(true);
702
+
703
+ uni.showToast({
704
+ title: '开始写入数据,请勿断开连接',
705
+ icon: 'none',
706
+ duration: 3000,
707
+ });
708
+
709
+ const message = createMessage();
710
+
711
+ // 容量检查
712
+ if (maxTransceiveLength < message.getByteArrayLength()) {
713
+ uni.showToast({
714
+ title: '写入数据超出标签容量',
715
+ icon: 'none',
716
+ duration: 3000,
717
+ });
718
+ try {
719
+ nfcInstance.close();
720
+ } catch (_e) {
721
+ // ignore error
722
+ }
723
+ return Promise.reject({ errMsg: '写入数据超出标签容量' });
724
+ }
725
+
726
+ if (nfcInstance.isWritable()) {
727
+ nfcInstance.writeNdefMessage(message);
728
+ nfcInstance.close();
729
+
730
+ uni.showToast({
731
+ title: '数据写入成功',
732
+ icon: 'none',
733
+ duration: 3000,
734
+ });
735
+ return Promise.resolve({ errMsg: '数据写入成功' });
736
+ } else {
737
+ uni.showToast({
738
+ title: 'NFC 标签不允许写入',
739
+ icon: 'none',
740
+ duration: 3000,
741
+ });
742
+ nfcInstance.close();
743
+ return Promise.reject({ errMsg: 'NFC 标签不允许写入' });
744
+ }
745
+ } catch (_error) {
746
+ uni.showToast({
747
+ title: '数据写入失败',
748
+ icon: 'none',
749
+ duration: 3000,
750
+ });
751
+ try {
752
+ nfcInstance?.close();
753
+ } catch (_e) {
754
+ // ignore error
755
+ }
756
+ return Promise.reject({ errMsg: '数据写入失败', error: _error });
757
+ }
758
+ }
759
+
760
+ /**
761
+ * 非 ndef 数据写入
762
+ *
763
+ * TODO: 暂时无法提供通用的写入方法
764
+ *
765
+ * @param {ArrayBuffer} writeData
766
+ * @return {*}
767
+ */
768
+ function write(writeData: ArrayBuffer) {
769
+ if (isNdef.value) {
770
+ uni.showToast({
771
+ title: 'NDEF 请勿使用该方法',
772
+ icon: 'none',
773
+ });
774
+ return Promise.reject({
775
+ errMsg: 'NDEF 请勿使用该方法',
776
+ });
777
+ }
778
+
779
+ if (!nfcInstance) {
780
+ uni.showToast({
781
+ title: '请先建立连接',
782
+ icon: 'none',
783
+ });
784
+ return Promise.reject({
785
+ errMsg: '请先建立连接',
786
+ });
787
+ }
788
+
789
+ if (!isConnected.value) {
790
+ uni.showToast({
791
+ title: '请先建立连接',
792
+ icon: 'none',
793
+ });
794
+ return Promise.reject({
795
+ errMsg: '请先建立连接',
796
+ });
797
+ }
798
+
799
+ uni.showToast({
800
+ title: '开始写入数据,请勿断开连接',
801
+ icon: 'none',
802
+ });
803
+
804
+ try {
805
+ nfcInstance.connect();
806
+ if (!nfcInstance.isWritable()) {
807
+ uni.showToast({ title: 'NFC 标签不允许写入', icon: 'none' });
808
+ nfcInstance.close();
809
+ return Promise.reject({
810
+ errMsg: 'NFC 标签不允许写入',
811
+ });
812
+ }
813
+ if (nfcInstance.getMaxTransceiveLength() < writeData.byteLength) {
814
+ uni.showToast({ title: '写入数据超出标签容量', icon: 'none' });
815
+ nfcInstance.close();
816
+ return Promise.reject({
817
+ errMsg: '写入数据超出标签容量',
818
+ });
819
+ }
820
+ const result = nfcInstance.transceive(writeData);
821
+ nfcInstance.close();
822
+ uni.showToast({ title: '数据写入成功', icon: 'none' });
823
+ return Promise.resolve({
824
+ errMsg: '数据写入成功',
825
+ data: result,
826
+ });
827
+ } catch (error) {
828
+ uni.showToast({ title: '数据写入失败', icon: 'none' });
829
+ return Promise.reject({
830
+ errMsg: '数据写入失败',
831
+ error,
832
+ });
833
+ }
834
+ }
835
+
836
+ /**
837
+ * 通用的 ndef 写入方法
838
+ *
839
+ * @param {NFC.NdefData} data
840
+ */
841
+ function writeNdef(data: NFC.NdefRecord[]) {
842
+ return executeNdefWrite(() => {
843
+ const records = data?.map((item) => {
844
+ const tnf = stringToBytes(item.tnf + '');
845
+ const id = item.id ? stringToBytes(item.id) : null;
846
+ const type = stringToBytes(item.type);
847
+
848
+ return new NdefRecord(tnf, type, id, item.payload);
849
+ });
850
+ return new NdefMessage(records);
851
+ });
852
+ }
853
+
854
+ /**
855
+ * 写入 NDEF 文本数据
856
+ *
857
+ * https://developer.android.google.cn/reference/kotlin/android/nfc/NdefRecord?hl=en#createtextrecord
858
+ *
859
+ * @param {string} text
860
+ * @param {string} [language='zh']
861
+ * @return {*}
862
+ */
863
+ function writeNdefText(text: string, language: string = 'zh') {
864
+ return executeNdefWrite(() => {
865
+ return new NdefMessage([NdefRecord.createTextRecord(language, text)]);
866
+ });
867
+ }
868
+
869
+ /**
870
+ * 写入 NDEF URI 数据
871
+ * https://developer.android.google.cn/reference/kotlin/android/nfc/NdefRecord?hl=en#createuri
872
+ *
873
+ * 会自动根据
874
+ * https://
875
+ * tel:
876
+ * ...
877
+ * 进行区分
878
+ *
879
+ * @param {string} uri
880
+ * @return {*}
881
+ */
882
+ function writeNdefUri(uri: string) {
883
+ return executeNdefWrite(() => {
884
+ return new NdefMessage([NdefRecord.createUri(uri)]);
885
+ });
886
+ }
887
+
888
+ /**
889
+ * Android 应用包名 写入
890
+ *
891
+ * @param {string} packageName - 数据
892
+ * @param {string} [typeName] - 类型名称,默认为 "a" (Android Application Record)
893
+ * @return {*}
894
+ */
895
+ function writeNdefAndroidApp(packageName: string) {
896
+ return executeNdefWrite(() => {
897
+ // 创建 Android 应用记录 (AAR - Android Application Record)
898
+ const appRecord = NdefRecord.createApplicationRecord(packageName);
899
+ return new NdefMessage([appRecord]);
900
+ });
901
+ }
902
+
903
+ /**
904
+ * 写入 WiFi 配置到 NFC 标签
905
+ *
906
+ * @param {NFC.WSCConfig} { ssid, password, authType }
907
+ * @return {*}
908
+ */
909
+ function writeNdefWifi({ ssid, password, authType }: NFC.WSCConfig) {
910
+ return executeNdefWrite(() => {
911
+ const mimeType = 'application/vnd.wfa.wsc';
912
+ const wifiData = generateWSCBytesForAndroid({
913
+ ssid,
914
+ password,
915
+ authType,
916
+ });
917
+ const wifiRecord = NdefRecord.createMime(mimeType, wifiData);
918
+ return new NdefMessage([wifiRecord]);
919
+ });
920
+ }
921
+
922
+ /**
923
+ * 写入 vCard 数据到 NFC 标签
924
+ *
925
+ * @param {NFC.VCardContactInfo} cardInfo
926
+ * @return {*}
927
+ */
928
+ function writeNdefVcard(cardInfo: NFC.VCardContactInfo) {
929
+ return executeNdefWrite(() => {
930
+ const mimeType = 'text/vcard';
931
+ const cardString = formatCardInfo(cardInfo);
932
+ const cardData = stringToBytes(cardString);
933
+ const cardRecord = NdefRecord.createMime(mimeType, cardData);
934
+ return new NdefMessage([cardRecord]);
935
+ });
936
+ }
937
+
938
+ onMounted(() => {
939
+ initNfcAdapter();
940
+ });
941
+
942
+ onUnmounted(() => {
943
+ stopHCE();
944
+ offHCEMessage();
945
+ });
946
+
947
+ return {
948
+ data,
949
+ isNdef,
950
+ isSupportNFC,
951
+ isListening,
952
+ isConnected,
953
+ nfcInstance,
954
+ startHCE,
955
+ stopHCE,
956
+ connect,
957
+ close,
958
+ write,
959
+ writeNdef,
960
+ writeNdefText,
961
+ writeNdefUri,
962
+ writeNdefAndroidApp,
963
+ writeNdefWifi,
964
+ writeNdefVcard,
965
+ };
966
+ }