You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 lines
9.1 KiB

"""
聊天记录搜索窗口(优化版 — 连接服务器搜索)
"""
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QLineEdit, QPushButton, QListWidget, QListWidgetItem,
QTextEdit, QSplitter, QFrame
)
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QColor
import html
class SearchResultItem(QListWidgetItem):
def __init__(self, message_data, keyword=''):
super().__init__()
self.message_data = message_data
self._update_display(keyword)
def _update_display(self, keyword=''):
sender = self.message_data.get('sender', '未知')
content = self.message_data.get('content', '')
timestamp = self.message_data.get('timestamp', '')
msg_type = self.message_data.get('msg_type', 'text')
if msg_type == 'image':
preview = '[图片]'
else:
preview = content[:60] + '...' if len(content) > 60 else content
display_text = f"{sender} {timestamp}\n{preview}"
self.setText(display_text)
self.setData(Qt.UserRole, self.message_data)
def highlight_keyword(self, keyword):
"""高亮关键词(未来可扩展富文本显示)"""
pass
class SearchWindow(QWidget):
search_requested = pyqtSignal(str, str, str) # chat_type, target_id, keyword
def __init__(self, chat_type, target_id, target_name, parent=None):
super().__init__(parent)
self.chat_type = chat_type
self.target_id = target_id
self.target_name = target_name
self.search_results = []
self._init_ui()
def _init_ui(self):
self.setWindowTitle(f"搜索聊天记录 - {self.target_name}")
self.setGeometry(200, 200, 820, 600)
self.setMinimumSize(600, 400)
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
# 搜索栏
search_frame = QFrame()
search_frame.setStyleSheet("""
QFrame {
background: #f8fafc; border-bottom: 1px solid #e2e8f0;
}
""")
search_layout = QHBoxLayout(search_frame)
search_layout.setContentsMargins(16, 10, 16, 10)
search_layout.setSpacing(8)
search_icon = QLabel("🔍")
search_icon.setStyleSheet("font-size: 16px; border: none;")
search_layout.addWidget(search_icon)
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("输入关键词搜索聊天记录,支持中英文…")
self.search_input.setStyleSheet("""
QLineEdit {
border: 1px solid #ddd; border-radius: 8px;
background: white; font-size: 14px; padding: 8px 12px;
}
QLineEdit:focus { border: 1px solid #6366f1; }
""")
self.search_input.returnPressed.connect(self._perform_search)
search_layout.addWidget(self.search_input)
search_btn = QPushButton("搜索")
search_btn.setStyleSheet("""
QPushButton {
background: #6366f1; color: white; border: none;
border-radius: 8px; padding: 8px 20px;
font-size: 13px; font-weight: bold;
}
QPushButton:hover { background: #4f46e5; }
""")
search_btn.clicked.connect(self._perform_search)
search_layout.addWidget(search_btn)
layout.addWidget(search_frame)
# 信息栏
info_frame = QFrame()
info_frame.setStyleSheet("background: #f1f5f9; border-bottom: 1px solid #e0e0e0;")
info_layout = QHBoxLayout(info_frame)
info_layout.setContentsMargins(16, 6, 16, 6)
chat_type_text = "私聊" if self.chat_type == 'private' else "群聊"
info_lbl = QLabel(f"搜索范围:{chat_type_text}{self.target_name}")
info_lbl.setStyleSheet("font-size: 12px; color: #888;")
info_layout.addWidget(info_lbl)
layout.addWidget(info_frame)
# 分割器
splitter = QSplitter(Qt.Horizontal)
self._build_results_panel(splitter)
self._build_detail_panel(splitter)
splitter.setSizes([320, 500])
layout.addWidget(splitter)
def _build_results_panel(self, parent_splitter):
results_frame = QFrame()
results_frame.setStyleSheet("QFrame { background: white; }")
results_layout = QVBoxLayout(results_frame)
results_layout.setContentsMargins(0, 0, 0, 0)
results_layout.setSpacing(0)
# 结果标题
header = QWidget()
header.setFixedHeight(36)
header.setStyleSheet("background: #fafafa; border-bottom: 1px solid #eee;")
hl = QHBoxLayout(header)
hl.setContentsMargins(12, 0, 12, 0)
self.results_title = QLabel("搜索结果")
self.results_title.setStyleSheet("font-size: 13px; font-weight: bold; color: #555;")
hl.addWidget(self.results_title)
results_layout.addWidget(header)
self.results_list = QListWidget()
self.results_list.setStyleSheet("""
QListWidget {
border: none; background: white; outline: none;
}
QListWidget::item {
padding: 10px 14px; border-bottom: 1px solid #f1f5f9;
font-size: 12px; line-height: 1.4;
}
QListWidget::item:hover { background: #f8fafc; }
QListWidget::item:selected { background: #eef2ff; }
""")
self.results_list.itemClicked.connect(self._show_message_detail)
results_layout.addWidget(self.results_list)
parent_splitter.addWidget(results_frame)
def _build_detail_panel(self, parent_splitter):
detail_frame = QFrame()
detail_frame.setStyleSheet("QFrame { background: #f8fafc; }")
detail_layout = QVBoxLayout(detail_frame)
detail_layout.setContentsMargins(0, 0, 0, 0)
detail_layout.setSpacing(0)
# 详情标题
header = QWidget()
header.setFixedHeight(36)
header.setStyleSheet("background: #fafafa; border-bottom: 1px solid #eee;")
hl = QHBoxLayout(header)
hl.setContentsMargins(12, 0, 12, 0)
detail_title = QLabel("消息详情")
detail_title.setStyleSheet("font-size: 13px; font-weight: bold; color: #555;")
hl.addWidget(detail_title)
detail_layout.addWidget(header)
self.detail_text = QTextEdit()
self.detail_text.setReadOnly(True)
self.detail_text.setStyleSheet("""
QTextEdit {
border: none; background: #f8fafc;
font-size: 13px; line-height: 1.6;
padding: 16px;
}
""")
self.detail_text.setPlaceholderText("选择左侧一条搜索结果查看消息详情…")
detail_layout.addWidget(self.detail_text)
parent_splitter.addWidget(detail_frame)
def _perform_search(self):
keyword = self.search_input.text().strip()
if not keyword:
return
self.results_list.clear()
self.detail_text.clear()
self.results_title.setText("搜索中…")
self.search_requested.emit(self.chat_type, str(self.target_id), keyword)
def _show_message_detail(self, item):
message_data = item.data(Qt.UserRole)
if not message_data:
return
sender = message_data.get('sender', '未知')
content = message_data.get('content', '')
timestamp = message_data.get('timestamp', '')
msg_type = message_data.get('msg_type', 'text')
if msg_type == 'image':
content_display = '<div style="font-size: 14px; color: #6366f1;">[图片消息]</div>'
else:
content_display = f'<div style="font-size: 14px; line-height: 1.7; color: #333;">{html.escape(content).replace(chr(92)+"n", "<br>")}</div>'
detail_html = f"""
<div style="font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;">
<div style="background: #eef2ff; padding: 10px 14px; border-radius: 8px; margin-bottom: 14px;">
<strong style="color: #6366f1;">发送者:</strong>{html.escape(sender)}<br>
<strong style="color: #6366f1;">时间:</strong>{html.escape(timestamp)}
</div>
<div style="background: white; padding: 14px; border-radius: 8px; border: 1px solid #e0e0e0;">
<strong style="color: #2c3e50;">消息内容:</strong><br><br>
{content_display}
</div>
</div>
"""
self.detail_text.setHtml(detail_html)
def update_search_results(self, results, keyword):
self.search_results = results
self.results_list.clear()
count = len(results)
self.results_title.setText(f"搜索结果({count} 条)")
if not results:
no_result = QListWidgetItem("未找到包含关键词的消息")
no_result.setForeground(QColor('#94a3b8'))
self.results_list.addItem(no_result)
return
for message in results:
item = SearchResultItem(message, keyword)
self.results_list.addItem(item)
def closeEvent(self, event):
self.search_results.clear()
event.accept()