Bladeren bron

更改样式

wkw 4 dagen geleden
bovenliggende
commit
b9bed987ec
2 gewijzigde bestanden met toevoegingen van 81 en 162 verwijderingen
  1. 81 56
      src/views/im/components/CallController/index.vue
  2. 0 106
      src/views/im/components/VoiceCallModal/index.vue

+ 81 - 56
src/views/im/components/CallController/index.vue

@@ -1,50 +1,47 @@
 <template>
   <div v-if="rtcStore.imSate.videoCallModal" class="weixin-call-modal">
-    <div class="caller-info">
+    <div class="caller-info" v-if="rtcStore.streamType == 'audio'">
       <img
-        v-if="rtcStore.streamType == 'audio'"
         class="avatar"
         :src="rtcStore.imSate.callAvatar || defaultAvatar"
         alt="头像"
       />
-      <div v-if="rtcStore.streamType == 'video'">
-        <!-- 本地视频 -->
-        <spn>本地发起方</spn>
-        <video
-          ref="localVideo"
-          autoplay
-          playsinline
-          muted
-          class="local-video"
-        ></video>
-      <spn>远端</spn>
-        <!-- 远程视频 -->
-        <video
-          ref="remoteVideo"
-          autoplay
-          playsinline
-          class="remote-video"
-        ></video>
-      </div>
-
-      <!-- video id="localPreviewSender" width="700px" height="auto" autoPlay muted controls />
-                    <video id="remoteVideoSender" width="700px" height="auto" autoPlay muted controls /> -->
       <div class="name">{{ rtcStore.imSate.callName || "未知用户" }}</div>
       <div class="status">{{ statusText }}</div>
     </div>
+
+    <!-- 视频通话 -->
+    <div v-if="rtcStore.streamType == 'video'" class="video-container">
+      <!-- 远端视频全屏 -->
+      <video
+        ref="remoteVideo"
+        autoplay
+        playsinline
+        class="remote-video"
+      ></video>
+
+      <!-- 本地视频小窗,可拖动 -->
+      <video
+        ref="localVideo"
+        autoplay
+        playsinline
+        muted
+        class="local-video-draggable"
+        :style="{ top: localVideoPos.top + 'px', left: localVideoPos.left + 'px' }"
+        @mousedown="startDrag"
+      ></video>
+    </div>
+
     <div class="btn-group">
-      <!-- 拨打方,只显示挂断按钮 -->
       <template v-if="rtcStore.isCaller">
         <button class="btn hangup" @click="hangupCall">挂断</button>
       </template>
 
-      <!-- 来电方,显示接听和拒绝按钮(未通话中) -->
       <template v-else-if="!inCall">
         <button class="btn reject" @click="rejectCall">拒绝</button>
         <button class="btn accept" @click="acceptCall">接听</button>
       </template>
 
-      <!-- 通话中显示挂断按钮 -->
       <template v-else>
         <button class="btn hangup" @click="hangupCall">挂断</button>
       </template>
@@ -53,7 +50,7 @@
 </template>
 
 <script setup>
-import { ref, computed, onMounted, onBeforeUnmount } from "vue";
+import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
 import { useWebSocketStore } from "@/stores/modules/webSocketStore";
 import { useWebRTCStore } from "@/stores/modules/webrtcStore";
 import * as Constant from "@/common/constant/Constant";
@@ -67,6 +64,11 @@ const remoteVideo = ref(null);
 const inCall = ref(false);
 const defaultAvatar = "https://example.com/default-avatar.png";
 
