吉安感知网项目-前端
张含笑
2026-01-15 a38ad5c0d21b384099c4720e219b5ed9a3154129
feat:语音通话更新
1 files modified
235 ■■■■ changed files
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue 235 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -1,5 +1,5 @@
<template>
    <div class="call-container">
    <div class="call-container" :class="{'incoming-call': state === 'ringing'} ">
      <div class="content-wrapper">
<!--        <div class="input-group">-->
<!--            <div class="input-item">-->
@@ -21,14 +21,16 @@
             <img :src="defaultAvatar" alt=""></img>
         </div>
         <div class="status-group">
<!--            <strong class="status-text">状态:<span :style="{color: getStateColor(state)}">{{ state }}</span></strong>-->
            <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>
        </div>
        <div class="button-group">
            <button v-if="false" @click="requestCall" :disabled="!connected || state!=='idle'" class="btn btn-call">呼叫</button>
          
            <button v-if="state==='ringing'" @click="acceptCall" class="btn btn-accept">接听</button>
            <button v-if="state==='ringing'" @click="rejectCall" class="btn btn-reject">拒绝</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>
          
            <button v-if="state==='calling' || state==='in-call'" @click="hangup" class="btn btn-hangup">挂断</button>
        </div>
@@ -132,6 +134,77 @@
    pingTimer = setInterval(() => {
      if (connected.value) send('ping', 'system', null)
    }, 25000)
  }
  // 来电提示相关
  let ringtoneAudio = null
  let vibrationTimer = null
  // 播放来电铃声
  function playRingtone() {
    // 创建音频对象(使用默认的系统提示音)
    ringtoneAudio = new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQAAAAAAA==');
    ringtoneAudio.loop = true;
    ringtoneAudio.volume = 0.5;
    try {
      ringtoneAudio.play();
      log('🔊 开始播放来电铃声')
    } catch (e) {
      log('❌ 播放来电铃声失败:', String(e))
    }
  }
  // 停止来电铃声
  function stopRingtone() {
    if (ringtoneAudio) {
      try {
        ringtoneAudio.pause();
        ringtoneAudio.currentTime = 0;
        ringtoneAudio = null;
        log('🔇 停止播放来电铃声')
      } catch (e) {
        log('❌ 停止来电铃声失败:', String(e))
      }
    }
  }
  // 开始震动
  function startVibration() {
    // 检查浏览器是否支持震动 API
    if ('vibrate' in navigator) {
      // 震动模式:震动1秒,暂停0.5秒,重复
      const vibrationPattern = [1000, 500];
      try {
        navigator.vibrate(vibrationPattern);
        vibrationTimer = setInterval(() => {
          navigator.vibrate(vibrationPattern);
        }, 1500); // 每1.5秒重复一次震动模式
        log('📳 开始震动提示')
      } catch (e) {
        log('❌ 震动失败:', String(e))
      }
    } else {
      log('ℹ️ 当前浏览器不支持震动功能')
    }
  }
  // 停止震动
  function stopVibration() {
    if (vibrationTimer) {
      clearInterval(vibrationTimer);
      vibrationTimer = null;
    }
    if ('vibrate' in navigator) {
      try {
        navigator.vibrate(0); // 停止震动
        log('📳 停止震动提示')
      } catch (e) {
        log('❌ 停止震动失败:', String(e))
      }
    }
  }
  function stopPing() {
    if (pingTimer) clearInterval(pingTimer)
@@ -270,6 +343,11 @@
        state.value = 'ringing'
        log('📞 收到来电 call,来自', incomingFrom)
        // 开始来电提示(铃声和震动)
        playRingtone()
        startVibration()
        return
    }
