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.
194 lines
7.2 KiB
194 lines
7.2 KiB
"""
|
|
全局聊天记录浏览窗口
|
|
"""
|
|
from PyQt5.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QListWidget, QListWidgetItem, QTextEdit,
|
|
QSplitter, QFrame, QPushButton, QComboBox
|
|
)
|
|
from PyQt5.QtCore import Qt, pyqtSignal
|
|
from PyQt5.QtGui import QColor
|
|
import html
|
|
from datetime import datetime
|
|
|
|
|
|
class HistoryWindow(QWidget):
|
|
refresh_requested = pyqtSignal()
|
|
search_requested = pyqtSignal(str) # keyword
|
|
|
|
def __init__(self, username, parent=None):
|
|
super().__init__(parent)
|
|
self.username = username
|
|
self.all_messages = []
|
|
self._init_ui()
|
|
|
|
def _init_ui(self):
|
|
self.setWindowTitle("聊天记录浏览器")
|
|
self.setGeometry(150, 150, 900, 650)
|
|
self.setMinimumSize(650, 450)
|
|
|
|
layout = QVBoxLayout(self)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
layout.setSpacing(0)
|
|
|
|
# 顶部工具栏
|
|
toolbar = QFrame()
|
|
toolbar.setStyleSheet("QFrame { background: #f8fafc; border-bottom: 1px solid #e2e8f0; }")
|
|
tl = QHBoxLayout(toolbar)
|
|
tl.setContentsMargins(14, 8, 14, 8)
|
|
tl.setSpacing(8)
|
|
|
|
title = QLabel("📜 聊天记录")
|
|
title.setStyleSheet("font-size: 15px; font-weight: bold; color: #1e293b;")
|
|
tl.addWidget(title)
|
|
|
|
# 分类筛选
|
|
self.filter_combo = QComboBox()
|
|
self.filter_combo.addItems(["全部消息", "私聊消息", "群聊消息"])
|
|
self.filter_combo.setStyleSheet("""
|
|
QComboBox { padding: 4px 10px; border: 1px solid #ddd;
|
|
border-radius: 5px; background: white; font-size: 12px; }
|
|
""")
|
|
self.filter_combo.currentIndexChanged.connect(self._apply_filter)
|
|
tl.addWidget(self.filter_combo)
|
|
|
|
tl.addStretch()
|
|
|
|
refresh_btn = QPushButton("🔄 刷新")
|
|
refresh_btn.setStyleSheet("""
|
|
QPushButton { padding: 5px 14px; border: 1px solid #ddd;
|
|
border-radius: 5px; background: white; font-size: 12px; }
|
|
QPushButton:hover { background: #f0f0f0; }
|
|
""")
|
|
refresh_btn.clicked.connect(self.refresh_requested.emit)
|
|
tl.addWidget(refresh_btn)
|
|
|
|
layout.addWidget(toolbar)
|
|
|
|
# 主区域
|
|
splitter = QSplitter(Qt.Horizontal)
|
|
|
|
# 左侧消息列表
|
|
left_frame = QFrame()
|
|
left_frame.setStyleSheet("QFrame { background: white; }")
|
|
ll = QVBoxLayout(left_frame)
|
|
ll.setContentsMargins(0, 0, 0, 0)
|
|
ll.setSpacing(0)
|
|
|
|
self.msg_list = QListWidget()
|
|
self.msg_list.setStyleSheet("""
|
|
QListWidget { border: none; background: white; outline: none; }
|
|
QListWidget::item { padding: 8px 12px; border-bottom: 1px solid #f1f5f9; font-size: 12px; }
|
|
QListWidget::item:hover { background: #f8fafc; }
|
|
QListWidget::item:selected { background: #eef2ff; }
|
|
""")
|
|
self.msg_list.itemClicked.connect(self._show_detail)
|
|
ll.addWidget(self.msg_list)
|
|
splitter.addWidget(left_frame)
|
|
|
|
# 右侧详情
|
|
right_frame = QFrame()
|
|
right_frame.setStyleSheet("QFrame { background: #fafbfc; }")
|
|
rl = QVBoxLayout(right_frame)
|
|
rl.setContentsMargins(0, 0, 0, 0)
|
|
rl.setSpacing(0)
|
|
|
|
detail_header = QWidget()
|
|
detail_header.setFixedHeight(36)
|
|
detail_header.setStyleSheet("background: #fafafa; border-bottom: 1px solid #eee;")
|
|
dhl = QHBoxLayout(detail_header)
|
|
dhl.setContentsMargins(12, 0, 12, 0)
|
|
dhl.addWidget(QLabel("消息详情"))
|
|
rl.addWidget(detail_header)
|
|
|
|
self.detail_view = QTextEdit()
|
|
self.detail_view.setReadOnly(True)
|
|
self.detail_view.setStyleSheet("""
|
|
QTextEdit { border: none; background: #fafbfc;
|
|
font-size: 13px; line-height: 1.6; padding: 14px; }
|
|
""")
|
|
self.detail_view.setPlaceholderText("选择左侧一条消息查看详情...")
|
|
rl.addWidget(self.detail_view)
|
|
splitter.addWidget(right_frame)
|
|
|
|
splitter.setSizes([350, 550])
|
|
layout.addWidget(splitter)
|
|
|
|
def load_messages(self, messages):
|
|
"""加载消息列表"""
|
|
self.all_messages = messages
|
|
self._apply_filter()
|
|
|
|
def _apply_filter(self):
|
|
self.msg_list.clear()
|
|
filter_idx = self.filter_combo.currentIndex()
|
|
|
|
filtered = self.all_messages
|
|
if filter_idx == 1: # 私聊
|
|
filtered = [m for m in self.all_messages if m.get('chat_type') == 'private']
|
|
elif filter_idx == 2: # 群聊
|
|
filtered = [m for m in self.all_messages if m.get('chat_type') == 'group']
|
|
|
|
for msg in filtered:
|
|
self._add_list_item(msg)
|
|
|
|
def _add_list_item(self, msg):
|
|
sender = msg.get('sender', '?')
|
|
content = msg.get('content', '')
|
|
timestamp = msg.get('timestamp', '')
|
|
chat_type = msg.get('chat_type', 'private')
|
|
target = msg.get('target_name', '')
|
|
msg_type = msg.get('msg_type', 'text')
|
|
icon = "👥" if chat_type == 'group' else "💬"
|
|
|
|
if msg_type == 'image':
|
|
preview = '[图片]'
|
|
else:
|
|
preview = content[:50] + '...' if len(content) > 50 else content
|
|
time_short = timestamp.split(' ')[-1][:5] if ' ' in timestamp else timestamp[:5]
|
|
text = f"{icon} {sender} → {target} {time_short}\n {preview}"
|
|
|
|
item = QListWidgetItem(text)
|
|
item.setData(Qt.UserRole, msg)
|
|
self.msg_list.addItem(item)
|
|
|
|
def _show_detail(self, item):
|
|
msg = item.data(Qt.UserRole)
|
|
if not msg:
|
|
return
|
|
|
|
sender = msg.get('sender', '?')
|
|
content = msg.get('content', '')
|
|
timestamp = msg.get('timestamp', '')
|
|
chat_type = msg.get('chat_type', 'private')
|
|
target = msg.get('target_name', '?')
|
|
msg_type = msg.get('msg_type', 'text')
|
|
type_label = "群聊" if chat_type == 'group' else "私聊"
|
|
|
|
if msg_type == 'image':
|
|
content_html = f'<div style="font-size: 14px; color: #6366f1;">[图片消息]</div>'
|
|
else:
|
|
content_html = f'<div style="font-size: 14px; line-height: 1.7; color: #333;">{html.escape(content)}</div>'
|
|
|
|
detail_html = f"""
|
|
<div style="font-family: 'Microsoft YaHei', sans-serif;">
|
|
<div style="background: #eef2ff; padding: 10px 14px; border-radius: 8px; margin-bottom: 12px;">
|
|
<table style="width:100%; font-size: 13px;">
|
|
<tr><td style="color:#6366f1; width:60px;"><b>发送者:</b></td><td>{html.escape(sender)}</td></tr>
|
|
<tr><td style="color:#6366f1;"><b>类型:</b></td><td>{type_label}</td></tr>
|
|
<tr><td style="color:#6366f1;"><b>对象:</b></td><td>{html.escape(target)}</td></tr>
|
|
<tr><td style="color:#6366f1;"><b>时间:</b></td><td>{html.escape(timestamp)}</td></tr>
|
|
</table>
|
|
</div>
|
|
<div style="background: white; padding: 14px; border-radius: 8px; border: 1px solid #e0e0e0;">
|
|
<b style="color:#1e293b;">消息内容:</b><br><br>
|
|
{content_html}
|
|
</div>
|
|
</div>
|
|
"""
|
|
self.detail_view.setHtml(detail_html)
|
|
|
|
def closeEvent(self, event):
|
|
self.all_messages.clear()
|
|
event.accept()
|