+// 小窗位置
+const localVideoPos = ref({ top: 20, left: 20 });
+let dragOffset = { x: 0, y: 0 };
+let dragging = false;
+
 const statusText = computed(() => {
   if (inCall.value && rtcStore.remoteStream) {
     return "通话中...";
@@ -82,13 +84,11 @@ function onMessage(message) {
       rtcStore.imSate.videoCallModal = true;
       rtcStore.imSate.callName = message.fromUserName || "未知用户";
       rtcStore.imSate.callAvatar = message.fromUserAvatar || "";
-      inCall.value = false; // 有来电,未接听
+      inCall.value = false;
       break;
-
     case Constant.ACCEPT_AUDIO_ONLINE:
-      inCall.value = true; // 对方已接听,通话中
+      inCall.value = true;
       break;
-
     case Constant.REJECT_AUDIO_ONLINE:
     case Constant.CANCELL_AUDIO_ONLINE:
     case Constant.DIAL_MEDIA_END:
@@ -106,7 +106,6 @@ async function acceptCall() {
       contentType: Constant.ACCEPT_AUDIO_ONLINE,
       type: Constant.MESSAGE_TRANS_TYPE,
     });
-    // 可以调用 rtcStore 初始化本地流、连接等逻辑
   } catch (err) {
     console.error("接听失败", err);
     inCall.value = false;
@@ -137,7 +136,6 @@ function hangupCall() {
 watch(
   () => rtcStore.remoteStream,
   (val) => {
-    console.log("rtcStore.streamType===", rtcStore.streamType)
     if (val && rtcStore.streamType == "video" && remoteVideo.value) {
       remoteVideo.value.srcObject = val;
     }
@@ -148,7 +146,6 @@ watch(
 watch(
   () => rtcStore.localStream,
   (val) => {
-        console.log("rtcStore.streamType===22", rtcStore.streamType)
     if (val && rtcStore.streamType == "video" && localVideo.value) {
       localVideo.value.srcObject = val;
     }
@@ -158,20 +155,41 @@ watch(
 
 onMounted(() => {
   wsStore.onMessageCallbacks.push(onMessage);
+  document.addEventListener("mousemove", onDrag);
+  document.addEventListener("mouseup", stopDrag);
 });
 
 onBeforeUnmount(() => {
   wsStore.onMessageCallbacks = wsStore.onMessageCallbacks.filter(
     (cb) => cb !== onMessage
   );
+  document.removeEventListener("mousemove", onDrag);
+  document.removeEventListener("mouseup", stopDrag);
 });
+
+// 拖动逻辑
+function startDrag(e) {
+  dragging = true;
+  dragOffset.x = e.clientX - localVideoPos.value.left;
+  dragOffset.y = e.clientY - localVideoPos.value.top;
+}
+
+function onDrag(e) {
+  if (!dragging) return;
+  localVideoPos.value.left = e.clientX - dragOffset.x;
+  localVideoPos.value.top = e.clientY - dragOffset.y;
+}
+
+function stopDrag() {
+  dragging = false;
+}
 </script>
 
 <style scoped lang="less">
 .weixin-call-modal {
   position: fixed;
   inset: 0;
-  background-color: rgba(0, 0, 0, 0.85);
+  background-color: black;
   display: flex;
   flex-direction: column;
   align-items: center;
@@ -192,7 +210,6 @@ onBeforeUnmount(() => {
   height: 80px;
   border-radius: 50%;
   margin-bottom: 16px;
-  border: 2px solid rgba(255, 255, 255, 0.2);
 }
 
 .name {
@@ -206,7 +223,33 @@ onBeforeUnmount(() => {
   color: #ccc;
 }
 
+.video-container {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
+.remote-video {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.local-video-draggable {
+  position: absolute;
+  width: 120px;
+  height: 160px;
+  border-radius: 8px;
+  background: black;
+  cursor: grab;
+  object-fit: cover;
+  z-index: 10;
+}
+
 .btn-group {
+  position: absolute;
+  bottom: 40px;
   display: flex;
   gap: 60px;
 }
@@ -222,32 +265,14 @@ onBeforeUnmount(() => {
   align-items: center;
   justify-content: center;
   cursor: pointer;
-  user-select: none;
-  outline: none;
-  transition: background-color 0.3s ease;
 }
 
 .btn.accept {
   background-color: #4cd964;
 }
 
-.btn.accept:hover {
-  background-color: #40c150;
-}
-
-.btn.reject {
-  background-color: #ff3b30;
-}
-
-.btn.reject:hover {
-  background-color: #e02d22;
-}
-
+.btn.reject,
 .btn.hangup {
   background-color: #ff3b30;
 }
-
-.btn.hangup:hover {
-  background-color: #e02d22;
-}
 </style>

+ 0 - 106
src/views/im/components/VoiceCallModal/index.vue

@@ -1,106 +0,0 @@
-<template>
-    <div class="voice-call-overlay">
-      <div class="call-container">
-        <img class="avatar" :src="user.avatar" />
-        <div class="nickname">{{ user.nickname }}</div>
-        <div class="call-status">{{ callStatus }}</div>
-        <div class="call-duration" v-if="callStatus === '通话中'">{{ callDuration }}</div>
-        <button class="hangup-button" @click="hangup">挂断</button>
-      </div>
-    </div>
-  </template>
-  
-  <script setup>
-  import { ref, onMounted, onBeforeUnmount } from 'vue';
-  const props = defineProps({
-    user: {
-      type: Object,
-      required: true
-    }
-  });
-  const emit = defineEmits(['hangup']);
-  
-  const callStatus = ref('正在呼叫...');
-  const callDuration = ref('00:00');
-  let timer = null;
-  
-  const startCallTimer = () => {
-    let seconds = 0;
-    timer = setInterval(() => {
-      seconds++;
-      const m = String(Math.floor(seconds / 60)).padStart(2, '0');
-      const s = String(seconds % 60).padStart(2, '0');
-      callDuration.value = `${m}:${s}`;
-    }, 1000);
-  };
-  
-  const hangup = () => {
-    emit('hangup');
-  };
-  
-  onMounted(() => {
-    // 模拟3秒后进入通话状态
-    setTimeout(() => {
-      callStatus.value = '通话中';
-      startCallTimer();
-    }, 3000);
-  });
-  
-  onBeforeUnmount(() => {
-    clearInterval(timer);
-  });
-  </script>
-  
-  <style scoped>
-  .voice-call-overlay {
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background: #000;
-    color: #fff;
-    z-index: 9999;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-  
-  .call-container {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-  }
-  
-  .avatar {
-    width: 100px;
-    height: 100px;
-    border-radius: 50%;
-    margin-bottom: 20px;
-  }
-  
-  .nickname {
-    font-size: 20px;
-    margin-bottom: 10px;
-  }
-  
-  .call-status {
-    font-size: 16px;
-    margin-bottom: 5px;
-  }
-  
-  .call-duration {
-    font-size: 16px;
-    margin-bottom: 20px;
-  }
-  
-  .hangup-button {
-    background-color: red;
-    border: none;
-    padding: 12px 30px;
-    color: white;
-    border-radius: 24px;
-    font-size: 16px;
-  }
-  </style>
-