2025-07-02 16:54:52 +08:00
|
|
|
|
import { defineStore } from "pinia"
|
2025-07-08 16:43:09 +08:00
|
|
|
|
import { ref, computed, watch } from "vue"
|
2025-07-03 15:36:34 +08:00
|
|
|
|
import { chatDB } from "@/utils/db"
|
2025-07-05 16:09:14 +08:00
|
|
|
|
import { useWebRTC } from "@/composables/useWebRTC"
|
|
|
|
|
|
import { useUserStore } from "@/stores/user"
|
2025-07-09 09:34:26 +08:00
|
|
|
|
import { sendCallSignal, callAPI } from "@/utils/request.js"
|
2025-07-02 16:54:52 +08:00
|
|
|
|
|
|
|
|
|
|
export const useChatStore = defineStore("chat", () => {
|
|
|
|
|
|
const friends = ref([])
|
|
|
|
|
|
const currentFriend = ref(null)
|
|
|
|
|
|
const messages = ref([])
|
|
|
|
|
|
const connectionStatus = ref("disconnected")
|
|
|
|
|
|
const socket = ref(null)
|
|
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
// 通话相关状态增强
|
2025-07-05 16:09:14 +08:00
|
|
|
|
const currentCall = ref(null)
|
|
|
|
|
|
const incomingCall = ref(null)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
const callStatus = ref("idle") // idle, calling, ringing, connecting, ongoing, ended, disconnected, hangup
|
2025-07-09 12:53:08 +08:00
|
|
|
|
const callError = ref(null)
|
|
|
|
|
|
const callConnectionStatus = ref("")
|
|
|
|
|
|
const callTimeout = ref(null)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
|
|
|
|
|
const webrtc = useWebRTC()
|
|
|
|
|
|
const userStore = useUserStore()
|
|
|
|
|
|
|
2025-07-02 16:54:52 +08:00
|
|
|
|
// 计算属性
|
|
|
|
|
|
const isConnected = computed(() => connectionStatus.value === "connected")
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const isInCall = computed(() => ["calling", "ringing", "connecting", "ongoing"].includes(callStatus.value))
|
2025-07-05 16:09:14 +08:00
|
|
|
|
const isCalling = computed(() => callStatus.value === "calling")
|
|
|
|
|
|
const isRinging = computed(() => callStatus.value === "ringing")
|
2025-07-08 16:43:09 +08:00
|
|
|
|
const isConnecting = computed(() => callStatus.value === "connecting")
|
2025-07-05 16:09:14 +08:00
|
|
|
|
const isOngoingCall = computed(() => callStatus.value === "ongoing")
|
2025-07-08 16:43:09 +08:00
|
|
|
|
const isDisconnected = computed(() => callStatus.value === "disconnected")
|
|
|
|
|
|
const isHangup = computed(() => callStatus.value === "hangup")
|
|
|
|
|
|
|
|
|
|
|
|
// 监听通话状态变化
|
2025-07-09 08:18:59 +08:00
|
|
|
|
watch(
|
|
|
|
|
|
() => callStatus.value,
|
|
|
|
|
|
(newStatus) => {
|
|
|
|
|
|
console.log("通话状态变化:", newStatus)
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
|
|
|
|
|
// 重置所有通话状态
|
|
|
|
|
|
const resetCallState = () => {
|
|
|
|
|
|
clearCallTimeout()
|
|
|
|
|
|
callStatus.value = "idle"
|
|
|
|
|
|
callError.value = null
|
|
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
currentCall.value = null
|
|
|
|
|
|
incomingCall.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清除通话超时
|
|
|
|
|
|
const clearCallTimeout = () => {
|
|
|
|
|
|
if (callTimeout.value) {
|
|
|
|
|
|
clearTimeout(callTimeout.value)
|
|
|
|
|
|
callTimeout.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置通话超时
|
|
|
|
|
|
const setCallTimeout = (callback, duration = 30000) => {
|
|
|
|
|
|
clearCallTimeout()
|
|
|
|
|
|
callTimeout.value = setTimeout(() => {
|
|
|
|
|
|
callback()
|
|
|
|
|
|
clearCallTimeout()
|
|
|
|
|
|
}, duration)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
// 发送通话信令
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const sendCallSignalInternal = async (signal) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log("发送通话信令:", signal)
|
|
|
|
|
|
|
|
|
|
|
|
if (!userStore.currentUser?.id) {
|
|
|
|
|
|
throw new Error("用户未登录")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!signal.receiver_user_id) {
|
|
|
|
|
|
throw new Error("缺少接收方用户ID")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!signal.call_id) {
|
|
|
|
|
|
throw new Error("缺少通话ID")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!signal.call_status) {
|
|
|
|
|
|
throw new Error("缺少通话状态")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const requestData = {
|
|
|
|
|
|
sender_user_id: userStore.currentUser.id,
|
|
|
|
|
|
receiver_user_id: signal.receiver_user_id,
|
2025-07-09 12:53:08 +08:00
|
|
|
|
message_type: signal.call_type || (signal.call_status === "invite" ? 6 : 7),
|
2025-07-09 09:34:26 +08:00
|
|
|
|
message_content: JSON.stringify(signal.data || {}),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
call_id: signal.call_id,
|
2025-07-09 09:34:26 +08:00
|
|
|
|
call_status: signal.call_status,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
2025-07-09 09:34:26 +08:00
|
|
|
|
|
|
|
|
|
|
console.log("发送通话信令请求数据:", requestData)
|
|
|
|
|
|
|
|
|
|
|
|
const response = await sendCallSignal(requestData)
|
|
|
|
|
|
console.log("发送通话信令成功:", signal)
|
|
|
|
|
|
return response
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("发送通话信令失败:", error)
|
|
|
|
|
|
throw error
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-02 16:54:52 +08:00
|
|
|
|
|
2025-07-03 15:36:34 +08:00
|
|
|
|
// 初始化数据库
|
|
|
|
|
|
const initDB = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await chatDB.init()
|
|
|
|
|
|
console.log("数据库初始化成功")
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("数据库初始化失败:", error)
|
|
|
|
|
|
}
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载好友列表
|
2025-07-03 15:36:34 +08:00
|
|
|
|
const loadFriends = async (presetUsers, currentUserId) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let friendList = await chatDB.getFriends(currentUserId)
|
|
|
|
|
|
|
|
|
|
|
|
if (friendList.length === 0) {
|
|
|
|
|
|
friendList = presetUsers.filter((user) => user.id !== currentUserId)
|
|
|
|
|
|
|
|
|
|
|
|
for (const friend of friendList) {
|
|
|
|
|
|
await chatDB.saveFriend(
|
|
|
|
|
|
{
|
|
|
|
|
|
...friend,
|
|
|
|
|
|
lastMessage: "",
|
|
|
|
|
|
unreadCount: 0,
|
|
|
|
|
|
},
|
2025-07-09 08:18:59 +08:00
|
|
|
|
currentUserId,
|
2025-07-03 15:36:34 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 15:36:34 +08:00
|
|
|
|
friends.value = friendList
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("加载好友列表失败:", error)
|
|
|
|
|
|
const friendList = presetUsers.filter((user) => user.id !== currentUserId)
|
|
|
|
|
|
friendList.forEach((friend) => {
|
|
|
|
|
|
friend.lastMessage = ""
|
|
|
|
|
|
friend.unreadCount = 0
|
|
|
|
|
|
})
|
2025-07-07 16:51:55 +08:00
|
|
|
|
friends.value = friendList
|
2025-07-03 15:36:34 +08:00
|
|
|
|
}
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-05 16:09:14 +08:00
|
|
|
|
// 设置当前好友
|
2025-07-03 17:02:08 +08:00
|
|
|
|
const setCurrentFriend = (friend) => {
|
2025-07-02 16:54:52 +08:00
|
|
|
|
currentFriend.value = friend
|
2025-07-03 17:02:08 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
friend.unreadCount = 0
|
2025-07-03 15:36:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-03 17:02:08 +08:00
|
|
|
|
|
2025-07-03 15:36:34 +08:00
|
|
|
|
// 切换当前聊天好友
|
2025-07-03 17:02:08 +08:00
|
|
|
|
const switchFriend = async (friend, currentUserId) => {
|
2025-07-03 15:36:34 +08:00
|
|
|
|
currentFriend.value = friend
|
2025-07-02 16:54:52 +08:00
|
|
|
|
|
2025-07-03 15:36:34 +08:00
|
|
|
|
friend.unreadCount = 0
|
|
|
|
|
|
await chatDB.clearUnreadCount(friend.id)
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const chatHistory = await chatDB.getChatHistory(currentUserId, friend.id)
|
|
|
|
|
|
messages.value = chatHistory.sort((a, b) => a.timestamp - b.timestamp)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("加载聊天记录失败:", error)
|
|
|
|
|
|
messages.value = []
|
|
|
|
|
|
}
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加消息
|
2025-07-03 15:36:34 +08:00
|
|
|
|
const addMessage = async (message, currentUserId) => {
|
2025-07-02 16:54:52 +08:00
|
|
|
|
messages.value.push(message)
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === message.senderId || f.id === currentFriend.value?.id)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
let lastMessage = message.content
|
|
|
|
|
|
if (message.type === "image") lastMessage = "[图片]"
|
|
|
|
|
|
if (message.type === "video") lastMessage = "[视频]"
|
|
|
|
|
|
if (message.type === "audio") lastMessage = "[语音]"
|
|
|
|
|
|
if (message.type === "video-call" || message.type === "audio-call") lastMessage = "[通话]"
|
2025-07-03 15:36:34 +08:00
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
friend.lastMessage = lastMessage.length > 20 ? lastMessage.substring(0, 20) + "..." : lastMessage
|
2025-07-03 15:36:34 +08:00
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
await chatDB.updateFriendLastMessage(friend.id, currentUserId, friend.lastMessage, 0)
|
|
|
|
|
|
}
|
2025-07-03 15:36:34 +08:00
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (currentFriend.value && friend && friend.id === currentFriend.value.id) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await chatDB.saveMessage(message, currentUserId, currentFriend.value.id)
|
2025-07-03 15:36:34 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("保存消息失败:", error)
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理接收到的消息
|
2025-07-03 15:36:34 +08:00
|
|
|
|
const handleIncomingMessage = async (messageData, currentUserId) => {
|
2025-07-07 16:51:55 +08:00
|
|
|
|
if (messageData.isFromHttp) {
|
|
|
|
|
|
console.log("忽略来自HTTP的消息,避免循环")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:54:52 +08:00
|
|
|
|
const senderId = messageData.sender_user_id
|
|
|
|
|
|
const receiverId = messageData.receiver_user_id
|
|
|
|
|
|
|
|
|
|
|
|
if (receiverId !== currentUserId) return
|
|
|
|
|
|
|
2025-07-05 16:09:14 +08:00
|
|
|
|
if (messageData.call_id || messageData.call_status) {
|
|
|
|
|
|
handleCallSignal(messageData)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:54:52 +08:00
|
|
|
|
const newMessage = {
|
|
|
|
|
|
type: getMessageType(messageData.message_type),
|
|
|
|
|
|
content: messageData.content,
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: senderId,
|
|
|
|
|
|
read: false,
|
|
|
|
|
|
duration: messageData.duration || 0,
|
2025-07-03 15:36:34 +08:00
|
|
|
|
timestamp: Date.now(),
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 15:36:34 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await chatDB.saveMessage(newMessage, currentUserId, senderId)
|
|
|
|
|
|
|
|
|
|
|
|
if (currentFriend.value && senderId == currentFriend.value.id) {
|
|
|
|
|
|
newMessage.read = true
|
|
|
|
|
|
messages.value.push(newMessage)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const friend = friends.value.find((f) => f.id == senderId)
|
|
|
|
|
|
if (friend) {
|
|
|
|
|
|
friend.unreadCount = (friend.unreadCount || 0) + 1
|
|
|
|
|
|
}
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
|
|
|
|
|
const friend = friends.value.find((f) => f.id == senderId)
|
|
|
|
|
|
if (friend) {
|
|
|
|
|
|
let lastMessage = newMessage.content
|
|
|
|
|
|
if (newMessage.type === "image") lastMessage = "[图片]"
|
|
|
|
|
|
if (newMessage.type === "video") lastMessage = "[视频]"
|
|
|
|
|
|
if (newMessage.type === "audio") lastMessage = "[语音]"
|
|
|
|
|
|
if (newMessage.type === "video-call" || newMessage.type === "audio-call") lastMessage = "[通话]"
|
|
|
|
|
|
friend.lastMessage = lastMessage
|
|
|
|
|
|
|
|
|
|
|
|
await chatDB.updateFriendLastMessage(friend.id, currentUserId, lastMessage, friend.unreadCount)
|
|
|
|
|
|
}
|
2025-07-03 15:36:34 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("处理接收消息失败:", error)
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据消息类型编号获取类型字符串
|
|
|
|
|
|
const getMessageType = (type) => {
|
2025-07-07 09:53:02 +08:00
|
|
|
|
const types = [
|
2025-07-09 08:18:59 +08:00
|
|
|
|
"text",
|
|
|
|
|
|
"image",
|
|
|
|
|
|
"audio",
|
|
|
|
|
|
"video",
|
|
|
|
|
|
"prescription",
|
|
|
|
|
|
"medical-record",
|
|
|
|
|
|
"video-call",
|
|
|
|
|
|
"audio-call",
|
|
|
|
|
|
"file",
|
2025-07-07 09:53:02 +08:00
|
|
|
|
]
|
2025-07-02 16:54:52 +08:00
|
|
|
|
return types[type] || "text"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-05 16:09:14 +08:00
|
|
|
|
// 处理通话信令
|
|
|
|
|
|
const handleCallSignal = (data) => {
|
|
|
|
|
|
console.log("处理通话信令:", data)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
console.log("通话状态:", data.call_status)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
|
|
|
|
|
switch (data.call_status) {
|
|
|
|
|
|
case "invite":
|
|
|
|
|
|
handleIncomingCall(data)
|
|
|
|
|
|
break
|
|
|
|
|
|
case "accepted":
|
|
|
|
|
|
handleCallAccepted(data)
|
|
|
|
|
|
break
|
|
|
|
|
|
case "rejected":
|
|
|
|
|
|
handleCallRejected(data)
|
|
|
|
|
|
break
|
|
|
|
|
|
case "ended":
|
|
|
|
|
|
handleCallEnded(data)
|
|
|
|
|
|
break
|
2025-07-09 08:18:59 +08:00
|
|
|
|
case "offer":
|
|
|
|
|
|
handleOffer(data)
|
|
|
|
|
|
break
|
|
|
|
|
|
case "answer":
|
|
|
|
|
|
handleAnswer(data)
|
|
|
|
|
|
break
|
2025-07-05 16:09:14 +08:00
|
|
|
|
case "candidate":
|
|
|
|
|
|
handleIceCandidate(data)
|
|
|
|
|
|
break
|
2025-07-08 16:43:09 +08:00
|
|
|
|
case "no-answer":
|
2025-07-07 16:51:55 +08:00
|
|
|
|
handleCallNoAnswer(data)
|
|
|
|
|
|
break
|
2025-07-08 16:43:09 +08:00
|
|
|
|
case "connecting":
|
|
|
|
|
|
handleCallConnecting(data)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
break
|
2025-07-08 16:43:09 +08:00
|
|
|
|
case "busy":
|
2025-07-07 16:51:55 +08:00
|
|
|
|
handleCallBusy(data)
|
|
|
|
|
|
break
|
2025-07-09 12:53:08 +08:00
|
|
|
|
case "hangup":
|
2025-07-08 16:43:09 +08:00
|
|
|
|
handleCallHangup(data)
|
|
|
|
|
|
break
|
2025-07-09 12:53:08 +08:00
|
|
|
|
case "disconnected":
|
2025-07-08 16:43:09 +08:00
|
|
|
|
handleCallDisconnected(data)
|
|
|
|
|
|
break
|
2025-07-09 12:53:08 +08:00
|
|
|
|
case "terminated":
|
2025-07-08 16:43:09 +08:00
|
|
|
|
handleCallTerminated(data)
|
|
|
|
|
|
break
|
|
|
|
|
|
case "failed":
|
|
|
|
|
|
handleCallFailed(data)
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
// 处理Offer信令
|
|
|
|
|
|
const handleOffer = async (data) => {
|
|
|
|
|
|
if (incomingCall.value && incomingCall.value.callId === data.call_id) {
|
2025-07-09 09:34:26 +08:00
|
|
|
|
if (callStatus.value === "ongoing") {
|
|
|
|
|
|
console.log("通话已建立,忽略重复的 Offer")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
try {
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const offer = JSON.parse(data.content || data.message_content || "{}")
|
2025-07-09 14:05:12 +08:00
|
|
|
|
|
|
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
|
|
|
|
|
console.log("📥 收到对方 OFFER 信令:")
|
|
|
|
|
|
console.log("📋 Offer 类型:", offer.type)
|
|
|
|
|
|
console.log("📋 Offer SDP (前200字符):", offer.sdp ? offer.sdp.substring(0, 200) + "..." : "无SDP")
|
|
|
|
|
|
console.log("📋 通话ID:", data.call_id)
|
|
|
|
|
|
console.log("📋 发送方:", data.sender_user_id)
|
|
|
|
|
|
console.log("📋 完整 Offer 对象:", offer)
|
|
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
2025-07-09 08:18:59 +08:00
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
if (!webrtc.peerConnection.value) {
|
|
|
|
|
|
console.log("创建 WebRTC 连接以处理 Offer")
|
|
|
|
|
|
webrtc.createPeerConnection(data.call_id, data.sender_user_id)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const answer = await webrtc.createAnswer(offer, data.call_id, data.sender_user_id)
|
2025-07-09 08:18:59 +08:00
|
|
|
|
|
2025-07-09 14:05:12 +08:00
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
|
|
|
|
|
console.log("📤 准备发送 ANSWER 信令:")
|
|
|
|
|
|
console.log("📋 Answer 类型:", answer.type)
|
|
|
|
|
|
console.log("📋 Answer SDP (前200字符):", answer.sdp ? answer.sdp.substring(0, 200) + "..." : "无SDP")
|
|
|
|
|
|
console.log("📋 通话ID:", data.call_id)
|
|
|
|
|
|
console.log("📋 接收方:", data.sender_user_id)
|
|
|
|
|
|
console.log("📋 完整 Answer 对象:", answer)
|
|
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
|
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
await callAPI.sendAnswer(userStore.currentUser.id, data.sender_user_id, data.call_id, answer, data.message_type)
|
|
|
|
|
|
|
2025-07-09 14:05:12 +08:00
|
|
|
|
console.log("✅ 发送Answer成功")
|
2025-07-09 08:18:59 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("处理Offer失败:", error)
|
|
|
|
|
|
rejectCall("failed")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理Answer信令
|
|
|
|
|
|
const handleAnswer = async (data) => {
|
|
|
|
|
|
if (currentCall.value && currentCall.value.callId === data.call_id) {
|
2025-07-09 09:34:26 +08:00
|
|
|
|
if (callStatus.value === "ongoing") {
|
|
|
|
|
|
console.log("通话已建立,忽略重复的 Answer")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
try {
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const answer = JSON.parse(data.content || data.message_content || "{}")
|
2025-07-09 14:05:12 +08:00
|
|
|
|
|
|
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
|
|
|
|
|
console.log("📥 收到对方 ANSWER 信令:")
|
|
|
|
|
|
console.log("📋 Answer 类型:", answer.type)
|
|
|
|
|
|
console.log("📋 Answer SDP (前200字符):", answer.sdp ? answer.sdp.substring(0, 200) + "..." : "无SDP")
|
|
|
|
|
|
console.log("📋 通话ID:", data.call_id)
|
|
|
|
|
|
console.log("📋 发送方:", data.sender_user_id)
|
|
|
|
|
|
console.log("📋 完整 Answer 对象:", answer)
|
|
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
2025-07-09 08:18:59 +08:00
|
|
|
|
|
|
|
|
|
|
await webrtc.setRemoteAnswer(answer)
|
|
|
|
|
|
|
|
|
|
|
|
callStatus.value = "connecting"
|
|
|
|
|
|
callConnectionStatus.value = "正在建立连接..."
|
2025-07-09 14:05:12 +08:00
|
|
|
|
|
|
|
|
|
|
console.log("✅ Answer处理成功,开始建立连接")
|
2025-07-09 08:18:59 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("处理Answer失败:", error)
|
|
|
|
|
|
endCall("failed")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
// 处理连接中状态
|
|
|
|
|
|
const handleCallConnecting = (data) => {
|
|
|
|
|
|
callConnectionStatus.value = "正在连接..."
|
|
|
|
|
|
if (currentCall.value?.callId === data.call_id) {
|
|
|
|
|
|
callStatus.value = "connecting"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理通话失败
|
|
|
|
|
|
const handleCallFailed = (data) => {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
if (
|
|
|
|
|
|
(currentCall.value && currentCall.value.callId === data.call_id) ||
|
|
|
|
|
|
(incomingCall.value && incomingCall.value.callId === data.call_id)
|
|
|
|
|
|
) {
|
2025-07-08 16:43:09 +08:00
|
|
|
|
callError.value = "failed"
|
|
|
|
|
|
callConnectionStatus.value = "连接失败"
|
|
|
|
|
|
|
|
|
|
|
|
const peerId = currentCall.value?.peerId || incomingCall.value?.callerId
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
const callMessage = {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
type:
|
|
|
|
|
|
currentCall.value?.callType === "video" || incomingCall.value?.callType === "video"
|
|
|
|
|
|
? "video-call"
|
|
|
|
|
|
: "audio-call",
|
2025-07-08 16:43:09 +08:00
|
|
|
|
content: "[连接失败]",
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: peerId,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: 0,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
|
|
|
|
|
friend.lastMessage = "[连接失败]"
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[连接失败]", friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
resetCallState()
|
|
|
|
|
|
}, 5000)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理来电
|
|
|
|
|
|
const handleIncomingCall = (data) => {
|
2025-07-07 16:51:55 +08:00
|
|
|
|
if (isInCall.value) {
|
2025-07-08 16:43:09 +08:00
|
|
|
|
console.log("当前正在通话中,发送忙线状态")
|
2025-07-09 09:34:26 +08:00
|
|
|
|
callAPI.sendBusy(userStore.currentUser.id, data.sender_user_id, data.call_id, data.message_type)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === data.sender_user_id)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
friend.lastMessage = "[未接来电]"
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[未接来电]", friend.unreadCount)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
|
|
|
|
|
const callMessage = {
|
2025-07-09 09:34:26 +08:00
|
|
|
|
type: data.message_type === 6 ? "video-call" : "audio-call",
|
2025-07-08 16:43:09 +08:00
|
|
|
|
content: "[未接来电]",
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
2025-07-09 09:34:26 +08:00
|
|
|
|
senderId: data.sender_user_id,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: 0,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
resetCallState()
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-05 16:09:14 +08:00
|
|
|
|
incomingCall.value = {
|
|
|
|
|
|
callId: data.call_id,
|
2025-07-09 09:34:26 +08:00
|
|
|
|
callerId: data.sender_user_id,
|
|
|
|
|
|
callType: data.message_type === 6 ? "video" : "audio",
|
|
|
|
|
|
content: data.content,
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
callStatus.value = "ringing"
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callError.value = null
|
|
|
|
|
|
callConnectionStatus.value = "收到来电"
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
|
|
|
|
|
console.log("收到来电:", incomingCall.value)
|
2025-07-09 10:37:57 +08:00
|
|
|
|
|
|
|
|
|
|
setCallTimeout(() => {
|
|
|
|
|
|
if (callStatus.value === "ringing" && incomingCall.value) {
|
|
|
|
|
|
console.log("来电超时,自动拒绝")
|
|
|
|
|
|
rejectCall("no-answer")
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 30000)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理通话接受
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const handleCallAccepted = async (data) => {
|
2025-07-05 16:09:14 +08:00
|
|
|
|
if (currentCall.value && currentCall.value.callId === data.call_id) {
|
2025-07-09 09:34:26 +08:00
|
|
|
|
if (callStatus.value === "connecting" || callStatus.value === "ongoing") {
|
|
|
|
|
|
console.log("通话已在连接中,忽略重复的 accepted 信令")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
callStatus.value = "connecting"
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callError.value = null
|
2025-07-09 08:18:59 +08:00
|
|
|
|
callConnectionStatus.value = "对方已接听,正在连接..."
|
|
|
|
|
|
console.log("通话已被接受,开始交换信令")
|
|
|
|
|
|
|
2025-07-09 14:05:12 +08:00
|
|
|
|
// 显示友好提示
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (callStatus.value === "connecting") {
|
|
|
|
|
|
callConnectionStatus.value = "对方已接听,正在建立连接..."
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 1000)
|
|
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
try {
|
2025-07-09 12:53:08 +08:00
|
|
|
|
if (!webrtc.localStream.value) {
|
|
|
|
|
|
console.log("获取本地媒体用于创建Offer")
|
|
|
|
|
|
await webrtc.getLocalMedia({
|
|
|
|
|
|
audio: true,
|
|
|
|
|
|
video: currentCall.value.callType === "video",
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const offer = await webrtc.createOffer(currentCall.value.callId, currentCall.value.peerId)
|
|
|
|
|
|
|
2025-07-09 14:05:12 +08:00
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
|
|
|
|
|
console.log("📤 准备发送 OFFER 信令:")
|
|
|
|
|
|
console.log("📋 Offer 类型:", offer.type)
|
|
|
|
|
|
console.log("📋 Offer SDP (前200字符):", offer.sdp ? offer.sdp.substring(0, 200) + "..." : "无SDP")
|
|
|
|
|
|
console.log("📋 通话ID:", currentCall.value.callId)
|
|
|
|
|
|
console.log("📋 接收方:", currentCall.value.peerId)
|
|
|
|
|
|
console.log("📋 完整 Offer 对象:", offer)
|
|
|
|
|
|
console.log("🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯🎯")
|
|
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
await callAPI.sendOffer(
|
|
|
|
|
|
userStore.currentUser.id,
|
|
|
|
|
|
currentCall.value.peerId,
|
|
|
|
|
|
currentCall.value.callId,
|
|
|
|
|
|
offer,
|
|
|
|
|
|
currentCall.value.callType === "video" ? 6 : 7,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-09 14:05:12 +08:00
|
|
|
|
console.log("✅ 发送Offer成功")
|
2025-07-09 09:34:26 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("创建Offer失败:", error)
|
|
|
|
|
|
endCall("failed")
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理通话拒绝
|
|
|
|
|
|
const handleCallRejected = (data) => {
|
|
|
|
|
|
if (currentCall.value && currentCall.value.callId === data.call_id) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
resetCallState()
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callConnectionStatus.value = "对方已拒绝"
|
2025-07-05 16:09:14 +08:00
|
|
|
|
console.log("通话已被拒绝")
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === currentCall.value.peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
const callMessage = {
|
|
|
|
|
|
type: currentCall.value.callType === "video" ? "video-call" : "audio-call",
|
|
|
|
|
|
content: "[对方已拒绝]",
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: userStore.currentUser?.id,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: 0,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
|
|
|
|
|
friend.lastMessage = "[对方已拒绝]"
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[对方已拒绝]", friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-07 16:51:55 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理通话结束
|
|
|
|
|
|
const handleCallEnded = (data) => {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
if (
|
|
|
|
|
|
(currentCall.value && currentCall.value.callId === data.call_id) ||
|
|
|
|
|
|
(incomingCall.value && incomingCall.value.callId === data.call_id)
|
|
|
|
|
|
) {
|
2025-07-08 16:43:09 +08:00
|
|
|
|
const peerId = currentCall.value?.peerId || incomingCall.value?.callerId
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const duration =
|
|
|
|
|
|
callStatus.value === "ongoing"
|
|
|
|
|
|
? Math.floor((Date.now() - (currentCall.value?.startTime || incomingCall.value?.startTime)) / 1000)
|
|
|
|
|
|
: 0
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
|
|
|
|
|
const callMessage = {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
type:
|
|
|
|
|
|
currentCall.value?.callType === "video" || incomingCall.value?.callType === "video"
|
|
|
|
|
|
? "video-call"
|
|
|
|
|
|
: "audio-call",
|
|
|
|
|
|
content:
|
|
|
|
|
|
callStatus.value === "ongoing"
|
|
|
|
|
|
? `[${currentCall.value?.callType === "video" ? "视频" : "语音"}通话] ${Math.floor(duration / 60)}分${duration % 60}秒`
|
|
|
|
|
|
: "[通话结束]",
|
2025-07-08 16:43:09 +08:00
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: peerId,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: duration,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
|
|
|
|
|
friend.lastMessage = callMessage.content
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, callMessage.content, friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
resetCallState()
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callConnectionStatus.value = "通话已结束"
|
2025-07-05 16:09:14 +08:00
|
|
|
|
console.log("通话已结束")
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理ICE候选
|
|
|
|
|
|
const handleIceCandidate = (data) => {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
if (
|
|
|
|
|
|
(currentCall.value && currentCall.value.callId === data.call_id) ||
|
|
|
|
|
|
(incomingCall.value && incomingCall.value.callId === data.call_id)
|
|
|
|
|
|
) {
|
|
|
|
|
|
try {
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const candidate = JSON.parse(data.content || data.message_content || "{}")
|
2025-07-09 08:18:59 +08:00
|
|
|
|
console.log("收到ICE候选:", candidate)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
webrtc.addIceCandidate(candidate)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("处理ICE候选失败:", error)
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-07 16:51:55 +08:00
|
|
|
|
// 处理无人接听
|
|
|
|
|
|
const handleCallNoAnswer = (data) => {
|
|
|
|
|
|
if (currentCall.value && currentCall.value.callId === data.call_id) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === currentCall.value.peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
const callMessage = {
|
|
|
|
|
|
type: currentCall.value.callType === "video" ? "video-call" : "audio-call",
|
|
|
|
|
|
content: "[未接来电]",
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: userStore.currentUser?.id,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: 0,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
|
|
|
|
|
friend.lastMessage = "[未接来电]"
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[未接来电]", friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
resetCallState()
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callConnectionStatus.value = "对方无人接听"
|
|
|
|
|
|
console.log("对方无人接听")
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理对方忙线
|
|
|
|
|
|
const handleCallBusy = (data) => {
|
|
|
|
|
|
if (currentCall.value && currentCall.value.callId === data.call_id) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === currentCall.value.peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
const callMessage = {
|
|
|
|
|
|
type: currentCall.value.callType === "video" ? "video-call" : "audio-call",
|
|
|
|
|
|
content: "[对方忙线]",
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: userStore.currentUser?.id,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: 0,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
|
|
|
|
|
friend.lastMessage = "[对方忙线]"
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[对方忙线]", friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
resetCallState()
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callConnectionStatus.value = "对方忙线中"
|
|
|
|
|
|
console.log("对方忙线中")
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
2025-07-08 16:43:09 +08:00
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理对方挂断
|
|
|
|
|
|
const handleCallHangup = (data) => {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const callId = data?.call_id || currentCall.value?.callId || incomingCall.value?.callId
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const peerId = data?.sender_user_id || currentCall.value?.peerId || incomingCall.value?.callerId
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
if (callId && peerId) {
|
|
|
|
|
|
const friend = friends.value.find((f) => f.id === peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const duration =
|
|
|
|
|
|
callStatus.value === "ongoing"
|
|
|
|
|
|
? Math.floor((Date.now() - (currentCall.value?.startTime || incomingCall.value?.startTime)) / 1000)
|
|
|
|
|
|
: 0
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
|
|
|
|
|
const callMessage = {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
type: currentCall.value?.callType || incomingCall.value?.callType === "video" ? "video-call" : "audio-call",
|
2025-07-09 12:53:08 +08:00
|
|
|
|
content: duration > 0 ? `[对方已挂断] ${Math.floor(duration / 60)}分${duration % 60}秒` : "[对方已挂断]",
|
2025-07-08 16:43:09 +08:00
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: peerId,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: duration,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
friend.lastMessage = callMessage.content
|
2025-07-08 16:43:09 +08:00
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, friend.lastMessage, friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
callStatus.value = "hangup"
|
2025-07-08 16:43:09 +08:00
|
|
|
|
callConnectionStatus.value = "对方已挂断"
|
|
|
|
|
|
console.log("对方已挂断通话")
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
2025-07-09 12:53:08 +08:00
|
|
|
|
resetCallState()
|
|
|
|
|
|
}, 3000)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理对方掉线
|
|
|
|
|
|
const handleCallDisconnected = (data) => {
|
|
|
|
|
|
const callId = data?.call_id || currentCall.value?.callId || incomingCall.value?.callId
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const peerId = data?.sender_user_id || currentCall.value?.peerId || incomingCall.value?.callerId
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
|
|
|
|
|
if (callId && peerId) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const duration =
|
|
|
|
|
|
callStatus.value === "ongoing"
|
|
|
|
|
|
? Math.floor((Date.now() - (currentCall.value?.startTime || incomingCall.value?.startTime)) / 1000)
|
|
|
|
|
|
: 0
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
|
|
|
|
|
const callMessage = {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
type: currentCall.value?.callType || incomingCall.value?.callType === "video" ? "video-call" : "audio-call",
|
2025-07-09 12:53:08 +08:00
|
|
|
|
content: duration > 0 ? `[网络中断] ${Math.floor(duration / 60)}分${duration % 60}秒` : "[网络中断]",
|
2025-07-08 16:43:09 +08:00
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: peerId,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: duration,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
friend.lastMessage = callMessage.content
|
2025-07-08 16:43:09 +08:00
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, friend.lastMessage, friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
callStatus.value = "disconnected"
|
2025-07-08 16:43:09 +08:00
|
|
|
|
callConnectionStatus.value = "网络连接已断开"
|
|
|
|
|
|
console.log("网络连接已断开")
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
2025-07-09 12:53:08 +08:00
|
|
|
|
resetCallState()
|
|
|
|
|
|
}, 3000)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理对方终止呼叫
|
|
|
|
|
|
const handleCallTerminated = (data) => {
|
|
|
|
|
|
const callId = data.call_id
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const peerId = data.sender_user_id
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
if (
|
|
|
|
|
|
(currentCall.value && currentCall.value.callId === callId) ||
|
|
|
|
|
|
(incomingCall.value && incomingCall.value.callId === callId)
|
|
|
|
|
|
) {
|
|
|
|
|
|
const friend = friends.value.find((f) => f.id === peerId)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
if (friend) {
|
|
|
|
|
|
const callMessage = {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
type: currentCall.value?.callType || incomingCall.value?.callType === "video" ? "video-call" : "audio-call",
|
2025-07-08 16:43:09 +08:00
|
|
|
|
content: "[对方已终止呼叫]",
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: peerId,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: 0,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
|
|
|
|
|
friend.lastMessage = "[对方已终止呼叫]"
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[对方已终止呼叫]", friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resetCallState()
|
|
|
|
|
|
callConnectionStatus.value = "对方已终止呼叫"
|
|
|
|
|
|
console.log("对方已终止呼叫")
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-05 16:09:14 +08:00
|
|
|
|
// 发起通话
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const startCall = async (peerId, type) => {
|
2025-07-07 16:51:55 +08:00
|
|
|
|
if (isInCall.value) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
alert("您正在通话中,请先结束当前通话。")
|
2025-07-07 16:51:55 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === peerId)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
if (!friend) {
|
2025-07-09 08:18:59 +08:00
|
|
|
|
alert("好友信息无效")
|
2025-07-07 16:51:55 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
if (!userStore.currentUser?.id) {
|
|
|
|
|
|
alert("用户未登录,无法发起通话")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
resetCallState()
|
|
|
|
|
|
|
2025-07-05 16:09:14 +08:00
|
|
|
|
const callId = Date.now().toString() + Math.random().toString(36).substr(2, 9)
|
|
|
|
|
|
|
|
|
|
|
|
currentCall.value = {
|
|
|
|
|
|
callId: callId,
|
|
|
|
|
|
peerId: peerId,
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callType: type,
|
|
|
|
|
|
startTime: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
status: "calling",
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
callStatus.value = "calling"
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callConnectionStatus.value = "等待对方接听..."
|
|
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
// 立即获取本地媒体并显示
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log("发起通话,立即获取本地媒体...")
|
|
|
|
|
|
await webrtc.getLocalMedia({
|
|
|
|
|
|
audio: true,
|
|
|
|
|
|
video: type === "video",
|
|
|
|
|
|
})
|
|
|
|
|
|
console.log("本地媒体获取成功,可以显示自己的摄像头")
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.warn("获取本地媒体失败,但继续通话流程:", error)
|
|
|
|
|
|
}
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
// 设置30秒超时
|
2025-07-08 16:43:09 +08:00
|
|
|
|
setCallTimeout(() => {
|
2025-07-07 16:51:55 +08:00
|
|
|
|
if (callStatus.value === "calling") {
|
2025-07-08 16:43:09 +08:00
|
|
|
|
resetCallState()
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callConnectionStatus.value = "对方无人接听"
|
|
|
|
|
|
console.log("呼叫超时,对方无人接听")
|
|
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
callAPI.sendNoAnswer(userStore.currentUser.id, peerId, callId, type === "video" ? 6 : 7)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
|
|
|
|
|
friend.lastMessage = "[未接来电]"
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[未接来电]", friend.unreadCount)
|
|
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
const callMessage = {
|
|
|
|
|
|
type: type === "video" ? "video-call" : "audio-call",
|
|
|
|
|
|
content: "[未接来电]",
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: userStore.currentUser?.id,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
2025-07-09 08:18:59 +08:00
|
|
|
|
duration: 0,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
|
|
|
|
|
|
2025-07-07 16:51:55 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 30000)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
// 发送通话请求
|
2025-07-09 09:34:26 +08:00
|
|
|
|
try {
|
|
|
|
|
|
console.log("准备发送通话邀请:", {
|
|
|
|
|
|
senderUserId: userStore.currentUser.id,
|
|
|
|
|
|
receiverUserId: peerId,
|
|
|
|
|
|
callId: callId,
|
|
|
|
|
|
callType: type === "video" ? 6 : 7,
|
|
|
|
|
|
})
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
await callAPI.invite(userStore.currentUser.id, peerId, callId, type === "video" ? 6 : 7)
|
|
|
|
|
|
|
|
|
|
|
|
console.log("发起通话成功:", currentCall.value)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("发起通话失败:", error)
|
|
|
|
|
|
resetCallState()
|
|
|
|
|
|
|
|
|
|
|
|
let errorMessage = "发起通话失败"
|
|
|
|
|
|
if (error.response?.status === 400) {
|
|
|
|
|
|
errorMessage = "请求参数错误,请检查网络连接"
|
|
|
|
|
|
} else if (error.response?.status === 500) {
|
|
|
|
|
|
errorMessage = "服务器错误,请稍后重试"
|
|
|
|
|
|
} else if (error.message) {
|
|
|
|
|
|
errorMessage = error.message
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
alert(errorMessage)
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 接听来电
|
2025-07-09 08:18:59 +08:00
|
|
|
|
const acceptCall = async () => {
|
2025-07-09 10:37:57 +08:00
|
|
|
|
if (!incomingCall.value) {
|
|
|
|
|
|
console.log("没有来电,无法接听")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
if (callStatus.value === "connecting" || callStatus.value === "ongoing") {
|
|
|
|
|
|
console.log("通话已在进行中,忽略重复接听")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 10:37:57 +08:00
|
|
|
|
clearCallTimeout()
|
|
|
|
|
|
|
|
|
|
|
|
console.log("开始接听通话:", incomingCall.value)
|
|
|
|
|
|
|
2025-07-08 16:43:09 +08:00
|
|
|
|
callStatus.value = "connecting"
|
|
|
|
|
|
callConnectionStatus.value = "正在连接..."
|
|
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
try {
|
|
|
|
|
|
// 获取本地媒体
|
2025-07-09 12:53:08 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await webrtc.getLocalMedia({
|
|
|
|
|
|
audio: true,
|
|
|
|
|
|
video: incomingCall.value.callType === "video",
|
|
|
|
|
|
})
|
|
|
|
|
|
console.log("接听时获取本地媒体成功")
|
|
|
|
|
|
} catch (mediaError) {
|
|
|
|
|
|
console.warn("接听时获取本地媒体失败,但继续通话流程:", mediaError)
|
|
|
|
|
|
}
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 12:53:08 +08:00
|
|
|
|
// 发送接受信号
|
2025-07-09 09:34:26 +08:00
|
|
|
|
await callAPI.accept(
|
|
|
|
|
|
userStore.currentUser.id,
|
|
|
|
|
|
incomingCall.value.callerId,
|
|
|
|
|
|
incomingCall.value.callId,
|
|
|
|
|
|
incomingCall.value.callType === "video" ? 6 : 7,
|
|
|
|
|
|
)
|
2025-07-09 08:18:59 +08:00
|
|
|
|
|
|
|
|
|
|
currentCall.value = {
|
|
|
|
|
|
callId: incomingCall.value.callId,
|
|
|
|
|
|
peerId: incomingCall.value.callerId,
|
|
|
|
|
|
callType: incomingCall.value.callType,
|
|
|
|
|
|
startTime: Date.now(),
|
|
|
|
|
|
status: "connecting",
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
console.log("接听通话成功,等待信令交换...")
|
2025-07-09 08:18:59 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("接听通话失败:", error)
|
|
|
|
|
|
rejectCall("failed")
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 拒绝来电
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const rejectCall = async (reason = "rejected") => {
|
2025-07-09 10:37:57 +08:00
|
|
|
|
if (!incomingCall.value) {
|
|
|
|
|
|
console.log("没有来电,无法拒绝")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (callStatus.value === "connecting" || callStatus.value === "ongoing") {
|
|
|
|
|
|
console.log("通话已在进行中,无法拒绝")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log("开始拒绝通话:", reason)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
try {
|
|
|
|
|
|
if (reason === "rejected") {
|
|
|
|
|
|
await callAPI.reject(
|
|
|
|
|
|
userStore.currentUser.id,
|
|
|
|
|
|
incomingCall.value.callerId,
|
|
|
|
|
|
incomingCall.value.callId,
|
|
|
|
|
|
incomingCall.value.callType === "video" ? 6 : 7,
|
|
|
|
|
|
)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await callAPI.sendFailed(
|
|
|
|
|
|
userStore.currentUser.id,
|
|
|
|
|
|
incomingCall.value.callerId,
|
|
|
|
|
|
incomingCall.value.callId,
|
|
|
|
|
|
incomingCall.value.callType === "video" ? 6 : 7,
|
|
|
|
|
|
)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === incomingCall.value.callerId)
|
|
|
|
|
|
if (friend) {
|
2025-07-09 10:37:57 +08:00
|
|
|
|
const content = reason === "busy" ? "[对方忙线]" : reason === "no-answer" ? "[未接来电]" : "[已拒绝]"
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const callMessage = {
|
|
|
|
|
|
type: incomingCall.value.callType === "video" ? "video-call" : "audio-call",
|
|
|
|
|
|
content: content,
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: userStore.currentUser?.id,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
duration: 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
friend.lastMessage = content
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, content, friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resetCallState()
|
2025-07-09 10:37:57 +08:00
|
|
|
|
callConnectionStatus.value = reason === "busy" ? "对方忙线" : reason === "no-answer" ? "无人接听" : "已拒绝"
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
|
|
|
|
|
|
|
|
|
|
|
console.log("拒绝通话成功")
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("拒绝通话失败:", error)
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 结束通话
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const endCall = async (reason = "ended") => {
|
2025-07-05 16:09:14 +08:00
|
|
|
|
const callId = currentCall.value?.callId || incomingCall.value?.callId
|
|
|
|
|
|
const peerId = currentCall.value?.peerId || incomingCall.value?.callerId
|
2025-07-07 16:51:55 +08:00
|
|
|
|
const callTypeVal = currentCall.value?.callType || incomingCall.value?.callType
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 08:18:59 +08:00
|
|
|
|
if (!callId || !peerId) {
|
|
|
|
|
|
resetCallState()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await callAPI.end(userStore.currentUser.id, peerId, callId, callTypeVal === "video" ? 6 : 7)
|
2025-07-05 16:09:14 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const friend = friends.value.find((f) => f.id === peerId)
|
|
|
|
|
|
if (friend) {
|
|
|
|
|
|
const duration =
|
|
|
|
|
|
callStatus.value === "ongoing"
|
|
|
|
|
|
? Math.floor((Date.now() - (currentCall.value?.startTime || incomingCall.value?.startTime)) / 1000)
|
|
|
|
|
|
: 0
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
let content = ""
|
|
|
|
|
|
if (reason === "no-answer") {
|
|
|
|
|
|
content = "[未接来电]"
|
|
|
|
|
|
} else if (duration > 0) {
|
|
|
|
|
|
content = `[${callTypeVal === "video" ? "视频" : "语音"}通话] ${Math.floor(duration / 60)}分${duration % 60}秒`
|
|
|
|
|
|
} else {
|
|
|
|
|
|
content = "[通话结束]"
|
|
|
|
|
|
}
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
const callMessage = {
|
|
|
|
|
|
type: callTypeVal === "video" ? "video-call" : "audio-call",
|
|
|
|
|
|
content: content,
|
|
|
|
|
|
time: new Date().toLocaleTimeString().slice(0, 5),
|
|
|
|
|
|
senderId: userStore.currentUser?.id,
|
|
|
|
|
|
read: true,
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
duration: duration,
|
|
|
|
|
|
}
|
|
|
|
|
|
chatDB.saveMessage(callMessage, userStore.currentUser.id, friend.id)
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
friend.lastMessage = content
|
|
|
|
|
|
chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, content, friend.unreadCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resetCallState()
|
|
|
|
|
|
callConnectionStatus.value = reason === "no-answer" ? "对方无人接听" : "通话结束"
|
2025-07-08 16:43:09 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
callConnectionStatus.value = ""
|
|
|
|
|
|
}, 5000)
|
2025-07-07 16:51:55 +08:00
|
|
|
|
|
2025-07-09 09:34:26 +08:00
|
|
|
|
console.log("结束通话成功")
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("结束通话失败:", error)
|
|
|
|
|
|
resetCallState()
|
|
|
|
|
|
}
|
2025-07-05 16:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:54:52 +08:00
|
|
|
|
return {
|
|
|
|
|
|
friends,
|
|
|
|
|
|
currentFriend,
|
|
|
|
|
|
messages,
|
|
|
|
|
|
connectionStatus,
|
|
|
|
|
|
socket,
|
2025-07-05 16:09:14 +08:00
|
|
|
|
currentCall,
|
|
|
|
|
|
incomingCall,
|
|
|
|
|
|
callStatus,
|
2025-07-07 16:51:55 +08:00
|
|
|
|
callError,
|
|
|
|
|
|
callConnectionStatus,
|
2025-07-02 16:54:52 +08:00
|
|
|
|
isConnected,
|
2025-07-05 16:09:14 +08:00
|
|
|
|
isInCall,
|
|
|
|
|
|
isCalling,
|
|
|
|
|
|
isRinging,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
isConnecting,
|
2025-07-05 16:09:14 +08:00
|
|
|
|
isOngoingCall,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
isDisconnected,
|
|
|
|
|
|
isHangup,
|
2025-07-03 15:36:34 +08:00
|
|
|
|
initDB,
|
2025-07-02 16:54:52 +08:00
|
|
|
|
loadFriends,
|
2025-07-03 15:36:34 +08:00
|
|
|
|
setCurrentFriend,
|
2025-07-03 17:02:08 +08:00
|
|
|
|
switchFriend,
|
2025-07-02 16:54:52 +08:00
|
|
|
|
addMessage,
|
|
|
|
|
|
handleIncomingMessage,
|
2025-07-05 16:09:14 +08:00
|
|
|
|
handleCallSignal,
|
|
|
|
|
|
startCall,
|
|
|
|
|
|
acceptCall,
|
|
|
|
|
|
rejectCall,
|
2025-07-08 16:43:09 +08:00
|
|
|
|
endCall,
|
|
|
|
|
|
resetCallState,
|
2025-07-09 09:34:26 +08:00
|
|
|
|
sendCallSignal: sendCallSignalInternal,
|
2025-07-09 08:18:59 +08:00
|
|
|
|
clearCallTimeout,
|
2025-07-02 16:54:52 +08:00
|
|
|
|
}
|
2025-07-09 08:18:59 +08:00
|
|
|
|
})
|