<template>
	<view>
		<u-navbar :is-back="true" title=' ' title-color="#000" :border-bottom="false" :background="bgc"
			id="navbar">
		</u-navbar>
		<view class="page">
			<!-- 有搜索到设备 -->
			<view class="you" v-if="flags">
				<view class="topone">
					<image src="https://api.ccttiot.com/smartmeter/img/static/ubrPcpGQEXTadkBa1gKh" mode=""></image>
					扫描到以下设备，点击添加！
				</view>
				<view class="toptwo">
					如未找到想添加的设备，点击重新搜索
				</view>
				<view class="list">
					<view class="list_item" v-for="(item,index) in sortedJiaohuaqi" :key="index" :class="{ show: item.show }">
						<image :src="item.modelPicture" mode=""></image>
						<view class="cen">
							<view class="name" style="color: #ccc;" v-if="item.userId && item.userId != userid">
								{{item.modelName == undefined  ? '' : item.modelName}}
							</view>
							<view class="name" v-else>
								{{item.modelName == undefined  ? '' : item.modelName}}
							</view>
							<view class="devmac">
								MAC：{{item.mac == undefined ? item.name.slice(-12) : item.mac}}
							</view>
							<view class="devmac" style="display: flex;align-items: center;">
							  信号：
							  <!-- 最强信号：-50 及以上（-1 到 -50） -->
							  <image 
							    style="width: 30rpx;height: 20rpx;" 
							    v-if="item.ssid >= -50" 
							    src="https://api.ccttiot.com/smartmeter/img/static/ueeDGk0mVPLUd0DHxrWj" 
							    mode=""
							  ></image>
							  
							  <!-- 较强信号：-51 到 -60 -->
							  <image 
							    style="width: 30rpx;height: 20rpx;" 
							    v-else-if="item.ssid >= -60 && item.ssid <= -51" 
							    src="https://api.ccttiot.com/smartmeter/img/static/uM1obZ76ittglMRKXWLq" 
							    mode=""
							  ></image>
							  
							  <!-- 中等信号：-61 到 -70 -->
							  <image 
							    style="width: 30rpx;height: 20rpx;" 
							    v-else-if="item.ssid >= -70 && item.ssid <= -61" 
							    src="https://api.ccttiot.com/smartmeter/img/static/ujO9AZIuUSQvHcCBKqc4" 
							    mode=""
							  ></image>
							  
							  <!-- 较弱信号：-71 到 -80 -->
							  <image 
							    style="width: 30rpx;height: 20rpx;" 
							    v-else-if="item.ssid >= -80 && item.ssid <= -71" 
							    src="https://api.ccttiot.com/smartmeter/img/static/uCSlbXZvho808NMCkIQP" 
							    mode=""
							  ></image>
							  
							  <!-- 最弱信号：-81 到 -100 -->
							  <image 
							    style="width: 30rpx;height: 20rpx;" 
							    v-else-if="item.ssid >= -100 && item.ssid <= -81" 
							    src="https://api.ccttiot.com/smartmeter/img/static/u8bj3ZNi8Zssunk69HWc" 
							    mode=""
							  ></image>
							</view>
						</view>
						<!-- 绑定状态：优先显示“已添加”，未查询完显示“查询中”，其余显示“添加” -->
						<view class="add" style="color: #ccc;border: 1px solid #ccc;" v-if="item.isBound === true">
							已添加
						</view>
						<view class="add" style="color: #ccc;border: 1px solid #ccc;" v-else-if="item.hasCheckBind !== true">
							查询中…
						</view>
						<view class="add" @click="btnadd(item)" v-else>
							添加
						</view> 
					</view>
				</view>
			</view>

			<!-- 未搜索到设备 -->
			<view class="wei" v-else>
				<image src="https://api.ccttiot.com/smartmeter/img/static/uQ4g6A27FGtF34ebOtea" mode=""></image>
				<view class="sbname">
					{{ lastBleIssue && lastBleIssue.title ? lastBleIssue.title : '搜索附近的设备失败' }}
				</view>
				<view class="sbwz">
					<text v-if="lastBleIssue && lastBleIssue.kind === 'WECHAT_AUTH'">检测到「微信未授权本小程序使用蓝牙」。{{ wechatBleAuthGuideBrief }}</text>
					<text v-else-if="lastBleIssue && lastBleIssue.kind === 'PHONE_BT_OFF'">检测到「手机系统蓝牙未开启」：请先到系统设置打开手机蓝牙，也可在弹窗中尝试「打开系统蓝牙设置」。</text>
					<text v-else-if="lastBleIssue && lastBleIssue.kind === 'LOCATION'">检测到「定位权限未开启」：部分安卓手机扫描蓝牙需开启定位。请在系统设置或弹窗中允许定位/附近设备权限后再试。</text>
					<text v-else-if="lastBleIssue && lastBleIssue.kind === 'GENERIC_INIT_FAIL'">「蓝牙初始化失败」在不少机型上是系统/微信偶发或权限组合问题，不一定只是蓝牙没开。请按弹窗里的顺序逐项排查，并可复制错误信息给客服。</text>
					<text v-else>未扫描到设备。请先确认手机系统蓝牙已打开；若反复提示初始化失败，请按弹窗「排查步骤」操作，并尝试重启微信或手机。{{ wechatBleAuthGuideBrief }}</text>
				</view>
			</view>

			<!-- 点击搜索 -->
			<view class="btnss" v-if="showSearchButton" @click="handleSearch">
				重新搜索
			</view>
		</view>

		<!-- 自定义名称弹框 -->
		<u-popup v-model="showNameDialog" mode="center" border-radius="14" width="600rpx">
			<view class="custom-name-dialog">
				<view class="dialog-title">设备名称</view>
				<u-input v-model="customDeviceName" placeholder="请输入设备名称" />
				<view class="dialog-btns">
					<view class="btn cancel" @click="showNameDialog = false">取消</view>
					<view class="btn confirm" @click="confirmAddDevice">确定</view>
				</view>
			</view>
		</u-popup>

	</view>
