<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{TITLE}}</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: {{FONT_FAMILY}};
background: {{BACKGROUND_GRADIENT}};
min-height: 100vh;
color: {{TEXT_COLOR}};
padding: 20px;
}
.dashboard-container {
max-width: 1600px;
margin: 0 auto;
}
/* Dashboard头部 */
.dashboard-header {
background: {{HEADER_GRADIENT}};
color: {{HEADER_TEXT_COLOR}};
padding: 30px;
border-radius: 15px;
margin-bottom: 20px;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.dashboard-header h1 {
font-size: 2.5em;
font-weight: 300;
margin: 0;
}
.dashboard-header .meta {
margin-top: 10px;
opacity: 0.9;
font-size: 0.95em;
}
/* 栅格布局 */
.dashboard-grid {
display: grid;
grid-template-columns: repeat({{GRID_COLS}}, 1fr);
gap: 20px;
grid-auto-rows: minmax({{ROW_HEIGHT}}px, auto);
}
/* 图表卡片 */
.chart-card {
background: {{CONTAINER_BACKGROUND}};
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
transition: transform 0.2s, box-shadow 0.2s;
}
.chart-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0,0,0,0.15);
}
.chart-card h3 {
margin: 0 0 15px 0;
font-size: 1.3em;
color: {{TEXT_COLOR}};
border-bottom: 2px solid {{HEADER_GRADIENT}};
padding-bottom: 10px;
font-weight: 500;
}
.chart-container {
flex: 1;
position: relative;
min-height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
.chart-container canvas {
max-height: 100%;
width: 100% !important;
height: auto !important;
}
/* 页脚 */
.dashboard-footer {
text-align: center;
padding: 30px 20px;
color: {{FOOTER_TEXT_COLOR}};
font-size: 0.9em;
margin-top: 30px;
border-top: 1px solid {{FOOTER_BORDER}};
}
/* 加载动画 */
.loading {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid {{FOOTER_BORDER}};
border-radius: 50%;
border-top-color: {{HEADER_GRADIENT}};
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 响应式设计 */
@media (max-width: 1200px) {
.dashboard-grid {
grid-template-columns: repeat(6, 1fr);
}
}
@media (max-width: 768px) {
body {
padding: 10px;
}
.dashboard-header {
padding: 20px;
}
.dashboard-header h1 {
font-size: 1.8em;
}
.dashboard-grid {
grid-template-columns: 1fr !important;
gap: 15px;
}
.chart-card {
grid-column: 1 / -1 !important;
padding: 15px;
}
.chart-card h3 {
font-size: 1.1em;
}
.chart-container {
min-height: 250px;
}
}
@media (max-width: 480px) {
.dashboard-header h1 {
font-size: 1.5em;
}
.chart-container {
min-height: 200px;
}
}
/* 打印样式 */
@media print {
body {
background: white;
padding: 0;
}
.dashboard-header {
background: #f5f5f5;
color: #333;
}
.chart-card {
break-inside: avoid;
box-shadow: none;
border: 1px solid #ddd;
}
.dashboard-footer {
page-break-before: always;
}
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: {{FOOTER_TEXT_COLOR}};
}
.empty-state h2 {
font-size: 1.5em;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="dashboard-container">
<!-- Dashboard头部 -->
<div class="dashboard-header">
<h1>{{TITLE}}</h1>
<div class="meta">
<span>生成时间: {{GENERATION_TIME}}</span>
<span style="margin: 0 10px;">•</span>
<span>图表数量: {{CHART_COUNT}}</span>
</div>
</div>
<!-- 图表网格 -->
<div class="dashboard-grid" id="dashboardGrid">
{{CHART_CARDS}}
</div>
<!-- 页脚 -->
<div class="dashboard-footer">
<p>由专业数据可视化MCP服务器生成 | Dashboard模式</p>
<p style="margin-top: 10px; font-size: 0.85em;">
支持多图表展示、智能布局和响应式设计
</p>
</div>
</div>
<script>
// 图表配置数组
const chartsConfig = {{CHARTS_CONFIG}};
// 用于存储所有图表实例
const chartInstances = new Map();
/**
* 初始化所有图表
*/
function initializeCharts() {
chartsConfig.forEach(config => {
try {
const canvas = document.getElementById(config.canvasId);
if (!canvas) {
console.error(`Canvas not found: ${config.canvasId}`);
return;
}
const ctx = canvas.getContext('2d');
const chartInstance = new Chart(ctx, config.chartConfig);
chartInstances.set(config.canvasId, chartInstance);
} catch (error) {
console.error(`Failed to initialize chart ${config.canvasId}:`, error);
}
});
console.log(`Successfully initialized ${chartInstances.size} charts`);
}
/**
* 销毁所有图表(用于重新渲染)
*/
function destroyAllCharts() {
chartInstances.forEach(chart => {
try {
chart.destroy();
} catch (error) {
console.error('Failed to destroy chart:', error);
}
});
chartInstances.clear();
}
/**
* 响应式处理
*/
let resizeTimeout;
function handleResize() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
chartInstances.forEach(chart => {
try {
chart.resize();
} catch (error) {
console.error('Failed to resize chart:', error);
}
});
}, 250);
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
console.log('Dashboard loading...');
// 初始化图表
initializeCharts();
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
// 打印前的处理
window.addEventListener('beforeprint', function() {
chartInstances.forEach(chart => {
chart.resize();
});
});
console.log('Dashboard loaded successfully');
});
// 页面卸载前清理
window.addEventListener('beforeunload', function() {
destroyAllCharts();
});
// 暴露全局方法供调试使用
window.DashboardAPI = {
getChartInstances: () => chartInstances,
refreshAllCharts: () => {
chartInstances.forEach(chart => chart.update());
},
exportChartData: (canvasId) => {
const chart = chartInstances.get(canvasId);
return chart ? chart.data : null;
}
};
</script>
</body>
</html>