This commit is contained in:
xuli
2025-12-02 11:44:38 +08:00
parent 11990a62da
commit d8d5ac02c9
17 changed files with 233 additions and 1099 deletions

View File

@@ -1,15 +1,12 @@
<template>
<view class="nfc-con">
<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'" />
<block v-if="formData.deviceId==''">
<view class="nfc-pic">
<img class="nfc-pic-animal" :src="'static/images/polling/nfc-logo.png'" />
</view>
<view class="nfc-desc">请将设备靠近NFC识别</view>
</block>
@@ -25,7 +22,7 @@
<view>识别时间</view>
<view>{{parseTime(new Date().getTime(),'{y}年{m}月{d}日 星期{a} {h}:{i}')}}</view>
</view>
<view class="btn-blue" @click="handleConfirm">确定</view>
<view class="btn-blue" @click="handleClose">确定</view>
</block>
<block v-else>
<view class="result-header">
@@ -35,293 +32,200 @@
<view class="result-value">
可能的失败原因手机距离NFC设备较远手机未联网NFC设备故障
</view>
<view class="btn-blue" @click="readData">重新识别</view>
<view class="btn-blue" @click="open">重新识别</view>
</block>
</view>
</view>
</view>
</template>
<script setup>
import { ref,watch } from 'vue'
import { onLoad,onHide} from '@dcloudio/uni-app';
import { ref, onBeforeUnmount,onMounted } from "vue";
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';
// 定义组件 emits
const emit = defineEmits(['close',"changeNfc"]);
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 formData = ref({
deviceId: "", //录入的id
});
const readStatus=ref(false);//读取状态 true 成功
const nfcResult=ref('')
// onLoad(() => {
// listenNFCStatus();
// })
// 变量初始化
let NfcAdapter = null;
let MifareClassic = null;
let isListening = false;
// 监听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;
// 检查NFC状态
function open() {
try {
const main = plus.android.runtimeMainActivity();
NfcAdapter = plus.android.importClass("android.nfc.NfcAdapter");
const nfcAdapter = NfcAdapter.getDefaultAdapter(main);
if (!nfcAdapter) {
uni.showToast({
icon: "error",
title: "该设备不支持NFC",
duration: 5000,
});
return;
}
if (!nfcAdapter.isEnabled()) {
uni.showToast({
icon: "error",
title: "NFC功能未打开",
duration: 5000,
});
return;
}
initNFC();
} catch (e) {
console.error("打开NFC失败:", e);
uni.showToast({
icon: "error",
title: "初始化NFC失败",
});
}
}
// 初始化NFC监听
function initNFC() {
try {
const main = plus.android.runtimeMainActivity();
const Intent = plus.android.importClass("android.content.Intent");
const Activity = plus.android.importClass("android.app.Activity");
const PendingIntent = plus.android.importClass("android.app.PendingIntent");
const IntentFilter = plus.android.importClass(
"android.content.IntentFilter"
);
// 导入需要的NFC类
plus.android.importClass("android.nfc.NdefRecord");
plus.android.importClass("android.nfc.NdefMessage");
MifareClassic = plus.android.importClass("android.nfc.tech.MifareClassic");
NfcAdapter = plus.android.importClass("android.nfc.NfcAdapter");
const nfcAdapter = NfcAdapter.getDefaultAdapter(main);
if (!nfcAdapter || !nfcAdapter.isEnabled()) return;
// 创建Intent过滤器
const ndef = new IntentFilter("android.nfc.action.NDEF_DISCOVERED");
const tag = new IntentFilter("android.nfc.action.TAG_DISCOVERED");
const tech = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
const intentFiltersArray = [ndef, tag, tech];
// 技术列表
const techListsArray = [
["android.nfc.tech.Ndef"],
["android.nfc.tech.IsoDep"],
["android.nfc.tech.NfcA"],
["android.nfc.tech.NfcB"],
["android.nfc.tech.NfcF"],
["android.nfc.tech.Nfcf"],
["android.nfc.tech.Nfef"],
["android.nfc.tech.Ndef"],
["android.nfc.tech.NfcV"],
["android.nfc.tech.NdefFormatable"],
["android.nfc.tech.MifareClassi"],
["android.nfc.tech.MifareUltralight"],
];
// 创建PendingIntent
const _intent = new Intent(main, main.getClass());
_intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
const pendingIntent = PendingIntent.getActivity(main, 0, _intent, 0);
// 监听NFC事件
startNFCListener();
// 启用前台调度
nfcAdapter.enableForegroundDispatch(
main,
pendingIntent,
intentFiltersArray,
techListsArray
);
} catch (e) {
console.error("初始化NFC失败:", e);
}
}
// 开始NFC监听
function startNFCListener() {
if (isListening) return;
plus.globalEvent.addEventListener("newintent", handleNewIntent);
isListening = true;
}
// 停止NFC监听
function stopNFCListener() {
if (isListening) {
plus.globalEvent.removeEventListener("newintent", handleNewIntent);
isListening = false;
}
}
// 处理新的NFC意图
function handleNewIntent() {
readNFCData();
stopNFCListener();
}
// 读取NFC数据
function readNFCData() {
try {
const main = plus.android.runtimeMainActivity();
const _intent = main.getIntent();
// 只处理 NDEF 数据
let rawMsgs = _intent.getParcelableArrayExtra(
"android.nfc.extra.NDEF_MESSAGES"
);
if (rawMsgs && rawMsgs.length > 0) {
try {
const records = rawMsgs[0].getRecords();
const result = records[0].getPayload();
// 取 deviceId
function bytesToString(result) {
return String.fromCharCode(...result);
}
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);
formData.value.deviceId = bytesToString(result).substring(3);
emit("changeNfc", formData.value.deviceId);
readStatus.value=true;
} catch (e) {
console.log("读取NDEF数据错误:", e);
uni.showToast({ title: "读取数据失败,请重新读取", icon: "none" });
}
} else {
uni.showToast({ title: "未识别到NFC标签", icon: "none" });
}
} catch (e) {
console.error("读取NFC数据失败:", e);
uni.showToast({ title: "读取数据失败,请重新读取", icon: "none" });
}
}
// 运行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
onMounted(() => {
open();
});
// 组件卸载前清理
onBeforeUnmount(() => {
stopNFCListener();
});
defineExpose({
open
});
</script>
<style lang="scss" scoped>

