@@ -1,5 +1,5 @@
import { defineStore } from "pinia"
import { ref , computed } from "vue"
import { ref , computed , watch } from "vue"
import { chatDB } from "@/utils/db"
import { useWebRTC } from "@/composables/useWebRTC"
import { useUserStore } from "@/stores/user"
@@ -12,22 +12,82 @@ export const useChatStore = defineStore("chat", () => {
const connectionStatus = ref ( "disconnected" )
const socket = ref ( null )
// 通话相关状态
// 通话相关状态增强
const currentCall = ref ( null )
const incomingCall = ref ( null )
const callStatus = ref ( "idle" ) // idle, calling, ringing, ongoing, ended
const callError = ref ( null ) // 错误状态:'rejected', 'no-answer', 'busy'
const callConnectionStatus = ref ( "" ) // 连接状态:等待接听、正在连接等
const callStatus = ref ( "idle" ) // idle, calling, ringing, connecting, ongoing, ended, disconnected, hangup
const callError = ref ( null ) // 错误状态:'rejected', 'no-answer', 'busy', 'failed'
const callConnectionStatus = ref ( "" ) // 连接状态描述
const callTimeout = ref ( null ) // 通话超时计时器
const webrtc = useWebRTC ( )
const userStore = useUserStore ( )
// 计算属性
const isConnected = computed ( ( ) => connectionStatus . value === "connected" )
const isInCall = computed ( ( ) => callStatus . value !== "idle" )
const isInCall = computed ( ( ) =>
[ "calling" , "ringing" , "connecting" , "ongoing" ] . includes ( callStatus . value )
)
const isCalling = computed ( ( ) => callStatus . value === "calling" )
const isRinging = computed ( ( ) => callStatus . value === "ringing" )
const isConnecting = computed ( ( ) => callStatus . value === "connecting" )
const isOngoingCall = computed ( ( ) => callStatus . value === "ongoing" )
const isDisconnected = computed ( ( ) => callStatus . value === "disconnected" )
const isHangup = computed ( ( ) => callStatus . value === "hangup" )
// 监听通话状态变化
watch ( ( ) => callStatus . value , ( newStatus ) => {
console . log ( "通话状态变化:" , newStatus )
switch ( newStatus ) {
case "disconnected" :
handleCallDisconnected ( )
break
case "hangup" :
handleCallHangup ( )
break
}
} )
// 重置所有通话状态
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 )
}
// 发送通话信令
const sendCallSignal = ( signal ) => {
if ( socket . value && socket . value . readyState === WebSocket . OPEN ) {
const message = {
request _type : "call_signal" ,
... signal ,
sender _user _id : userStore . currentUser ? . id
}
socket . value . send ( JSON . stringify ( message ) )
} else {
console . error ( "WebSocket未连接, 无法发送通话信令" )
}
}
// 初始化数据库
const initDB = async ( ) => {
@@ -106,24 +166,25 @@ export const useChatStore = defineStore("chat", () => {
const addMessage = async ( message , currentUserId ) => {
messages . value . push ( message )
if ( currentFriend . value ) {
// 更新好友列表中的最后消息
const friend = friends . value . find ( ( f ) => ( f . id === message . senderId || f . id === currentFriend . value ? . id ) )
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 = "[通话]"
friend . lastMessage = lastMessage . length > 20 ? lastMessage . substring ( 0 , 20 ) + "..." : lastMessage
// 更新数据库中的好友信息
await chatDB . updateFriendLastMessage ( friend . id , currentUserId , friend . lastMessage , 0 )
}
if ( currentFriend . value && friend && friend . id === currentFriend . value . id ) {
try {
// 保存到数据库
await chatDB . saveMessage ( message , currentUserId , currentFriend . value . id )
// 更新好友列表中的最后消息
const friend = friends . value . find ( ( f ) => f . id === currentFriend . value . id )
if ( friend ) {
let lastMessage = message . content
if ( message . type === "image" ) lastMessage = "[图片]"
if ( message . type === "video" ) lastMessage = "[视频]"
if ( message . type === "audio" ) lastMessage = "[语音]"
friend . lastMessage = lastMessage . length > 20 ? lastMessage . substring ( 0 , 20 ) + "..." : lastMessage
// 更新数据库中的好友信息
await chatDB . updateFriendLastMessage ( friend . id , currentUserId , friend . lastMessage , 0 )
}
} catch ( error ) {
console . error ( "保存消息失败:" , error )
}
@@ -172,18 +233,22 @@ export const useChatStore = defineStore("chat", () => {
const friend = friends . value . find ( ( f ) => f . id == senderId )
if ( friend ) {
friend . unreadCount = ( friend . unreadCount || 0 ) + 1
// 更新最后消息
let lastMessage = newMessage . content
if ( newMessage . type === "image" ) lastMessage = "[图片]"
if ( newMessage . type === "video" ) lastMessage = "[视频]"
if ( newMessage . type === "audio" ) lastMessage = "[语音]"
friend . lastMessage = lastMessage
// 更新数据库
await chatDB . updateFriendLastMessage ( friend . id , currentUserId , lastMessage , friend . unreadCount )
}
}
// 更新最后消息
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 )
}
} catch ( error ) {
console . error ( "处理接收消息失败:" , error )
}
@@ -202,6 +267,7 @@ export const useChatStore = defineStore("chat", () => {
// 处理通话信令
const handleCallSignal = ( data ) => {
console . log ( "处理通话信令:" , data )
console . log ( "通话状态:" , data . call _status )
// 根据通话状态处理
switch ( data . call _status ) {
@@ -220,22 +286,79 @@ export const useChatStore = defineStore("chat", () => {
case "candidate" :
handleIceCandidate ( data )
break
case "no-answer" : // 添加无人接听状态
case "no-answer" :
handleCallNoAnswer ( data )
break
case "connecting" : // 新增连接状态
c allConnectionStatus . value = "正在连接..."
case "connecting" :
handleC allConnecting ( data )
break
case "busy" : // 对方忙线中
case "busy" :
handleCallBusy ( data )
break
case "hangup" : // 对方主动挂断
handleCallHangup ( data )
break
case "disconnected" : // 对方掉线
handleCallDisconnected ( data )
break
case "terminated" : // 对方终止呼叫
handleCallTerminated ( data )
break
case "failed" :
handleCallFailed ( data )
break
}
}
// 处理连接中状态
const handleCallConnecting = ( data ) => {
callConnectionStatus . value = "正在连接..."
if ( currentCall . value ? . callId === data . call _id ) {
callStatus . value = "connecting"
}
}
// 处理通话失败
const handleCallFailed = ( data ) => {
if ( ( currentCall . value && currentCall . value . callId === data . call _id ) ||
( incomingCall . value && incomingCall . value . callId === data . call _id ) ) {
callError . value = "failed"
callConnectionStatus . value = "连接失败"
// 添加通话记录
const peerId = currentCall . value ? . peerId || incomingCall . value ? . callerId
const friend = friends . value . find ( f => f . id === peerId )
if ( friend ) {
const callMessage = {
type : currentCall . value ? . callType === "video" || incomingCall . value ? . callType === "video" ?
"video-call" : "audio-call" ,
content : "[连接失败]" ,
time : new Date ( ) . toLocaleTimeString ( ) . slice ( 0 , 5 ) ,
senderId : peerId ,
read : true ,
timestamp : Date . now ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = "[连接失败]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[连接失败]" , friend . unreadCount )
}
// 5秒后清除状态
setTimeout ( ( ) => {
resetCallState ( )
} , 5000 )
}
}
// 处理来电
const handleIncomingCall = ( data ) => {
// 如果自己正在通话中,自动拒绝并发送忙线状态
// 只有在当前没有通话时才处理来电
if ( isInCall . value ) {
// console.log 输出当前状态
console . log ( "当前正在通话中,发送忙线状态" )
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : data . sender _user _id ,
@@ -249,11 +372,26 @@ export const useChatStore = defineStore("chat", () => {
if ( friend ) {
friend . lastMessage = "[未接来电]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[未接来电]" , friend . unreadCount )
// 添加通话记录
const callMessage = {
type : data . message _type === 6 ? "video-call" : "audio-call" ,
content : "[未接来电]" ,
time : new Date ( ) . toLocaleTimeString ( ) . slice ( 0 , 5 ) ,
senderId : data . sender _user _id ,
read : true ,
timestamp : Date . now ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
}
return
}
// 重置所有通话状态
resetCallState ( ) ;
incomingCall . value = {
callId : data . call _id ,
callerId : data . sender _user _id ,
@@ -280,16 +418,31 @@ export const useChatStore = defineStore("chat", () => {
// 处理通话拒绝
const handleCallRejected = ( data ) => {
if ( currentCall . value && currentCall . value . callId === data . call _id ) {
c allStatus . value = "ended"
callError . value = "rejected"
resetC allState ( ) ;
callConnectionStatus . value = "对方已拒绝"
currentCall . value = null
console . log ( "通话已被拒绝" )
// 添加通话记录
const friend = friends . value . find ( f => f . id === currentCall . value . peerId )
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 ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = "[对方已拒绝]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[对方已拒绝]" , friend . unreadCount )
}
// 5秒后清除状态
setTimeout ( ( ) => {
callStatus . value = "idle"
callError . value = null
callConnectionStatus . value = ""
} , 5000 )
}
@@ -299,16 +452,38 @@ export const useChatStore = defineStore("chat", () => {
const handleCallEnded = ( data ) => {
if ( ( currentCall . value && currentCall . value . callId === data . call _id ) ||
( incomingCall . value && incomingCall . value . callId === data . call _id ) ) {
callStatus . value = "ended"
callError . value = null
// 添加通话记录
const peerId = currentCall . value ? . peerId || incomingCall . value ? . callerId
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
const callMessage = {
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 } 秒 ` :
"[通话结束]" ,
time : new Date ( ) . toLocaleTimeString ( ) . slice ( 0 , 5 ) ,
senderId : peerId ,
read : true ,
timestamp : Date . now ( ) ,
duration : duration
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = callMessage . content
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , callMessage . content , friend . unreadCount )
}
resetCallState ( ) ;
callConnectionStatus . value = "通话已结束"
currentCall . value = null
incomingCall . value = null
console . log ( "通话已结束" )
// 5秒后清除状态
setTimeout ( ( ) => {
callStatus . value = "idle"
callConnectionStatus . value = ""
} , 5000 )
}
@@ -316,25 +491,43 @@ export const useChatStore = defineStore("chat", () => {
// 处理ICE候选
const handleIceCandidate = ( data ) => {
if ( currentCall . value && currentCall . value . callId === data . call _id ) {
console . log ( "收到ICE候选:" , data . content )
// 这里可以添加WebRTC处理逻辑
if ( ( currentCall . value && currentCall . value . callId === data . call _id ) ||
( incomingCall . value && incomingCall . value . callId === data . call _id ) ) {
console . log ( "收到ICE候选:" , data . candidate )
// 添加到WebRTC连接
webrtc . addIceCandidate ( data . candidate )
}
}
// 处理无人接听
const handleCallNoAnswer = ( data ) => {
if ( currentCall . value && currentCall . value . callId === data . call _id ) {
callStatus . value = "ended"
callError . value = "no-answer"
// 添加未接来电记录
const friend = friends . value . find ( f => f . id === currentCall . value . peerId )
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 ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = "[未接来电]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[未接来电]" , friend . unreadCount )
}
resetCallState ( ) ;
callConnectionStatus . value = "对方无人接听"
currentCall . value = null
console . log ( "对方无人接听" )
// 5秒后清除状态
setTimeout ( ( ) => {
callStatus . value = "idle"
callError . value = null
callConnectionStatus . value = ""
} , 5000 )
}
@@ -343,16 +536,152 @@ export const useChatStore = defineStore("chat", () => {
// 处理对方忙线
const handleCallBusy = ( data ) => {
if ( currentCall . value && currentCall . value . callId === data . call _id ) {
callStatus . value = "ended"
callError . value = "busy"
// 添加忙线记录
const friend = friends . value . find ( f => f . id === currentCall . value . peerId )
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 ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = "[对方忙线]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[对方忙线]" , friend . unreadCount )
}
resetCallState ( ) ;
callConnectionStatus . value = "对方忙线中"
currentCall . value = null
console . log ( "对方忙线中" )
// 5秒后清除状态
setTimeout ( ( ) => {
callStatus . value = "idle "
callError . value = null
callConnection Status . value = ""
} , 5000 )
}
}
// 处理对方挂断
const handleCallHangup = ( data ) => {
const callId = data . call _id
const peerId = data . sender _user _id
if ( ( currentCall . value && currentCall . value . callId === callId ) ||
( incomingCall . value && incomingCall . value . callId === callId ) ) {
// 添加通话记录
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
const callMessage = {
type : currentCall . value ? . callType || incomingCall . value ? . callType === "video" ?
"video-call" : "audio-call" ,
content : ` [对方已挂断] ${ Math . floor ( duration / 60 ) } 分 ${ duration % 60 } 秒 ` ,
time : new Date ( ) . toLocaleTimeString ( ) . slice ( 0 , 5 ) ,
senderId : peerId ,
read : true ,
timestamp : Date . now ( ) ,
duration : duration
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = ` [对方已挂断] ${ Math . floor ( duration / 60 ) } 分 ${ duration % 60 } 秒 `
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , friend . lastMessage , friend . unreadCount )
}
resetCallState ( )
callConnectionStatus . value = "对方已挂断"
console . log ( "对方已挂断通话" )
// 5秒后清除状态
setTimeout ( ( ) => {
callConnectionStatus . value = ""
} , 5000 )
}
}
// 处理对方掉线
const handleCallDisconnected = ( data ) => {
const callId = data ? . call _id || currentCall . value ? . callId || incomingCall . value ? . callId
const peerId = data ? . sender _user _id || currentCall . value ? . peerId || incomingCall . value ? . callerId
if ( callId && peerId ) {
// 添加通话记录
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
const callMessage = {
type : currentCall . value ? . callType || incomingCall . value ? . callType === "video" ?
"video-call" : "audio-call" ,
content : ` [网络中断] ${ Math . floor ( duration / 60 ) } 分 ${ duration % 60 } 秒 ` ,
time : new Date ( ) . toLocaleTimeString ( ) . slice ( 0 , 5 ) ,
senderId : peerId ,
read : true ,
timestamp : Date . now ( ) ,
duration : duration
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = ` [网络中断] ${ Math . floor ( duration / 60 ) } 分 ${ duration % 60 } 秒 `
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , friend . lastMessage , friend . unreadCount )
}
resetCallState ( )
callConnectionStatus . value = "网络连接已断开"
console . log ( "网络连接已断开" )
// 5秒后清除状态
setTimeout ( ( ) => {
callConnectionStatus . value = ""
} , 5000 )
}
}
// 处理对方终止呼叫
const handleCallTerminated = ( data ) => {
const callId = data . call _id
const peerId = data . sender _user _id
if ( ( currentCall . value && currentCall . value . callId === callId ) ||
( incomingCall . value && incomingCall . value . callId === callId ) ) {
// 添加通话记录
const friend = friends . value . find ( f => f . id === peerId )
if ( friend ) {
const callMessage = {
type : currentCall . value ? . callType || incomingCall . value ? . callType === "video" ?
"video-call" : "audio-call" ,
content : "[对方已终止呼叫]" ,
time : new Date ( ) . toLocaleTimeString ( ) . slice ( 0 , 5 ) ,
senderId : peerId ,
read : true ,
timestamp : Date . now ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = "[对方已终止呼叫]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[对方已终止呼叫]" , friend . unreadCount )
}
resetCallState ( )
callConnectionStatus . value = "对方已终止呼叫"
console . log ( "对方已终止呼叫" )
// 5秒后清除状态
setTimeout ( ( ) => {
callConnectionStatus . value = ""
} , 5000 )
}
@@ -371,6 +700,8 @@ export const useChatStore = defineStore("chat", () => {
return
}
resetCallState ( )
const callId = Date . now ( ) . toString ( ) + Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 )
currentCall . value = {
@@ -382,14 +713,32 @@ export const useChatStore = defineStore("chat", () => {
}
callStatus . value = "calling"
callError . value = null
callConnectionStatus . value = "等待对方接听..."
// 创建WebRTC连接
webrtc . createPeerConnection ( callId , peerId , ( ) => {
// WebRTC连接成功回调
callStatus . value = "ongoing"
callConnectionStatus . value = "通话中"
clearCallTimeout ( )
} , ( error ) => {
// WebRTC连接失败回调
console . error ( "WebRTC连接失败:" , error )
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : peerId ,
type : type === "video" ? "video-call" : "audio-call" ,
callId : callId ,
callStatus : "failed"
} )
resetCallState ( )
callConnectionStatus . value = "连接失败"
} )
// 设置30秒超时( 无人接听)
const timeoutId = setTimeout ( ( ) => {
setCall Timeout ( ( ) => {
if ( callStatus . value === "calling" ) {
c allStatus . value = "ended"
callError . value = "no-answer"
resetC allState ( )
callConnectionStatus . value = "对方无人接听"
console . log ( "呼叫超时,对方无人接听" )
@@ -406,17 +755,25 @@ export const useChatStore = defineStore("chat", () => {
friend . lastMessage = "[未接来电]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[未接来电]" , friend . unreadCount )
// 添加通话记录
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 ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 5秒后清除状态
setTimeout ( ( ) => {
callStatus . value = "idle"
currentCall . value = null
callConnectionStatus . value = ""
} , 5000 )
}
} , 30000 )
currentCall . value . timeoutId = timeoutId
// 发送通话请求
sendMessage ( {
senderId : userStore . currentUser ? . id ,
@@ -433,37 +790,67 @@ export const useChatStore = defineStore("chat", () => {
const acceptCall = ( ) => {
if ( ! incomingCall . value ) return
// 清除可能存在的超时计时器
if ( currentCall . value ? . timeoutId ) {
clearTimeout ( currentCall . value . timeoutId )
}
// 发送接受信号
// 发送连接中信号
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : incomingCall . value . callerId ,
type : incomingCall . value . callType === "video" ? "video-call" : "audio-call" ,
callId : incomingCall . value . callId ,
callStatus : "accepted "
callStatus : "connecting "
} )
currentCall . value = {
callId : incomingCall . value . callId ,
peerId : incomingCall . value . callerId ,
callType : incomingCall . value . callType ,
startTime : Date . now ( ) ,
status : 'ongoing'
}
callStatus . value = "connecting"
callConnectionStatus . value = "正在连接..."
callStatus . value = "ongoing"
callConnectionStatus . value = "通话中"
incomingCall . value = null
callError . value = null
console . log ( "接听通话" )
// 创建WebRTC连接
webrtc . createPeerConnection (
incomingCall . value . callId ,
incomingCall . value . callerId ,
( ) => {
// WebRTC连接成功回调
callStatus . value = "ongoing"
callConnectionStatus . value = "通话中"
// 发送接受信号
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : incomingCall . value . callerId ,
type : incomingCall . value . callType === "video" ? "video-call" : "audio-call" ,
callId : incomingCall . value . callId ,
callStatus : "accepted"
} )
currentCall . value = {
callId : incomingCall . value . callId ,
peerId : incomingCall . value . callerId ,
callType : incomingCall . value . callType ,
startTime : Date . now ( ) ,
status : 'ongoing'
}
incomingCall . value = null
console . log ( "通话已连接" )
} ,
( error ) => {
// WebRTC连接失败回调
console . error ( "WebRTC连接失败:" , error )
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : incomingCall . value . callerId ,
type : incomingCall . value . callType === "video" ? "video-call" : "audio-call" ,
callId : incomingCall . value . callId ,
callStatus : "failed"
} )
resetCallState ( )
callConnectionStatus . value = "连接失败"
}
)
console . log ( "正在连接通话..." )
}
// 拒绝来电
const rejectCall = ( ) => {
const rejectCall = ( reason = "rejected" ) => {
if ( ! incomingCall . value ) return
// 发送拒绝信号
@@ -472,26 +859,35 @@ export const useChatStore = defineStore("chat", () => {
receiverId : incomingCall . value . callerId ,
type : incomingCall . value . callType === "video" ? "video-call" : "audio-call" ,
callId : incomingCall . value . callId ,
callStatus : "rejected"
callStatus : reason // 可以是'rejected'或'busy'
} )
callStatus . value = "ended"
callConnectionStatus . value = "已拒绝"
callError . value = "rejected"
// 更新好友最后消息为已拒绝
// 添加通话记录
const friend = friends . value . find ( f => f . id === incomingCall . value . callerId )
if ( friend ) {
friend . lastMessage = "[已拒绝]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[已拒绝]" , friend . unreadCount )
const content = reason === "busy" ? "[对方忙线]" : "[已拒绝]"
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 )
// 更新最后消息
friend . lastMessage = content
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , content , friend . unreadCount )
}
incomingCall . value = null
resetCallState ( )
callConnectionStatus . value = reason === "busy" ? "对方忙线" : "已拒绝"
// 5秒后清除状态
setTimeout ( ( ) => {
callStatus . value = "idle"
callError . value = null
callConnectionStatus . value = ""
} , 5000 )
@@ -499,41 +895,157 @@ export const useChatStore = defineStore("chat", () => {
}
// 结束通话
const endCall = ( ) => {
const endCall = ( reason = "ended" ) => {
const callId = currentCall . value ? . callId || incomingCall . value ? . callId
const peerId = currentCall . value ? . peerId || incomingCall . value ? . callerId
const callTypeVal = currentCall . value ? . callType || incomingCall . value ? . callType
if ( ! callId || ! peerId ) return
// 清除超时计时器
if ( currentCall . value ? . timeoutId ) {
clearTimeout ( currentCall . value . timeoutId )
}
// 发送结束信号
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : peerId ,
type : callTypeVal === "video" ? "video-call" : "audio-call" ,
callId : callId ,
callStatus : "ended"
callStatus : reason // 可以是'ended'或'no-answer'
} )
callStatus . value = "ended"
callConnectionStatus . value = "通话结束"
// 添加通话记录
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
let content = ""
if ( reason === "no-answer" ) {
content = "[未接来电]"
} else if ( duration > 0 ) {
content = ` [ ${ callTypeVal === "视频" ? "视频" : "语音" } 通话] ${ Math . floor ( duration / 60 ) } 分 ${ duration % 60 } 秒 `
} else {
content = "[通话结束]"
}
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 )
// 更新最后消息
friend . lastMessage = content
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , content , friend . unreadCount )
}
resetCallState ( )
callConnectionStatus . value = reason === "no-answer" ? "对方无人接听" : "通话结束"
// 5秒后清除状态
setTimeout ( ( ) => {
callStatus . value = "idle"
currentCall . value = null
incomingCall . value = null
callError . value = null
callConnectionStatus . value = ""
} , 5000 )
console . log ( "结束通话" )
}
// 主动挂断通话(用户点击挂断按钮)
const hangupCall = ( ) => {
const callId = currentCall . value ? . callId || incomingCall . value ? . callId
const peerId = currentCall . value ? . peerId || incomingCall . value ? . callerId
const callTypeVal = currentCall . value ? . callType || incomingCall . value ? . callType
if ( ! callId || ! peerId ) return
// 发送挂断信号
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : peerId ,
type : callTypeVal === "video" ? "video-call" : "audio-call" ,
callId : callId ,
callStatus : "hangup"
} )
// 添加通话记录
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
const callMessage = {
type : callTypeVal === "video" ? "video-call" : "audio-call" ,
content : ` [已挂断] ${ Math . floor ( duration / 60 ) } 分 ${ duration % 60 } 秒 ` ,
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 )
// 更新最后消息
friend . lastMessage = ` [已挂断] ${ Math . floor ( duration / 60 ) } 分 ${ duration % 60 } 秒 `
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , friend . lastMessage , friend . unreadCount )
}
resetCallState ( )
callConnectionStatus . value = "已挂断"
// 5秒后清除状态
setTimeout ( ( ) => {
callConnectionStatus . value = ""
} , 5000 )
console . log ( "主动挂断通话" )
}
// 终止呼叫(在对方接听前取消)
const terminateCall = ( ) => {
if ( ! currentCall . value ) return
// 发送终止信号
sendMessage ( {
senderId : userStore . currentUser ? . id ,
receiverId : currentCall . value . peerId ,
type : currentCall . value . callType === "video" ? "video-call" : "audio-call" ,
callId : currentCall . value . callId ,
callStatus : "terminated"
} )
// 添加通话记录
const friend = friends . value . find ( f => f . id === currentCall . value . peerId )
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 ( ) ,
duration : 0
}
chatDB . saveMessage ( callMessage , userStore . currentUser . id , friend . id )
// 更新最后消息
friend . lastMessage = "[已取消]"
chatDB . updateFriendLastMessage ( friend . id , userStore . currentUser . id , "[已取消]" , friend . unreadCount )
}
resetCallState ( )
callConnectionStatus . value = "已取消呼叫"
// 5秒后清除状态
setTimeout ( ( ) => {
callConnectionStatus . value = ""
} , 5000 )
console . log ( "终止呼叫" )
}
return {
friends ,
currentFriend ,
@@ -549,7 +1061,10 @@ export const useChatStore = defineStore("chat", () => {
isInCall ,
isCalling ,
isRinging ,
isConnecting ,
isOngoingCall ,
isDisconnected ,
isHangup ,
initDB ,
loadFriends ,
setCurrentFriend ,
@@ -560,6 +1075,11 @@ export const useChatStore = defineStore("chat", () => {
startCall ,
acceptCall ,
rejectCall ,
endCall
endCall ,
hangupCall ,
terminateCall ,
resetCallState ,
sendCallSignal ,
clearCallTimeout
}
} )