部分联系人信息和会话模块
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { storage } from '@/utils/storage'
|
||||
import type { FriendRequest, ContactGroup, Contact } from '@/types/api'
|
||||
|
||||
export type LeftPanelMode = 'default' | 'friend-manager' | 'friend-notify' | 'group-notify'
|
||||
@@ -7,9 +8,9 @@ export type LeftPanelMode = 'default' | 'friend-manager' | 'friend-notify' | 'gr
|
||||
export const useContactStore = defineStore('contact', () => {
|
||||
const friendRequests = ref<FriendRequest[]>([])
|
||||
const groups = ref<ContactGroup[]>([])
|
||||
const leftPanelMode = ref<LeftPanelMode>('default')
|
||||
const leftPanelMode = ref<LeftPanelMode>(storage.getContactLeftPanelMode() as LeftPanelMode)
|
||||
const selectedContact = ref<Contact | null>(null)
|
||||
const contactListTab = ref<'friends' | 'groups'>('friends')
|
||||
const contactListTab = ref<'friends' | 'groups'>(storage.getContactListTab())
|
||||
|
||||
/**
|
||||
* 设置好友申请列表
|
||||
@@ -74,6 +75,7 @@ export const useContactStore = defineStore('contact', () => {
|
||||
*/
|
||||
function setLeftPanelMode(mode: LeftPanelMode) {
|
||||
leftPanelMode.value = mode
|
||||
storage.setContactLeftPanelMode(mode)
|
||||
// 切换到管理/通知模式时,清除选中联系人
|
||||
if (mode !== 'default') {
|
||||
selectedContact.value = null
|
||||
@@ -91,6 +93,11 @@ export const useContactStore = defineStore('contact', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 监听 contactListTab 变化并保存
|
||||
watch(contactListTab, (newTab) => {
|
||||
storage.setContactListTab(newTab)
|
||||
})
|
||||
|
||||
return {
|
||||
friendRequests,
|
||||
groups,
|
||||
|
||||
@@ -6,6 +6,10 @@ const TOKEN_KEY = 'token'
|
||||
const USER_ID_KEY = 'user_id'
|
||||
const USER_INFO_KEY = 'user_info'
|
||||
const REMEMBER_KEY = 'remember'
|
||||
const CURRENT_TAB_KEY = 'current_tab'
|
||||
const CONTACT_LEFT_PANEL_MODE_KEY = 'contact_left_panel_mode'
|
||||
const CONTACT_LIST_TAB_KEY = 'contact_list_tab'
|
||||
const SELECTED_CONVERSATION_KEY = 'selected_conversation'
|
||||
|
||||
export const storage = {
|
||||
// Token
|
||||
@@ -57,12 +61,55 @@ export const storage = {
|
||||
return localStorage.getItem(REMEMBER_KEY) === 'true'
|
||||
},
|
||||
|
||||
// Current Tab (chat/contact)
|
||||
setCurrentTab(tab: 'chat' | 'contact') {
|
||||
localStorage.setItem(CURRENT_TAB_KEY, tab)
|
||||
},
|
||||
|
||||
getCurrentTab(): 'chat' | 'contact' {
|
||||
const tab = localStorage.getItem(CURRENT_TAB_KEY)
|
||||
return (tab === 'chat' || tab === 'contact') ? tab : 'chat'
|
||||
},
|
||||
|
||||
// Contact Left Panel Mode
|
||||
setContactLeftPanelMode(mode: string) {
|
||||
localStorage.setItem(CONTACT_LEFT_PANEL_MODE_KEY, mode)
|
||||
},
|
||||
|
||||
getContactLeftPanelMode(): string {
|
||||
return localStorage.getItem(CONTACT_LEFT_PANEL_MODE_KEY) || 'default'
|
||||
},
|
||||
|
||||
// Contact List Tab
|
||||
setContactListTab(tab: 'friends' | 'groups') {
|
||||
localStorage.setItem(CONTACT_LIST_TAB_KEY, tab)
|
||||
},
|
||||
|
||||
getContactListTab(): 'friends' | 'groups' {
|
||||
const tab = localStorage.getItem(CONTACT_LIST_TAB_KEY)
|
||||
return (tab === 'friends' || tab === 'groups') ? tab : 'friends'
|
||||
},
|
||||
|
||||
// Selected Conversation
|
||||
setSelectedConversation(targetId: string | null) {
|
||||
if (targetId) {
|
||||
localStorage.setItem(SELECTED_CONVERSATION_KEY, targetId)
|
||||
} else {
|
||||
localStorage.removeItem(SELECTED_CONVERSATION_KEY)
|
||||
}
|
||||
},
|
||||
|
||||
getSelectedConversation(): string | null {
|
||||
return localStorage.getItem(SELECTED_CONVERSATION_KEY)
|
||||
},
|
||||
|
||||
// Clear all
|
||||
clear() {
|
||||
this.removeToken()
|
||||
this.removeUserId()
|
||||
this.removeUserInfo()
|
||||
this.setRemember(false)
|
||||
// 不清除 UI 状态,让用户刷新后保持界面状态
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -264,6 +264,7 @@ import * as messageApi from '@/api/modules/message'
|
||||
import * as contactApi from '@/api/modules/contact'
|
||||
import * as attachmentApi from '@/api/modules/attachment'
|
||||
import { formatTime } from '@/utils/format'
|
||||
import { storage } from '@/utils/storage'
|
||||
import type { Contact, ChatMessage } from '@/types/api'
|
||||
import type { Conversation } from '@/types/conversation'
|
||||
import { useWebRTC } from '@/composables/useWebRTC'
|
||||
@@ -296,8 +297,8 @@ function handleIncomingCall(senderUserId: string) {
|
||||
}
|
||||
const webrtc = useWebRTC(authStore.user?.id || '', handleIncomingCall)
|
||||
|
||||
// State
|
||||
const currentTab = ref<'chat' | 'contact'>('chat')
|
||||
// State - 从本地存储恢复
|
||||
const currentTab = ref<'chat' | 'contact'>(storage.getCurrentTab())
|
||||
const searchQuery = ref('')
|
||||
const inputText = ref('')
|
||||
const chatVisible = ref(false)
|
||||
@@ -364,6 +365,8 @@ function scrollToBottom(smooth = true) {
|
||||
}
|
||||
|
||||
async function selectChat(contact: Contact) {
|
||||
// 保存选中的会话
|
||||
storage.setSelectedConversation(contact.user_id || contact.id)
|
||||
chatStore.setCurrentTarget(contact)
|
||||
chatVisible.value = true
|
||||
unreadCount.value = 0 // 切换聊天时重置
|
||||
@@ -496,6 +499,11 @@ async function handleRecordStop(blob: Blob, duration: number) { isRecording.valu
|
||||
function handleRecordCancel() { isRecording.value = false }
|
||||
async function loadContacts() { try { const contacts = await contactApi.getContacts(); chatStore.setContacts(contacts) } catch (e) { console.error(e) } }
|
||||
|
||||
// 监听 currentTab 变化并保存
|
||||
watch(currentTab, (newTab) => {
|
||||
storage.setCurrentTab(newTab)
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
if (!authStore.isAuthenticated) {
|
||||
const isValid = await authStore.checkAuth()
|
||||
@@ -509,6 +517,16 @@ onMounted(async () => {
|
||||
}
|
||||
await loadContacts()
|
||||
await conversationStore.loadConversations()
|
||||
|
||||
// 恢复选中的会话
|
||||
const savedTargetId = storage.getSelectedConversation()
|
||||
if (savedTargetId) {
|
||||
const contact = chatStore.contacts.find(c => c.user_id === savedTargetId || c.id === savedTargetId)
|
||||
if (contact) {
|
||||
await selectChat(contact)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => isMobile.value = window.innerWidth < 768)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
|
||||
@@ -274,14 +274,16 @@ function handleSendMessage() {
|
||||
|
||||
function handleAudioCall() {
|
||||
if (contact.value) {
|
||||
const receiverUserId = contact.value.user_id || contact.value.id
|
||||
const receiverUserId = contact.value.user_id || contact.value.id || contact.value.contact_user_id
|
||||
// 直接发起通话,不跳转到会话模块
|
||||
webrtc.startCall('audio', receiverUserId)
|
||||
}
|
||||
}
|
||||
|
||||
function handleVideoCall() {
|
||||
if (contact.value) {
|
||||
const receiverUserId = contact.value.user_id || contact.value.id
|
||||
const receiverUserId = contact.value.user_id || contact.value.id || contact.value.contact_user_id
|
||||
// 直接发起通话,不跳转到会话模块
|
||||
webrtc.startCall('video', receiverUserId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,26 +288,16 @@ function handleSendMessage() {
|
||||
function handleAudioCall() {
|
||||
if (contact.value) {
|
||||
const receiverUserId = contact.value.user_id || contact.value.id
|
||||
chatStore.setCurrentTarget(contact.value)
|
||||
// 切换到 Chat Tab 后再发起通话
|
||||
emit('switchToChat')
|
||||
// 延迟一下确保 Tab 切换完成
|
||||
setTimeout(() => {
|
||||
// 直接发起通话,不跳转到会话模块
|
||||
webrtc.startCall('audio', receiverUserId)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
function handleVideoCall() {
|
||||
if (contact.value) {
|
||||
const receiverUserId = contact.value.user_id || contact.value.id
|
||||
chatStore.setCurrentTarget(contact.value)
|
||||
// 切换到 Chat Tab 后再发起通话
|
||||
emit('switchToChat')
|
||||
// 延迟一下确保 Tab 切换完成
|
||||
setTimeout(() => {
|
||||
// 直接发起通话,不跳转到会话模块
|
||||
webrtc.startCall('video', receiverUserId)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,15 @@
|
||||
<div
|
||||
v-for="request in friendRequests"
|
||||
:key="request.id"
|
||||
class="flex items-center p-4 bg-input/50 rounded-2xl border border-primary/20 hover:border-primary/50 transition relative overflow-hidden"
|
||||
class="flex items-center p-4 bg-input/50 rounded-2xl border transition relative overflow-hidden"
|
||||
:class="request.status === 'rejected'
|
||||
? 'border-danger/20 hover:border-danger/50'
|
||||
: 'border-primary/20 hover:border-primary/50'"
|
||||
>
|
||||
<div class="absolute left-0 top-0 bottom-0 w-1 bg-primary"></div>
|
||||
<div
|
||||
class="absolute left-0 top-0 bottom-0 w-1"
|
||||
:class="request.status === 'rejected' ? 'bg-danger' : 'bg-primary'"
|
||||
></div>
|
||||
<Avatar
|
||||
:name="request.from_user?.name"
|
||||
:avatar="request.from_user?.avatar"
|
||||
@@ -28,13 +34,18 @@
|
||||
<div class="font-bold text-white">{{ request.from_user?.name }}</div>
|
||||
<div class="text-xs text-gray-400 mt-1 flex items-center gap-1">
|
||||
<i class="fas fa-quote-left text-[10px] opacity-50"></i>
|
||||
<span v-if="request.status === 'rejected'">
|
||||
对方拒绝了你的好友申请
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ request.message || '请求添加你为好友' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ formatTime(new Date(request.created_at).getTime()) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div v-if="request.status === 'pending'" class="flex gap-2">
|
||||
<button
|
||||
class="w-9 h-9 bg-success/20 hover:bg-success text-success hover:text-white rounded-lg transition flex items-center justify-center"
|
||||
@click="handleAcceptRequest(request.id)"
|
||||
@@ -50,6 +61,10 @@
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="flex items-center gap-2 text-danger text-sm">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
<span>已拒绝</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user