From 5f848e6c78368c5f01f731c8ff7095f5ccadf317 Mon Sep 17 00:00:00 2001 From: Ironsp <1522278303@qq.com> Date: Mon, 14 Sep 2026 10:42:40 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E9=85=8D=E7=BD=AEWiFi=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.config.ts | 1 + src/components/settings/wifiNetwork.vue | 71 +++++++- src/utils/wifi.ts | 217 +++++++++++++++++------- 3 files changed, 224 insertions(+), 65 deletions(-) diff --git a/manifest.config.ts b/manifest.config.ts index c8699b4..b07a012 100644 --- a/manifest.config.ts +++ b/manifest.config.ts @@ -39,6 +39,7 @@ export default defineManifestConfig({ '', '', '', + '', '', '', '', diff --git a/src/components/settings/wifiNetwork.vue b/src/components/settings/wifiNetwork.vue index 84462ee..ce8e1f4 100644 --- a/src/components/settings/wifiNetwork.vue +++ b/src/components/settings/wifiNetwork.vue @@ -2,12 +2,16 @@ import wifi from '@/utils/wifi' const wifiEnabled = ref(true) +const scanning = ref(false) -const networks = ref([ - { name: 'TP_LINK_5G', signal: '信号强度:强', ip: 'IP地址:192.168.1.100', connected: true }, - { name: 'TP_LINK_5G', signal: '信号强度:强', ip: 'IP地址:192.168.1.100', connected: false }, - { name: 'TP_LINK_5G', signal: '信号强度:强', ip: 'IP地址:192.168.1.100', connected: false }, -]) +interface NetworkItem { + name: string + signal: string + ip: string + connected: boolean +} + +const networks = ref([]) function handleDisconnect(name: string) { console.log('断开', name) @@ -18,9 +22,35 @@ function handleConnect(name: string) { } async function handleRefreshWifi() { + if (scanning.value) + return + + scanning.value = true console.log('点击了搜索WiFi') - const result = await wifi.scanWifi() - console.log('扫描到的WiFi:', result) + try { + const result = await wifi.scanWifi() + console.log('扫描到的WiFi:', result) + const connected = await wifi.getConnectedWifi().catch(() => null) + + networks.value = result.map(item => ({ + name: item.SSID, + signal: `信号强度:${wifi.getSignalDescription(item.signalStrength)}`, + ip: connected?.SSID === item.SSID ? '已连接' : (wifi.is5GHz(item.frequency || 0) ? '5GHz' : '2.4GHz'), + connected: connected?.SSID === item.SSID, + })) + + if (!result.length) { + uni.showToast({ title: '未发现可用网络', icon: 'none' }) + } + } + catch (err) { + const message = err instanceof Error ? err.message : '扫描失败' + console.error('WiFi 扫描失败:', err) + uni.showToast({ title: message, icon: 'none', duration: 3000 }) + } + finally { + scanning.value = false + } } @@ -50,7 +80,16 @@ async function handleRefreshWifi() { 可用网络 - + + + + 点击右上角刷新扫描附近 WiFi @@ -119,6 +158,22 @@ async function handleRefreshWifi() { color: #999; } +.refresh-icon.spinning { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.empty-tip { + font-size: 14px; + color: #999; + padding: 12px 0; + text-align: center; +} + .wifi-toggle { display: flex; justify-content: space-between; diff --git a/src/utils/wifi.ts b/src/utils/wifi.ts index 5c8323e..b6821f0 100644 --- a/src/utils/wifi.ts +++ b/src/utils/wifi.ts @@ -55,6 +55,9 @@ * - android.permission.ACCESS_WIFI_STATE * - android.permission.CHANGE_WIFI_STATE * - android.permission.ACCESS_NETWORK_STATE + * - android.permission.ACCESS_FINE_LOCATION + * - android.permission.ACCESS_COARSE_LOCATION + * - Android 6+ 还需运行时授予定位权限,且系统「位置信息」开关需开启 */ // ======================== 类型定义 ======================== @@ -111,6 +114,90 @@ function importClass(name: string) { return (plus.android as any).importClass(name) } +/** 读取 Java 对象字段(ScanResult.SSID 等是字段不是方法) */ +function getAttr(obj: any, name: string) { + try { + return (plus.android as any).getAttribute(obj, name) + } + catch { + return invoke(obj, name) + } +} + +/** 申请定位权限(Android 扫描 WiFi 必需) */ +function requestLocationPermission(): Promise { + return new Promise((resolve) => { + try { + ;(plus.android as any).requestPermissions( + [ + 'android.permission.ACCESS_FINE_LOCATION', + 'android.permission.ACCESS_COARSE_LOCATION', + ], + (result: { granted?: string[], deniedAlways?: string[], deniedPresent?: string[] }) => { + const granted = result?.granted || [] + const ok = granted.includes('android.permission.ACCESS_FINE_LOCATION') + || granted.includes('android.permission.ACCESS_COARSE_LOCATION') + console.log('定位权限申请结果:', result) + resolve(ok) + }, + (err: unknown) => { + console.error('定位权限申请失败:', err) + resolve(false) + }, + ) + } + catch (err) { + console.error('requestPermissions 不可用:', err) + resolve(false) + } + }) +} + +/** 系统定位开关是否开启 */ +function isLocationEnabled(): boolean { + try { + const LocationManager = importClass('android.location.LocationManager') + const Context = importClass('android.content.Context') + const activity = (plus.android as any).runtimeMainActivity() + const lm = activity.getSystemService(Context.LOCATION_SERVICE) + return !!( + invoke(lm, 'isProviderEnabled', LocationManager.GPS_PROVIDER) + || invoke(lm, 'isProviderEnabled', LocationManager.NETWORK_PROVIDER) + ) + } + catch { + return true + } +} + +/** 解析 getScanResults 列表 */ +function parseScanResults(wm: any): WifiInfo[] { + const results = invoke(wm, 'getScanResults') + if (!results) + return [] + + const size = invoke(results, 'size') || 0 + const networks: WifiInfo[] = [] + + for (let i = 0; i < size; i++) { + const r = invoke(results, 'get', i) + const ssid = String(getAttr(r, 'SSID') || '').replace(/"/g, '') + if (!ssid || ssid === '') + continue + + networks.push({ + SSID: ssid, + BSSID: String(getAttr(r, 'BSSID') || ''), + signalStrength: Number(getAttr(r, 'level') || 0), + securityType: String(getAttr(r, 'capabilities') || ''), + frequency: Number(getAttr(r, 'frequency') || 0), + connected: false, + }) + } + + return networks +} + // ======================== WiFi 工具 ======================== const wifi = { @@ -157,74 +244,89 @@ const wifi = { }, /** 扫描附近 WiFi 网络 */ - scanWifi(): Promise { + async scanWifi(): Promise { + const wm = getWifiManager() + const isEnabled = invoke(wm, 'isWifiEnabled') + console.log('WiFi 是否开启:', isEnabled) + + if (!isEnabled) + throw new Error('请先开启 WiFi') + + const hasPermission = await requestLocationPermission() + if (!hasPermission) + throw new Error('需要定位权限才能扫描 WiFi,请在系统设置中允许') + + if (!isLocationEnabled()) + throw new Error('请先打开系统「位置信息/定位」开关,否则无法扫描 WiFi') + return new Promise((resolve, reject) => { - try { - const wm = getWifiManager() + let settled = false + let receiver: any = null + let timer: ReturnType | undefined + const activity = (plus.android as any).runtimeMainActivity() - // 检查 WiFi 是否开启 - const isEnabled = invoke(wm, 'isWifiEnabled') - console.log('WiFi 是否开启:', isEnabled) - - if (!isEnabled) { - console.warn('WiFi 未开启,返回模拟数据') - // 模拟器或 WiFi 未开启时返回模拟数据 - resolve([ - { SSID: 'MOCK_WIFI_1', BSSID: 'AA:BB:CC:DD:EE:01', signalStrength: -45, frequency: 5180, connected: false }, - { SSID: 'MOCK_WIFI_2', BSSID: 'AA:BB:CC:DD:EE:02', signalStrength: -65, frequency: 2437, connected: false }, - { SSID: 'TestNetwork', BSSID: 'AA:BB:CC:DD:EE:03', signalStrength: -72, frequency: 2412, connected: false }, - ]) + const finish = (networks: WifiInfo[], err?: Error) => { + if (settled) return + settled = true + if (timer) + clearTimeout(timer) + try { + if (receiver) + activity.unregisterReceiver(receiver) } + catch {} + if (err) + reject(err) + else + resolve(networks) + } + + try { + const Filter = importClass('android.content.IntentFilter') + const Receiver = importClass('android.content.BroadcastReceiver') + const filter = new Filter() + filter.addAction('android.net.wifi.SCAN_RESULTS') + + receiver = new Receiver({ + onReceive(_ctx: any, intent: any) { + const updated = invoke(intent, 'getBooleanExtra', 'resultsUpdated', false) + console.log('收到 SCAN_RESULTS, resultsUpdated:', updated) + const networks = parseScanResults(wm) + console.log('解析后网络数量:', networks.length) + finish(networks) + }, + }) + activity.registerReceiver(receiver, filter) - // 启动扫描 const started = invoke(wm, 'startScan') console.log('WiFi 扫描启动:', started) - // 等待 3 秒确保扫描完成 - setTimeout(() => { - try { - const results = invoke(wm, 'getScanResults') - if (!results) { - console.log('扫描结果为 null') - resolve([]) - return - } - - const size = invoke(results, 'size') - console.log('扫描到网络数量:', size) - - const networks: WifiInfo[] = [] - - for (let i = 0; i < size; i++) { - const r = invoke(results, 'get', i) - const ssid = (invoke(r, 'SSID') || '').replace(/"/g, '') - if (!ssid || ssid === '') - continue - - networks.push({ - SSID: ssid, - BSSID: invoke(r, 'BSSID') || '', - signalStrength: invoke(r, 'level') || 0, - securityType: invoke(r, 'capabilities') || '', - frequency: invoke(r, 'frequency') || 0, - connected: false, - }) - } - - console.log('解析后网络数量:', networks.length) - resolve(networks) + // startScan 失败时仍尝试读取缓存结果(限流时常见) + if (!started) { + const cached = parseScanResults(wm) + console.log('startScan 失败,缓存网络数量:', cached.length) + if (cached.length > 0) { + finish(cached) + return } - catch (err) { - console.error('获取扫描结果失败:', err) - reject(err) - } - }, 3000) + finish([], new Error('WiFi 扫描被拒绝(可能被系统限流,请稍后再试)')) + return + } } catch (err) { - console.error('扫描启动失败:', err) - reject(err) + finish([], err instanceof Error ? err : new Error(String(err))) + return } + + timer = setTimeout(() => { + const networks = parseScanResults(wm) + console.log('扫描超时,当前网络数量:', networks.length) + if (networks.length > 0) + finish(networks) + else + finish([], new Error('扫描超时,未发现可用网络')) + }, 8000) }) }, @@ -453,7 +555,8 @@ const wifi = { const receiver = new Receiver({ onReceive() { - wifi.scanWifi().then(callback) + const wm = getWifiManager() + callback(parseScanResults(wm)) }, })