From ee584cc262617b9483c6959e6c9753a72fe39bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=83=E9=B9=8F?= Date: Wed, 2 Apr 2025 10:48:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20wifi=E9=85=8D=E7=BD=91=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E9=80=BB=E8=BE=91+p2p=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.cjs | 14 +- api/sdk.js | 12 + .../LockDatetimePicker/LockDatetimePicker.vue | 12 +- components/SwitchLoading/SwitchLoading.vue | 45 +- config/constants.js | 9 + config/env.js | 10 +- exportForPlayerPlugin.js | 3 + exportForXp2pPlugin.js | 22 + manifest.json | 17 +- package-lock.json | 6968 ++++++++--------- package.json | 8 +- pages.json | 19 +- pages/addDeviceForBluetooth/searchDevice.vue | 4 +- .../addDeviceForWiFi/distributionNetwork.vue | 244 +- pages/main/home.vue | 14 +- pages/main/mine.vue | 48 +- pages/p2p/p2pPlayer.vue | 92 + pages/p2p/utils.js | 29 + pages/p2p/xp2pManager.js | 118 + stores/basic.js | 5 + stores/sdk.js | 45 + 21 files changed, 4038 insertions(+), 3700 deletions(-) create mode 100644 api/sdk.js create mode 100644 config/constants.js create mode 100644 exportForPlayerPlugin.js create mode 100644 exportForXp2pPlugin.js create mode 100644 pages/p2p/p2pPlayer.vue create mode 100644 pages/p2p/utils.js create mode 100644 pages/p2p/xp2pManager.js create mode 100644 stores/sdk.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 4f69855..2e0c9c9 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -41,8 +41,17 @@ module.exports = { 'no-var': 'error', // 要求使用 let 或 const 而不是 var 'no-multiple-empty-lines': ['error', { max: 1 }], // 不允许多个空行 'prefer-const': 'off', // 使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const - 'no-use-before-define': 'off', // 禁止在 函数/类/变量 定义之前使用它们 - 'no-irregular-whitespace': 'off', // 禁止不规则的空白\ + 'no-use-before-define': 'error', // 禁止在 函数/类/变量 定义之前使用它们 + 'no-irregular-whitespace': 'off', // 禁止不规则的空白 + 'no-undef': 'error', // 禁止使用未声明的变量 + 'vue/script-setup-uses-vars': 'off', // 关闭此规则,因为它可能会干扰no-undef的检测 + 'vue/no-undef-components': 'off', // 关闭组件未定义的检查 + 'vue/no-undef-properties': [ + 'error', + { + ignore: ['getDeviceInfo', 'getBluetoothDevices', 'stopGetBluetoothDevices'] + } + ], // 忽略特定方法的未定义检查 'import/no-cycle': 0, 'no-nested-ternary': 0, 'import/prefer-default-export': 0, @@ -61,7 +70,6 @@ module.exports = { 'no-debugger': 0, 'no-promise-executor-return': 0, // vue (https://eslint.vuejs.org/rules) - 'vue/script-setup-uses-vars': 'error', // 防止 diff --git a/pages/main/home.vue b/pages/main/home.vue index 61bcb9d..45605ef 100644 --- a/pages/main/home.vue +++ b/pages/main/home.vue @@ -182,6 +182,7 @@ import { deleteKeyRequest } from '@/api/key' import { deleteLockRequest } from '@/api/lock' import { setStorage, getStorage } from '@/utils/storage' + import { useSdkStore } from '@/stores/sdk' export default { data() { @@ -224,7 +225,11 @@ this.deviceInfo = await this.getDeviceInfo() const token = getStorage('token') if (token) { - await Promise.all([this.getLockList(this.lockSearch), this.getUserInfo()]).then(res => { + await Promise.all([ + this.getLockList(this.lockSearch), + this.getUserInfo(), + this.initSdk() + ]).then(res => { this.pending = false uni.hideLoading() const list = getStorage('lockList') @@ -272,6 +277,7 @@ 'resetDevice' ]), ...mapActions(useBasicStore, ['routeJump', 'getDeviceInfo', 'getNetworkType', 'shareJump']), + ...mapActions(useSdkStore, ['initSdk']), async deleteLock(lock, groupIndex, lockIndex) { const that = this const netWork = await this.getNetworkType() @@ -416,6 +422,7 @@ that.getLockList(that.lockSearch) await that.getUserInfo() that.updateLoginStatus(true) + that.initSdk() resolve(true) } else { that.updateLoginStatus(false) @@ -530,8 +537,11 @@ this.focus = false }, async toSearchDevice() { + // this.routeJump({ + // name: 'selectDeviceType' + // }) this.routeJump({ - name: 'selectDeviceType' + name: 'p2pPlayer' }) }, async toLockDetail(lock) { diff --git a/pages/main/mine.vue b/pages/main/mine.vue index 6220686..4a3e249 100644 --- a/pages/main/mine.vue +++ b/pages/main/mine.vue @@ -156,7 +156,7 @@ removeStorage('userInfo') removeStorage('lockList') uni.reLaunch({ - url: '/pages/home/home' + url: '/pages/main/home' }) }, async changePhone(res) { @@ -273,30 +273,30 @@ diff --git a/pages/p2p/p2pPlayer.vue b/pages/p2p/p2pPlayer.vue new file mode 100644 index 0000000..14fc533 --- /dev/null +++ b/pages/p2p/p2pPlayer.vue @@ -0,0 +1,92 @@ + + + diff --git a/pages/p2p/utils.js b/pages/p2p/utils.js new file mode 100644 index 0000000..6d7d0c3 --- /dev/null +++ b/pages/p2p/utils.js @@ -0,0 +1,29 @@ +export const getUserId = () => { + return 1 +} + +export function compareVersion(ver1, ver2) { + const v1 = ver1.split('.') + const v2 = ver2.split('.') + const len = Math.max(v1.length, v2.length) + + while (v1.length < len) { + v1.push('0') + } + while (v2.length < len) { + v2.push('0') + } + + for (let i = 0; i < len; i++) { + const num1 = parseInt(v1[i]) + const num2 = parseInt(v2[i]) + + if (num1 > num2) { + return 1 + } else if (num1 < num2) { + return -1 + } + } + + return 0 +} diff --git a/pages/p2p/xp2pManager.js b/pages/p2p/xp2pManager.js new file mode 100644 index 0000000..6eefc30 --- /dev/null +++ b/pages/p2p/xp2pManager.js @@ -0,0 +1,118 @@ +import { getUserId, compareVersion } from './utils' + +let xp2pManager = null +export const getXp2pManager = () => { + if (!xp2pManager) { + let xp2pPlugin = requirePlugin('xp2p') + + console.log(11111, xp2pPlugin) + + const iotExports = xp2pPlugin.iot + const app = getApp() + + // 用户id,微信用户在此小程序中的唯一标识 + iotExports?.setUserId?.(getUserId() || 'demo') + + // 开发版才打插件log + if (app.pluginLogger && uni.getAccountInfoSync().miniProgram.envVersion === 'develop') { + iotExports?.setPluginLogger?.(app.pluginLogger) + } + + // 设置优先使用的打洞协议 + if ( + compareVersion(uni.getSystemInfoSync().SDKVersion, '3.4.1') >= 0 && + xp2pPlugin.p2p.setTcpFirst + ) { + const tcpFirstKey = 'tcpFirst' + const tcpFirstTime = parseInt(uni.getStorageSync(tcpFirstKey), 10) + const tcpFirst = !!(tcpFirstTime && Date.now() - tcpFirstTime < 3600000 * 24) // 24小时内有效 + console.log('tcpFirst', tcpFirst) + xp2pPlugin.p2p.setTcpFirst(tcpFirst) + + // 给index页用,方便测试时调整tcpFirst + app.tcpFirst = tcpFirst + app.toggleTcpFirst = async () => { + const modalRes = await uni.showModal({ + title: '确定切换 tcpFirst 吗?', + content: '切换后需要重新进入小程序' + }) + if (!modalRes || !modalRes.confirm) { + return + } + uni.setStorageSync(tcpFirstKey, tcpFirst ? '' : Date.now()) + app.restart() + } + } + + // 设置是否连接对端stun + if (xp2pPlugin.p2p.setCrossStunTurn) { + const crossStunTurnKey = 'crossStunTurn' + const crossStunTurnTime = parseInt(uni.getStorageSync(crossStunTurnKey), 10) + const crossStunTurn = !!(crossStunTurnTime && Date.now() - crossStunTurnTime < 3600000 * 24) // 24小时内有效 + xp2pPlugin.p2p.setCrossStunTurn(crossStunTurn) + // 给index页用,方便测试时调整crossStunTurn + app.crossStunTurn = crossStunTurn + app.toggleCrossStunTurn = async () => { + const modalRes = await uni.showModal({ + title: '确定切换 crossStunTurn 吗?', + content: '切换后需要重新进入小程序' + }) + if (!modalRes || !modalRes.confirm) { + return + } + uni.setStorageSync(crossStunTurnKey, crossStunTurn ? '' : Date.now()) + app.restart() + } + } + + // 设置切换端口 + if (xp2pPlugin.p2p.updateStunPort) { + const portKey = 'STUN_PORT' + const stunPort = uni.getStorageSync(portKey) || 20002 + xp2pPlugin.p2p.updateStunPort(stunPort) + app.stunPort = stunPort + app.togglePort = async port => { + const modalRes = await uni.showModal({ + title: '确定切换 stunPort 吗?', + content: '切换后需要重新进入小程序' + }) + if (!modalRes || !modalRes.confirm) { + return + } + uni.setStorageSync(portKey, port) + app.restart() + } + } + + // 设置切换配置跟随 + if (xp2pPlugin.p2p.setUseDeliveryConfig) { + const useDeliveryConfigKey = 'useDeliveryConfig' + const useDeliveryConfigTime = parseInt(uni.getStorageSync(useDeliveryConfigKey), 10) + const useDeliveryConfig = !!( + useDeliveryConfigTime && Date.now() - useDeliveryConfigTime < 3600000 * 24 + ) // 24小时内有效 + xp2pPlugin.p2p.setUseDeliveryConfig(useDeliveryConfig) + app.useDeliveryConfig = useDeliveryConfig + app.toggleUseDeliveryConfig = async () => { + const modalRes = await uni.showModal({ + title: '确定切换 使用设备跟随 吗?', + content: '切换后需要重新进入小程序' + }) + if (!modalRes || !modalRes.confirm) { + return + } + uni.setStorageSync(useDeliveryConfigKey, useDeliveryConfig ? '' : Date.now()) + app.restart() + } + } + + xp2pManager = iotExports.getXp2pManager() + app.logger?.log('xp2pManager', { + P2PPlayerVersion: xp2pManager.P2PPlayerVersion, + XP2PVersion: xp2pManager.XP2PVersion, + // uuid,插件随机生成的id,存储在小程序本地,删除小程序后会重新生成 + uuid: xp2pManager.uuid + }) + } + return xp2pManager +} diff --git a/stores/basic.js b/stores/basic.js index 6973f2b..8faa7b9 100644 --- a/stores/basic.js +++ b/stores/basic.js @@ -391,6 +391,11 @@ const pages = [ name: 'distributionNetwork', path: '/pages/addDeviceForWiFi/distributionNetwork', tabBar: false + }, + { + name: 'p2pPlayer', + path: '/pages/p2p/p2pPlayer', + tabBar: false } ] diff --git a/stores/sdk.js b/stores/sdk.js new file mode 100644 index 0000000..05cfd76 --- /dev/null +++ b/stores/sdk.js @@ -0,0 +1,45 @@ +import { defineStore } from 'pinia' +import { AppDevSdk } from 'qcloud-iotexplorer-appdev-sdk' +import { getSdkToken } from '@/api/sdk' + +export const useSdkStore = defineStore('sdk', { + state() { + return { + sdk: null + } + }, + actions: { + async getSdk() { + if (this.sdk.loginManager.isLogin) { + return this.sdk + } + await this.initSdk() + return this.sdk + }, + async initSdk() { + try { + const getAccessToken = async () => { + const { code, data, message } = await getSdkToken() + if (code === 0) { + return data + } + uni.showToast({ + title: message, + icon: 'none' + }) + return null + } + + this.sdk = await new AppDevSdk({ + appKey: 'mtBfaXFtISXFYVVsd', + getAccessToken + }) + + this.sdk.init() + } catch (error) { + console.log(111, this.sdk) + console.log('error', error) + } + } + } +})