68893236+KINDNICK@users.noreply.github.com 1179d6832e ADD : 클로드 스킬 추가
2026-01-30 21:59:38 +09:00

590 lines
17 KiB
JavaScript

// ========================================
// 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
};