## 重写DNS日志详情弹窗方案 ### 1. 问题分析 当前弹窗代码存在以下问题: - 代码结构复杂,大量嵌套条件和重复逻辑 - 样式不够现代,布局不够灵活 - 响应式设计不足,在大屏幕上显示效果不佳 - 代码冗余,重复的记录解析逻辑 - 可维护性差,HTML结构直接写在JavaScript字符串中 ### 2. 设计思路 - **组件化设计**:将弹窗拆分为更小的可复用组件 - **简化逻辑**:重构DNS解析记录处理逻辑,减少重复代码 - **现代样式**:使用更现代的CSS布局和样式 - **响应式设计**:优化在不同屏幕尺寸下的显示效果 - **更好的用户体验**:添加动画效果、改进交互体验 - **更清晰的代码结构**:提高可维护性 ### 3. 实现方案 #### 3.1 重构`showLogDetailModal`函数 - 简化函数结构,分离关注点 - 将DNS解析记录处理逻辑提取为独立函数 - 使用更现代的DOM API创建元素 #### 3.2 优化DNS解析记录处理 - 创建独立的`formatDNSRecords`函数 - 统一处理各种格式的DNS记录 - 减少重复代码,提高可维护性 #### 3.3 现代CSS样式 - 使用更现代的CSS类名和布局 - 优化响应式设计,支持不同屏幕尺寸 - 添加动画效果,提升用户体验 #### 3.4 改进HTML结构 - 使用更清晰的语义化HTML - 优化布局结构,提高可读性 - 支持更好的响应式设计 ### 4. 代码实现 ```javascript // 独立的DNS记录格式化函数 function formatDNSRecords(log, result) { if (result === 'blocked') return '无'; let records = ''; const sources = [ log.answers, log.answer, log.Records, log.records, log.response ]; for (const source of sources) { if (records) break; if (!source || source === '无') continue; // 处理数组类型 if (Array.isArray(source)) { records = source.map(answer => { const type = answer.type || answer.Type || '未知'; let value = answer.value || answer.Value || answer.data || answer.Data || '未知'; const ttl = answer.TTL || answer.ttl || answer.expires || '未知'; // 增强的记录值提取逻辑 if (typeof value === 'string') { value = value.trim(); // 处理制表符分隔的格式 if (value.includes('\t') || value.includes('\\t')) { const parts = value.replace(/\\t/g, '\t').split('\t'); if (parts.length >= 4) { value = parts[parts.length - 1].trim(); } } // 处理JSON格式 else if (value.startsWith('{') && value.endsWith('}')) { try { const parsed = JSON.parse(value); value = parsed.data || parsed.value || value; } catch (e) {} } } return `${type}: ${value} (ttl=${ttl})`; }).join('\n').trim(); } // 处理字符串类型 else if (typeof source === 'string') { // 尝试解析为JSON数组 if (source.startsWith('[') && source.endsWith(']')) { try { const parsed = JSON.parse(source); if (Array.isArray(parsed)) { records = parsed.map(answer => { const type = answer.type || answer.Type || '未知'; let value = answer.value || answer.Value || answer.data || answer.Data || '未知'; const ttl = answer.TTL || answer.ttl || answer.expires || '未知'; if (typeof value === 'string') { value = value.trim(); } return `${type}: ${value} (ttl=${ttl})`; }).join('\n').trim(); } } catch (e) { // 解析失败,尝试直接格式化 records = formatDNSString(source); } } else { // 直接格式化字符串 records = formatDNSString(source); } } } return records || '无解析记录'; } // 格式化DNS字符串记录 function formatDNSString(str) { const recordLines = str.split('\n').map(line => line.trim()).filter(line => line !== ''); return recordLines.map(line => { // 检查是否已经是标准格式 if (line.includes(':') && line.includes('(')) { return line; } // 尝试解析为标准DNS格式 const parts = line.split(/\s+/); if (parts.length >= 5) { const type = parts[3]; const value = parts.slice(4).join(' '); const ttl = parts[1]; return `${type}: ${value} (ttl=${ttl})`; } // 无法解析,返回原始行 return line; }).join('\n'); } // 重写后的showLogDetailModal函数 async function showLogDetailModal(log) { if (!log) { console.error('No log data provided!'); return; } try { // 安全获取log属性,提供默认值 const timestamp = log.timestamp ? new Date(log.timestamp) : null; const dateStr = timestamp ? timestamp.toLocaleDateString() : '未知'; const timeStr = timestamp ? timestamp.toLocaleTimeString() : '未知'; const domain = log.domain || '未知'; const queryType = log.queryType || '未知'; const result = log.result || '未知'; const responseTime = log.responseTime || '未知'; const clientIP = log.clientIP || '未知'; const location = log.location || '未知'; const fromCache = log.fromCache || false; const dnssec = log.dnssec || false; const edns = log.edns || false; const dnsServer = log.dnsServer || '无'; const dnssecServer = log.dnssecServer || '无'; const blockRule = log.blockRule || '无'; // 检查域名是否在跟踪器数据库中 const trackerInfo = await isDomainInTrackerDatabase(log.domain); const isTracker = trackerInfo !== null; // 格式化DNS解析记录 const dnsRecords = formatDNSRecords(log, result); // 创建模态框容器 const modalContainer = document.createElement('div'); modalContainer.className = 'fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4 animate-fade-in'; modalContainer.style.zIndex = '9999'; // 创建模态框内容 const modalContent = document.createElement('div'); modalContent.className = 'bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-y-auto animate-slide-in'; // 创建标题栏 const header = document.createElement('div'); header.className = 'sticky top-0 bg-white border-b border-gray-200 px-6 py-4 flex justify-between items-center'; const title = document.createElement('h3'); title.className = 'text-xl font-semibold text-gray-900'; title.textContent = '日志详情'; const closeButton = document.createElement('button'); closeButton.innerHTML = ''; closeButton.className = 'text-gray-500 hover:text-gray-700 focus:outline-none transition-colors'; closeButton.onclick = () => closeModal(); header.appendChild(title); header.appendChild(closeButton); // 创建内容区域 const content = document.createElement('div'); content.className = 'p-6 space-y-6'; // 基本信息部分 const basicInfo = document.createElement('div'); basicInfo.className = 'space-y-4'; const basicInfoTitle = document.createElement('h4'); basicInfoTitle.className = 'text-sm font-medium text-gray-700 uppercase tracking-wider'; basicInfoTitle.textContent = '基本信息'; const basicInfoGrid = document.createElement('div'); basicInfoGrid.className = 'grid grid-cols-1 md:grid-cols-2 gap-4'; // 添加基本信息项 basicInfoGrid.innerHTML = `