View File

@@ -26,7 +26,7 @@ const props = defineProps({
})
const isEnlarged = ref(props.visible);
const mediaUrl = ref(props.url);//http://192.168.236.184:9000/718ys-test/polling/2483ee76b28b4d43b87c13386dad90bb.jpg
const mediaUrl = ref(props.url);
// const mediaType = ref('');//类型
// 显示隐藏
watch(() => props.visible, (newVal, oldVal) => {

View File

@@ -4,7 +4,7 @@ export const AGREEWELCOME_KEY="agreewelcome";
// clientId 默认写2
export const CLIENT_ID="2";
// #区分内外网 //1-内网2-外网
export const NETWORK_ENV=1;
export const NETWORK_ENV=2;
// miniIo 参数对象
export const MINIO_KEY="minioKey"

View File

@@ -3,7 +3,7 @@
"name" : "718友晟",
"appid" : "__UNI__4C459F4",
"description" : "",
"versionName" : "1.0.0",
"versionName" : "1.0.2",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
@@ -61,7 +61,8 @@
"<uses-permission android:name=\"android.permission.BROADCAST_PACKAGE_REPLACED\" />",
"<uses-permission android:name=\"android.permission.RESTART_PACKAGES\" />",
"<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />",
"<uses-permission android:name=\"android.permission.NFC\" />"
"<uses-permission android:name=\"android.permission.NFC\"/>",
"<uses-feature android:name=\"android.hardware.nfc\" android:required=\"true\"/>"
],
"abiFilters" : [ "armeabi-v7a", "arm64-v8a" ],
"minSdkVersion" : 23,

View File

@@ -663,12 +663,6 @@
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/polling/nfcTest/nfcTemplate",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/polling/nfcTest/index",
"style": {

View File

@@ -9,11 +9,11 @@
</template>
<script>
import nfc from '@/utils/ouu-nfc.js'
import nfc from './ouu-nfc.js'
export default {
data() {
return {
inputText: 'https://baidu.com'
inputText: 'testtesttesttesttestfdsf'
}
},
onLoad() {

View File

@@ -1,784 +0,0 @@
<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>

View File

@@ -94,10 +94,20 @@
<view>{{ item.pointName }}</view>
</view>
<!-- #ifdef APP-PLUS -->
<view class="r-right" @click.stop="initNFC(item,index)">
<view class="r-right r-active" v-if="item.resultContent" @click.stop="initNFC(item,index)">
<img :src="'static/images/polling/icon-NFCcode-b.png'" class="img-nfc" />
<view>识别成功<view class="r-font">点击再次识别</view></view>
</view>
<view class="r-right" v-else @click.stop="initNFC(item,index)">
<img :src="'static/images/polling/icon-NFCcode.png'" class="img-nfc" /> 开始识别
</view>
<!-- #endif -->
<!-- #ifndef APP-PLUS -->
<view class="r-right r-active">
<img :src="'static/images/polling/icon-NFCcode-b.png'" class="img-nfc" />
<view>识别成功</view>
</view>
<!-- #endif -->
</view>
</block>
<!-- 扫码 -->
@@ -204,17 +214,21 @@
@close="handlePreviewClose"
></mediaPreview>
<!-- NFC 弹窗 -->
<NFCTemplate v-if="nfcShow" ref="nfcTemplateRef"
@close="nfcClose"
@confirm="nfcConfirm"
></NFCTemplate>
<!-- NFC 数据读取 -->
<NFCTemplate
v-if="nfcShow"
ref="nfcReaderRef"
@changeNfc="handleNfcData"
@close="nfcClose"
/>
</view>
</template>
<script setup>
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';
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
import multipleSelect from "@/components/multipleSelect.vue";
@@ -222,6 +236,7 @@ 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'
@@ -370,7 +385,11 @@ const chooseImage = (item) => {
minioUpload(param).then(res=>{
let data = res.data;
// console.log("444图片上传成功=>",data)
imgArr2.value.push(data.fileUrl)
// imgArr2.value.push(data.fileUrl)
imgArr2.value.push({
shortUrl:minioObj.minioThumbUrl +"/"+data.fileName,
url:minioObj.minioUrl +"/"+data.fileName,
})
imgArr.value.push(data.fileName)//传给后台的路径
item.resultContent = imgArr.value.join(",")
})
@@ -432,7 +451,7 @@ let isVisible= ref(false);//放大处理
let mediaUrl= ref('');//放大地址
let videoShow = ref(true);
const showMediaPreview=(item)=>{
console.log("showMediaPreview===")
// console.log("showMediaPreview===")
isVisible.value = true;
videoShow.value = false;
mediaUrl.value = item.url
@@ -443,10 +462,8 @@ const handlePreviewClose=()=>{
}
// nfc 处理
let cardUID = ref('');
let cardValue = ref('');
let nfcShow = ref(false);
const nfcTemplateRef = ref(null);
const nfcReaderRef = ref(null);
// optionObj.pointList
let nfcIndex = ref(0);
const initNFC = async(item,index) => {
@@ -455,19 +472,22 @@ const initNFC = async(item,index) => {
// uni.navigateTo({url:'/pages/business/polling/nfcTest/index'})
// #ifdef APP-PLUS
setTimeout(()=>{
// console.log("nfcTemplateRef==",nfcTemplateRef.value)
if (nfcTemplateRef.value) {
nfcTemplateRef.value.listenNFCStatus();
if(nfcReaderRef.value){
nfcReaderRef.value.open();
}
},500)
},50)
// #endif
}
const nfcClose = async(item) => {
nfcShow.value = false;
}
const nfcConfirm=(result)=>{
console.log("confirm=>",result)
optionObj.pointList[nfcIndex].resultContent = result
const handleNfcData=(data)=>{
console.log("NFC数据:", data);
console.log("NFC数据1111:", optionObj.value.pointList,nfcIndex.value)
optionObj.value.pointList[nfcIndex.value].resultContent = data;
console.log("NFC数据1111:", optionObj.value.pointList[nfcIndex.value])
// nfcShow.value = false;
}
// 扫二维码