| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168 |
- <template>
- <view class="page">
- <!-- 导航按钮 -->
- <button @click="logout" class="nav-btn">
- <text class="icon fa fa-sign-out"></text>
- </button>
- <button @click="toggleServerConfig" class="nav-btn">
- <text class="icon fa fa-cog"></text>
- </button>
- <scroll-view scroll-y class="scroll-area">
- <view v-if="!showServerConfig" class="form-section">
- <view class="section-title">
- <text class="title"></text>
- </view>
- <!-- RFID扫描区域 -->
- <view class="rfid-card">
- <text class="icon fa fa-qrcode scan-icon"></text>
- <view class="btn-group">
- <button class="manual-btn" @click="init" :disabled="isDisable">
- <text class="fa fa-power-off"></text> {{ isDeviceReady ? '已开启' : '开启设备' }}
- </button>
- <!-- <button class="btn" type="primary" @click="release" :disabled="isReDisabled">release</button> -->
- <button class="scan-btn" @click="scan" :disabled="!isDisable">
- <text class="fa fa-camera"></text> 扫描耳标签
- </button>
- </view>
- <input class="input-box" v-model="form.earId" disabled placeholder="当前FID标签号码将显示在这里" style="height: 30px;width: 95%;" />
- <!-- 存储的FID标签数据 -->
- <scroll-view scroll-y class="sv">
- <view v-for="(item, index) in dataList" :key="index" class="data-item">
- <p style="margin-left: 10px;">{{ item.id }}</p>
- <picker @change="onTypeChange($event, index)" :value="item.typeIndex" :range="types">
- <view class="picker">
- {{ types[item.typeIndex] }}
- </view>
- </picker>
- <button class="delete-btn" @click="deleteItem(index)">删除</button>
- </view>
- </scroll-view>
- </view>
- <!-- 操作按钮组 -->
- <view class="btn-group">
- <button class="manual-btn" @click="resetForm">重置</button>
- <button class="scan-btn" @click="submitForm">提交数据</button>
- </view>
- </view>
- </scroll-view>
- </view>
- </template>
- <script>
- // 导入API配置
- import API from '@/api/index.js'
- export default {
- data() {
- return {
- dataList: [], // 存储扫描的耳标数据
- types: ['正常', '淘汰', '死亡'], // 耳标类型选项
- scanProgress: 0, // 当前扫描进度
- scanTotalAttempts: 0, // 总扫描尝试次数
- isDisable: false, // 设备禁用状态
- isDeviceReady: false, // 设备就绪状态
- isInitializing: false, // 设备初始化中状态
- currentDate: '', // 当前日期
- showServerConfig: false,// 是否显示服务器配置
- uhfSFHelper: null, // UHF插件实例
- scanTimeout: null, // 扫描超时计时器
- retryTimeout: null, // 重试计时器
- maxScanTimer: null, // 最大扫描时间计时器
- settingChangeListener: null, // 设置变化监听器
-
- // 列表数据
- buildingList: [], // 栋舍列表
- roomList: [], // 房间列表
- Fieldnumber: [], // 栏位列表
-
- // 表单数据
- form: {
- earId: '', // 耳标ID
- buildingName: '', // 栋舍名称
- roomName: '', // 房间名称
- penNo: '', // 栏位编号
- status: 'healthy', // 状态
- note: '' // 备注
- },
-
- // 提交状态
- isSubmitting: false,
-
- }
- },
-
- mounted() {
- // 标记组件已挂载
- this._isMounted = true;
-
- // 初始化日期
- const now = new Date()
- this.currentDate = now.toISOString().split('T')[0]
-
- // 加载已保存的设置
- this.loadSavedSettings();
-
- // 获取列表数据(即使插件未初始化也可以加载)
- this.fetchBuildingList();
-
- // 监听登录成功事件,重新加载用户设置并清空数据
- this.reloadUserSettingsListener = uni.$on('reloadUserSettings', () => {
- // 清空数据
- this.resetForm();
-
- this.loadSavedSettings();
- // 重新加载栋舍列表,确保使用最新的用户信息
- this.fetchBuildingList();
- });
-
- // 初始化插件实例
- this.initializePluginWithRetry(0);
-
- // 监听全局设置变化
- this.settingChangeListener = uni.$on('settingsUpdated', (settings) => {
- this.loadSavedSettings(settings);
- });
- },
-
- /**
- * 带重试的插件初始化
- * @param {number} retryCount - 当前重试次数
- */
- initializePluginWithRetry(retryCount = 0) {
-
- console.log('设备初始化成功');
- this.isDisable = false;
- uni.showToast({
- title: '设备初始化成功',
- icon: 'success',
- duration: 2000
- });
- //如果开启设备 初始化失败,重试,请打开下面代码,上方代码可以删除掉
- const MAX_RETRIES = 1;
- const RETRY_DELAY = 2000; // 2秒
- const isPluginInitialized = this.initPluginInstance();
- // if (!isPluginInitialized) {
- // console.warn(`Plugin initialization failed (attempt ${retryCount + 1}/${MAX_RETRIES})`);
- // this.isDisable = true; // 禁用依赖插件的按钮
-
- // // 如果未达到最大重试次数,继续重试
- // if (retryCount < MAX_RETRIES) {
- // console.log(`Retrying plugin initialization after ${RETRY_DELAY}ms`);
- // setTimeout(() => {
- // if (this._isMounted) {
- // this.initializePluginWithRetry(retryCount + 1);
- // }
- // }, RETRY_DELAY);
- // } else {
- // console.error('Max retries reached for plugin initialization');
- // uni.showToast({
- // title: '设备初始化失败,请重启应用',
- // icon: 'none',
- // duration: 3000
- // });
- // }
- // } else {
- // console.log('Plugin initialized successfully');
- // this.isDisable = false;
- // uni.showToast({
- // title: '设备初始化成功',
- // icon: 'success',
- // duration: 2000
- // });
- // }
- },
- beforeUnmount() {
- // 标记组件已卸载
- this._isMounted = false;
-
- // 清除所有计时器
- this.cancelScan();
-
- // 释放设备
- this.releaseDevice();
-
- // 清理插件实例
- this.uhfSFHelper = null;
-
- // 移除事件监听
- if (this.settingChangeListener) {
- uni.$off('settingsUpdated', this.settingChangeListener);
- }
-
- // 移除登录成功事件监听
- if (this.reloadUserSettingsListener) {
- uni.$off('reloadUserSettings', this.reloadUserSettingsListener);
- }
- },
-
- /**
- * 页面显示时触发,确保获取最新的编号信息
- */
- onShow() {
- this.loadSavedSettings();
- },
-
- methods: {
- /**
- * 带重试的插件初始化
- * @param {number} retryCount - 当前重试次数
- */
- initializePluginWithRetry(retryCount = 0) {
- const MAX_RETRIES = 1;
- const RETRY_DELAY = 2000; // 2秒
- const isPluginInitialized = this.initPluginInstance();
- if (!isPluginInitialized) {
- console.warn(`Plugin initialization failed (attempt ${retryCount + 1}/${MAX_RETRIES})`);
- this.isDisable = true; // 禁用依赖插件的按钮
-
- // 如果未达到最大重试次数,继续重试
- if (retryCount < MAX_RETRIES) {
- console.log(`Retrying plugin initialization after ${RETRY_DELAY}ms`);
- setTimeout(() => {
- if (this._isMounted) {
- this.initializePluginWithRetry(retryCount + 1);
- }
- }, RETRY_DELAY);
- } else {
- console.error('Max retries reached for plugin initialization');
- uni.showToast({
- title: '设备初始化失败,请重启应用',
- icon: 'none',
- duration: 3000
- });
- }
- } else {
- console.log('插件初始化成功');
- this.isDisable = false;
- uni.showToast({
- title: '设备初始化成功',
- icon: 'success',
- duration: 2000
- });
- }
- },
- /**
- * 获取栋舍列表(带重试机制)
- * @param {number} retryCount - 当前重试次数(默认0)
- */
- fetchBuildingList(retryCount = 0) {
- // 显示加载提示
- if (retryCount === 0) {
- uni.showLoading({ title: '加载栋舍列表...', mask: true });
- }
-
- // 发送请求到API获取栋舍列表
- uni.request({
- url: API.getBuilding,
- method: 'GET',
- timeout: 10000, // 增加超时时间到10秒
- success: (res) => {
- // 隐藏加载提示
- if (retryCount === 0) {
- uni.hideLoading();
- }
- // 更新栋舍列表
- this.buildingList = res.data.data || [];
- }
- });
- },
- /**
- * 加载已保存的设置
- * @param {object} settings - 可选的设置参数
- */
- loadSavedSettings(settings = null) {
- const app = getApp();
-
- let buildingName, roomName, penNo;
-
- // 如果提供了设置参数,优先使用参数值
- if (settings && typeof settings === 'object') {
- buildingName = settings.building || '';
- roomName = settings.room || '';
- penNo = settings.pen || '';
-
- // 更新全局数据
- app.globalData.buildingName = buildingName;
- app.globalData.roomName = roomName;
- app.globalData.penNo = penNo;
-
- // 更新本地存储
- uni.setStorageSync('buildingName', buildingName);
- uni.setStorageSync('roomName', roomName);
- uni.setStorageSync('penNo', penNo);
- } else {
- // 从全局数据或本地存储获取编号信息
- buildingName = app.globalData.buildingName || uni.getStorageSync('buildingName') || '';
- roomName = app.globalData.roomName || uni.getStorageSync('roomName') || '';
- penNo = app.globalData.penNo || uni.getStorageSync('penNo') || '';
- }
-
- // 更新表单数据
- this.form.buildingName = buildingName;
- this.form.roomName = roomName;
- this.form.penNo = penNo;
-
- // 如果栋舍不为空且房间列表为空,加载房间列表
- if (buildingName && this.roomList.length === 0) {
- this.fetchRoomList(buildingName);
- } else if (buildingName && this.roomList.length > 0 && roomName) {
- // 如果房间已变更,更新房间列表
- this.fetchRoomList(buildingName);
- }
-
- // 如果房间不为空且栏位列表为空,加载栏位列表
- if (roomName && this.Fieldnumber.length === 0) {
- this.fetchFieldList(roomName);
- } else if (roomName && this.Fieldnumber.length > 0 && penNo) {
- // 如果栏位已变更,更新栏位列表
- this.fetchFieldList(roomName);
- }
- },
-
- /**
- * 根据栋舍获取房间列表(带重试机制)
- * @param {string} buildingName - 栋舍名称
- * @param {number} retryCount - 当前重试次数
- */
- fetchRoomList(buildingName, retryCount = 0) {
- // 显示加载提示
- if (retryCount === 0) {
- uni.showLoading({ title: '加载房间列表...', mask: true });
- }
-
- // 发送请求到API获取房间列表
- uni.request({
- url: API.getRoom,
- method: 'GET',
- data: { building: buildingName },
- timeout: 10000, // 增加超时时间到10秒
- success: (res) => {
- // 隐藏加载提示
- if (retryCount === 0) {
- uni.hideLoading();
- }
- // 更新房间列表
- this.roomList = res.data.data || [];
- // 重置房间和栏位选择
- this.form.roomName = '';
- this.Fieldnumber = [];
- this.form.penNo = '';
- }
- });
- },
-
- /**
- * 根据房间名称获取栏位列表(带重试机制)
- * @param {string} roomName - 房间名称
- * @param {number} retryCount - 当前重试次数(默认0)
- */
- fetchFieldList(roomName, retryCount = 0) {
- // 显示加载提示
- if (retryCount === 0) {
- uni.showLoading({ title: '加载栏位列表...', mask: true });
- }
- const MAX_RETRIES = 1;
- const RETRY_DELAY = 2000; // 2秒
- // 发送请求到API获取栏位列表
- uni.request({
- url: API.getField,
- method: 'GET',
- data: { room: roomName },
- timeout: 10000, // 增加超时时间到10秒
- success: (res) => {
- // 隐藏加载提示
- if (retryCount === 0) {
- uni.hideLoading();
- }
- // 更新栏位列表
- this.Fieldnumber = res.data.data || [];
- // 重置栏位选择
- this.form.penNo = '';
- },
- fail: (err) => {
- // 隐藏加载提示
- if (retryCount === 0) {
- uni.hideLoading();
- }
- // 如果未达到最大重试次数,尝试重试
- if (retryCount < MAX_RETRIES) {
- console.log(`Retrying request (${retryCount + 1}/${MAX_RETRIES})`);
- setTimeout(() => {
- this.fetchFieldList(roomName, retryCount + 1);
- }, RETRY_DELAY);
- } else {
-
- }
- }
- });
- },
- /**
- * 初始化UHF插件实例
- * @returns {boolean} 初始化是否成功
- */
- initPluginInstance() {
- if (this.uhfSFHelper) {
- console.log('插件实例已存在');
- return true;
- }
-
- try {
- console.log('初始化UHF插件实例');
-
- // 检查运行环境是否支持原生插件
- if (typeof uni.requireNativePlugin !== 'function') {
- console.error('当前环境不支持uni.requireNativePlugin');
- return false;
- }
-
- // 尝试加载插件
- this.uhfSFHelper = uni.requireNativePlugin('Alvin-CBZUhfModule');
-
- // 验证插件实例是否有效
- if (!this.uhfSFHelper) {
- console.error('插件实例为空或未定义');
- // 检查插件是否存在于项目中
- console.log('检查插件是否存在于项目中...');
- // 提示可能的解决方案
- console.warn('可能的解决方案:');
- console.warn('1. 验证插件名称是否正确:Alvin-CBZUhfModule');
- console.warn('2. 检查插件是否正确安装在nativeplugins目录中');
- console.warn('3. 确保您的开发环境支持原生插件');
- return false;
- }
-
- if (typeof this.uhfSFHelper.doInitDevice !== 'function') {
- console.error('插件实例缺少必要方法: doInitDevice');
- // 输出插件实例的所有方法,帮助调试
- console.log('插件实例可用方法:', Object.keys(this.uhfSFHelper));
- this.uhfSFHelper = null;
- return false;
- }
-
- console.log('UHF插件实例创建成功');
- return true;
- } catch (e) {
- console.error('加载UHF插件失败:', e.message);
- console.error('错误栈:', e.stack);
- this.uhfSFHelper = null;
- uni.showToast({
- title: '设备功能不可用: ' + e.message,
- icon: 'none',
- duration: 3000
- });
- return false;
- }
- },
-
- /**
- * 检查并恢复插件实例
- * @param {number} retryCount - 当前重试次数(默认0)
- * @returns {boolean} 插件实例是否有效
- */
- checkAndRestorePluginInstance(retryCount = 0) {
- // 设置最大重试次数和超时时间
- const MAX_RETRIES = 1;
- const RETRY_DELAY = 1000; // 1秒
-
- // 如果实例不存在,尝试初始化
- if (!this.uhfSFHelper) {
- console.log(`没有插件实例,尝试初始化 (重试: ${retryCount})`);
- const result = this.initPluginInstance();
-
- // 如果初始化失败且未达到最大重试次数,递归重试
- if (!result && retryCount < MAX_RETRIES) {
- console.log(`初始化失败,重试 (${retryCount + 1}/${MAX_RETRIES}) 后 ${RETRY_DELAY}ms`);
- // 延迟后重试
- setTimeout(() => {
- this.checkAndRestorePluginInstance(retryCount + 1);
- }, RETRY_DELAY);
- return false;
- }
-
- return result;
- }
-
- // 检查实例方法是否存在且有效
- const requiredMethods = ['doInitDevice', 'doStartScan', 'doReleaseDevice'];
- const missingMethods = requiredMethods.filter(method => typeof this.uhfSFHelper[method] !== 'function');
-
- if (missingMethods.length > 0) {
- console.error(`插件实例缺少必要方法: ${missingMethods.join(', ')}`);
- console.log('插件实例可用方法:', Object.keys(this.uhfSFHelper));
- this.uhfSFHelper = null;
-
- // 如果未达到最大重试次数,尝试重新初始化
- if (retryCount < MAX_RETRIES) {
- console.log(`Attempting to reinitialize plugin (${retryCount + 1}/${MAX_RETRIES}) after ${RETRY_DELAY}ms`);
- setTimeout(() => {
- this.checkAndRestorePluginInstance(retryCount + 1);
- }, RETRY_DELAY);
- return false;
- }
-
- return false;
- }
-
- console.log('插件实例有效且准备使用');
- return true;
- },
-
- /**
- * 取消当前扫描操作
- */
- cancelScan() {
- // 清除所有相关计时器
- if (this.scanTimeout) {
- clearTimeout(this.scanTimeout);
- this.scanTimeout = null;
- }
-
- if (this.retryTimeout) {
- clearTimeout(this.retryTimeout);
- this.retryTimeout = null;
- }
-
- if (this.maxScanTimer) {
- clearTimeout(this.maxScanTimer);
- this.maxScanTimer = null;
- }
-
- // 隐藏加载提示
- uni.hideLoading();
-
- console.log('扫描已取消');
- uni.showToast({ title: '扫描已取消', icon: 'none' });
- },
-
- /**
- * 释放设备资源
- */
- releaseDevice() {
- if (this.isDeviceReady && this.uhfSFHelper) {
- try {
- this.uhfSFHelper.doReleaseDevice()
- } catch (e) {
- console.error('释放设备失败', e)
- }
- this.isDeviceReady = false
- this.isDisable = false
- }
- },
-
- /**
- * 初始化设备
- */
- init() {
- // 确保插件实例已初始化且有效
- if (!this.checkAndRestorePluginInstance()) {
- return;
- }
-
- if (this.isInitializing) {
- console.log('设备初始化已在进行中');
- return;
- }
-
- if (this.isDeviceReady) {
- console.log('设备已初始化');
- return uni.showToast({ title: '设备已开启', icon: 'none' });
- }
-
- this.isDisable = false;
- this.isInitializing = true;
-
- try {
- console.log('开始初始化设备');
- // 再次检查插件实例是否有效
- if (!this.checkAndRestorePluginInstance()) {
- this.isInitializing = false;
- return;
- }
-
- this.uhfSFHelper.doInitDevice(res => {
- this.isInitializing = false;
-
- if (res === true) {
- this.isDeviceReady = true;
- this.isDisable = true;
- console.log('Device initialized successfully');
- uni.showToast({ title: '设备已开启', icon: 'success' });
- } else {
- this.isDisable = false;
- console.error('设备初始化失败');
- uni.showToast({ title: '初始化失败', icon: 'none' });
- }
- });
- } catch (e) {
- this.isInitializing = false;
- console.error('Error during device initialization:', e);
- this.isDisable = false;
- // 清除插件实例,以便下次初始化尝试
- this.uhfSFHelper = null;
- uni.showToast({ title: '初始化异常', icon: 'none' });
- }
- },
-
- /**
- * 开始扫描耳标
- */
- scan() {
- // 确保插件实例已初始化且有效
- if (!this.checkAndRestorePluginInstance()) {
- return;
- }
-
- if (!this.isDeviceReady) {
- return uni.showToast({ title: '请先开启设备', icon: 'none' })
- }
-
- // 设置扫描参数 - 优化参数以提高成功率
- const scanConfig = {
- retryCount: 3, // 增加重试次数
- currentRetry: 0,
- timeout: 2000, // 增加超时时间
- interval: 400, // 增加间隔,给设备恢复时间
- signalThreshold: 0.5 // 降低信号阈值,接受更多信号
- };
-
- // 初始化扫描进度变量
- this.scanProgress = 0;
- this.scanTotalAttempts = scanConfig.retryCount;
-
- // 显示扫描中提示,添加mask以禁止背景操作
- uni.showLoading({
- title: '正在扫描耳标...',
- mask: true
- });
-
- // 执行扫描函数
- this.performScan(scanConfig);
- },
- // release() {
- // this.isDeviceReady = false;
- // this.isDisable = false;
- // },
-
- /**
- * 执行扫描(带重试机制)
- * @param {object} config - 扫描配置参数
- * @param {number} config.retryCount - 最大重试次数
- * @param {number} config.currentRetry - 当前重试次数
- * @param {number} config.timeout - 单次扫描超时时间(毫秒)
- * @param {number} config.interval - 重试间隔(毫秒)
- */
- /**
- * 处理类型选择变化
- */
- onTypeChange(e, index) {
- if (index >= 0 && index < this.dataList.length) {
- this.dataList[index].typeIndex = e.detail.value;
- console.log(`耳标 ${this.dataList[index].id} 类型变更为: ${this.types[e.detail.value]}`);
- }
- },
- /**
- * 删除耳标条目
- */
- deleteItem(index) {
- if (index >= 0 && index < this.dataList.length) {
- const deletedItem = this.dataList.splice(index, 1);
- console.log(`删除耳标: ${deletedItem[0].id}`);
- uni.showToast({ title: '删除成功', icon: 'success' });
- }
- },
- performScan(config) {
- // 检查组件是否已卸载或设备是否已准备好
- if (!this._isMounted || !this.isDeviceReady) {
- uni.hideLoading();
- console.log('Scan aborted: component not mounted or device not ready');
- return;
- }
-
- if (config.currentRetry >= config.retryCount) {
- uni.hideLoading();
- console.log('Scan failed after maximum retries');
- return uni.showToast({ title: '扫描失败,请调整位置重试', icon: 'none' });
- }
-
- // 添加最大扫描时间限制 (总时间 = 超时时间 * 重试次数)
- const maxScanTime = config.timeout * config.retryCount;
- if (!this.maxScanTimer) {
- this.maxScanTimer = setTimeout(() => {
- console.log('Maximum scan time exceeded');
- this.cancelScan();
- }, maxScanTime);
- }
-
- // 增加重试计数
- config.currentRetry++;
- this.scanProgress = config.currentRetry;
-
- // 更新加载提示(简化提示,避免频繁更新)
- if (config.currentRetry % 2 === 0 || config.currentRetry === config.retryCount) {
- uni.hideLoading();
- uni.showLoading({
- title: `扫描中 (${Math.round(config.currentRetry/config.retryCount*100)}%)...`,
- mask: true
- });
- }
-
- console.log(`开始扫描尝试 ${config.currentRetry}/${config.retryCount},超时时间: ${config.timeout}ms`);
-
- // 清除之前的超时
- if (this.scanTimeout) {
- clearTimeout(this.scanTimeout);
- this.scanTimeout = null;
- }
-
- // 设置超时机制
- this.scanTimeout = setTimeout(() => {
- console.log(`Scan timeout, retrying (${config.currentRetry}/${config.retryCount})`);
- // 重试扫描前先检查实例
- if (this._isMounted) {
- // 检查并恢复插件实例
- if (this.checkAndRestorePluginInstance()) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval);
- } else {
- console.error('Failed to restore plugin instance before retry');
- uni.hideLoading();
- return uni.showToast({ title: '设备功能异常,无法重试', icon: 'none' });
- }
- }
- }, config.timeout);
-
- // 执行扫描
- try {
- // 清除之前可能存在的重试计时器
- if (this.retryTimeout) {
- clearTimeout(this.retryTimeout);
- this.retryTimeout = null;
- }
- // 检查插件实例是否有效
- if (!this.checkAndRestorePluginInstance()) {
- // 清除超时计时器
- if (this.scanTimeout) {
- clearTimeout(this.scanTimeout);
- this.scanTimeout = null;
- }
-
- console.error('Invalid plugin instance for scanning after restore attempt');
- // 尝试重新初始化设备
- this.initDevice();
- uni.hideLoading();
- return uni.showToast({ title: '设备功能异常,正在重新初始化', icon: 'none' });
- }
-
- // 保存当前实例引用,防止闭包中实例变化
- const currentPluginInstance = this.uhfSFHelper;
-
- // 执行扫描命令
- try {
- currentPluginInstance.doStartScan(result => {
- // 再次检查插件实例是否与执行扫描时相同
- if (this.uhfSFHelper !== currentPluginInstance) {
- console.warn('Plugin instance changed during scan, ignoring result');
- // 尝试重新扫描
- if (this._isMounted) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval);
- }
- return;
- }
-
- // 检查组件是否已卸载
- if (!this._isMounted) return;
-
- // 清除超时计时器
- if (this.scanTimeout) {
- clearTimeout(this.scanTimeout);
- this.scanTimeout = null;
- }
-
- if (result) {
- // 假设result是一个包含id和signalStrength的对象
- // 如果result只是ID字符串,我们将其包装成对象
- const scanResult = typeof result === 'string' ? { id: result, signalStrength: 1 } : result;
-
- // 检查信号强度是否足够
- if (scanResult.signalStrength >= config.signalThreshold) {
- // 清除所有计时器
- if (this.maxScanTimer) {
- clearTimeout(this.maxScanTimer);
- this.maxScanTimer = null;
- }
- if (this.retryTimeout) {
- clearTimeout(this.retryTimeout);
- this.retryTimeout = null;
- }
-
- uni.hideLoading();
- console.log('扫描成功:', scanResult);
- this.form.earId = scanResult.id;
-
- // 检查是否重复扫描
- const isDuplicate = this.dataList.some(item => item.id === scanResult.id);
-
- if (isDuplicate) {
- console.log('耳标已存在:', scanResult.id);
- uni.showToast({ title: '该耳标已扫描过', icon: 'none' });
- } else {
- // 默认添加为'正常'类型
- this.dataList.push({ id: scanResult.id, typeIndex: 0 });
- uni.showToast({ title: '扫描成功', icon: 'success' });
- }
- } else {
- console.log(`Scan result with weak signal (${scanResult.signalStrength}), retrying`);
- // 信号太弱,继续重试前检查实例
- if (this._isMounted) {
- if (this.checkAndRestorePluginInstance()) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval);
- } else {
- console.error('Failed to restore plugin instance for retry');
- uni.hideLoading();
- return uni.showToast({ title: '设备功能异常', icon: 'none' });
- }
- }
- }
- } else {
- console.log(`Scan failed, retrying (${config.currentRetry}/${config.retryCount})`);
- // 重试扫描前检查实例
- if (this._isMounted) {
- if (this.checkAndRestorePluginInstance()) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval);
- } else {
- console.error('Failed to restore plugin instance for retry');
- uni.hideLoading();
- return uni.showToast({ title: '设备功能异常', icon: 'none' });
- }
- }
- }
- });
- } catch (e) {
- console.error('Exception during scan execution:', e);
- // 清除超时计时器
- if (this.scanTimeout) {
- clearTimeout(this.scanTimeout);
- this.scanTimeout = null;
- }
-
- // 检查是否是实例不可用错误
- if (e.message && e.message.includes('instance is not available')) {
- console.error('Plugin instance not available during scan');
- // 尝试重新初始化
- this.uhfSFHelper = null;
- if (this.checkAndRestorePluginInstance()) {
- // 重新执行扫描
- if (this._isMounted) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval * 2);
- }
- } else {
- uni.hideLoading();
- return uni.showToast({ title: '设备功能异常,无法扫描', icon: 'none' });
- }
- } else {
- // 其他错误
- if (this._isMounted) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval);
- }
- }
- }
- } catch (e) {
- // 清除超时计时器
- if (this.scanTimeout) {
- clearTimeout(this.scanTimeout);
- this.scanTimeout = null;
- }
-
- console.error('Error during scan setup:', e);
- // 检查错误是否与实例不可用相关
- if (e.message && e.message.includes('instance is not available')) {
- console.error('Plugin instance not available during scan setup');
- // 清除当前实例
- this.uhfSFHelper = null;
- // 尝试重新初始化
- if (this.checkAndRestorePluginInstance()) {
- // 重新执行扫描
- if (this._isMounted) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval * 2); // 增加间隔
- }
- } else {
- uni.hideLoading();
- return uni.showToast({ title: '设备功能异常', icon: 'none' });
- }
- } else {
- // 其他错误,继续重试
- if (this._isMounted) {
- this.retryTimeout = setTimeout(() => this.performScan(config), config.interval);
- }
- }
- }
- },
-
- /**
- * 提交表单数据
- */
- submitForm() {
- // 确保加载最新的设置
- this.loadSavedSettings();
- // 验证编号是否已选择
- let missingField = '';
- if (!this.form.buildingName) missingField = '栋舍';
- else if (!this.form.roomName) missingField = '房间';
- else if (!this.form.penNo) missingField = '栏位';
- if (missingField) {
- return uni.showToast({ title: `未选择${missingField}编号`, icon: 'none', duration: 3000 })
- }
- // 验证耳标是否已扫描
- if (this.dataList.length === 0) {
- return uni.showToast({ title: '请先扫描耳标', icon: 'none', duration: 3000 })
- }
- // 验证用户ID是否存在
- const app = getApp();
- const userInfo = app.globalData.userInfo || uni.getStorageSync('user_info') || {};
- const userId = userInfo.id || '';
- if (!userId) {
- return uni.showToast({ title: '用户未登录', icon: 'none', duration: 3000 })
- }
- // 显示加载提示
- uni.showLoading({ title: '提交中...', mask: true });
- // 将dataList数组转换为包含类型的逗号分隔字符串
- // 格式: id:type,id:type,...
- const rfidString = this.dataList.map(item => {
- return `${item.id}:${this.types[item.typeIndex]}`;
- }).join(',');
- // 准备提交数据
- const submitData = {
- rfid: rfidString,
- buildingName: this.form.buildingName,
- roomName: this.form.roomName,
- penNo: this.form.penNo,
- userId: userId,
- username: userInfo.username || '',
- time: new Date().toISOString()
- };
- console.log('准备提交的数据:', submitData);
- // 发送请求到API
- this.submitData(submitData);
- },
-
- /**
- * 提交数据
- * @param {object} data - 要提交的数据
- */
- submitData(data) {
- // 设置独立的提交加载状态标志
- this.isSubmitting = true;
-
- uni.request({
- url: API.postListAdd,
- method: 'POST',
- data: data,
- timeout: 10000, // 设置10秒超时
- method: 'POST',
- data: data,
- timeout: 10000, // 设置10秒超时
- success: (res) => {
- console.log('API响应:', res);
-
- // 先隐藏加载提示
- if (this.isSubmitting) {
- uni.hideLoading();
- this.isSubmitting = false;
- }
-
- if (res.data) {
- console.log('服务器返回数据:', res.data);
- // 显示结果提示
- const message = res.data.msg || '提交成功';
- console.log('显示提示:', message);
-
- uni.showToast({
- title: message,
- icon: res.data.code === 0 ? 'success' : 'none',
- duration: 3000
- });
-
- if (res.data.code === 0) {
- // 添加本地记录
- this.records.unshift({
- rfid: data.rfid,
- building: data.buildingName,
- roomName: data.roomName,
- pen: data.penNo,
- userId: data.userId,
- time: new Date().toLocaleTimeString()
- });
- this.resetForm();
- }
- } else {
- console.error('提交失败: 响应数据格式不正确', res);
- // 显示错误提示
- uni.showToast({
- title: '提交失败,请重试',
- icon: 'none',
- duration: 3000
- });
- }
- },
- fail: (err) => {
- console.error('网络请求失败:', err);
- // 隐藏加载提示并显示错误提示
- if (this.isSubmitting) {
- uni.hideLoading();
- this.isSubmitting = false;
- }
- uni.showToast({
- title: '网络请求失败,请重试',
- icon: 'none',
- duration: 3000
- });
- // 接口失败不影响耳标扫描,无需额外处理
- },
- complete: () => {
- // 确保加载提示被隐藏
- setTimeout(() => {
- if (this.isSubmitting) {
- uni.hideLoading();
- this.isSubmitting = false;
- }
- }, 2000);
- }
- });
- },
-
- // 重置
- resetForm() {
- this.form.earId = ''
- this.form.buildingName = ''
- this.form.roomName = ''
- this.form.penNo = ''
- this.form.status = 'healthy'
- this.form.note = ''
- // 清除扫描结果列表
- this.dataList = []
- },
-
- // 选择
- onBuildingChange(e) {
- const buildingName = this.buildingList[e.detail.value];
- this.form.buildingName = buildingName;
- // 根据选择的栋舍加载房间列表
- if (buildingName) {
- this.fetchRoomList(buildingName);
- } else {
- // 清空房间和栏位列表
- this.roomList = [];
- this.Fieldnumber = [];
- this.form.roomName = '';
- this.form.penNo = '';
- }
- },
-
- onRoomChange(e) {
- const roomName = this.roomList[e.detail.value];
- this.form.roomName = roomName;
- // 根据选择的房间加载栏位列表
- if (roomName) {
- this.fetchFieldList(roomName);
- } else {
- // 清空栏位列表
- this.Fieldnumber = [];
- this.form.penNo = '';
- }
- },
-
- onFieldChange(e) {
- this.form.penNo = this.Fieldnumber[e.detail.value]
- },
-
- // 其他
- logout() {
- // 清空数据
- this.resetForm();
-
- // 释放设备资源
- this.releaseDevice();
-
- // 清除用户信息
- const app = getApp();
- app.globalData.userInfo = null;
- uni.removeStorageSync('user_info');
-
- // 跳转到登录页面
- uni.redirectTo({ url: '/pages/login/login' });
-
- uni.showToast({ title: '已退出登录', icon: 'none' })
- },
-
- toggleServerConfig() {
- this.showServerConfig = !this.showServerConfig
- }
- }
- }
- </script>
- <style>
- .page { display: flex; flex-direction: column; height: 100vh; width: 100%; box-sizing: border-box; padding: 0 10rpx; }
- .data-item { display: flex; justify-content: space-between; align-items: center; padding: 10rpx; margin-bottom: 10rpx; background-color: #f5f5f5; border-radius: 5rpx; }
- .picker { padding: 5rpx 10rpx;
- margin-left: 20px;
- background-color: #fff; border: 1px solid #ddd; border-radius: 5rpx; min-width: 120rpx; text-align: center; }
- .arrow::after { content: '▾'; font-size: 12rpx; margin-left: 5rpx; }
- .delete-btn { background-color: #ff4d4f; color: white; font-size: 24rpx; padding: 5rpx 10rpx; min-width: 80rpx; line-height: normal; }
- .nav-bar {
- display: flex; justify-content: space-between; align-items: center;
- background: #007aff; color: white; padding: 15rpx 30rpx;
- }
- .nav-title { font-weight: bold; font-size: 32rpx; }
- .nav-btn { background: transparent; border: none; font-size: 32rpx; }
- .scroll-area { flex: 1; }
- .section-title { display: flex; justify-content: space-between; margin-bottom: 20rpx; }
- .section-title .title { font-weight: bold; font-size: 30rpx; }
- .section-title .date { font-size: 24rpx; color: #666; }
- .rfid-card {
- background: #f6f6f6; border: 2rpx dashed #ccc;
- border-radius: 20rpx; text-align: center;
- }
- .scan-icon { font-size: 50rpx; color: #007aff; margin-bottom: 10rpx; display: block; }
- .btn-group { display: flex; gap: 20rpx; margin: 20rpx 0; flex-wrap: wrap; justify-content: center; }
- .scan-btn { background: #007aff; color: white; flex: 1; min-width: 200rpx; padding: 20rpx; border-radius: 12rpx; font-size: 28rpx; }
- .manual-btn { background: #ccc; color: black; flex: 1; min-width: 200rpx; padding: 20rpx; border-radius: 12rpx; font-size: 28rpx; }
- .form-item { margin-bottom: 20rpx; }
- .form-item .label { display: block; font-weight: bold; margin-bottom: 10rpx; }
- .input-box { width: 100%; border: 1rpx solid #ccc; border-radius: 10rpx; background: #fff; }
- .status-options { display: flex; gap: 20rpx; margin-top: 10rpx; }
- .status-option {
- flex: 1; border: 1rpx solid #ccc; border-radius: 10rpx;
- padding: 20rpx; text-align: center; color: #666;
- }
- .status-option.active { border-color: #007aff; color: #007aff; }
- .sv {
- height: 300rpx;
- margin-top: 20rpx;
- }
- </style>
|