移动端app获取wifi步骤 用的uni

1manifest.json配置权限

说明:

  • WiFi:启用 uni.startWifi / uni.getWifiList / uni.onGetWifiList 等 API

  • GeolocationWi-Fi 扫描在 Android 8+ 必须依赖定位模块

✅ 这是正确且必须 的配置

⚠️ 没有 Geolocation,Wi-Fi 列表在真机上会直接返回空

2UniApp 权限弹窗描述(Android 13+ 必须)

复制代码
"permissions": {
  "Location": {
    "desc": "应用需要访问位置信息"
  },
  "NearbyWiFiDevices": {
    "desc": "应用需要扫描附近 Wi-Fi 设备"
  },
  "AccessNetworkState": {
    "desc": "应用需要访问网络状态"
  },
  "ChangeNetworkState": {
    "desc": "应用需要更改网络状态"
  },
  "ChangeWiFiState": {
    "desc": "应用需要更改 Wi-Fi 状态"
  }
}

说明:

  • 这些 desc 是:

    • Android 12+ / 13+ 弹窗文案来源
  • 没有 desc

    • 打包正常

    • 真机运行 → 权限请求失败 / 静默拒绝

你这里是完整且合规的

3Android 权限声明(Manifest 层)

复制代码
"distribute": {
  "android": {
   /* android打包配置 */
    "permissions": [
      "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
      "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
      "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
      "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",

      "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
      "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",

      "<uses-permission android:name=\"android.permission.NEARBY_WIFI_DEVICES\"/>"
    ]
  }
}
下面是我的完整配置
复制代码
  "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
                    "<uses-feature android:name=\"android.hardware.camera\"/>",
                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
                    "<uses-permission android:name=\"android.permission.NEARBY_WIFI_DEVICES\"/>" //Android 13+ 必须
                ]

vue业务代码

html

复制代码
<template>
  <view class="wifi-wrapper">
    <view class="wifi-container">
      <!-- 顶部当前连接信息 -->
      <view class="title">当前连接</view>
      <view class="current-connection">
        <text class="wifi-info">
          <image src="/static/icons/WiFiok.svg" class="icon" />
          {{ currentWifi.SSID || '未连接' }}
        </text>
        <text class="connection-status" v-if="currentWifi.SSID">已连接</text>
        <text class="connection-status" v-else>未连接</text>
      </view>

      <!-- Tab 切换 -->
      <view class="tab-container">
        <text class="tab-item" :style="{ color: activeTab === 'available' ? '#fff' : '#888' }"
          @click="switchTab('available')">
          本地可连接WiFi
        </text>
        <text class="tab-item" :style="{ color: activeTab === 'history' ? '#fff' : '#888' }"
          @click="switchTab('history')">
          历史连接WiFi
        </text>
      </view>

      <!-- WiFi 列表 -->
      <scroll-view class="wifi-list" scroll-y="true">
        <view v-for="(wifi, index) in displayedWifiList" :key="index" class="wifi-item">
          <view class="wifi-left">
            <image :src="getSignalIcon(typeof wifi.signal === 'number' ? wifi.signal : signalToPercent(wifi.signal))"
              class="signal-icon" />
            <text class="wifi-name">{{ wifi.SSID }}</text>
          </view>
          <text class="connect-btn" :style="{ color: activeTab === 'available' ? '#4e9dc2' : '#888' }"
            @click="connectToWifi(wifi)">
            {{ activeTab === 'available' ? '立即连接' : '连接' }}
          </text>
        </view>
      </scroll-view>

      <!-- 底部按钮 -->
      <view class="button-container">
        <button class="back-btn" @click="goBack">返回</button>
        <button class="next-btn" @click="goToNext">下一步</button>
      </view>
    </view>
    <view class="side"></view>
    <!-- 弹窗组件 -->
    <uni-popup ref="isModalVisible" @close="closeModal">
      <view class="popupContent">
        <view class="top">
          <view>连接网络</view>
          <uni-icons type="closeempty" size="18" color="#fff" @click="closeModal"></uni-icons>
        </view>
        <view class="popcontent">
          <view class="title">
            请输入
            <text class="colortitle">{{ wifiInfo }}</text>的密码
          </view>
          <view class="popinpbox">
            <uni-easyinput v-model="passwordvalue" type="password" :clearable="false" passwordIcon
              placeholder="请输入密码" />
          </view>
        </view>
        <view class="foolter">
          <view class="cancel" @click="closeModal">取消</view>
          <view class="confirm" @click="handleConfirm">连接</view>
        </view>
      </view>
    </uni-popup>

  </view>
</template>