</template>

<script>
	var xBlufi = require("@/components/blufi/xBlufi.js");
	export default {
		data() {
			return {
				bgc: {
					backgroundColor: "#fff",
				},
				active: 1,
				flag: true,
				devicesList: [],
				newlist:[],
				deviceId: '',
				name: '',
				mac: '',
				flags: true,
				userid: '',
				arr: [],
				jiaohuaqi: [],
				getpre: [],
				showNameDialog: false,
				customDeviceName: '',
				currentDevice: null,
				searchTimer: null,
				checkTimer: null,
				isSearching: false,
				searchTimeout: null,
				throttleTimer: null,
				lastSearchTime: 0,
				searchInterval: 800, // 搜索节流间隔（更细致）
				displayQueue: [], // 显示队列
				processingQueue: false, // 是否正在处理队列
				isBatchRequesting: false, // 是否有批量请求进行中
				rssiMap: {}, // 记录每个 mac 的信号强度
				discoveredDeviceMap: {}, // 扫描过程中累计发现的设备（按 mac 去重）
				discoverStartRetryCount: 0, // 扫描启动重试次数
				maxDiscoverStartRetry: 5, // 扫描启动最大重试次数（部分机型需多试几次）
				maxBluetoothInitRetry: 6, // openAdapter 重试次数
				scanRestartTimer: null, // 周期重启扫描，避免系统扫描卡死
				showSearchButton: true, // 重新搜索按钮是否显示
				searchButtonCooldownTimer: null, // 重新搜索按钮冷却计时器
				// 最近一次蓝牙失败：WECHAT_AUTH | PHONE_BT_OFF | LOCATION | GENERIC_INIT_FAIL | UNKNOWN
				lastBleIssue: null,
			}
		},
		computed: {
			sortedJiaohuaqi() {
				return this.jiaohuaqi.slice().sort((a, b) => {
					if (a.ssid === undefined) return 1;
					if (b.ssid === undefined) return -1;
					return Math.abs(a.ssid) - Math.abs(b.ssid);
				});
			},
			// 与弹窗一致：指微信小程序标题栏右侧「⋯」路径，避免说「左上角」与真实界面不符
			wechatBleAuthGuideBrief() {
				return '看本页最顶部：标题栏最右侧点「⋯」→ 弹出层里点「设置」→ 再点「蓝牙」并打开，然后点下面「重新搜索」。'
			}
		},
		// 分享到好友（会话）
		onShareAppMessage: function() {
			return {
				title: '绿小能',
				path: '/pages/index/index'
			}
		},

		// 分享到朋友圈  
		onShareTimeline: function() {
			return {
				title: '绿小能',
				query: '',
				path: '/pages/index/index'
			}
		},
		onLoad() {
			this.getmodel()
			this.getinfo()
			// 预热蓝牙适配器，减少首次进入时初始化失败
			this.ensureBluetoothReady(false)
		},
		onShow() {
			this.startSearch()
		},
		onHide() {
			this.stopSearch()
		},
		onUnload() {
			if (this.searchButtonCooldownTimer) {
				clearTimeout(this.searchButtonCooldownTimer)
				this.searchButtonCooldownTimer = null
			}
			this.stopSearch()
		},
		methods: {
			sleep(ms) {
				return new Promise(resolve => setTimeout(resolve, ms))
			},
			normalizeMac(text) {
				const raw = String(text || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase()
				if (raw.length < 12) return ''
				return raw.slice(-12)
			},
			parseBleIdentity(device) {
				const prefixes = ['WATER', 'SMSJ:', 'DATER', 'TATER', 'SATER']
				const candidates = [device.localName, device.name, device.deviceId]
				for (let i = 0; i < candidates.length; i++) {
					const value = String(candidates[i] || '')
					if (!value) continue
					const upper = value.toUpperCase()
					const pre = prefixes.find(p => upper.startsWith(p))
					const mac = this.normalizeMac(value)
					if (pre && mac) {
						return { pre, mac }
					}
				}
				return null
			},
			// 是否在微信内被关闭「蓝牙」授权（与手机系统蓝牙开关不同）
			isWechatBluetoothUnauthorized() {
				if (typeof uni.getAppAuthorizeSetting !== 'function') return false
				try {
					const as = uni.getAppAuthorizeSetting()
					return !!(as && typeof as.bluetoothAuthorized === 'boolean' && as.bluetoothAuthorized === false)
				} catch (e) {
					return false
				}
			},
			// 根据 uni/wx 返回的 err 归类（各厂商 errMsg 差异大，无法识别时走 GENERIC_INIT_FAIL）
			classifyBluetoothError(err) {
				if (!err) return { kind: 'UNKNOWN' }
				const msg = String(err.errMsg || err.message || '').toLowerCase()
				const code = err.errCode
				if (
					msg.includes('auth deny') ||
					msg.includes('authoriz') ||
					(msg.includes('permission') && (msg.includes('bluetooth') || msg.includes('nearby') || msg.includes('device'))) ||
					msg.includes('not authorized') ||
					msg.includes('system permission denied') ||
					msg.includes('user denied') ||
					msg.includes('privacy') && msg.includes('bluetooth')
				) {
					return { kind: 'WECHAT_AUTH' }
				}
				if (
					msg.includes('location') ||
					msg.includes('定位') ||
					msg.includes('location services') ||
					msg.includes('need location') ||
					msg.includes('gps') && msg.includes('enable')
				) {
					return { kind: 'LOCATION' }
				}
				if (
					code === 10001 ||
					msg.includes('not available') ||
					msg.includes('unavailable') ||
					msg.includes('powered off') ||
					msg.includes('bluetooth is closed') ||
					msg.includes('蓝牙未打开') ||
					msg.includes('请打开蓝牙') ||
					msg.includes('蓝牙不可用') ||
					msg.includes('adapter not') ||
					msg.includes('bluetooth off') ||
					msg.includes('turn on bluetooth')
				) {
					return { kind: 'PHONE_BT_OFF' }
				}
				if (
					msg.includes('timeout') ||
					msg.includes('time out') ||
					msg.includes('超时') ||
					msg.includes('busy') ||
					msg.includes('正在使用') ||
					msg.includes('already') && msg.includes('open')
				) {
					return { kind: 'GENERIC_INIT_FAIL' }
				}
				return { kind: 'UNKNOWN' }
			},
			formatBleDiagnostic(err) {
				if (!err) return '（无详细错误码）'
				const code = err.errCode != null ? String(err.errCode) : ''
				const msg = String(err.errMsg || err.message || '').trim()
				const line = [code && `errCode=${code}`, msg && `errMsg=${msg}`].filter(Boolean).join('\n')
				return line || '（无详细错误码）'
			},
			setBleIssueFromKind(kind, extra = {}) {
				const titles = {
					WECHAT_AUTH: '微信未授权蓝牙',
					PHONE_BT_OFF: '手机蓝牙未开启',
					LOCATION: '需开启定位权限',
					GENERIC_INIT_FAIL: '蓝牙初始化失败',
					UNKNOWN: '蓝牙不可用'
				}
				this.lastBleIssue = { kind, title: titles[kind] || titles.UNKNOWN, ...extra }
			},
			// 弹窗说明 + 可跳转：微信授权页 / 系统蓝牙设置；无法归类时用「综合排查」避免误导
			showBluetoothIssueModal(parsed, rawErr) {
				const kindRaw = parsed && parsed.kind ? parsed.kind : 'UNKNOWN'
				const diag = this.formatBleDiagnostic(rawErr)
				const mergeInit = kindRaw === 'UNKNOWN' || kindRaw === 'GENERIC_INIT_FAIL'
				const kindUi = mergeInit ? 'GENERIC_INIT_FAIL' : kindRaw
				this.setBleIssueFromKind(kindUi, { diagnostic: diag })
				if (kindRaw === 'WECHAT_AUTH') {
					const content = [
						'【原因】当前是「微信不让本小程序用蓝牙」，和手机「设置→蓝牙」总开关不是同一项。',
						'',
						'【点哪里】看屏幕最上方小程序那一横条：',
						'· 最左边一般是「‹」返回聊天，不要点它。',
						'· 中间是本页标题。',
						'· 最右边有一小块区域，里面有「⋯」或「···」（更多），和「关小程序」的圆圈在同一行——点这个「⋯」。',
						'',
						'【接下来】',
						'① 点「⋯」后会从下往上弹出一块面板；',
						'② 在面板里点「设置」；',
						'③ 进入后找到「蓝牙」这一行，把开关打开；',
						'④ 若没看到「蓝牙」，在「设置」页里往上滑找一下。',
						'',
						'做完后回到本页，点「重新搜索」。',
						'',
						'（若下方按钮能打开微信权限页，也可作辅助；以「⋯→设置→蓝牙」为准。）'
					].join('\n')
					uni.showModal({
						title: '请打开：小程序的蓝牙权限',
						content,
						confirmText: '打开权限页(辅助)',
						cancelText: '我先按步骤点',
						success: (res) => {
							if (res.confirm && typeof uni.openAppAuthorizeSetting === 'function') {
								uni.openAppAuthorizeSetting({})
							}
						}
					})
					return
				}
				if (kindRaw === 'PHONE_BT_OFF') {
					uni.showModal({
						title: '手机蓝牙未开启',
						content: '系统蓝牙处于关闭状态，微信无法扫描设备。\n\n请先打开手机「设置」→「蓝牙」并开启；也可点「打开系统蓝牙设置」尝试跳转（若系统支持）。',
						confirmText: '打开系统蓝牙设置',
						cancelText: '取消',
						success: (res) => {
							if (res.confirm && typeof uni.openSystemBluetoothSetting === 'function') {
								uni.openSystemBluetoothSetting({
									fail: () => {
										uni.showToast({ title: '请手动在系统设置中打开蓝牙', icon: 'none', duration: 2500 })
									}
								})
							}
						}
					})
					return
				}
				if (kindRaw === 'LOCATION') {
					uni.showModal({
						title: '需开启定位权限',
						content: '部分安卓机型扫描蓝牙需开启「定位」或「附近设备」权限。\n\n请点「去设置」，在权限页中允许定位；或到系统设置 → 应用 → 微信 → 权限中开启。',
						confirmText: '去设置',
						cancelText: '取消',
						success: (res) => {
							if (res.confirm && typeof uni.openAppAuthorizeSetting === 'function') {
								uni.openAppAuthorizeSetting({})
							}
						}
					})
					return
				}
				// UNKNOWN / GENERIC_INIT_FAIL：不猜测单一原因，给完整排查 + 可复制诊断
				let plat = ''
				try {
					plat = String((uni.getSystemInfoSync() || {}).platform || '').toLowerCase()
				} catch (e) {}
				const androidLine = plat === 'android'
					? '· 【安卓】系统「定位」总开关建议打开；并在 设置→应用→微信→权限 里允许「附近设备/蓝牙/定位」相关项。\n'
					: ''
				const content = [
					'不少机型会反复提示「蓝牙初始化失败」，常见原因包括：系统蓝牙栈卡住、微信权限未开全、安卓定位限制等，不一定是硬件损坏。',
					'',
					'请按顺序尝试（每完成一两步即可点「重新搜索」试一次）：',
					'① 手机系统设置里关闭蓝牙，等 2～3 秒再打开',
					'② 微信：标题栏右侧「⋯」→「设置」→「蓝牙」→ 允许本小程序',
					androidLine + '③ 从多任务里划掉微信，重新打开后再进本页',
					'④ 仍失败可重启手机，或将微信更新到最新版本',
					'⑤ 若偶发成功、偶发失败，多为系统资源占用，可多试几次',
					'',
					'【本机返回】复制后发给客服更易排查：',
					diag
				].filter(Boolean).join('\n')
				const safeContent = content.length > 1750 ? content.slice(0, 1747) + '…' : content
				uni.showModal({
					title: '蓝牙初始化失败',
					content: safeContent,
					confirmText: '复制诊断信息',
					cancelText: '关闭',
					success: (res) => {
						if (res.confirm) {
							const clip = `绿小能-蓝牙诊断\n${diag}\nplatform=${plat || 'unknown'}\nraw=${String((rawErr && rawErr.errMsg) || '').slice(0, 400)}`
							uni.setClipboardData({
								data: clip.slice(0, 800),
								success: () => uni.showToast({ title: '已复制', icon: 'none' })
							})
						}
					}
				})
			},
			async ensureBluetoothReady(showErrorModal = true) {
				if (this.isWechatBluetoothUnauthorized()) {
					if (showErrorModal) this.showBluetoothIssueModal({ kind: 'WECHAT_AUTH' }, {})
					return false
				}
				let lastErr = null
				let lastStateUnavailable = false
				for (let i = 0; i < this.maxBluetoothInitRetry; i++) {
					try {
						await new Promise((resolve, reject) => {
							uni.openBluetoothAdapter({
								success: resolve,
								fail: reject
							})
						})
						const state = await new Promise((resolve, reject) => {
							uni.getBluetoothAdapterState({
								success: resolve,
								fail: reject
							})
						})
						if (state && state.available) {
							if (showErrorModal) this.lastBleIssue = null
							return true
						}
						lastStateUnavailable = true
						lastErr = { errMsg: 'getBluetoothAdapterState: unavailable', errCode: 10001 }
					} catch (err) {
						lastErr = err
					}
					try {
						uni.closeBluetoothAdapter({})
					} catch (e) {}
					await this.sleep(350 + Math.min(i, 5) * 200)
				}
				if (showErrorModal) {
					const parsed = this.classifyBluetoothError(lastErr)
					// 适配器 unavailable 也可能是权限/系统卡住，不再强行归类为「仅手机蓝牙未开」
					const hint = lastStateUnavailable && parsed.kind === 'UNKNOWN'
						? { errMsg: `${String(lastErr && lastErr.errMsg || '')} (adapter state: unavailable)`.trim(), errCode: lastErr && lastErr.errCode }
						: lastErr
					this.showBluetoothIssueModal(parsed, hint)
				}
				return false
			},
			startDiscoverWithRetry() {
				xBlufi.notifyStartDiscoverBle({ isStart: true })
			},
			// 获取用户信息
			getinfo() {
				this.$u.get(`/system/user/profile`).then((res) => {
					if (res.code == 200) {
						this.userid = res.data.userId
						uni.setStorageSync('user',res.data)
						uni.setStorageSync('userId',res.data.userId)
					} else if (res.code == 401) {
						uni.showModal({
							title: '提示',
							content: '您还未登录，是否前去登录？',
							success: (r) => {
								if (r.confirm) {
									uni.navigateTo({
										url: '/pages/login/login'
									})
								}
							}
						})
					}								
				})
			},
			
			// 点击添加按钮
			btnadd(e) {
				this.currentDevice = e;
				this.customDeviceName = e.modelName || '未知设备';
				this.showNameDialog = true;
			},
			// 确认添加设备
			confirmAddDevice() {
				if (!this.customDeviceName.trim()) {
					uni.showToast({
						title: '请输入设备名称',
						icon: 'none'
					});
					return;
				}
				
				let mac = this.currentDevice.name.slice(-12);	
				let data = {
					mac: mac,
					// userId: this.userid,
					pre: this.currentDevice.pre,
					deviceName: this.customDeviceName
				}
				console.log(data,'参数');
				this.$u.post(`/app/device/bindDeviceByBlueTooth`, data).then((res) => {
					if (res.code == 200) {
						uni.showToast({
							title: '绑定成功',
							icon: 'none',
							duration: 3000
						})
						this.showNameDialog = false;
						setTimeout(() => {
							uni.navigateBack()
						}, 2000)
						// let datas = {
						// 	mac:mac
						// }
						// this.$u.post(`/app/device/bindDevice`, datas).then(resp =>{
						// 	if(resp.code == 200){
						// 		uni.showToast({
						// 			title:'绑定成功',
						// 			icon: 'none',
						// 			duration: 3000
						// 		})
						// 		this.showNameDialog = false;
						// 		setTimeout(() => {
						// 			uni.navigateBack()
						// 		}, 2000)
						// 	}else{
						// 		uni.showToast({
						// 			title: resp.msg,
						// 			icon: 'none',
						// 			duration: 3000
						// 		})
						// 	}
						// })
					} else {
						console.log(res,'报错');
						uni.showToast({
							title: res.msg,
							icon: 'none',
							duration: 3000
						})
					}
				})
			},
			getmodel() {
				this.$u.get(`/app/getAllModelList`).then(res => {
					if (res.code == 200) {
						this.getpre = res.data
					} 
				})
			},
			
			getpipei(pre) {
				// 添加默认返回值防止undefined
				return this.getpre.find(item => item.pre == pre) || {
					modelName: '未知型号',
					picture: ''
				};
			},
			
			// 开始搜索
			async startSearch() {
				if (this.isSearching) return
				const ready = await this.ensureBluetoothReady(true)
				if (!ready) {
					this.flags = false
					return
				}
				this.lastBleIssue = null
				this.isSearching = true
				this.jiaohuaqi = []
				this.displayQueue = []
				this.processingQueue = false
				this.discoveredDeviceMap = {}
				this.rssiMap = {}
				this.arr = []
				this.lastSearchTime = 0
				this.discoverStartRetryCount = 0
				this.flag = false
				
				// 开始蓝牙搜索
				xBlufi.listenDeviceMsgEvent(true, this.funListenDeviceMsgEvent)
				this.startDiscoverWithRetry()
				// 周期重启扫描，降低系统层扫描卡住导致搜不到的概率
				this.scanRestartTimer = setInterval(() => {
					if (!this.isSearching) return
					xBlufi.notifyStartDiscoverBle({ isStart: false })
					setTimeout(() => {
						if (!this.isSearching) return
						this.startDiscoverWithRetry()
					}, 250)
				}, 8000)
				
				// 持续慢速扫描，不自动停止；仅在离开页面或手动重新搜索时停止
			},
			
			// 停止搜索
			stopSearch() {
				this.isSearching = false
				if (this.checkTimer) {
					clearInterval(this.checkTimer)
					this.checkTimer = null
				}
				if (this.searchTimeout) {
					clearTimeout(this.searchTimeout)
					this.searchTimeout = null
				}
				if (this.scanRestartTimer) {
					clearInterval(this.scanRestartTimer)
					this.scanRestartTimer = null
				}
				if (this.throttleTimer) {
					clearTimeout(this.throttleTimer)
					this.throttleTimer = null
				}
				xBlufi.notifyStartDiscoverBle({ 'isStart': false })
				xBlufi.listenDeviceMsgEvent(false, this.funListenDeviceMsgEvent)
				this.flag = true
			},
			
			// 处理显示队列
			processDisplayQueue() {
				if (this.processingQueue || this.displayQueue.length === 0) return
				
				this.processingQueue = true
				const device = this.displayQueue.shift()
				
				// 检查设备是否已存在
				if (!this.jiaohuaqi.some(item => item.name === device.name)) {
					this.jiaohuaqi.push(device)
				}
				
				// 延迟处理下一个设备
				setTimeout(() => {
					this.processingQueue = false
					this.processDisplayQueue()
				}, 700) // 每个设备显示间隔700ms，慢速渐进展示
			},
			
			// 添加设备到显示队列
			addToDisplayQueue(device) {
				this.displayQueue.push(device)
				if (!this.processingQueue) {
					this.processDisplayQueue()
				}
			},
			// 扫描到设备后先直接展示，再由接口补充绑定状态
			upsertScannedDevice(item, parsed) {
				const mac = parsed.mac
				const pre = parsed.pre || ''
				const rssi = item && item.RSSI != null ? item.RSSI : undefined
				const matched = this.getpipei(pre)
				const exist = this.jiaohuaqi.find(dev => dev.mac === mac)
				if (exist) {
					if (pre) exist.pre = pre
					if (!exist.modelName) exist.modelName = matched.modelName
					if (!exist.modelPicture) exist.modelPicture = matched.picture
					if (rssi !== undefined) exist.ssid = rssi
					return
				}
				this.addToDisplayQueue({
					name: mac,
					mac,
					pre,
					modelName: matched.modelName || '未知型号',
					modelPicture: matched.picture || '',
					userId: null,
					isBound: false,
					hasCheckBind: false,
					ssid: rssi
				})
			},
			
			// 更新设备列表（只展示接口返回的数据，累加去重）
			updateDeviceList(existList) {
				if (!Array.isArray(existList) || existList.length === 0) return
				
				existList.forEach(item => {
					// 后端返回的设备信息中 mac 为唯一标识
					const mac = item.mac
					if (!mac) return
					
					// 计算信号强度：优先取本地扫描到的 rssiMap
					const ssid = this.rssiMap[mac] != null ? this.rssiMap[mac] : undefined
					
					// 设备是否已绑定
					const isBound = item.userId != null
					
					// 如果列表中已存在该设备：更新状态
					const exist = this.jiaohuaqi.find(dev => dev.mac === mac)
					if (exist) {
						exist.userId = item.userId
						exist.isBound = isBound
						exist.hasCheckBind = true
						exist.modelName = item.modelName || exist.modelName
						exist.modelPicture = item.modelPicture || exist.modelPicture
						if (ssid !== undefined) {
							exist.ssid = ssid
						}
					} else {
						// 不存在则新增（只用接口返回的数据来展示）
						const pre = item.pre || ''
						const matched = this.getpipei(pre)
						const newDevice = {
							name: mac,
							mac: mac,
							pre: pre,
							modelName: item.modelName || matched.modelName,
							modelPicture: item.modelPicture || matched.picture,
							userId: item.userId,
							isBound: isBound,
							hasCheckBind: true,
							ssid: ssid
						}
						this.addToDisplayQueue(newDevice)
					}
				})
			},
			
			// 获取附近蓝牙设备列表
			funListenDeviceMsgEvent: function(options) {
				switch (options.type) {
					case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS:
						if (options.result) {
							const now = Date.now()
							if (now - this.lastSearchTime < this.searchInterval) {
								return
							}
							this.lastSearchTime = now
							
							let hasNewDevice = false
							const deviceData = Array.isArray(options.data) ? options.data : []
							this.devicesList = deviceData
							// 同时兼容 localName/name/deviceId，累计去重后再批量查询
							deviceData.forEach(item => {
								const parsed = this.parseBleIdentity(item || {})
								if (!parsed || !parsed.mac) return
								this.upsertScannedDevice(item, parsed)
								const old = this.discoveredDeviceMap[parsed.mac]
								if (!old) {
									hasNewDevice = true
									this.discoveredDeviceMap[parsed.mac] = {
										pre: parsed.pre,
										mac: parsed.mac
									}
								} else if (!old.pre && parsed.pre) {
									this.discoveredDeviceMap[parsed.mac].pre = parsed.pre
								}
								if (item.RSSI != null) {
									const oldRssi = this.rssiMap[parsed.mac]
									this.rssiMap[parsed.mac] = oldRssi == null ? item.RSSI : Math.max(oldRssi, item.RSSI)
								}
							})
							this.arr = Object.keys(this.discoveredDeviceMap).map(mac => this.discoveredDeviceMap[mac])
							if (hasNewDevice) {
								this.flags = true
							}
							
							// 使用防抖 + 请求中的标记，避免一次性触发很多请求
							if (this.throttleTimer) {
								clearTimeout(this.throttleTimer)
							}
							// 500ms 内多次回调只发一个请求
							this.throttleTimer = setTimeout(() => {
								if (!this.devicesList.length || !this.arr.length) return
								// 如果上一次请求还在进行中，则本次略过，等待下一轮回调再发
								if (this.isBatchRequesting) return
								this.isBatchRequesting = true
								const payload = this.arr.slice(0) // 拷贝一份当前 mac 列表
								this.$u.post(`/app/device/batchInsert`, payload).then(res => {
									if (res.code === 200 && Array.isArray(res.data)) {
										this.updateDeviceList(res.data)
									}
								}).finally(() => {
									this.isBatchRequesting = false
								})
							}, 500)
						}
						break
					case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START:
						if (!options.result) {
							// 扫描启动失败自动重试，减少偶发初始化失败
							if (this.isSearching && this.discoverStartRetryCount < this.maxDiscoverStartRetry) {
								this.discoverStartRetryCount += 1
								setTimeout(async () => {
									if (!this.isSearching) return
									const ready = await this.ensureBluetoothReady(false)
									if (!ready) return
									this.startDiscoverWithRetry()
								}, 700)
								return
							}
							const failData = options.data || {}
							let parsed = this.classifyBluetoothError(failData)
							if (this.isWechatBluetoothUnauthorized()) {
								parsed = { kind: 'WECHAT_AUTH' }
							}
							if (parsed.kind === 'UNKNOWN') {
								const msg = String(failData.errMsg || '').toLowerCase()
								if (msg.includes('startbluetoothdevicesdiscovery') && (msg.includes('10001') || msg.includes('not available'))) {
									parsed = { kind: 'PHONE_BT_OFF' }
								}
							}
							this.showBluetoothIssueModal(parsed, failData)
							this.flags = this.jiaohuaqi.length > 0
							return
						}
						this.discoverStartRetryCount = 0
						break
				}
			},
			
			btnss() {
				// 每次点击后隐藏 6 秒，避免连续重复点击
				this.showSearchButton = false
				if (this.searchButtonCooldownTimer) {
					clearTimeout(this.searchButtonCooldownTimer)
				}
				this.searchButtonCooldownTimer = setTimeout(() => {
					this.showSearchButton = true
					this.searchButtonCooldownTimer = null
				}, 6000)
				this.stopSearch()
				this.jiaohuaqi = []
				this.displayQueue = []
				this.processingQueue = false
				this.discoveredDeviceMap = {}
				this.rssiMap = {}
				this.startSearch()
			},
			// 处理搜索按钮点击
			handleSearch() {
				this.btnss()
			},
		}
	}
</script>

<style lang="less">
::v-deep .u-input__input{
	border: 1px solid #ccc;
	border-radius: 10rpx;
	padding-left: 10rpx;
	box-sizing: border-box;
}
	::v-deep .u-title {
		margin-bottom: 22rpx;
	}

	::v-deep .uicon-nav-back {
		margin-bottom: 22rpx;
	}

	.page {
		padding-bottom: 300rpx;
		box-sizing: border-box;
	}

	.wei {
		text-align: center;

		image {
			width: 380rpx;
			height: 394rpx;
		}

		.sbname {
			font-size: 40rpx;
			color: #3D3D3D;
			margin-top: 80rpx;
			width: 100%;
			text-align: center;
		}

		.sbwz {
			font-size: 28rpx;
			color: #737B80;
			margin-top: 24rpx;
			width: 100%;
			text-align: left;
			padding: 0 40rpx;
			box-sizing: border-box;
			line-height: 1.55;
		}
	}

	.btnss {
		width: 512rpx;
		height: 92rpx;
		background: #48893B;
		border-radius: 46rpx 46rpx 46rpx 46rpx;
		border-radius: 50rpx;
		text-align: center;
		line-height: 92rpx;
		font-weight: 600;
		font-size: 40rpx;
		color: #FFFFFF;
		position: fixed;
		left: 50%;
		transform: translateX(-50%);
		bottom: 106rpx;
		transition: all 0.3s ease;
	}

	.list {
		width: 100%;
		border-radius: 20rpx;
		margin: auto;
		margin-top: 72rpx;
		will-change: transform; // 优化动画性能

		.list_item {
			margin-top: 18rpx;
			display: flex;
			justify-content: space-between;
			align-items: center;
			width: 100%;
			height: 152rpx;
			background: #FFFFFF;
			border-radius: 20rpx;
			box-shadow: 0rpx 10rpx 64rpx 0rpx rgba(0, 0, 0, 0.08);
			padding: 18rpx 30rpx;
			box-sizing: border-box;
			animation: slideIn 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
			opacity: 0;
			transform: translateX(-100%);
			will-change: transform, opacity; // 优化动画性能

			@keyframes slideIn {
				from {
					opacity: 0;
					transform: translateX(-100%);
				}
				to {
					opacity: 1;
					transform: translateX(0);
				}
			}

			image {
				width: 94rpx;
				height: 94rpx;
			}

			.cen {
				.name {
					font-size: 32rpx;
					color: #50565A;
				}

				.devmac {
					font-size: 24rpx;
					color: #BDBCBC;
					margin-top: 6rpx;
				}
			}

			.add {
				width: 108rpx;
				height: 60rpx;
				background: #FFFFFF;
				border: 3rpx solid #48893B;
				filter: blur(0px);
				border-radius: 20rpx;
				text-align: center;
				line-height: 60rpx;
				font-size: 28rpx;
				color: #48893B;
			}
		}
	}

	page {
		width: 100%;
		padding: 20rpx 64rpx;
		box-sizing: border-box;
		background-color: #fff;
	}

	.topone {
		font-size: 36rpx;
		color: #3D3D3D;
		display: flex;

		image {
			width: 48rpx;
			height: 48rpx;
		}
	}

	.toptwo {
		font-size: 28rpx;
		color: #737B7F;
		margin-top: 14rpx;
		width: 100%;
		padding-left: 48rpx;
		box-sizing: border-box;
	}

	.custom-name-dialog {
		background: #fff;
		padding: 40rpx;
		
		.dialog-title {
			font-size: 32rpx;
			color: #333;
			text-align: center;
			margin-bottom: 30rpx;
		}
		
		.dialog-btns {
			display: flex;
			justify-content: space-between;
			margin-top: 40rpx;
			
			.btn {
				width: 240rpx;
				height: 80rpx;
				line-height: 80rpx;
				text-align: center;
				border-radius: 40rpx;
				font-size: 28rpx;
				
				&.cancel {
					background: #f5f5f5;
					color: #666;
				}
				
				&.confirm {
					background: #48893B;
					color: #fff;
				}
			}
		}
	}
</style>