|
@@ -167,12 +167,20 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <!-- 语音 -->
|
|
|
- <VoiceCallModal
|
|
|
- v-if="showVoiceCall"
|
|
|
- :user="currentUser"
|
|
|
- @hangup="handleHangup"
|
|
|
- />
|
|
|
+
|
|
|
+
|
|
|
+ <!-- 来电弹窗 -->
|
|
|
+ <div v-if="imState.videoCallModal && !inCall" class="call-modal">
|
|
|
+ <p>{{ imState.callName }} 正在呼叫你</p>
|
|
|
+ <button @click="acceptCall">接听</button>
|
|
|
+ <button @click="rejectCall">拒接</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 通话中显示挂断按钮 -->
|
|
|
+ <div v-if="inCall" class="call-modal">
|
|
|
+ <p>与 {{ imState.callName }} 通话中...</p>
|
|
|
+ <button @click="hangupCall">挂断</button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
@@ -188,7 +196,6 @@ import messageAudio from "@/views/im/components/messageAudio/index.vue";
|
|
|
import { showToast, showImagePreview } from "vant";
|
|
|
import { useWebRTCStore } from "@/stores/modules/webrtcStore";
|
|
|
import * as Constant from "@/common/constant/Constant";
|
|
|
-import VoiceCallModal from "@/views/im/components/VoiceCallModal/index.vue";
|
|
|
|
|
|
const IM_PATH = import.meta.env.VITE_IM_PATH_FIlE;
|
|
|
// 路由 & store
|
|
@@ -209,7 +216,8 @@ const appBoxHeight = ref(210);
|
|
|
const chatListRef = ref(null);
|
|
|
const emojiRef = ref(null);
|
|
|
const toolsRef = ref(null);
|
|
|
-const showVoiceCall = ref(false);
|
|
|
+const imState = reactive(rtcStore.imSate); // 来电弹窗显示,callName,fromUserUuid
|
|
|
+const inCall = ref(false); // 是否处于通话中
|
|
|
|
|
|
// 示例用户
|
|
|
const currentUser = ref({
|
|
@@ -426,33 +434,86 @@ const beforeRead = (file) => {
|
|
|
};
|
|
|
|
|
|
// 创建呼叫:开启语音电话: 调试用,正式逻辑读src/views/im/hook/messagesHook.js
|
|
|
+// ==== 1. 发起语音通话 ====
|
|
|
const startAudioOnline = async () => {
|
|
|
- showVoiceCall.value = true;
|
|
|
- // 初始化webrtc连接
|
|
|
- rtcStore.initConnection(true);
|
|
|
-
|
|
|
- navigator.mediaDevices
|
|
|
- .getUserMedia({ audio: true })
|
|
|
- .then((stream) => {
|
|
|
- rtcStore.addLocalStream(stream);
|
|
|
- return rtcStore.createOffer();
|
|
|
- })
|
|
|
- .then((offer) => {
|
|
|
- // 发送offer
|
|
|
- wsStore.sendMessage({
|
|
|
- contentType: Constant.AUDIO_ONLINE, // 消息内容类型
|
|
|
- content: JSON.stringify(offer),
|
|
|
- type: Constant.MESSAGE_TRANS_TYPE,
|
|
|
- });
|
|
|
- })
|
|
|
- .catch((error) => {
|
|
|
- console.error("发起呼叫失败:", error);
|
|
|
+ try {
|
|
|
+ rtcStore.initConnection(true); // 主叫
|
|
|
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
|
+ await rtcStore.addLocalStream(stream);
|
|
|
+
|
|
|
+ const offer = await rtcStore.createOffer();
|
|
|
+
|
|
|
+ wsStore.sendMessage({
|
|
|
+ contentType: Constant.DIAL_AUDIO_ONLINE,
|
|
|
+ content: JSON.stringify(offer),
|
|
|
+ type: Constant.MESSAGE_TRANS_TYPE,
|
|
|
+ to: imState.fromUserUuid || "对方用户ID", // 实际传对方ID
|
|
|
});
|
|
|
-};
|
|
|
|
|
|
-const handleHangup = () => {
|
|
|
- showVoiceCall.value = false;
|
|
|
+ imState.videoCallModal = true;
|
|
|
+ imState.callName = "对方昵称"; // 实际昵称
|
|
|
+ inCall.value = true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("发起语音通话失败", error);
|
|
|
+ }
|
|
|
};
|
|
|
+// ==== 2. 接听来电 ====
|
|
|
+async function acceptCall() {
|
|
|
+ try {
|
|
|
+ rtcStore.initConnection(false); // 被叫
|
|
|
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
|
+ await rtcStore.addLocalStream(stream);
|
|
|
+
|
|
|
+ // 远端offer SDP,存在imState.offerContent,messagesHook里收到后需保存offerContent
|
|
|
+ const offerSDP = JSON.parse(imState.offerContent);
|
|
|
+ await rtcStore.setRemoteDescription(new RTCSessionDescription(offerSDP));
|
|
|
+
|
|
|
+ const answer = await rtcStore.createAnswer();
|
|
|
+
|
|
|
+ wsStore.sendMessage({
|
|
|
+ contentType: Constant.ACCEPT_AUDIO_ONLINE,
|
|
|
+ content: JSON.stringify(answer),
|
|
|
+ type: Constant.MESSAGE_TRANS_TYPE,
|
|
|
+ to: imState.fromUserUuid,
|
|
|
+ });
|
|
|
+
|
|
|
+ inCall.value = true;
|
|
|
+ imState.videoCallModal = true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("接听失败", error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ==== 3. 拒接来电 ====
|
|
|
+function rejectCall() {
|
|
|
+ wsStore.sendMessage({
|
|
|
+ contentType: Constant.REJECT_AUDIO_ONLINE,
|
|
|
+ content: "",
|
|
|
+ type: Constant.MESSAGE_TRANS_TYPE,
|
|
|
+ to: imState.fromUserUuid,
|
|
|
+ });
|
|
|
+
|
|
|
+ rtcStore.videoCallModal = false;
|
|
|
+ inCall.value = false;
|
|
|
+}
|
|
|
+
|
|
|
+// ==== 4. 挂断通话 ====
|
|
|
+function hangupCall() {
|
|
|
+ rtcStore.cleanup();
|
|
|
+ wsStore.sendMessage({
|
|
|
+ contentType: Constant.CANCELL_AUDIO_ONLINE,
|
|
|
+ content: "",
|
|
|
+ type: Constant.MESSAGE_TRANS_TYPE,
|
|
|
+ to: imState.fromUserUuid,
|
|
|
+ });
|
|
|
+ rtcStore.videoCallModal = false;
|
|
|
+ inCall.value = false;
|
|
|
+}
|
|
|
+
|
|
|
+// ==== 5. 监听信令消息 ====
|
|
|
+// 建议你在 useWebSocketStore 里实现 onMessage 订阅信令消息
|
|
|
+// 这里模拟简易监听,示范关键流程
|
|
|
+
|
|
|
|
|
|
// 时间格式化
|
|
|
const formatTime = (timestamp) => {
|