@@ -385,13 +463,17 @@
  function acceptCall() {
    if (!incomingFrom) return
    acceptedByMe = true
    // 停止来电提示
    stopRingtone()
    stopVibration()
    // 先告诉对方我接听了(后端会把双方置忙)
    send('accept', incomingFrom, null)
    log('✅ 已点击接听,发送 accept 给', incomingFrom)
    state.value = 'calling'
    // 如果 offer 已经提前到达(我们暂存了),立刻 answer
    if (lastOfferSdp) {
      answerOffer(lastOfferSdp, incomingFrom)
@@ -401,7 +483,11 @@
  
  function rejectCall() {
    if (!incomingFrom) return
    // 停止来电提示
    stopRingtone()
    stopVibration()
    // 你后端没有 reject,用 busy 表示拒绝/不可接听
    send('busy', incomingFrom, null)
    log('❌ 已拒绝来电,发送 busy 给', incomingFrom)
@@ -467,7 +553,10 @@
    } catch {}
  
    stopTimer()
    // 停止来电提示
    stopRingtone()
    stopVibration()
    if (pc) {
      pc.getSenders().forEach(s => s.track && s.track.stop())
      pc.close()
@@ -519,17 +608,34 @@
  </script>
  <style scoped lang="scss">
    .call-container {
        padding:100px 20px 20px 20px;
        padding: 20px;
        min-height: 100vh;
        font-family: Arial, sans-serif;
        background-color: #f5f5f5;
        transition: all 0.3s ease;
    }
    /* 来电时的全屏样式 */
    .call-container.incoming-call {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 99999;
        background-color: #ffffff;
        padding: 0;
    }
    .content-wrapper {
        display: flex;
        flex-direction: column;
        gap: 15px;
        gap: 20px;
        max-width: 600px;
        margin: 0 auto;
        height: 100vh;
        justify-content: center;
        align-items: center;
    }
    .input-group {
@@ -559,24 +665,56 @@
    .button-group {
        display: flex;
        gap: 10px;
        gap: 50px;
        flex-wrap: wrap;
        justify-content: center;
        margin-top: 80px;
    }
    .btn {
        padding: 10px 20px;
        padding: 15px 30px;
        color: white;
        border: none;
        border-radius: 4px;
        border-radius: 8px;
        cursor: pointer;
        font-size: 14px;
        transition: background-color 0.3s;
        font-size: 16px;
        font-weight: bold;
        transition: all 0.3s ease;
        min-width: 120px;
        &:disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }
    }
    /* 来电时的接听和拒绝按钮样式 */
    .btn.incoming-accept, .btn.incoming-reject {
        width: 80px;
        height: 80px;
        border-radius: 50%;
        padding: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
    }
    .btn.incoming-accept {
        background-color: #4CAF50;
        transform: scale(1.1);
    }
    .btn.incoming-reject {
        background-color: #f44336;
    }
    .btn.incoming-accept:active {
        transform: scale(0.95);
    }
    .btn.incoming-reject:active {
        transform: scale(0.95);
    }
    .btn-connect {
@@ -597,19 +735,51 @@
    .btn-hangup {
        background-color: #9E9E9E;
        padding: 15px 40px;
        min-width: 150px;
    }
    .status-group {
        display: flex;
        flex-direction: column;
        gap: 5px;
        gap: 10px;
        align-items: center;
    }
    .status-text {
        font-size: 16px;
        font-size: 18px;
        color: #333;
        text-align: center;
        margin-bottom: 50px;
        margin-bottom: 20px;
    }
    /* 来电状态样式 */
    .status-text.incoming-status {
        font-size: 24px;
        color: #333;
        font-weight: bold;
        animation: pulse 1.5s infinite;
    }
    .status-text.calling-status {
        font-size: 20px;
        color: #666;
    }
    /* 呼吸灯动画 */
    @keyframes pulse {
        0% {
            opacity: 1;
            transform: scale(1);
        }
        50% {
            opacity: 0.7;
            transform: scale(1.05);
        }
        100% {
            opacity: 1;
            transform: scale(1);
        }
    }
    .tip-message {
@@ -651,16 +821,31 @@
    .hidden-audio {
        display: none;
    }
    /* 头像样式 */
    .contactAvatar {
        width: 172rpx;
        height: 172rpx;
        width: 180px;
        height: 180px;
        display: flex;
        justify-content: center;
        align-items: center;
        margin-bottom: 200px;
        img{
            width: 172rpx;
            height: 172rpx;
        margin-bottom: 40px;
        border-radius: 50%;
        background-color: #f0f0f0;
        overflow: hidden;
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
        img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
    }
    /* 来电时的头像放大效果 */
    .call-container.incoming-call .contactAvatar {
        width: 220px;
        height: 220px;
        margin-bottom: 60px;
    }
  </style>