吉安感知网项目-前端
张含笑
2026-01-13 17e23efd4ad847f3316903c2f00786a6ba227f36
feat:通话demo
4 files modified
222 ■■■■ changed files
applications/drone-command/vite.config.mjs 4 ●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue 210 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/hooks/useGlobalWS.js 4 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/voiceCallDetail/index.vue 4 ●●● patch | view | raw | blame | history
applications/drone-command/vite.config.mjs
@@ -3,6 +3,7 @@
import createVitePlugins from './vite/plugins'
import postCssPxToRem from 'postcss-pxtorem'
import { fileURLToPath, URL } from 'node:url';
import  basic from '@vitejs/plugin-basic-ssl'
// https://vitejs.dev/config/
export default ({ mode, command }) => {
  const env = loadEnv(mode, fileURLToPath(new URL("./env", import.meta.url)))
@@ -38,6 +39,7 @@
      __INTLIFY_PROD_DEVTOOLS__: false,
    },
    server: {
      https: true,
      port: 5174,
      // host: '192.168.1.178',
      proxy: {
@@ -79,7 +81,7 @@
        ]
      }
    },
    plugins: createVitePlugins(env, command === 'build'),
    plugins: [...createVitePlugins(env, command === 'build'), basic()],
    build: buildConfig,
    optimizeDeps: {
      esbuildOptions: {
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -1,34 +1,49 @@
<template>
    <div style="padding:16px">
      <h2>1v1 语音通话 WebRTC Demo(对齐后端 ChatMessage,JS)</h2>
    <div class="call-container">
      <div class="content-wrapper">
        <div class="input-group">
            <div class="input-item">
                <span class="input-label">我的 userId:</span>
                <input v-model="uid" class="input-field" />
            </div>
            <div class="input-item">
                <span class="input-label">对方 userId:</span>
                <input v-model="peerUid" class="input-field" />
            </div>
        </div>
  
      <div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
        <span>我的 userId:</span>
        <input v-model="uid" style="width:120px" />
        <div class="button-group">
            <button @click="connectWS" :disabled="connected" class="btn btn-connect">连接WS</button>
        </div>
  
        <span>对方 userId:</span>
        <input v-model="peerUid" style="width:120px" />
        <div class="button-group">
            <button @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 @click="hangup" :disabled="!connected || (state!=='calling' && state!=='in-call')" class="btn btn-hangup">挂断</button>
        </div>
  
        <button @click="connectWS" :disabled="connected">连接WS</button>
        <button @click="requestCall" :disabled="!connected || state!=='idle'">呼叫</button>
        <button v-if="state==='ringing'" @click="acceptCall">接听</button>
        <button v-if="state==='ringing'" @click="rejectCall">拒绝</button>
        <button @click="hangup" :disabled="!connected || (state!=='calling' && state!=='in-call')">挂断</button>
        <strong>状态:{{ state }}</strong>
        <strong v-if="state==='in-call'">通话时长:{{ durationText }}</strong>
        <div class="status-group">
            <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>
  
      <div style="margin-top:8px;color:#666">
      <div class="tip-message">
        <small>提示:若 getUserMedia 不可用,请用 https 或 localhost 访问页面。</small>
      </div>
  
      <pre style="margin-top:12px;max-height:280px;overflow:auto;">{{ logText }}</pre>
      <div class="log-container">
        <div class="log-box">
            <div class="log-title">日志信息:</div>
            <pre class="log-content">{{ logText }}</pre>
        </div>
      </div>
  
      <audio ref="remoteAudio" autoplay></audio>
      <audio ref="remoteAudio" autoplay class="hidden-audio"></audio>
    </div>
  </template>
  
@@ -36,7 +51,7 @@
import { ref, computed, onBeforeUnmount, onMounted } from 'vue'
  
  /** ✅ 你的 WS 地址前缀(后面拼 userId) */
  const WS_BASE = 'ws://192.168.1.33:8081/ws/chat?userId='
  const WS_BASE = 'wss://wrj.shuixiongit.com/ws/chat?userId='
  // 解析URL参数
  const parseUrlParams = () => {
@@ -55,13 +70,26 @@
    }
  }
  const uid = ref('2')
  const peerUid = ref('3')
  const uid = ref('3')
  const peerUid = ref('2')
  const connected = ref(false)
  const state = ref('idle') // idle | calling | ringing | in-call
  const logText = ref('')
  const remoteAudio = ref(null)
    /**
     * 根据状态返回对应颜色
     */
    const getStateColor = (state) => {
        const colors = {
            'idle': '#666',
            'calling': '#2196F3',
            'ringing': '#FF9800',
            'in-call': '#4CAF50'
        }
        return colors[state] || '#666'
    }
  
  let ws = null
  let pc = null
@@ -458,4 +486,136 @@
    if (ws) ws.close()
  })
  </script>
  <style scoped lang="scss">
    .call-container {
        padding: 20px;
        background-color: #f5f5f5;
        min-height: 100vh;
        font-family: Arial, sans-serif;
    }
    .content-wrapper {
        display: flex;
        flex-direction: column;
        gap: 15px;
        max-width: 600px;
        margin: 0 auto;
    }
    .input-group {
        display: flex;
        flex-direction: column;
        gap: 10px;
    }
    .input-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
    .input-label {
        font-weight: bold;
        color: #333;
    }
    .input-field {
        width: 120px;
        padding: 8px;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 14px;
    }
    .button-group {
        display: flex;
        gap: 10px;
        flex-wrap: wrap;
    }
    .btn {
        padding: 10px 20px;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        transition: background-color 0.3s;
        &:disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }
    }
    .btn-connect {
        background-color: #4CAF50;
    }
    .btn-call {
        background-color: #2196F3;
    }
    .btn-accept {
        background-color: #4CAF50;
    }
    .btn-reject {
        background-color: #f44336;
    }
    .btn-hangup {
        background-color: #9E9E9E;
    }
    .status-group {
        display: flex;
        flex-direction: column;
        gap: 5px;
    }
    .status-text {
        font-size: 16px;
        color: #333;
    }
    .tip-message {
        margin-top: 15px;
        color: #666;
        text-align: center;
    }
    .log-container {
        margin-top: 20px;
        max-width: 600px;
        margin-left: auto;
        margin-right: auto;
    }
    .log-box {
        background-color: white;
        border: 1px solid #ddd;
        border-radius: 8px;
        padding: 10px;
    }
    .log-title {
        font-weight: bold;
        color: #333;
        margin-bottom: 8px;
    }
    .log-content {
        max-height: 200px;
        overflow: auto;
        background-color: #fafafa;
        padding: 10px;
        border-radius: 4px;
        font-size: 12px;
        line-height: 1.4;
    }
    .hidden-audio {
        display: none;
    }
  </style>
uniapps/work-app/src/hooks/useGlobalWS.js
@@ -1,6 +1,6 @@
import {useUserStore} from "@/store/index.js";
import {getEnvObj} from "@/utils/index.js";
import {enterRoom} from "@/utils/voiceCallByTX/index.js";
// import {enterRoom} from "@/utils/voiceCallByTX/index.js";
import useAppStore from "../store/modules/app/index.js";
let socketTask = null
@@ -31,7 +31,7 @@
                })
                break
            case 'VoiceCall':
                enterRoom(payload, userId.value)
                // enterRoom(payload, userId.value)
                break
            default:
                break;
uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
@@ -16,8 +16,10 @@
onLoad((options) => {
      // const currentItem = options.currentItem;
      // viewUrl.value = getWebViewUrl("/mapWork", { currentItem: currentItem });
            viewUrl.value = getWebViewUrl("/voiceCallDetail");
      //       viewUrl.value = getWebViewUrl("/voiceCallDetail");
          viewUrl.value = 'https://192.168.57.124:5175/drone-command/demo';
    });
    function onPostMessage(data) {}
// #ifdef APP-PLUS
function requestAndroidMicPermission() {
  return new Promise((resolve) => {