UI设计
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useTheme } from './composables/useTheme'
|
||||
import GlobalCallProvider from './components/call/GlobalCallProvider.vue'
|
||||
|
||||
const { theme } = useTheme()
|
||||
</script>
|
||||
@@ -7,6 +8,7 @@ const { theme } = useTheme()
|
||||
<template>
|
||||
<wd-config-provider :theme="theme">
|
||||
<KuRootView />
|
||||
<!-- 全局通话组件(单聊 + 群通话) -->
|
||||
<global-call-provider />
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -3,13 +3,11 @@ import { onLaunch, onShow } from '@dcloudio/uni-app'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { useAuthStore, useConversationStore } from '@/stores'
|
||||
import { wsManager } from '@/api/websocket'
|
||||
import { useGroupWebRTC } from '@/composables/useGroupWebRTC'
|
||||
import type { ChatMessage } from '@/types/api'
|
||||
|
||||
const { initTheme } = useTheme()
|
||||
const authStore = useAuthStore()
|
||||
const conversationStore = useConversationStore()
|
||||
const groupWebRTC = useGroupWebRTC()
|
||||
|
||||
// 全局消息处理器
|
||||
function handleGlobalMessage(message: ChatMessage) {
|
||||
@@ -30,8 +28,7 @@ async function initWebSocket() {
|
||||
await wsManager.connect(userId)
|
||||
// 注册全局消息处理器
|
||||
wsManager.onMessage(handleGlobalMessage)
|
||||
// 初始化群通话信令监听器
|
||||
groupWebRTC.initListener()
|
||||
// 群通话信令监听器已移至 App.ku.vue 初始化
|
||||
console.log('✅ WebSocket 全局初始化成功')
|
||||
} catch (error) {
|
||||
console.error('WebSocket 初始化失败:', error)
|
||||
|
||||
@@ -31,13 +31,15 @@
|
||||
|
||||
<!-- 视频区域 -->
|
||||
<view v-if="call.type === 'video'" class="video-area">
|
||||
<!-- 远端视频 - 使用原生 video 元素以支持 srcObject -->
|
||||
<video
|
||||
v-if="remoteStream"
|
||||
ref="remoteVideoRef"
|
||||
class="remote-video"
|
||||
autoplay
|
||||
playsinline
|
||||
/>
|
||||
:srcObject="remoteStream"
|
||||
></video>
|
||||
<view v-else class="video-placeholder">
|
||||
<view class="avatar-section">
|
||||
<view class="avatar-ring ring-1"></view>
|
||||
@@ -47,6 +49,7 @@
|
||||
<text class="caller-name">{{ call.callerName || '对方' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 本地视频 - 使用原生 video 元素以支持 srcObject -->
|
||||
<video
|
||||
v-if="localStream && !call.camOff"
|
||||
ref="localVideoRef"
|
||||
@@ -54,7 +57,8 @@
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
/>
|
||||
:srcObject="localStream"
|
||||
></video>
|
||||
</view>
|
||||
|
||||
<!-- 语音通话 / 视频占位 -->
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
<template>
|
||||
<!-- ========== 单聊通话组件 ========== -->
|
||||
<call-window
|
||||
v-if="callActive"
|
||||
:call="callState"
|
||||
:local-stream="localStreamValue"
|
||||
:remote-stream="remoteStreamValue"
|
||||
:format-duration="webrtc.formatDuration"
|
||||
@accept="webrtc.acceptCall()"
|
||||
@reject="webrtc.rejectCall()"
|
||||
@end="webrtc.endCall()"
|
||||
@toggle-mute="webrtc.toggleMute()"
|
||||
@toggle-camera="webrtc.toggleCamera()"
|
||||
@toggle-minimize="webrtc.toggleMinimize()"
|
||||
/>
|
||||
|
||||
<!-- ========== 群通话组件 ========== -->
|
||||
<!-- 全局群通话来电覆盖层 -->
|
||||
<group-call-incoming />
|
||||
|
||||
@@ -10,16 +26,30 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useWebRTC } from '@/composables/useWebRTC'
|
||||
import { useGroupWebRTC } from '@/composables/useGroupWebRTC'
|
||||
import CallWindow from './CallWindow.vue'
|
||||
import GroupCallIncoming from './GroupCallIncoming.vue'
|
||||
import GroupCallWindow from './GroupCallWindow.vue'
|
||||
import GroupCallBanner from './GroupCallBanner.vue'
|
||||
|
||||
const { initListener } = useGroupWebRTC()
|
||||
// 单聊通话
|
||||
const webrtc = useWebRTC()
|
||||
|
||||
// 使用计算属性确保响应式
|
||||
const callActive = computed(() => webrtc.call.active)
|
||||
const callState = computed(() => webrtc.call)
|
||||
const localStreamValue = computed(() => webrtc.localStream.value)
|
||||
const remoteStreamValue = computed(() => webrtc.remoteStream.value)
|
||||
|
||||
// 群通话
|
||||
const { initListener: initGroupListener } = useGroupWebRTC()
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化信令监听器
|
||||
initListener()
|
||||
// 初始化单聊通话信令监听器
|
||||
webrtc.initListener()
|
||||
// 初始化群通话信令监听器
|
||||
initGroupListener()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
/**
|
||||
* WebRTC 通话组合式函数 - UniApp 适配版
|
||||
* 主要支持 H5 平台,APP/小程序需要平台特定实现
|
||||
* WebRTC 通话组合式函数 - 全局单例模式
|
||||
* 支持在任意页面接听来电
|
||||
*/
|
||||
import { reactive, shallowRef, ref } from 'vue'
|
||||
import { reactive, shallowRef, ref, computed } from 'vue'
|
||||
import * as messageApi from '@/api/modules/message'
|
||||
import { wsManager } from '@/api/websocket'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useChatStore } from '@/stores/chat'
|
||||
import { useConversationStore } from '@/stores/conversation'
|
||||
import type { ChatMessage, Contact } from '@/types/api'
|
||||
import type { CallStatus } from '@/types/message'
|
||||
|
||||
@@ -24,15 +26,10 @@ export interface CallState {
|
||||
startTime: number | null
|
||||
callerName?: string
|
||||
callerAvatar?: string
|
||||
callerId?: string
|
||||
}
|
||||
|
||||
export function useWebRTC(
|
||||
userId: string,
|
||||
onIncomingCall?: (senderUserId: string) => void,
|
||||
getRoomId?: (receiverUserId?: string) => string
|
||||
) {
|
||||
const chatStore = useChatStore()
|
||||
|
||||
// --- 全局单例状态 ---
|
||||
const call = reactive<CallState>({
|
||||
active: false,
|
||||
minimized: false,
|
||||
@@ -56,14 +53,19 @@ export function useWebRTC(
|
||||
const pendingCandidates: RTCIceCandidate[] = []
|
||||
let currentReceiverUserId = ''
|
||||
let currentRoomId = ''
|
||||
|
||||
// 音频播放(使用内置音频方法)
|
||||
let audioContext: UniApp.InnerAudioContext | null = null
|
||||
|
||||
export function useWebRTC() {
|
||||
const authStore = useAuthStore()
|
||||
const chatStore = useChatStore()
|
||||
const conversationStore = useConversationStore()
|
||||
|
||||
const userId = computed(() => authStore.user?.id || '')
|
||||
const isActive = computed(() => call.active)
|
||||
|
||||
function playRingtone() {
|
||||
stopRingtone()
|
||||
// #ifdef H5
|
||||
// H5 使用简单的提示音
|
||||
try {
|
||||
const AudioContext = window.AudioContext || (window as any).webkitAudioContext
|
||||
const ctx = new AudioContext()
|
||||
@@ -87,22 +89,38 @@ export function useWebRTC(
|
||||
|
||||
function getSafeRoomId(targetUserId?: string): string | null {
|
||||
if (currentRoomId) return currentRoomId
|
||||
if (targetUserId && getRoomId) {
|
||||
const rid = getRoomId(targetUserId)
|
||||
if (rid) return rid
|
||||
|
||||
// 尝试从联系人获取房间ID
|
||||
if (targetUserId) {
|
||||
const contact = chatStore.contacts.find(c => c.contact_user_id === targetUserId)
|
||||
if (contact?.room_id) return contact.room_id
|
||||
}
|
||||
if (targetUserId && userId) {
|
||||
return [userId, targetUserId].sort().join('_')
|
||||
|
||||
// 尝试从会话获取房间ID
|
||||
if (targetUserId) {
|
||||
const conv = conversationStore.conversations.find(c => c.target_id === targetUserId)
|
||||
if (conv?.room_id) return conv.room_id
|
||||
}
|
||||
|
||||
// 生成临时房间ID
|
||||
if (targetUserId && userId.value) {
|
||||
return [userId.value, targetUserId].sort().join('_')
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 初始化信令监听器
|
||||
function initListener() {
|
||||
wsManager.offSignal(handleSignaling)
|
||||
wsManager.onSignal(handleSignaling)
|
||||
}
|
||||
|
||||
// 信令处理
|
||||
async function handleSignaling(message: ChatMessage) {
|
||||
try {
|
||||
const content = message.content ? JSON.parse(message.content) : {}
|
||||
|
||||
// 忽略群聊信令
|
||||
// 忽略群聊信令(由 useGroupWebRTC 处理)
|
||||
if (content.callRoomId || content.participantIds) {
|
||||
return
|
||||
}
|
||||
@@ -128,16 +146,28 @@ export function useWebRTC(
|
||||
call.minimized = false
|
||||
call.status = 'incoming'
|
||||
call.statusText = `邀请你${call.type === 'video' ? '视频' : '语音'}通话`
|
||||
call.callerId = message.sender_user_id
|
||||
|
||||
// 获取来电者信息
|
||||
// 获取来电者信息 - 从联系人列表
|
||||
const contact = chatStore.contacts.find(c => c.contact_user_id === message.sender_user_id)
|
||||
if (contact?.user) {
|
||||
call.callerName = contact.remark_name || contact.user.name
|
||||
call.callerAvatar = contact.user.avatar
|
||||
} else {
|
||||
// 尝试从会话列表获取
|
||||
const conv = conversationStore.conversations.find(c => c.target_id === message.sender_user_id)
|
||||
if (conv) {
|
||||
call.callerName = conv.name
|
||||
call.callerAvatar = conv.avatar
|
||||
} else {
|
||||
// 最后尝试从消息中的额外信息获取
|
||||
if (extra.senderName) call.callerName = extra.senderName
|
||||
if (extra.senderAvatar) call.callerAvatar = extra.senderAvatar
|
||||
}
|
||||
}
|
||||
|
||||
playRingtone()
|
||||
if (onIncomingCall) onIncomingCall(message.sender_user_id)
|
||||
console.log('📞 来电:', call.callerName, call.callerAvatar)
|
||||
|
||||
} else if (signal === 'accepted') {
|
||||
stopRingtone()
|
||||
@@ -180,6 +210,8 @@ export function useWebRTC(
|
||||
uni.showToast({ title: '已在其他设备接听', icon: 'none' })
|
||||
} else if (signal === 'reject') {
|
||||
uni.showToast({ title: '对方已拒绝', icon: 'none' })
|
||||
} else if (signal === 'hangup' || signal === 'ended') {
|
||||
uni.showToast({ title: '通话已结束', icon: 'none' })
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -249,6 +281,9 @@ export function useWebRTC(
|
||||
const targetUserId = receiverUserId || currentReceiverUserId
|
||||
const roomId = getSafeRoomId(targetUserId)
|
||||
if (!roomId) return
|
||||
|
||||
// 获取当前用户信息用于传递给对方
|
||||
const currentUser = authStore.user
|
||||
const payload = {
|
||||
sender_client_id: wsManager.getClientId() || '',
|
||||
receiver_user_id: targetUserId,
|
||||
@@ -257,7 +292,11 @@ export function useWebRTC(
|
||||
content: JSON.stringify(data || {}),
|
||||
call_id: call.id,
|
||||
call_status: status,
|
||||
extra: JSON.stringify({ type: call.type }),
|
||||
extra: JSON.stringify({
|
||||
type: call.type,
|
||||
senderName: currentUser?.name,
|
||||
senderAvatar: currentUser?.avatar
|
||||
}),
|
||||
}
|
||||
messageApi.sendMessage(payload).catch(console.error)
|
||||
}
|
||||
@@ -281,12 +320,11 @@ export function useWebRTC(
|
||||
}
|
||||
|
||||
// 发起通话
|
||||
async function startCall(type: 'audio' | 'video', receiverUserId: string, roomId?: string, contact?: Contact) {
|
||||
async function startCall(type: 'audio' | 'video', receiverUserId: string, roomId?: string, targetName?: string, targetAvatar?: string) {
|
||||
if (roomId) currentRoomId = roomId
|
||||
else if (contact?.room_id) currentRoomId = contact.room_id
|
||||
else if (getRoomId) {
|
||||
const found = getRoomId(receiverUserId)
|
||||
if (found) currentRoomId = found
|
||||
else {
|
||||
const foundRoomId = getSafeRoomId(receiverUserId)
|
||||
if (foundRoomId) currentRoomId = foundRoomId
|
||||
}
|
||||
|
||||
if (!currentRoomId) {
|
||||
@@ -306,6 +344,27 @@ export function useWebRTC(
|
||||
call.camOff = false
|
||||
call.remoteCamOff = false
|
||||
call.remoteMuted = false
|
||||
call.callerId = receiverUserId
|
||||
|
||||
// 设置对方信息
|
||||
if (targetName) call.callerName = targetName
|
||||
if (targetAvatar) call.callerAvatar = targetAvatar
|
||||
|
||||
// 尝试从联系人/会话获取对方信息
|
||||
if (!call.callerName || !call.callerAvatar) {
|
||||
const contact = chatStore.contacts.find(c => c.contact_user_id === receiverUserId)
|
||||
if (contact?.user) {
|
||||
call.callerName = call.callerName || contact.remark_name || contact.user.name
|
||||
call.callerAvatar = call.callerAvatar || contact.user.avatar
|
||||
} else {
|
||||
const conv = conversationStore.conversations.find(c => c.target_id === receiverUserId)
|
||||
if (conv) {
|
||||
call.callerName = call.callerName || conv.name
|
||||
call.callerAvatar = call.callerAvatar || conv.avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remoteStream.value = null
|
||||
|
||||
try {
|
||||
@@ -359,12 +418,18 @@ export function useWebRTC(
|
||||
function closeCall() {
|
||||
stopRingtone()
|
||||
currentRoomId = ''
|
||||
currentReceiverUserId = ''
|
||||
call.active = false
|
||||
call.status = 'idle'
|
||||
call.statusText = ''
|
||||
call.id = null
|
||||
call.callerName = undefined
|
||||
call.callerAvatar = undefined
|
||||
call.callerId = undefined
|
||||
call.muted = false
|
||||
call.camOff = false
|
||||
call.remoteMuted = false
|
||||
call.remoteCamOff = false
|
||||
stopCallTimer()
|
||||
|
||||
// #ifdef H5
|
||||
@@ -437,6 +502,8 @@ export function useWebRTC(
|
||||
call,
|
||||
localStream,
|
||||
remoteStream,
|
||||
isActive,
|
||||
initListener,
|
||||
startCall,
|
||||
acceptCall,
|
||||
rejectCall,
|
||||
@@ -450,4 +517,3 @@ export function useWebRTC(
|
||||
}
|
||||
|
||||
export default useWebRTC
|
||||
|
||||
|
||||
@@ -37,21 +37,6 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- WebRTC 通话窗口 -->
|
||||
<call-window
|
||||
v-if="webrtc"
|
||||
:call="webrtc.call"
|
||||
:local-stream="webrtc.localStream.value"
|
||||
:remote-stream="webrtc.remoteStream.value"
|
||||
:format-duration="webrtc.formatDuration"
|
||||
@accept="webrtc.acceptCall()"
|
||||
@reject="webrtc.rejectCall()"
|
||||
@end="webrtc.endCall()"
|
||||
@toggle-mute="webrtc.toggleMute()"
|
||||
@toggle-camera="webrtc.toggleCamera()"
|
||||
@toggle-minimize="webrtc.toggleMinimize()"
|
||||
/>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<scroll-view
|
||||
class="chat-scroll-area custom-scrollbar"
|
||||
@@ -372,7 +357,6 @@ import { useToast } from 'wot-design-uni'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { useWebRTC } from '@/composables/useWebRTC'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
import CallWindow from '@/components/call/CallWindow.vue'
|
||||
import MentionPicker from '@/components/chat/MentionPicker.vue'
|
||||
import type { MentionUser } from '@/components/chat/MentionPicker.vue'
|
||||
import * as roomApi from '@/api/modules/room'
|
||||
@@ -384,15 +368,8 @@ const conversationStore = useConversationStore()
|
||||
const toast = useToast()
|
||||
const { isDark } = useTheme()
|
||||
|
||||
const webrtc = useWebRTC(
|
||||
authStore.user?.id || '',
|
||||
(senderUserId) => { console.log('来电:', senderUserId) },
|
||||
(receiverUserId) => {
|
||||
if (roomId.value) return roomId.value
|
||||
const contact = chatStore.contacts.find(c => c.contact_user_id === receiverUserId)
|
||||
return contact?.room_id || ''
|
||||
}
|
||||
)
|
||||
// 使用全局单例 webrtc
|
||||
const webrtc = useWebRTC()
|
||||
|
||||
const roomId = ref(''); const targetId = ref(''); const chatName = ref(''); const targetAvatar = ref('');
|
||||
const inputText = ref(''); const messages = ref<ChatMessage[]>([]); const scrollToId = ref(''); const scrollTop = ref(0); const scrollWithAnimation = ref(false); const loadingMore = ref(false); const hasMore = ref(true); const showMore = ref(false); const showEmoji = ref(false); const playingAudioId = ref<number | null>(null); const page = ref(1);
|
||||
@@ -409,16 +386,15 @@ const targetUser = computed(() => { const contact = chatStore.contacts.find(c =>
|
||||
|
||||
onLoad((options: any) => { roomId.value = options?.roomId || ''; targetId.value = options?.targetId || ''; chatName.value = decodeURIComponent(options?.name || '聊天'); targetAvatar.value = decodeURIComponent(options?.avatar || ''); isGroupChat.value = !targetId.value && !!roomId.value; loadMessages(); setupWebSocket(); if (isGroupChat.value && roomId.value) { loadGroupMembers() } const callType = options?.callType; if (callType && targetId.value) { setTimeout(() => { if (callType === 'audio') { startAudioCall() } else if (callType === 'video') { startVideoCall() } }, 500) } });
|
||||
onMounted(() => { if (targetId.value) { conversationStore.clearUnread(targetId.value) } setTimeout(() => { scrollToBottom(false) }, 300) });
|
||||
onUnmounted(() => { wsManager.offMessage(handleNewMessage); wsManager.offSignal(handleSignal); stopAudio(); if (webrtc.call.active) { webrtc.endCall() } });
|
||||
onUnmounted(() => { wsManager.offMessage(handleNewMessage); stopAudio(); });
|
||||
|
||||
async function loadGroupMembers() { if (!roomId.value) return; try { groupMembers.value = await roomApi.getGroupMembers(roomId.value) } catch (e) { console.error('加载群成员失败:', e) } }
|
||||
async function loadMessages() { if (!roomId.value) return; try { const cached = chatStore.getRoomMessages(roomId.value); if (cached.length > 0) { messages.value = cached.map((m) => ({ ...m, isSelf: m.sender_user_id === currentUser.value?.id, extra: typeof m.extra === 'string' ? JSON.parse(m.extra || '{}') : m.extra })); scrollToBottom(false); return } const res = await messageApi.getMessages(roomId.value, 1, 50); messages.value = (res.data || []).reverse().map((m: ChatMessage) => ({ ...m, isSelf: m.sender_user_id === currentUser.value?.id, extra: typeof m.extra === 'string' ? JSON.parse(m.extra || '{}') : m.extra })); chatStore.setRoomMessages(roomId.value, messages.value); hasMore.value = res.data.length >= 50; scrollToBottom(false) } catch (error) { console.error('加载消息失败:', error) } }
|
||||
function onScrollToUpper() { if (!loadingMore.value && hasMore.value) { loadMoreMessages() } }
|
||||
async function loadMoreMessages() { if (loadingMore.value || !hasMore.value) { return } loadingMore.value = true; page.value++; const firstMsgId = messages.value.length > 0 ? messages.value[0].id : null; try { const res = await messageApi.getMessages(roomId.value, page.value, 50); const newMessages = (res.data || []).reverse().map((m: ChatMessage) => ({ ...m, isSelf: m.sender_user_id === currentUser.value?.id, extra: typeof m.extra === 'string' ? JSON.parse(m.extra || '{}') : m.extra })); messages.value = [...newMessages, ...messages.value]; hasMore.value = res.data.length >= 50; if (firstMsgId) { nextTick(() => { scrollWithAnimation.value = false; scrollToId.value = `msg-${firstMsgId}` }) } } catch { page.value-- } finally { loadingMore.value = false } }
|
||||
function setupWebSocket() { wsManager.onMessage(handleNewMessage); wsManager.onSignal(handleSignal) }
|
||||
function handleSignal(message: ChatMessage) { webrtc.handleSignaling(message) }
|
||||
function startAudioCall() { if (!targetId.value) { toast.show('无法发起通话'); return } webrtc.startCall('audio', targetId.value, roomId.value) }
|
||||
function startVideoCall() { if (!targetId.value) { toast.show('无法发起通话'); return } webrtc.startCall('video', targetId.value, roomId.value) }
|
||||
function setupWebSocket() { wsManager.onMessage(handleNewMessage) }
|
||||
function startAudioCall() { if (!targetId.value) { toast.show('无法发起通话'); return } webrtc.startCall('audio', targetId.value, roomId.value, targetUser.value?.name, targetUser.value?.avatar) }
|
||||
function startVideoCall() { if (!targetId.value) { toast.show('无法发起通话'); return } webrtc.startCall('video', targetId.value, roomId.value, targetUser.value?.name, targetUser.value?.avatar) }
|
||||
function onMoreAudioCall() { showMore.value = false; startAudioCall() }
|
||||
function onMoreVideoCall() { showMore.value = false; startVideoCall() }
|
||||
function handleNewMessage(msg: ChatMessage) { if (msg.message_type === 6) return; const parsedExtra = typeof msg.extra === 'string' ? JSON.parse(msg.extra || '{}') : msg.extra; const newMsg: ChatMessage = { ...msg, isSelf: msg.sender_user_id === currentUser.value?.id, extra: parsedExtra }; const isCurrentChat = msg.room_id === roomId.value; conversationStore.handleMessageUpdate(newMsg, newMsg.isSelf || false, isCurrentChat); if (!isCurrentChat) return; const exists = messages.value.some( (m) => m.id === newMsg.id || (m.isSelf && m.content === newMsg.content && Math.abs(new Date(m.created_at).getTime() - new Date(newMsg.created_at).getTime()) < 2000) ); if (!exists) { messages.value.push(newMsg); chatStore.addMessage(roomId.value, newMsg); scrollToBottom(true) } }
|
||||
|
||||
@@ -270,9 +270,6 @@
|
||||
</view>
|
||||
</wd-popup>
|
||||
|
||||
<!-- 全局群通话组件 -->
|
||||
<global-call-provider />
|
||||
|
||||
<wd-toast />
|
||||
<wd-message-box />
|
||||
<app-tab-bar current="contacts" />
|
||||
@@ -290,7 +287,6 @@ import * as contactApi from '@/api/modules/contact'
|
||||
import * as roomApi from '@/api/modules/room'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
import AppTabBar from '@/components/common/AppTabBar.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
import type { Contact, ContactGroup } from '@/types/api'
|
||||
|
||||
// --- 逻辑完全不变 ---
|
||||
|
||||
@@ -166,9 +166,6 @@
|
||||
@logout="logout"
|
||||
/>
|
||||
|
||||
<!-- 全局群通话组件 -->
|
||||
<global-call-provider />
|
||||
|
||||
<wd-toast />
|
||||
<wd-message-box :z-index="11000" />
|
||||
<app-tab-bar current="messages" />
|
||||
@@ -188,7 +185,6 @@ import * as conversationApi from '@/api/modules/conversation'
|
||||
import AppTabBar from '@/components/common/AppTabBar.vue'
|
||||
import AppDrawer from '@/components/common/AppDrawer.vue'
|
||||
import PlusMenu from '@/components/common/PlusMenu.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
import type { Conversation } from '@/types/conversation'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
@@ -161,9 +161,6 @@
|
||||
cancel-text="取消"
|
||||
/>
|
||||
|
||||
<!-- 全局群通话组件 -->
|
||||
<global-call-provider />
|
||||
|
||||
<wd-toast />
|
||||
<wd-message-box />
|
||||
|
||||
@@ -183,7 +180,6 @@ import { parseMediaUrls } from '@/types/moment'
|
||||
import { useToast, useMessage } from 'wot-design-uni'
|
||||
import AppTabBar from '@/components/common/AppTabBar.vue'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
import type { Moment } from '@/types/moment'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
Reference in New Issue
Block a user