修复了一些逻辑漏洞
This commit is contained in:
@@ -34,4 +34,9 @@ export function deleteConversation(targetId: string) {
|
||||
})
|
||||
}
|
||||
|
||||
// 根据 room_id 获取或创建会话
|
||||
export function getConversationByRoom(roomId: string) {
|
||||
return request.get<Conversation>(`/conversations/by-room/${roomId}`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { ChatMessage } from '@/types/api'
|
||||
import { formatTime } from '@/utils/format'
|
||||
import { formatMessageTime } from '@/utils/format'
|
||||
import { isSystemOrNotifyMessage, getMessageTypeIcon, getMessageSummary } from '@/utils/messageTypes'
|
||||
import { MessageType } from '@/types/message'
|
||||
import TextBubble from './bubble/Text.vue'
|
||||
@@ -84,16 +84,8 @@ const bubbleShapeClass = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// 使用 format.ts 中的逻辑,但可以根据需要微调为 HH:mm
|
||||
// 使用相对时间格式化
|
||||
const formattedTime = computed(() => {
|
||||
const date = new Date(props.message.created_at)
|
||||
// 如果是当天,只显示时间,否则显示日期+时间
|
||||
const now = new Date()
|
||||
const isToday = date.toDateString() === now.toDateString()
|
||||
|
||||
if (isToday) {
|
||||
return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
||||
}
|
||||
return formatTime(props.message.created_at)
|
||||
return formatMessageTime(props.message.created_at)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ref, computed } from 'vue'
|
||||
import type { Conversation } from '@/types/conversation'
|
||||
import type { ChatMessage } from '@/types/api'
|
||||
import * as conversationApi from '@/api/modules/conversation'
|
||||
import * as groupApi from '@/api/modules/room'
|
||||
import { getMessageSummary } from '@/utils/messageTypes'
|
||||
import { useChatStore } from './chat'
|
||||
|
||||
@@ -30,13 +31,21 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
// 将后端的 room 字段映射为前端的 target_group
|
||||
const targetGroup = c.room || c.target_group
|
||||
|
||||
// 转换 last_time 为时间戳(确保排序正确)
|
||||
let lastTime = c.last_time
|
||||
if (typeof lastTime === 'string') {
|
||||
lastTime = new Date(lastTime).getTime()
|
||||
} else if (!lastTime) {
|
||||
lastTime = 0
|
||||
}
|
||||
|
||||
return {
|
||||
...c,
|
||||
room_type: roomType,
|
||||
is_group: isGroup,
|
||||
// 群聊显示群名称,单聊显示用户名称
|
||||
name: isGroup
|
||||
? (targetGroup?.room_name || targetGroup?.name || c.name || '未知群聊')
|
||||
? (targetGroup?.room_name || targetGroup?.name || c.name || c.room_id || '群聊')
|
||||
: (c.target_user?.name || c.name || '未知用户'),
|
||||
avatar: isGroup
|
||||
? (targetGroup?.room_avatar || targetGroup?.avatar || c.avatar || '')
|
||||
@@ -45,6 +54,9 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
room_id: isGroup ? (c.room_id || c.target_id) : c.room_id,
|
||||
member_count: targetGroup?.member_count,
|
||||
owner_id: targetGroup?.owner_id,
|
||||
// 确保 last_time 是时间戳
|
||||
last_time: lastTime,
|
||||
last_message_time: c.last_time || c.last_message_time,
|
||||
// 映射 room 为 target_group(前端期望的字段名)
|
||||
target_group: targetGroup ? {
|
||||
id: targetGroup.room_id || targetGroup.id,
|
||||
@@ -57,6 +69,30 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
created_at: targetGroup.created_at,
|
||||
} : undefined,
|
||||
}
|
||||
}).sort((a, b) => {
|
||||
// 确保排序:置顶优先 > 时间倒序(最新在最上面)
|
||||
if (a.is_top !== b.is_top) return a.is_top ? -1 : 1
|
||||
return (b.last_time || 0) - (a.last_time || 0)
|
||||
})
|
||||
|
||||
// 对于群聊信息缺失的会话,尝试异步获取群信息
|
||||
conversations.value.forEach(async (conv) => {
|
||||
if (conv.is_group && conv.room_id && !conv.target_group && !conv.name) {
|
||||
try {
|
||||
const groupInfo = await groupApi.getGroup(conv.room_id)
|
||||
// 更新会话的群信息
|
||||
const index = conversations.value.findIndex(c => c.id === conv.id)
|
||||
if (index !== -1) {
|
||||
conversations.value[index].target_group = groupInfo as any
|
||||
conversations.value[index].name = groupInfo.room_name || groupInfo.name || conv.room_id
|
||||
conversations.value[index].avatar = groupInfo.room_avatar || groupInfo.avatar
|
||||
conversations.value[index].member_count = groupInfo.member_count
|
||||
conversations.value[index].owner_id = groupInfo.owner_id
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch group info for room ${conv.room_id}:`, error)
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Fetch conversations failed:', error)
|
||||
@@ -117,6 +153,7 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
return
|
||||
}
|
||||
|
||||
// 更新后重新排序,确保最新消息在最上面
|
||||
sortConversations()
|
||||
}
|
||||
|
||||
@@ -177,6 +214,7 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
loadConversations,
|
||||
handleMessageUpdate,
|
||||
incrementUnread,
|
||||
clearUnread
|
||||
clearUnread,
|
||||
sortConversations
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
* 格式化时间(相对时间)
|
||||
* 显示:刚刚、X分钟前、X小时前、昨天、X天前、具体日期
|
||||
*/
|
||||
export function formatTime(timestamp: number | string): string {
|
||||
const date = new Date(typeof timestamp === 'string' ? timestamp : timestamp)
|
||||
@@ -14,14 +15,104 @@ export function formatTime(timestamp: number | string): string {
|
||||
const hours = Math.floor(minutes / 60)
|
||||
const days = Math.floor(hours / 24)
|
||||
|
||||
if (days > 0) {
|
||||
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })
|
||||
} else if (hours > 0) {
|
||||
return `${hours}小时前`
|
||||
} else if (minutes > 0) {
|
||||
// 刚刚(1分钟内)
|
||||
if (seconds < 60) {
|
||||
return '刚刚'
|
||||
}
|
||||
|
||||
// X分钟前(1小时内)
|
||||
if (minutes < 60) {
|
||||
return `${minutes}分钟前`
|
||||
}
|
||||
|
||||
// X小时前(24小时内)
|
||||
if (hours < 24) {
|
||||
return `${hours}小时前`
|
||||
}
|
||||
|
||||
// 昨天
|
||||
const yesterday = new Date(now)
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
if (date.toDateString() === yesterday.toDateString()) {
|
||||
return '昨天'
|
||||
}
|
||||
|
||||
// X天前(7天内)
|
||||
if (days < 7) {
|
||||
return `${days}天前`
|
||||
}
|
||||
|
||||
// 超过7天,显示具体日期
|
||||
const currentYear = now.getFullYear()
|
||||
const messageYear = date.getFullYear()
|
||||
|
||||
if (currentYear === messageYear) {
|
||||
// 今年:显示月-日
|
||||
return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })
|
||||
} else {
|
||||
return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
||||
// 去年或更早:显示年-月-日
|
||||
return date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'short', day: 'numeric' })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化消息时间(用于聊天记录)
|
||||
* 显示:刚刚、X分钟前、X小时前、昨天 HH:mm、MM-DD HH:mm、YYYY-MM-DD HH:mm
|
||||
*/
|
||||
export function formatMessageTime(timestamp: number | string): string {
|
||||
const date = new Date(typeof timestamp === 'string' ? timestamp : timestamp)
|
||||
const now = new Date()
|
||||
const diff = now.getTime() - date.getTime()
|
||||
const seconds = Math.floor(diff / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const hours = Math.floor(minutes / 60)
|
||||
const days = Math.floor(hours / 24)
|
||||
|
||||
// 刚刚(1分钟内)
|
||||
if (seconds < 60) {
|
||||
return '刚刚'
|
||||
}
|
||||
|
||||
// X分钟前(1小时内)
|
||||
if (minutes < 60) {
|
||||
return `${minutes}分钟前`
|
||||
}
|
||||
|
||||
// X小时前(24小时内)
|
||||
if (hours < 24) {
|
||||
return `${hours}小时前`
|
||||
}
|
||||
|
||||
// 昨天 HH:mm
|
||||
const yesterday = new Date(now)
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
if (date.toDateString() === yesterday.toDateString()) {
|
||||
return `昨天 ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}`
|
||||
}
|
||||
|
||||
// 本周内:显示星期几 HH:mm
|
||||
const weekAgo = new Date(now)
|
||||
weekAgo.setDate(weekAgo.getDate() - 7)
|
||||
if (date > weekAgo) {
|
||||
const weekdays = ['日', '一', '二', '三', '四', '五', '六']
|
||||
const weekday = weekdays[date.getDay()]
|
||||
return `周${weekday} ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}`
|
||||
}
|
||||
|
||||
// 今年:MM-DD HH:mm
|
||||
const currentYear = now.getFullYear()
|
||||
const messageYear = date.getFullYear()
|
||||
|
||||
if (currentYear === messageYear) {
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
return `${month}-${day} ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}`
|
||||
} else {
|
||||
// 去年或更早:YYYY-MM-DD HH:mm
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,20 +137,22 @@
|
||||
<!-- 优化后的未读徽标 -->
|
||||
<div
|
||||
v-if="(conv.unread_count || 0) > 0"
|
||||
class="h-[18px] min-w-[18px] px-1.5 rounded-full flex items-center justify-center font-bold shadow-sm cursor-pointer transition-all duration-300 shrink-0 group/badge hover:scale-110"
|
||||
class="h-[18px] min-w-[18px] px-1.5 rounded-full flex items-center justify-center font-bold shadow-sm cursor-pointer transition-all duration-300 shrink-0 hover:scale-110"
|
||||
:class="[
|
||||
'bg-red-500 text-white hover:bg-emerald-500' // 默认红,悬浮绿
|
||||
]"
|
||||
title="标记已读"
|
||||
@click.stop="handleMarkRead(conv)"
|
||||
@mouseenter="hoveredBadgeId = conv.id"
|
||||
@mouseleave="hoveredBadgeId = null"
|
||||
>
|
||||
<!-- 默认显示数字 -->
|
||||
<span class="text-[10px] block group-hover/badge:hidden transition-all duration-200">
|
||||
<span v-if="hoveredBadgeId !== conv.id" class="text-[10px]">
|
||||
{{ conv.unread_count > 99 ? '99+' : conv.unread_count }}
|
||||
</span>
|
||||
|
||||
<!-- 悬浮显示对号 -->
|
||||
<i class="fas fa-check text-[10px] hidden group-hover/badge:block animate-pulse-fast"></i>
|
||||
<i v-if="hoveredBadgeId === conv.id" class="fas fa-check text-[10px]"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -295,6 +297,15 @@
|
||||
</div>
|
||||
<ContextMenu />
|
||||
<FileConfirmModal :show="fileModal.show" :type="fileModal.type" :preview="fileModal.preview" :name="fileModal.name" :size="fileModal.size" @close="fileModal.show = false" @confirm="confirmSendFile" />
|
||||
<ConfirmModal
|
||||
:show="showDeleteConversationConfirm"
|
||||
title="删除会话"
|
||||
message="确认删除该会话记录吗?"
|
||||
type="danger"
|
||||
confirm-text="确认删除"
|
||||
@confirm="handleDeleteConversation"
|
||||
@cancel="showDeleteConversationConfirm = false; pendingDeleteConversation = null"
|
||||
/>
|
||||
|
||||
<!-- 创建群聊弹窗 -->
|
||||
<SelectContactsModal
|
||||
@@ -408,6 +419,7 @@ import { wsManager } from '@/api/websocket'
|
||||
import * as messageApi from '@/api/modules/message'
|
||||
import * as contactApi from '@/api/modules/contact'
|
||||
import * as attachmentApi from '@/api/modules/attachment'
|
||||
import * as conversationApi from '@/api/modules/conversation'
|
||||
import { formatTime, generateColor } from '@/utils/format'
|
||||
import { getMessageSummary } from '@/utils/messageTypes'
|
||||
import { storage } from '@/utils/storage'
|
||||
@@ -418,6 +430,7 @@ import { useWebRTCStore } from '@/stores/webrtc'
|
||||
import Avatar from '@/components/common/Avatar.vue'
|
||||
import ContextMenu from '@/components/common/ContextMenu.vue'
|
||||
import FileConfirmModal from '@/components/common/FileConfirmModal.vue'
|
||||
import ConfirmModal from '@/components/common/ConfirmModal.vue'
|
||||
import MessageList from '@/components/chat/MessageList.vue'
|
||||
import MessageInput from '@/components/chat/MessageInput.vue'
|
||||
import ContactView from '@/views/contact/ContactView.vue'
|
||||
@@ -451,7 +464,10 @@ const selectedUser = ref<Contact | User | null>(null)
|
||||
const isRecording = ref(false)
|
||||
const fileModal = ref({ show: false, type: 0, preview: '', name: '', size: 0, file: null as File | null })
|
||||
const showCreateGroupModal = ref(false)
|
||||
const showDeleteConversationConfirm = ref(false)
|
||||
const pendingDeleteConversation = ref<Conversation | null>(null)
|
||||
const showGroupInfoPanel = ref(false)
|
||||
const hoveredBadgeId = ref<number | null>(null)
|
||||
const refreshingConversations = ref(false)
|
||||
const roomMembers = ref<Record<string, Record<string, { name: string; avatar?: string }>>>({})
|
||||
|
||||
@@ -827,8 +843,113 @@ function handleWebSocketMessage(message: ChatMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
chatStore.addMessage(roomId, message)
|
||||
// 先检查会话是否存在,确保消息添加到正确的会话
|
||||
// 对于群聊消息,使用 room_id 查找;对于私聊消息,使用 target_id 查找
|
||||
const isGroupMessage = message.room_id && (message.room_id.startsWith('group_') || message.receiver_user_id === message.room_id)
|
||||
let existingConv = null
|
||||
let actualRoomId = roomId
|
||||
|
||||
if (isGroupMessage) {
|
||||
// 群聊消息:使用 room_id 查找会话
|
||||
existingConv = conversationStore.conversations.find(c => c.room_id === roomId)
|
||||
} else {
|
||||
// 私聊消息:使用 target_id 查找会话
|
||||
const targetId = message.isSelf ? message.receiver_user_id : message.sender_user_id
|
||||
existingConv = conversationStore.conversations.find(c => c.target_id === targetId || c.room_id === roomId)
|
||||
// 如果找到会话,使用会话的 room_id 确保匹配
|
||||
if (existingConv && existingConv.room_id) {
|
||||
actualRoomId = existingConv.room_id
|
||||
}
|
||||
}
|
||||
|
||||
// 如果会话不存在,先获取或创建会话,然后再添加消息
|
||||
if (!existingConv && !message.isSelf && roomId) {
|
||||
const now = Date.now()
|
||||
const lastRefresh = (window as any).__lastConversationRefresh || 0
|
||||
// 防抖:1秒内最多请求一次
|
||||
if (now - lastRefresh > 1000) {
|
||||
(window as any).__lastConversationRefresh = now
|
||||
// 调用新接口获取会话信息,只添加新会话到列表
|
||||
conversationApi.getConversationByRoom(roomId).then((conv: any) => {
|
||||
// 检查会话是否已存在(可能在其他地方已添加)
|
||||
const exists = conversationStore.conversations.find(c =>
|
||||
c.room_id === conv.room_id ||
|
||||
(c.target_id === conv.target_id && c.type === conv.type)
|
||||
)
|
||||
if (!exists) {
|
||||
// 转换格式并添加到列表(使用与 loadConversations 相同的格式)
|
||||
const isGroup = conv.type === 2
|
||||
// 获取群信息(与 loadConversations 保持一致)
|
||||
const targetGroup = conv.room || conv.target_group
|
||||
|
||||
const formattedConv: Conversation = {
|
||||
id: conv.id,
|
||||
target_id: conv.target_id,
|
||||
room_id: conv.room_id,
|
||||
type: conv.type,
|
||||
is_group: isGroup,
|
||||
is_top: conv.is_top || false,
|
||||
is_muted: conv.is_muted || false,
|
||||
unread_count: conv.unread_count || 0,
|
||||
last_message: conv.last_message || '',
|
||||
last_message_time: conv.last_time,
|
||||
// 使用与 loadConversations 相同的逻辑获取名称
|
||||
name: isGroup
|
||||
? (targetGroup?.room_name || targetGroup?.name || conv.name || conv.room_id || '群聊')
|
||||
: (conv.target_user?.name || conv.name || '未知用户'),
|
||||
display_name: isGroup
|
||||
? (targetGroup?.room_name || targetGroup?.name || conv.name || conv.room_id || '群聊')
|
||||
: (conv.target_user?.name || conv.name || '未知用户'),
|
||||
avatar: isGroup
|
||||
? (targetGroup?.room_avatar || targetGroup?.avatar || conv.avatar || '')
|
||||
: (conv.target_user?.avatar || conv.avatar || ''),
|
||||
user: conv.target_user,
|
||||
room: conv.room,
|
||||
target_group: targetGroup ? {
|
||||
id: targetGroup.room_id || targetGroup.id,
|
||||
room_id: targetGroup.room_id,
|
||||
room_type: 'group',
|
||||
name: targetGroup.room_name || targetGroup.name,
|
||||
avatar: targetGroup.room_avatar || targetGroup.avatar,
|
||||
owner_id: targetGroup.owner_id,
|
||||
member_count: targetGroup.member_count,
|
||||
created_at: targetGroup.created_at,
|
||||
} : undefined
|
||||
}
|
||||
conversationStore.conversations.push(formattedConv)
|
||||
// 添加新会话后重新排序
|
||||
conversationStore.sortConversations()
|
||||
}
|
||||
// 会话创建后,使用正确的 room_id 添加消息
|
||||
const finalRoomId = conv.room_id || roomId
|
||||
chatStore.addMessage(finalRoomId, message)
|
||||
processMessageAfterConversation(message, finalRoomId)
|
||||
}).catch((error) => {
|
||||
console.error('Failed to get conversation by room:', error)
|
||||
// 如果接口失败,仍然添加消息(使用原始 roomId),然后刷新列表
|
||||
chatStore.addMessage(roomId, message)
|
||||
processMessageAfterConversation(message, roomId)
|
||||
conversationStore.loadConversations()
|
||||
})
|
||||
return // 异步处理,先返回
|
||||
} else {
|
||||
// 防抖期间,仍然添加消息(使用原始 roomId)
|
||||
actualRoomId = roomId
|
||||
}
|
||||
} else if (existingConv) {
|
||||
// 会话存在,使用会话的 room_id 确保匹配
|
||||
actualRoomId = existingConv.room_id || roomId
|
||||
}
|
||||
|
||||
// 添加消息到正确的会话(使用确认后的 roomId)
|
||||
chatStore.addMessage(actualRoomId, message)
|
||||
|
||||
// 处理消息的后续逻辑
|
||||
processMessageAfterConversation(message, actualRoomId)
|
||||
}
|
||||
|
||||
// 处理消息的后续逻辑(会话确认后)
|
||||
function processMessageAfterConversation(message: ChatMessage, roomId: string) {
|
||||
const contact = chatStore.contacts.find(c => c.user_id === message.sender_user_id || c.id === message.sender_user_id)
|
||||
|
||||
// 检查是否为群聊消息,如果是且当前正在查看该群聊,检查群成员信息
|
||||
@@ -845,31 +966,6 @@ function handleWebSocketMessage(message: ChatMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查会话是否存在
|
||||
// 对于群聊消息,使用 room_id 查找;对于私聊消息,使用 target_id 查找
|
||||
const isGroupMessage = message.room_id && (message.room_id.startsWith('group_') || message.receiver_user_id === message.room_id)
|
||||
let existingConv = null
|
||||
|
||||
if (isGroupMessage) {
|
||||
// 群聊消息:使用 room_id 查找会话
|
||||
existingConv = conversationStore.conversations.find(c => c.room_id === roomId)
|
||||
} else {
|
||||
// 私聊消息:使用 target_id 查找会话
|
||||
const targetId = message.isSelf ? message.receiver_user_id : message.sender_user_id
|
||||
existingConv = conversationStore.conversations.find(c => c.target_id === targetId || c.room_id === roomId)
|
||||
}
|
||||
|
||||
// 如果会话不存在,刷新会话列表(添加防抖,避免频繁刷新)
|
||||
if (!existingConv && !message.isSelf) {
|
||||
const now = Date.now()
|
||||
const lastRefresh = (window as any).__lastConversationRefresh || 0
|
||||
// 防抖:1秒内最多刷新一次
|
||||
if (now - lastRefresh > 1000) {
|
||||
(window as any).__lastConversationRefresh = now
|
||||
conversationStore.loadConversations()
|
||||
}
|
||||
}
|
||||
|
||||
const isCurrentChat = currentRoomId === roomId
|
||||
|
||||
if (contact) {
|
||||
@@ -998,10 +1094,8 @@ function showConversationMenu(event: MouseEvent, conv: any) {
|
||||
icon: 'fas fa-trash-alt',
|
||||
danger: true,
|
||||
action: () => {
|
||||
if(confirm('确认删除该会话记录吗?')) {
|
||||
conversationStore.conversations = conversationStore.conversations.filter(c => c.id !== conv.id)
|
||||
toastStore.success('会话已移除')
|
||||
}
|
||||
pendingDeleteConversation.value = conv
|
||||
showDeleteConversationConfirm.value = true
|
||||
}
|
||||
}
|
||||
])
|
||||
@@ -1012,6 +1106,15 @@ async function handleMarkRead(conv: Conversation) {
|
||||
toastStore.success('已标记为已读')
|
||||
}
|
||||
|
||||
function handleDeleteConversation() {
|
||||
if (!pendingDeleteConversation.value) return
|
||||
const conv = pendingDeleteConversation.value
|
||||
conversationStore.conversations = conversationStore.conversations.filter(c => c.id !== conv.id)
|
||||
toastStore.success('会话已移除')
|
||||
showDeleteConversationConfirm.value = false
|
||||
pendingDeleteConversation.value = null
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
function showChatOptionsMenu(event: MouseEvent, contact: Contact) {
|
||||
|
||||
@@ -387,6 +387,15 @@
|
||||
@confirm="handleDeleteGroup"
|
||||
@cancel="showDeleteGroupConfirm = false"
|
||||
/>
|
||||
<ConfirmModal
|
||||
:show="showDeleteContactConfirm"
|
||||
title="删除好友"
|
||||
:message="deleteContactMessage"
|
||||
type="danger"
|
||||
confirm-text="确认删除"
|
||||
@confirm="handleDeleteContact"
|
||||
@cancel="showDeleteContactConfirm = false; pendingDeleteContact = null"
|
||||
/>
|
||||
|
||||
<!-- 创建群聊弹窗 -->
|
||||
<SelectContactsModal
|
||||
@@ -502,6 +511,13 @@ const groupModalName = ref('')
|
||||
const currentGroupId = ref<number | null>(null)
|
||||
const groupInputRef = ref<HTMLInputElement | null>(null)
|
||||
const showDeleteGroupConfirm = ref(false)
|
||||
const showDeleteContactConfirm = ref(false)
|
||||
const pendingDeleteContact = ref<Contact | null>(null)
|
||||
|
||||
const deleteContactMessage = computed(() => {
|
||||
const name = pendingDeleteContact.value?.remark_name || pendingDeleteContact.value?.user?.name || '未知用户'
|
||||
return `确定删除好友 "${name}" 吗?`
|
||||
})
|
||||
|
||||
const showRemarkModal = ref(false)
|
||||
const tempRemark = ref('')
|
||||
@@ -749,17 +765,9 @@ function showContactMenu(e: MouseEvent, contact: Contact) {
|
||||
label: '删除好友',
|
||||
icon: 'fas fa-trash-alt',
|
||||
danger: true,
|
||||
action: async () => {
|
||||
try {
|
||||
if(confirm(`确定删除好友 "${contact.remark_name || contact.user?.name}" 吗?`)) {
|
||||
await contactApi.deleteContact(contact.id)
|
||||
chatStore.contacts = chatStore.contacts.filter(c => c.id !== contact.id)
|
||||
if(contactStore.selectedContact?.id === contact.id) contactStore.setSelectedContact(null)
|
||||
toastStore.success('已删除')
|
||||
}
|
||||
} catch(e: any) {
|
||||
toastStore.error(e.message)
|
||||
}
|
||||
action: () => {
|
||||
pendingDeleteContact.value = contact
|
||||
showDeleteContactConfirm.value = true
|
||||
}
|
||||
}
|
||||
])
|
||||
@@ -825,6 +833,21 @@ async function handleDeleteGroup() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteContact() {
|
||||
if (!pendingDeleteContact.value) return
|
||||
const contact = pendingDeleteContact.value
|
||||
try {
|
||||
await contactApi.deleteContact(contact.id)
|
||||
chatStore.contacts = chatStore.contacts.filter(c => c.id !== contact.id)
|
||||
if(contactStore.selectedContact?.id === contact.id) contactStore.setSelectedContact(null)
|
||||
toastStore.success('已删除')
|
||||
showDeleteContactConfirm.value = false
|
||||
pendingDeleteContact.value = null
|
||||
} catch(e: any) {
|
||||
toastStore.error(e.message || '删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
// --- 辅助功能 ---
|
||||
|
||||
async function submitRemark() {
|
||||
|
||||
Reference in New Issue
Block a user