<script setup>

复制代码
<script setup>
import { ref, computed, onMounted } from 'vue'

// ---------- 响应式变量 ----------
const isModalVisible = ref(false)
const useinfo = ref({})
const wifi = ref({})
const wifiInfo = ref('')
const passwordvalue = ref('')

const activeTab = ref('available')
const currentWifi = ref({ SSID: '' })
const wifiList = ref([])
const historyWifiList = ref([])
const selectedWifi = ref({})

// ---------- 信号强度辅助 ----------
const getSignalIcon = (signal) => {
  if (!signal && signal !== 0) return '/static/icons/wifi4.svg'
  if (signal <= 25) return '/static/icons/wifi3.svg'
  if (signal <= 50) return '/static/icons/wifi2.svg'
  if (signal <= 75) return '/static/icons/wifi1.svg'
  return '/static/icons/wifi3.svg'
}

// ---------- 显示列表 ----------
const displayedWifiList = computed(() => {
  return activeTab.value === 'available' ? wifiList.value : historyWifiList.value
})

// ---------- Tab 切换 ----------
const switchTab = (tab) => {
  activeTab.value = tab
  if (tab === 'history') loadHistoryWifi()
}

// ---------- 历史 Wi-Fi ----------
const saveHistoryWifi = (wifi) => {
  let history = uni.getStorageSync('historyWifi') || []
  const existIndex = history.findIndex(item => item.SSID === wifi.SSID)
  if (existIndex >= 0) history.splice(existIndex, 1)
  history.unshift(wifi)
  if (history.length > 20) history = history.slice(0, 20)
  uni.setStorageSync('historyWifi', history)
}

const loadHistoryWifi = () => {
  historyWifiList.value = uni.getStorageSync('historyWifi') || []
}

// ---------- 连接 Wi-Fi ----------
const connectToWifi = (wifiObj) => {
  selectedWifi.value = wifiObj
  wifiInfo.value = wifiObj.SSID || ''
  if (!wifiInfo.value) return
  isModalVisible.value.open()
}

const closeModal = () => {
  isModalVisible.value.close()
  passwordvalue.value = ''
}

const handleConfirm = () => {
  console.log(333333333);
  
  if (!passwordvalue.value) {
    uni.showToast({ title: '请输入密码', icon: 'none' })
    return
  }

  uni.showLoading({ title: '连接中...' })
uni.connectWifi({
  SSID: selectedWifi.value.SSID,
  password: passwordvalue.value, // 只传密码
  success: () => {
    uni.hideLoading()
    uni.showToast({ title: `已连接 ${selectedWifi.value.SSID}`, icon: 'success' })
    saveHistoryWifi(selectedWifi.value)
    currentWifi.value = selectedWifi.value
    closeModal()
  },
  fail: (err) => {
    uni.hideLoading()
    uni.showToast({ title: '连接失败,请检查密码', icon: 'none' })
    console.error('连接 Wi-Fi 失败', err)
  }
})

}

// ---------- 页面跳转和登录 ----------
const goBack = () => uni.navigateTo({ url: '/pages/login/index' })


const goToNext = () => {
  useinfo.value.userWifiSsid = currentWifi.value.SSID
  useinfo.value.userEquipmentNumber = useinfo.value.userName
  uni.setStorageSync('useinfo', useinfo.value)
  const wifiInfoStr = encodeURIComponent(JSON.stringify(currentWifi.value))
  uni.navigateTo({
    url: `/sub-device/user/index?wifiInfo=${wifiInfoStr}`
  })
}

