// ======================================== // Claude Code SEO Assistant - Dashboard Logic // ======================================== // Global State const state = { currentView: 'overview', currentDomain: 'yoursite.com', isLoading: false, data: null, charts: {} }; // Initialize Dashboard document.addEventListener('DOMContentLoaded', () => { initializeNavigation(); initializeCharts(); initializeRefreshButton(); initializeDomainSelect(); loadDashboardData(); }); // Navigation function initializeNavigation() { const navItems = document.querySelectorAll('.nav-item'); navItems.forEach(item => { item.addEventListener('click', () => { const view = item.dataset.view; switchView(view); }); }); } function switchView(viewName) { // Update navigation document.querySelectorAll('.nav-item').forEach(item => { item.classList.remove('active'); if (item.dataset.view === viewName) { item.classList.add('active'); } }); // Update views document.querySelectorAll('.view').forEach(view => { view.classList.remove('active'); }); const targetView = document.getElementById(`${viewName}-view`); if (targetView) { targetView.classList.add('active'); state.currentView = viewName; } } // Charts Initialization function initializeCharts() { initializeCitationTrendChart(); initializeEngineComparisonChart(); initializeGeoRadarChart(); initializeContentTypeChart(); } function initializeCitationTrendChart() { const ctx = document.getElementById('citationTrendChart'); if (!ctx) return; state.charts.citationTrend = new Chart(ctx, { type: 'line', data: { labels: generateDateLabels(30), datasets: [ { label: 'ChatGPT', data: generateRandomData(30, 200, 234), borderColor: '#10A37F', backgroundColor: 'rgba(16, 163, 127, 0.1)', tension: 0.4, fill: true }, { label: 'Claude', data: generateRandomData(30, 150, 189), borderColor: '#8B5CF6', backgroundColor: 'rgba(139, 92, 246, 0.1)', tension: 0.4, fill: true }, { label: 'Perplexity', data: generateRandomData(30, 130, 156), borderColor: '#1DB954', backgroundColor: 'rgba(29, 185, 84, 0.1)', tension: 0.4, fill: true }, { label: 'Google SGE', data: generateRandomData(30, 80, 98), borderColor: '#4285F4', backgroundColor: 'rgba(66, 133, 244, 0.1)', tension: 0.4, fill: true } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', }, tooltip: { mode: 'index', intersect: false, } }, scales: { y: { beginAtZero: true, ticks: { callback: function(value) { return value + ' 次'; } } } }, interaction: { mode: 'nearest', axis: 'x', intersect: false } } }); } function initializeEngineComparisonChart() { const ctx = document.getElementById('engineComparisonChart'); if (!ctx) return; state.charts.engineComparison = new Chart(ctx, { type: 'bar', data: { labels: ['ChatGPT', 'Claude', 'Perplexity', 'Google SGE'], datasets: [ { label: '可见性评分', data: [68, 75, 70, 55], backgroundColor: [ 'rgba(16, 163, 127, 0.8)', 'rgba(139, 92, 246, 0.8)', 'rgba(29, 185, 84, 0.8)', 'rgba(66, 133, 244, 0.8)' ], borderWidth: 0 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { callbacks: { label: function(context) { return `评分: ${context.parsed.y}/100`; } } } }, scales: { y: { beginAtZero: true, max: 100 } } } }); } function initializeGeoRadarChart() { const ctx = document.getElementById('geoRadarChart'); if (!ctx) return; state.charts.geoRadar = new Chart(ctx, { type: 'radar', data: { labels: ['权威性', '实体关系', '内容结构', '数据质量', '引用密度', '技术优化'], datasets: [ { label: '当前评分', data: [78, 82, 75, 80, 72, 68], backgroundColor: 'rgba(79, 70, 229, 0.2)', borderColor: 'rgba(79, 70, 229, 1)', borderWidth: 2 }, { label: '目标评分', data: [85, 85, 85, 85, 85, 85], backgroundColor: 'rgba(16, 185, 129, 0.2)', borderColor: 'rgba(16, 185, 129, 1)', borderWidth: 2 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } }, scales: { r: { beginAtZero: true, max: 100, ticks: { stepSize: 20 } } } } }); } function initializeContentTypeChart() { const ctx = document.getElementById('contentTypeChart'); if (!ctx) return; state.charts.contentType = new Chart(ctx, { type: 'pie', data: { labels: ['指南教程', '案例分析', '对比分析', '列表文章', '博客文章'], datasets: [{ data: [45, 25, 18, 12, 20], backgroundColor: [ 'rgba(16, 163, 127, 0.8)', 'rgba(139, 92, 246, 0.8)', 'rgba(29, 185, 84, 0.8)', 'rgba(66, 133, 244, 0.8)', 'rgba(245, 158, 11, 0.8)' ], borderWidth: 2, borderColor: '#fff' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right' }, tooltip: { callbacks: { label: function(context) { const label = context.label || ''; const value = context.parsed || 0; const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = ((value / total) * 100).toFixed(1); return `${label}: ${value} (${percentage}%)`; } } } } } }); } // Data Loading function loadDashboardData() { showLoading(); // Simulate API call setTimeout(() => { // In real implementation, this would fetch from API state.data = generateMockData(); updateDashboard(); hideLoading(); updateLastRefresh(); }, 1000); } function generateMockData() { return { geoScore: 72, citations: { chatgpt: 234, claude: 189, perplexity: 156, googleSge: 98, total: 677 }, ranking: { current: 1, total: 15, trend: 'up' }, traffic: { organic: 12500, trend: '+28%' }, engines: { chatgpt: { score: 68, citations: 234, rank: 4.2, trend: '+18%' }, claude: { score: 75, citations: 189, rank: 3.8, trend: '+22%' }, perplexity: { score: 70, citations: 156, rank: 4.5, trend: '+15%' }, googleSge: { score: 55, citations: 98, rank: 7.2, trend: '稳定' } }, content: { total: 156, avgScore: 68, highCitations: 23, needsOptimization: 18 }, competitors: [ { name: '你们', chatgpt: 68, claude: 75, perplexity: 70, googleSge: 55, total: 268, trend: 'up' }, { name: 'competitor-a.com', chatgpt: 45, claude: 52, perplexity: 48, googleSge: 62, total: 207, trend: 'up' }, { name: 'competitor-b.com', chatgpt: 38, claude: 41, perplexity: 35, googleSge: 48, total: 162, trend: 'stable' }, { name: 'competitor-c.com', chatgpt: 28, claude: 32, perplexity: 30, googleSge: 35, total: 125, trend: 'down' } ], alerts: { critical: 0, warning: 2, info: 12 }, automation: { weeklyAudit: { active: true, executions: 24, successRate: 96, avgTime: 845 }, monthlyReport: { active: true, executions: 6, successRate: 100, avgTime: 420 }, competitorMonitor: { active: false, executions: 45, successRate: 100, avgTime: 180 } } }; } function updateDashboard() { if (!state.data) return; updateMetrics(); updateCharts(); updateCompetitorsTable(); updateAutomationStatus(); updateAlerts(); } function updateMetrics() { // Metrics are static in HTML, but could be updated dynamically console.log('Metrics updated'); } function updateCharts() { // Update chart data if needed console.log('Charts updated'); } function updateCompetitorsTable() { // Table is static in HTML, but could be updated dynamically console.log('Competitors table updated'); } function updateAutomationStatus() { // Automation status is static in HTML, but could be updated dynamically console.log('Automation status updated'); } function updateAlerts() { // Alerts are static in HTML, but could be updated dynamically console.log('Alerts updated'); } // Refresh Button function initializeRefreshButton() { const refreshBtn = document.getElementById('refreshBtn'); if (!refreshBtn) return; refreshBtn.addEventListener('click', () => { loadDashboardData(); showNotification('数据已刷新', 'success'); }); } function initializeDomainSelect() { const domainSelect = document.getElementById('domainSelect'); if (!domainSelect) return; domainSelect.addEventListener('change', (e) => { state.currentDomain = e.target.value; loadDashboardData(); showNotification(`已切换到域名: ${state.currentDomain}`, 'info'); }); } // Utility Functions function showLoading() { const loadingIndicator = document.getElementById('loadingIndicator'); if (loadingIndicator) { loadingIndicator.classList.remove('hidden'); } state.isLoading = true; } function hideLoading() { const loadingIndicator = document.getElementById('loadingIndicator'); if (loadingIndicator) { loadingIndicator.classList.add('hidden'); } state.isLoading = false; } function showNotification(message, type = 'info') { const notification = document.getElementById('notification'); if (!notification) return; const messageEl = notification.querySelector('.message'); if (messageEl) { messageEl.textContent = message; } notification.classList.remove('hidden'); // Auto-hide after 3 seconds setTimeout(() => { hideNotification(); }, 3000); } function hideNotification() { const notification = document.getElementById('notification'); if (notification) { notification.classList.add('hidden'); } } function updateLastRefresh() { const lastUpdateEl = document.getElementById('lastUpdate'); if (lastUpdateEl) { const now = new Date(); const formatted = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); lastUpdateEl.textContent = formatted; } } function generateDateLabels(days) { const labels = []; const now = new Date(); for (let i = days - 1; i >= 0; i--) { const date = new Date(now); date.setDate(date.getDate() - i); labels.push(date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })); } return labels; } function generateRandomData(count, min, max) { const data = []; let current = Math.floor((min + max) / 2); for (let i = 0; i < count; i++) { const change = Math.floor(Math.random() * 20) - 10; current = Math.max(min, Math.min(max, current + change)); data.push(current); } return data; } // Command Execution (Integration with CLI) function runCommand(command) { showNotification(`执行命令: ${command}`, 'info'); // In a real implementation, this would execute the actual command // For now, just simulate the action console.log(`Executing command: ${command}`); // Simulate command completion setTimeout(() => { showNotification(`命令完成: ${command}`, 'success'); }, 2000); } // Filter Buttons (for chart time periods) document.addEventListener('click', (e) => { if (e.target.classList.contains('filter-btn')) { const period = e.target.dataset.period; // Update active state document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.remove('active'); }); e.target.classList.add('active'); // Update chart data based on period if (state.charts.citationTrend) { const labels = generateDateLabels(parseInt(period)); state.charts.citationTrend.data.labels = labels; state.charts.citationTrend.data.datasets.forEach(dataset => { dataset.data = generateRandomData(parseInt(period), 100, 250); }); state.charts.citationTrend.update(); } showNotification(`已切换到 ${period} 天视图`, 'info'); } }); // Keyboard Shortcuts document.addEventListener('keydown', (e) => { // Ctrl/Cmd + R to refresh if ((e.ctrlKey || e.metaKey) && e.key === 'r') { e.preventDefault(); loadDashboardData(); showNotification('数据已刷新', 'success'); } // Number keys to switch views if (e.key >= '1' && e.key <= '6') { const views = ['overview', 'geo', 'competitors', 'content', 'automation', 'alerts']; const viewIndex = parseInt(e.key) - 1; if (views[viewIndex]) { switchView(views[viewIndex]); } } }); // Auto-refresh (optional) // setInterval(() => { // loadDashboardData(); // }, 300000); // Refresh every 5 minutes // Export functions (for future implementation) function exportDashboard() { console.log('Exporting dashboard...'); showNotification('导出功能开发中', 'info'); } function printDashboard() { window.print(); } // Responsive chart resize window.addEventListener('resize', () => { Object.values(state.charts).forEach(chart => { if (chart) { chart.resize(); } }); }); // Service Worker registration (for PWA support - future) if ('serviceWorker' in navigator) { window.addEventListener('load', () => { // navigator.serviceWorker.register('/sw.js') // .then(registration => console.log('SW registered')) // .catch(error => console.log('SW registration failed')); }); } // Performance monitoring window.addEventListener('load', () => { const perfData = performance.timing; const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart; console.log(`Page load time: ${pageLoadTime}ms`); }); // Error handling window.addEventListener('error', (e) => { console.error('Dashboard error:', e); showNotification('发生错误,请刷新页面', 'error'); }); // Export for debugging window.dashboardDebug = { state, charts: state.charts, loadData: loadDashboardData, switchView, runCommand };