| | |
| | | <div class="status-group"> |
| | | <strong v-if="state==='ringing'" class="status-text incoming-status">来电中...</strong> |
| | | <strong v-if="state==='calling'" class="status-text calling-status">呼叫中...</strong> |
| | | <!-- <strong class="status-text">状态:<span :style="{color: getStateColor(state)}">{{ state }}</span></strong>--> |
| | | <strong v-if="state==='in-call'" class="status-text">通话时长:{{ durationText }}</strong> |
| | | <!-- 通话结束提示--> |
| | | <strong v-if="callEnded" class="status-text ended-status">{{ endMessage }}</strong> |
| | | </div> |
| | | <div class="button-group"> |
| | | <button @click="requestCall" :disabled="!connected || state!=='idle'" class="btn btn-call">呼叫</button> |
| | | <button v-if="state === 'idle' && !receiveParameters" @click="requestCall" :disabled="!connected" class="btn btn-call">呼叫</button> |
| | | |
| | | <button v-if="state==='ringing'" @click="acceptCall" class="btn btn-accept incoming-accept">接听</button> |
| | | <button v-if="state==='ringing'" @click="rejectCall" class="btn btn-reject incoming-reject">拒绝</button> |
| | |
| | | |
| | | // 解析URL参数 |
| | | const parseUrlParams = () => { |
| | | const url = new URL(window.location.href) |
| | | const paramsStr = url.searchParams.get('params') |
| | | if (paramsStr) { |
| | | // 1. 优先从route.query.voiceparams获取参数 |
| | | if (route.query && route.query.voiceparams) { |
| | | console.log('从route.query.voiceparams获取参数:', route.query.voiceparams); |
| | | try { |
| | | const params = JSON.parse(decodeURIComponent(paramsStr)) |
| | | // 解码并解析voiceparams参数 |
| | | const params = JSON.parse(decodeURIComponent(route.query.voiceparams)); |
| | | console.log('解析后的voiceparams:', params); |
| | | |
| | | if (params.peerUid) { |
| | | peerUid.value = String(params.peerUid) |
| | | log('🔗 从URL参数获取peerUid:', 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('❌ 解析URL参数失败:', String(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)); |
| | | } |
| | | } |
| | | } |
| | |
| | | const state = ref('idle') // idle | calling | ringing | in-call |
| | | const logText = ref('') |
| | | const remoteAudio = ref(null) |
| | | // 通话结束相关 |
| | | const callEnded = ref(false) |
| | | const endMessage = ref('对方已挂断') |
| | | const endTimer = ref(null) |
| | | |
| | | /** |
| | | * 根据状态返回对应颜色 |
| | |
| | | /* ---------------- 信令处理 ---------------- */ |
| | | |
| | | async function onSignal(msg) { |
| | | console.log('1cesss',msg); |
| | | |
| | | if (!msg) return |
| | | const t = (msg.type || '').toString() |
| | | |
| | |
| | | lastOfferSdp = null |
| | | |
| | | state.value = 'idle' |
| | | |
| | | // 返回上一页(uni-app的语音通话列表页面) |
| | | if (window.uni) { |
| | | window.uni.navigateBack() |
| | | |
| | | // 清除之前的定时器 |
| | | if (endTimer.value) { |
| | | clearTimeout(endTimer.value) |
| | | endTimer.value = null |
| | | } |
| | | |
| | | // 根据是谁发起的挂断决定不同的行为 |
| | | if (sendToPeer) { |
| | | // 自己主动挂断:不提示,不延迟,直接跳转 |
| | | log('⏰ 自己主动挂断,直接返回') |
| | | callEnded.value = false |
| | | // 返回上一页(uni-app的语音通话列表页面) |
| | | if (window.uni) { |
| | | window.uni.navigateBack() |
| | | } else { |
| | | log('❌ uni对象不存在,无法返回上一页') |
| | | } |
| | | } else { |
| | | log('❌ uni对象不存在,无法返回上一页') |
| | | // 对方挂断:显示提示,延迟10秒跳转 |
| | | callEnded.value = true |
| | | endMessage.value = '对方已挂断' |
| | | |
| | | // 延迟10秒后跳转页面 |
| | | endTimer.value = setTimeout(() => { |
| | | log('⏰ 通话结束10秒后自动返回') |
| | | // 返回上一页(uni-app的语音通话列表页面) |
| | | if (window.uni) { |
| | | window.uni.navigateBack() |
| | | } else { |
| | | log('❌ uni对象不存在,无法返回上一页') |
| | | } |
| | | // 重置通话结束状态 |
| | | callEnded.value = false |
| | | }, 5000) |
| | | } |
| | | } |
| | | |
| | | // 定义receiveParameters变量,用于模板中判断是否显示呼叫按钮 |
| | | const receiveParameters = ref(route.query.contact || null); |
| | | |
| | | onMounted(() => { |
| | | const receiveParameters = route.query.contact |
| | | console.log('接收参数', receiveParameters) |
| | | console.log('收拾收拾',route.query.voiceparams); |
| | | console.log('接收参数', receiveParameters.value) |
| | | |
| | | // 解析并使用contact参数 |
| | | if (receiveParameters) { |
| | | if (receiveParameters.value) { |
| | | try { |
| | | const contact = JSON.parse(decodeURIComponent(receiveParameters)) |
| | | const contact = JSON.parse(decodeURIComponent(receiveParameters.value)) |
| | | if (contact.friendId) { |
| | | peerUid.value = contact.friendId |
| | | log('🔗 从contact参数获取peerUid:', contact.friendId) |
| | |
| | | parseUrlParams() |
| | | // 自动连接WebSocket |
| | | connectWS() |
| | | }) |
| | | }); |
| | | |
| | | // 监听连接状态,连接成功后自动呼叫 |
| | | // watch(() => connected.value, (newVal) => { |
| | | // if (newVal && state.value === 'idle') { |
| | | // requestCall() |
| | | // } |
| | | // }) |
| | | // 监听连接状态,当connected变为true且有receiveParameters时自动呼叫 |
| | | watch(() => connected.value, (newVal) => { |
| | | if (newVal && state.value === 'idle' && receiveParameters.value) { |
| | | log('📞 连接成功且有contact参数,自动发起呼叫'); |
| | | requestCall(); |
| | | } |
| | | }); |
| | | |
| | | |
| | | onBeforeUnmount(() => { |
| | | cleanup(false) |
| | |
| | | padding: 0; |
| | | } |
| | | |
| | | /* 通话结束状态样式 */ |
| | | .status-text.ended-status { |
| | | color: #e74c3c; |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-top: 10px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .content-wrapper { |
| | | display: flex; |
| | | flex-direction: column; |