// ---------- 初始化 Wi-Fi 插件 ----------
const initWifiPlugin = () => {
    // Android App-plus 原生获取位置权限
// Android App-plus 原生动态申请 Wi-Fi 所需权限
if (plus && plus.os.name === 'Android') {
  const main = plus.android.runtimeMainActivity()
  const PackageManager = plus.android.importClass('android.content.pm.PackageManager')
  const permission = plus.android.importClass('android.Manifest$permission')
  const ActivityCompat = plus.android.importClass('androidx.core.app.ActivityCompat')

  // 需要申请的权限列表
  const permissions = [
    permission.ACCESS_FINE_LOCATION,
    permission.ACCESS_COARSE_LOCATION,
    permission.ACCESS_WIFI_STATE,
    permission.CHANGE_WIFI_STATE,
    permission.INTERNET,
    permission.ACCESS_NETWORK_STATE
  ]

  // 检查哪些权限未被授予
  const needRequest = permissions.filter(p =>
    plus.android.invoke(main, 'checkSelfPermission', p) !== PackageManager.PERMISSION_GRANTED
  )

  if (needRequest.length > 0) {
    ActivityCompat.requestPermissions(main, needRequest, 0)
    console.log('请求 Wi-Fi 相关权限中...')
    return
  }
}

  uni.getLocation({
  type: 'wgs84',
  success() { 
// 初始化 Wi-Fi
      startWifiScan()
  },
  fail() {
    uni.showModal({
      title: '提示',
      content: '请开启手机定位服务,否则无法扫描 Wi-Fi',
      showCancel: false
    })
  }
})

      // 获取当前已连接 Wi-Fi
      uni.getConnectedWifi({
        success(res) {
          currentWifi.value = res.wifi
        },
        fail(err) {
          uni.showToast({ title: '获取当前连接 Wi-Fi失败', icon: 'none' })
          console.error('获取当前连接 Wi-Fi失败', err)
        }
      })
  // uni.startWifi({
  //   success() {
  //     console.log('Wi-Fi 初始化成功')
  //     // 获取当前已连接 Wi-Fi
  //     uni.getConnectedWifi({
  //       success(res) {
  //         currentWifi.value = res.wifi
  //       },
  //       fail(err) {
  //         console.error('获取当前连接 Wi-Fi失败', err)
  //         uni.showToast({ title: '获取当前连接 Wi-Fi失败', icon: 'none' })
  //       }
  //     })

  //     // 扫描 Wi-Fi
  //     uni.onGetWifiList(res => {
  //       console.log('扫描到 Wi-Fi 列表', res.wifiList)
  //       wifiList.value = res.wifiList.map(item => ({
  //         SSID: item.SSID,
  //         BSSID: item.BSSID,
  //         signal: item.signalStrength || item.signal,
  //         capabilities: item.security
  //       }))
  //     })

  //     // ⚠️ 必须传一个空对象 {} 避免 NullPointerException
  //     uni.getWifiList({})
  //   },
  //   fail(err) {
  //     console.error('Wi-Fi 初始化失败', err)
  //   }
  // })
}
// 抽离 Wi-Fi 扫描逻辑
const startWifiScan = () => {
  uni.startWifi({
    success() {
      console.log('Wi-Fi 初始化成功')
      uni.getConnectedWifi({
        success(res) {
          currentWifi.value = res.wifi
        },
        fail(err) {
          console.error('获取当前连接 Wi-Fi失败', err)
          uni.showToast({ title: '获取当前 Wi-Fi 失败', icon: 'none' })
        }
      })

      uni.onGetWifiList(res => {
        console.log('扫描到 Wi-Fi 列表', res.wifiList)
        wifiList.value = res.wifiList.map(item => ({
          SSID: item.SSID,
          BSSID: item.BSSID,
          signal: item.signalStrength || item.signal,
          capabilities: item.security
        }))
      })

      uni.getWifiList({})
    },
    fail(err) {
      console.error('Wi-Fi 初始化失败', err)
      uni.showToast({ title: 'Wi-Fi 初始化失败,请检查权限', icon: 'none' })
    }
  })
}

// ---------- 页面初始化 ----------
onMounted(() => {
  useinfo.value = uni.getStorageSync('useinfo') || {}
  setTimeout(() => {
  initWifiPlugin()
  }, 600);
  loadHistoryWifi()
})
</script>
相关推荐
QN1幻化引擎3 天前
SFA 信号场注意力:用8KB参数换248x KV Cache压缩,边缘设备也能跑长序列
人工智能·算法·gitee·gitlab
J14818529146 天前
md文件图片在其他地方不能预览
gitee
Mark White8 天前
使用 Codex 自动完成 Gitee 仓库全托管
gitee
hkj88089 天前
vscode提交gitee远程仓库出现Permission denied (publickey)解决方法
ide·vscode·gitee
雪的季节10 天前
ffmpeg源码国内gitee下载
ffmpeg·gitee
御坂嘀喵12 天前
Speed Tools:一套低侵入的 Android 插件化 + 动态换肤 + 字体切换框架
android·gitee
风信子IT人18 天前
【5天实战】从零构建AI-Native组织:飞书+Bot+Gitee全链路自动化实战指南—Day 5:完整场景实操验证
gitee·自动化·飞书·ai-native·bot开发
zakariyaa3318 天前
Android 绘制调度机制
android·gitee
m0_526119401 个月前
ssh key生成,gitee配置ssh
运维·gitee·ssh
我最爱吃鱼香茄子1 个月前
【保姆级】Git配置用户名和邮箱(全局/局部)解决提交记录不识别、贡献图不亮问题
git·gitee·github·小白教程·git配置·程序员开发工具