| applications/drone-command/src/views/audioCall.vue | ●●●●● patch | view | raw | blame | history | |
| applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue | ●●●●● patch | view | raw | blame | history | |
| uniapps/work-app/src/App.vue | ●●●●● patch | view | raw | blame | history | |
| uniapps/work-app/src/hooks/useGlobalWS.js | ●●●●● patch | view | raw | blame | history | |
| uniapps/work-app/src/manifest.json | ●●●●● patch | view | raw | blame | history | |
| uniapps/work-app/src/subPackages/voiceCallDetail/index.vue | ●●●●● patch | view | raw | blame | history |
applications/drone-command/src/views/audioCall.vue
@@ -36,10 +36,10 @@ import { ref, computed, onBeforeUnmount } from 'vue' /** ✅ 你的 WS 地址前缀(后面拼 userId) */ const WS_BASE = 'wss://wrj.shuixiongit.com/ws/chat?userId=' const WS_BASE = 'ws://218.202.104.82:38201/ws/chat' const uid = ref('2') const peerUid = ref('3') const uid = ref('2021474568497131521') const peerUid = ref('2021475329842999298') const connected = ref(false) const state = ref('idle') // idle | calling | ringing | in-call @@ -310,7 +310,7 @@ function connectWS() { if (ws) ws.close() ws = new WebSocket(WS_BASE + encodeURIComponent(uid.value)) ws = new WebSocket(WS_BASE, "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoyMDIxNDc0NTY4NDk3MTMxNTIxLCJ1c2VyX2tleSI6ImI4MzJhNzJhLTA5NDMtNGI1ZC04ZDRhLTZiYjlmZTJmM2VmMyIsInVzZXJuYW1lIjoiemhpZmVpIn0.amIbSnRkyof2nAal8U1uPO407fg9JfJk1yKoaAnX6nxbWwG5Yf8VY4Bto0D0EBvUNdoSFB2hbcOv5a80XEDDmA") ws.onopen = () => { connected.value = true applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -8,7 +8,7 @@ <div class="contactAvatar"> <img :src="defaultAvatar" alt=""></img> </div> <!-- 联系人名称--> <div class="contactName"> {{ contactName }} @@ -39,77 +39,14 @@ <script setup> import { useRoute } from 'vue-router' import defaultAvatar from '@/appDataSource/appwork/defaultAvatar.svg' import { showToast } from 'vant' const route = useRoute() /** ✅ 你的 WS 地址前缀(后面拼 userId) */ // const WS_BASE = 'ws://218.202.104.82:38201/ws/chat?userId=' const WS_BASE = 'ws://218.202.104.82:38201/ws/chat' // 解析URL参数 const parseUrlParams = () => { // 1. 优先从route.query.voiceparams获取参数 if (route.query && route.query.voiceparams) { console.log('从route.query.voiceparams获取参数:', route.query.voiceparams); try { // 解码并解析voiceparams参数 const params = JSON.parse(decodeURIComponent(route.query.voiceparams)); console.log('解析后的voiceparams:', params); if (params.peerUid) { peerUid.value = String(params.peerUid); log('🔗 从voiceparams获取peerUid:', params.peerUid); } // 如果是来电类型,直接触发来电状态 if (params.type === 'incoming') { log('📞 voiceparams检测到来电,直接进入来电状态'); incomingFrom = String(params.from); peerUid.value = incomingFrom; acceptedByMe = false; offeredByPeer = false; state.value = 'ringing'; log('📞 收到来电 call,来自', incomingFrom); // 开始来电提示(铃声和震动) playRingtone(); startVibration(); } } catch (e) { log('❌ 解析voiceparams参数失败:', String(e)); } } // 2. 同时保留原有的URL参数解析逻辑,作为备选 const url = new URL(window.location.href); const paramsStr = url.searchParams.get('params'); if (paramsStr) { try { const params = JSON.parse(decodeURIComponent(paramsStr)); if (params.peerUid) { peerUid.value = String(params.peerUid); log('🔗 从URL参数获取peerUid:', params.peerUid); } } catch (e) { log('❌ 解析URL参数失败:', String(e)); } } } // 从URL参数或本地存储获取用户ID const getUserIdFromStorage = () => { // 从url获取用户id const contact = JSON.parse(decodeURIComponent(receiveParameters.value)) console.log('从url获取用户id',contact?.userId) // 从本地获取用户id const userId = localStorage.getItem('userId') || sessionStorage.getItem('userId') console.log('从本地存储获取userId:', userId) if (userId) { return userId } return '3' // 默认值 } const uid = ref('3') const peerUid = ref('2') const uid = ref('2021475329842999298') const peerUid = ref('2021474568497131521') // 联系人名称 const contactName = ref('张三') @@ -150,6 +87,70 @@ function stopTimer() { if (timer) clearInterval(timer) timer = null } // 解析URL参数 const parseUrlParams = () => { // 1. 优先从route.query.voiceparams获取参数 if (route.query && route.query.voiceparams) { console.log('从route.query.voiceparams获取参数:', route.query.voiceparams); try { // 解码并解析voiceparams参数 const params = JSON.parse(decodeURIComponent(route.query.voiceparams)); console.log('解析后的voiceparams:', params); if (params.peerUid) { peerUid.value = String(params.peerUid); log('🔗 从voiceparams获取peerUid:', params.peerUid); } // 如果是来电类型,直接触发来电状态 if (params.type === 'incoming') { log('📞 voiceparams检测到来电,直接进入来电状态'); incomingFrom = String(params.from); peerUid.value = incomingFrom; acceptedByMe = false; offeredByPeer = false; state.value = 'ringing'; log('📞 收到来电 call,来自', incomingFrom); // 开始来电提示(铃声和震动) playRingtone(); startVibration(); } } catch (e) { log('❌ 解析voiceparams参数失败:', String(e)); } } // 2. 同时保留原有的URL参数解析逻辑,作为备选 const url = new URL(window.location.href); const paramsStr = url.searchParams.get('params'); if (paramsStr) { try { const params = JSON.parse(decodeURIComponent(paramsStr)); if (params.peerUid) { peerUid.value = String(params.peerUid); log('🔗 从URL参数获取peerUid:', params.peerUid); } } catch (e) { log('❌ 解析URL参数失败:', String(e)); } } } // 从URL参数或本地存储获取用户ID const getUserIdFromStorage = () => { // 从url获取用户id const contact = JSON.parse(decodeURIComponent(receiveParameters.value)) console.log('从url获取用户id',contact?.userId) // 从本地获取用户id const userId = localStorage.getItem('userId') || sessionStorage.getItem('userId') console.log('从本地存储获取userId:', userId) if (userId) { return userId } return '2021475329842999298' // 默认值 } // 心跳 @@ -229,21 +230,43 @@ } try { localStream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true, // 移动端特定配置 channelCount: 1, sampleRate: 16000, sampleSize: 16 } }) log('✅ 已获取本地麦克风') // 尝试使用完整约束 try { localStream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true, channelCount: 1, sampleRate: 16000, sampleSize: 16 } }) log('✅ 已获取本地麦克风(标准约束)') } catch (strictError) { // 如果标准约束失败,尝试最简单的约束 log('⚠️ 标准约束失败,尝试简化约束:', String(strictError)) localStream = await navigator.mediaDevices.getUserMedia({ audio: true }) log('✅ 已获取本地麦克风(简化约束)') } return localStream } catch (e) { log('❌ 获取麦克风失败:', String(e)) // 提供更详细的错误信息 if (e.name === 'NotReadableError') { log('❌ NotReadableError: 麦克风可能被其他应用占用,或权限未正确配置') showToast('麦克风被占用或权限未授予') } else if (e.name === 'NotAllowedError') { log('❌ NotAllowedError: 用户拒绝了麦克风权限') showToast('请授予麦克风权限') } else if (e.name === 'NotFoundError') { log('❌ NotFoundError: 未找到麦克风设备') showToast('未找到麦克风设备') } throw e } } @@ -530,13 +553,13 @@ lastOfferSdp = null state.value = 'idle' // 清除之前的定时器 if (endTimer.value) { clearTimeout(endTimer.value) endTimer.value = null } // 根据是谁发起的挂断决定不同的行为 if (sendToPeer) { // 自己主动挂断:不提示,不延迟,直接跳转 @@ -552,7 +575,7 @@ // 对方挂断:显示提示,延迟10秒跳转 callEnded.value = true endMessage.value = '对方已挂断' // 延迟5秒后跳转页面 endTimer.value = setTimeout(() => { // 返回上一页(uni-app的语音通话列表页面) @@ -580,7 +603,7 @@ if (params.access_token) { accessToken.value = params.access_token; log('🔗 从params获取access_token:', accessToken.value); } } } catch (e) { log('❌ 解析params参数失败:', String(e)); } @@ -882,4 +905,4 @@ height: 220px; margin-bottom: 60px; } </style> </style> uniapps/work-app/src/App.vue
@@ -4,7 +4,7 @@ import { useGlobalWS } from '@/hooks/useGlobalWS.js' // #ifdef APP-PLUS import { keepAliveStart, requestIgnoreBatteryOptimization } from '@/uni_modules/keep-app' import { openDialog, allowFloat,getBatteryCapacity } from '@/uni_modules/lgh-dialog' import { openDialog, allowFloat, getBatteryCapacity } from '@/uni_modules/lgh-dialog' // #endif const appStore = useAppStore() @@ -24,11 +24,8 @@ // testKeep() // 开启悬浮窗 allowFloat() // allowFloat() // 假设五秒后来电 setTimeout(() => { openDialog() }, 5000) // #endif useGlobalWS() @@ -57,9 +54,9 @@ // 初始化系统信息 appStore.initSystemInfo() // #ifdef APP-PLUS keepAliveStart() // keepAliveStart() // 请求用户允许忽略电池优化(重要:用于保活) setTimeout(requestIgnoreBatteryOptimization, 2000) // setTimeout(requestIgnoreBatteryOptimization, 2000) // #endif if (!userStore.userInfo) { //不存在则跳转至登录页 @@ -106,4 +103,3 @@ background: transparent !important; } </style> uniapps/work-app/src/hooks/useGlobalWS.js
@@ -6,11 +6,14 @@ const userStore = useUserStore(); const appStore = useAppStore(); const callStatus = ref(null) // token 存储 const accessToken = ref('') // 设置默认用户ID为3 const defaultUserId = '8'; const defaultUserId = '2021475329842999298'; // WebSocket基础URL const WS_BASE = 'wss://wrj.shuixiongit.com/ws/chat?userId='; // const WS_BASE = 'wss://wrj.shuixiongit.com/ws/chat?userId='; const WS_BASE = 'ws://218.202.104.82:38201/ws/chat'; // 消息处理 function messageHandler(payload) { @@ -76,6 +79,8 @@ // 初始化WebSocket连接 function initWS() { const userStore = useUserStore() accessToken.value =userStore?.userInfo.access_token try { websocketService.setOnMessageCallback(messageHandler); uniapps/work-app/src/manifest.json
@@ -53,12 +53,14 @@ "<uses-feature android:name=\"android.hardware.camera\"/>", "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>", "<uses-permission android:name=\"android.permission.RECORD_AUDIO\" />", "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />", "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />", "<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />", "<uses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\" />", "<uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" />", "<uses-permission android:name=\"android.permission.USE_FULL_SCREEN_INTENT\" />" ] ], "minSdkVersion" : 21 }, /* ios打包配置 */ "ios" : { uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
@@ -23,42 +23,41 @@ */ async function requestAndroidMicPermission() { try { // 使用 uni 的权限 API(需要 HBuilderX 3.4.0+) if (uni.requestPermissions) { const result = await uni.requestPermissions({ permissions: ['android.permission.RECORD_AUDIO'] }) console.log('权限请求结果:', result) if (result[0].granted) { console.log('✅ 麦克风权限已授予') return true } else { console.warn('⚠️ 麦克风权限被拒绝') // 可以提示用户 uni.showModal({ title: '权限提示', content: '语音通话需要麦克风权限,请前往设置开启', showCancel: false, success: () => { // 跳转到应用设置 plus.runtime.openURL(plus.runtime.getProperty('package') + '://settings') // 获取麦克风权限 return await new Promise((resolve) => { plus.android.requestPermissions( ['android.permission.RECORD_AUDIO'], (result) => { if (result.granted.length > 0) { console.log('麦克风权限已获取') resolve(true) return } }) return false } } // 降级方案:使用原生方法 return await requestAndroidMicPermissionNative() if (result.deniedAlways.length > 0) { uni.showModal({ title: '提示', content: '需要麦克风权限才能正常使用语音功能,请前往系统设置开启', success(res) { if (res.confirm) { plus.runtime.openURL('app-settings:') } }, }) } resolve(false) }, (error) => { console.error('请求麦克风权限失败', error) resolve(false) } ) }) } catch (error) { console.error('❌ 请求麦克风权限失败:', error) console.error('❌ 请求麦克风权限失败', error) return false } } // #endif onLoad(async (options) => {