/**
* HTML 导出生成器
* 负责生成聊天记录的 HTML 展示页面
* 使用外部资源引用,避免文件过大
*/
export interface HtmlExportMessage {
timestamp: number
sender: string
senderName: string
type: number
content: string | null
rawContent: string
isSend: boolean
chatRecords?: HtmlChatRecord[]
}
export interface HtmlChatRecord {
sender: string
senderDisplayName: string
timestamp: number
formattedTime: string
type: string
datatype: number
content: string
senderAvatar?: string
fileExt?: string
fileSize?: number
}
export interface HtmlMember {
id: string
name: string
avatar?: string
}
export interface HtmlExportData {
meta: {
sessionId: string
sessionName: string
isGroup: boolean
exportTime: number
messageCount: number
dateRange: { start: number; end: number } | null
}
members: HtmlMember[]
messages: HtmlExportMessage[]
}
export class HtmlExportGenerator {
/**
* 生成 HTML 主文件(引用外部 CSS 和 JS)
*/
static generateHtmlWithData(exportData: HtmlExportData): string {
const escapedSessionName = this.escapeHtml(exportData.meta.sessionName)
const dateRangeText = exportData.meta.dateRange
? `${new Date(exportData.meta.dateRange.start * 1000).toLocaleDateString('zh-CN')} - ${new Date(exportData.meta.dateRange.end * 1000).toLocaleDateString('zh-CN')}`
: ''
return `
';
chatRecordsHtml += '
📋 聊天记录引用
';
for (const record of msg.chatRecords) {
chatRecordsHtml += \`
\${this.escapeHtml(record.senderDisplayName)}
\${this.escapeHtml(record.formattedTime)}
\${this.escapeHtml(record.content)}
\`;
}
chatRecordsHtml += '
';
}
return \`
\${this.escapeHtml(senderName)}
\${contentHtml}
\${chatRecordsHtml}
\${time}
\`;
}
searchMessages() {
const keyword = document.getElementById('searchInput').value.trim().toLowerCase();
if (!keyword) {
this.filteredMessages = this.allData.messages;
} else {
this.filteredMessages = this.allData.messages.filter(msg => {
// 搜索消息内容
if (msg.content && msg.content.toLowerCase().includes(keyword)) {
return true;
}
// 搜索发送者名称
const member = this.allData.members.find(m => m.id === msg.sender);
const senderName = member ? member.name : msg.senderName;
if (senderName.toLowerCase().includes(keyword)) {
return true;
}
// 搜索聊天记录内容
if (msg.chatRecords) {
for (const record of msg.chatRecords) {
if (record.content.toLowerCase().includes(keyword) ||
record.senderDisplayName.toLowerCase().includes(keyword)) {
return true;
}
}
}
return false;
});
}
// 重置并重新加载
this.reset();
}
clearSearch() {
document.getElementById('searchInput').value = '';
this.filteredMessages = this.allData.messages;
this.reset();
}
reset() {
// 停止观察
if (this.loadMoreObserver && this.sentinel && this.sentinel.parentNode) {
this.loadMoreObserver.unobserve(this.sentinel);
}
// 清空容器
this.messagesContainer.innerHTML = '';
// 重置状态
this.loadedCount = 0;
this.isLoading = false;
// 滚动到顶部
this.scrollContainer.scrollTop = 0;
// 重新设置观察器(必须在 loadMoreMessages 之前)
this.setupIntersectionObserver();
// 重新加载
this.loadMoreMessages();
}
updateStats() {
const totalCount = this.filteredMessages.length;
document.getElementById('messageStats').textContent = \`共 \${totalCount} 条消息\`;
document.getElementById('loadedStats').textContent = \`已加载 \${this.loadedCount} 条\`;
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// 初始化应用
const app = new ChatApp();`;
}
/**
* 生成数据 JSON 文件
*/
static generateDataJson(exportData: HtmlExportData): string {
return JSON.stringify(exportData, null, 2);
}
/**
* HTML 转义
*/
private static escapeHtml(text: string): string {
const map: Record