|
| 1 | +<template> |
| 2 | + <div class="flex-1 flex flex-col min-h-0 min-w-0"> |
| 3 | + <div v-if="selectedContact" class="flex-1 flex flex-col min-h-0 relative"> |
| 4 | + <div class="chat-header"> |
| 5 | + <div class="flex items-center gap-3"> |
| 6 | + <h2 class="text-base font-medium text-gray-900" :class="{ 'privacy-blur': privacyMode }"> |
| 7 | + {{ selectedContact ? selectedContact.name : '' }} |
| 8 | + </h2> |
| 9 | + </div> |
| 10 | + <div class="ml-auto flex items-center gap-2"> |
| 11 | + <button class="header-btn-icon" @click="refreshSelectedMessages" :disabled="isLoadingMessages" title="刷新消息"> |
| 12 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 13 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/> |
| 14 | + </svg> |
| 15 | + </button> |
| 16 | + <button class="header-btn-icon" @click="openExportModal" :disabled="isExportCreating" title="导出聊天记录"> |
| 17 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 18 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/> |
| 19 | + </svg> |
| 20 | + </button> |
| 21 | + <button class="header-btn-icon" :class="{ 'header-btn-icon-active': reverseMessageSides }" @click="toggleReverseMessageSides" :disabled="!selectedContact" :title="reverseMessageSides ? '取消反转消息位置' : '反转消息位置'"> |
| 22 | + <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> |
| 23 | + <path d="M4 7h14" /> |
| 24 | + <path d="M14 3l4 4-4 4" /> |
| 25 | + <path d="M20 17H6" /> |
| 26 | + <path d="M10 13l-4 4 4 4" /> |
| 27 | + </svg> |
| 28 | + </button> |
| 29 | + <button class="header-btn-icon" :class="{ 'header-btn-icon-active': messageSearchOpen }" @click="toggleMessageSearch" :title="messageSearchOpen ? '关闭搜索 (Esc)' : '搜索聊天记录 (Ctrl+F)'"> |
| 30 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 16 16"> |
| 31 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7.33333 12.6667C10.2789 12.6667 12.6667 10.2789 12.6667 7.33333C12.6667 4.38781 10.2789 2 7.33333 2C4.38781 2 2 4.38781 2 7.33333C2 10.2789 4.38781 12.6667 7.33333 12.6667Z" /> |
| 32 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M14 14L11.1 11.1" /> |
| 33 | + </svg> |
| 34 | + </button> |
| 35 | + <button class="header-btn-icon" :class="{ 'header-btn-icon-active': timeSidebarOpen }" @click="toggleTimeSidebar" :disabled="!selectedContact || isLoadingMessages" title="按日期定位"> |
| 36 | + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 37 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M8 7V3m8 4V3M3 11h18" /> |
| 38 | + <rect x="4" y="5" width="16" height="16" rx="2" ry="2" stroke-width="1.8" /> |
| 39 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M7 14h2m3 0h2m3 0h2M7 18h2m3 0h2" /> |
| 40 | + </svg> |
| 41 | + </button> |
| 42 | + <select |
| 43 | + v-model="messageTypeFilter" |
| 44 | + class="message-filter-select" |
| 45 | + :disabled="isLoadingMessages || searchContext.active" |
| 46 | + :title="searchContext.active ? '上下文模式下暂不可筛选' : '筛选消息类型'" |
| 47 | + > |
| 48 | + <option v-for="opt in messageTypeFilterOptions" :key="opt.value" :value="opt.value"> |
| 49 | + {{ opt.label }} |
| 50 | + </option> |
| 51 | + </select> |
| 52 | + </div> |
| 53 | + </div> |
| 54 | + |
| 55 | + <div v-if="searchContext.active" class="px-6 py-2 border-b border-emerald-200 bg-emerald-50 flex items-center gap-3"> |
| 56 | + <div class="text-sm text-emerald-900"> |
| 57 | + {{ searchContextBannerText }} |
| 58 | + </div> |
| 59 | + <div class="ml-auto flex items-center gap-2"> |
| 60 | + <button type="button" class="text-xs px-3 py-1 rounded-md bg-white border border-emerald-200 hover:bg-emerald-100" @click="exitSearchContext"> |
| 61 | + 退出定位 |
| 62 | + </button> |
| 63 | + <button type="button" class="text-xs px-3 py-1 rounded-md bg-white border border-gray-200 hover:bg-gray-50" @click="refreshSelectedMessages"> |
| 64 | + 返回最新 |
| 65 | + </button> |
| 66 | + </div> |
| 67 | + </div> |
| 68 | + |
| 69 | + <MessageList :state="state" /> |
| 70 | + |
| 71 | + <button |
| 72 | + v-if="showJumpToBottom" |
| 73 | + type="button" |
| 74 | + class="absolute bottom-6 right-6 z-20 w-10 h-10 rounded-full bg-white/90 border border-gray-200 shadow hover:bg-white flex items-center justify-center" |
| 75 | + title="回到最新" |
| 76 | + @click="scrollToBottom" |
| 77 | + > |
| 78 | + <svg class="w-5 h-5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 79 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> |
| 80 | + </svg> |
| 81 | + </button> |
| 82 | + </div> |
| 83 | + |
| 84 | + <div v-else class="flex-1 flex items-center justify-center"> |
| 85 | + <div class="text-center"> |
| 86 | + <div class="w-20 h-20 mx-auto mb-5 rounded-2xl bg-gradient-to-br from-[#03C160]/10 to-[#03C160]/5 flex items-center justify-center"> |
| 87 | + <svg class="w-10 h-10 text-[#03C160]/60" viewBox="0 0 24 24" fill="currentColor"> |
| 88 | + <path d="M12 19.8C17.52 19.8 22 15.99 22 11.3C22 6.6 17.52 2.8 12 2.8C6.48 2.8 2 6.6 2 11.3C2 13.29 2.8 15.12 4.15 16.57C4.6 17.05 4.82 17.29 4.92 17.44C5.14 17.79 5.21 17.99 5.23 18.4C5.24 18.59 5.22 18.81 5.16 19.26C5.1 19.75 5.07 19.99 5.13 20.16C5.23 20.49 5.53 20.71 5.87 20.72C6.04 20.72 6.27 20.63 6.72 20.43L8.07 19.86C8.43 19.71 8.61 19.63 8.77 19.59C8.95 19.55 9.04 19.54 9.22 19.54C9.39 19.53 9.64 19.57 10.14 19.65C10.74 19.75 11.37 19.8 12 19.8Z"/> |
| 89 | + </svg> |
| 90 | + </div> |
| 91 | + <h3 class="text-base font-medium text-gray-700 mb-1.5">选择一个会话</h3> |
| 92 | + <p class="text-sm text-gray-400"> |
| 93 | + 从左侧列表选择联系人查看聊天记录 |
| 94 | + </p> |
| 95 | + </div> |
| 96 | + </div> |
| 97 | + </div> |
| 98 | +</template> |
| 99 | + |
| 100 | +<script> |
| 101 | +import { defineComponent } from 'vue' |
| 102 | +import MessageList from '~/components/chat/MessageList.vue' |
| 103 | +
|
| 104 | +export default defineComponent({ |
| 105 | + name: 'ConversationPane', |
| 106 | + components: { MessageList }, |
| 107 | + props: { |
| 108 | + state: { type: Object, required: true } |
| 109 | + }, |
| 110 | + setup(props) { |
| 111 | + return { |
| 112 | + ...props.state |
| 113 | + } |
| 114 | + } |
| 115 | +}) |
| 116 | +</script> |
0 commit comments