<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{CHART_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;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.chart-wrapper {
width: 100%;
max-width: 1200px;
background: {{CONTAINER_BACKGROUND}};
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.chart-header {
margin-bottom: 20px;
text-align: center;
}
.chart-header h1 {
font-size: 1.8em;
font-weight: 500;
color: {{TEXT_COLOR}};
margin-bottom: 10px;
}
.chart-header .meta {
font-size: 0.9em;
color: {{FOOTER_TEXT_COLOR}};
}
.chart-container {
position: relative;
width: 100%;
height: 500px;
margin: 0 auto;
}
.chart-container canvas {
width: 100% !important;
height: 100% !important;
}
.chart-footer {
text-align: center;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid {{FOOTER_BORDER}};
color: {{FOOTER_TEXT_COLOR}};
font-size: 0.85em;
}
/* 加载动画 */
.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: 768px) {
body {
padding: 10px;
}
.chart-wrapper {
padding: 20px;
}
.chart-header h1 {
font-size: 1.4em;
}
.chart-container {
height: 350px;
}
}
/* 打印样式 */
@media print {
body {
background: white;
padding: 0;
}
.chart-wrapper {
box-shadow: none;
border: 1px solid #ddd;
}
.chart-footer {
page-break-before: avoid;
}
}
</style>
</head>
<body>
<div class="chart-wrapper">
<!-- 图表标题 -->
<div class="chart-header">
<h1>{{CHART_TITLE}}</h1>
<div class="meta">
<span>生成时间: {{GENERATION_TIME}}</span>
<span style="margin: 0 10px;">•</span>
<span>图表类型: {{CHART_TYPE}}</span>
</div>
</div>
<!-- 图表容器 -->
<div class="chart-container">
<canvas id="mainChart"></canvas>
</div>
<!-- 页脚 -->
<div class="chart-footer">
<p>由专业数据可视化MCP服务器生成</p>
<p style="margin-top: 5px; opacity: 0.8;">
{{SQL_QUERY}}
</p>
</div>
</div>
<script>
// 图表配置
const chartConfig = {{CHART_CONFIG}};
// 图表实例
let chartInstance = null;
/**
* 初始化图表
*/
function initializeChart() {
try {
const canvas = document.getElementById('mainChart');
if (!canvas) {
console.error('Canvas not found');
return;
}
const ctx = canvas.getContext('2d');
chartInstance = new Chart(ctx, chartConfig);
console.log('Chart initialized successfully');
} catch (error) {
console.error('Failed to initialize chart:', error);
document.querySelector('.chart-container').innerHTML =
'<div style="text-align: center; padding: 50px; color: #999;">图表加载失败</div>';
}
}
/**
* 响应式处理
*/
let resizeTimeout;
function handleResize() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
if (chartInstance) {
try {
chartInstance.resize();
} catch (error) {
console.error('Failed to resize chart:', error);
}
}
}, 250);
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
console.log('Single chart page loading...');
initializeChart();
window.addEventListener('resize', handleResize);
console.log('Single chart page loaded successfully');
});
// 页面卸载前清理
window.addEventListener('beforeunload', function() {
if (chartInstance) {
try {
chartInstance.destroy();
} catch (error) {
console.error('Failed to destroy chart:', error);
}
}
});
// 暴露全局方法供调试使用
window.ChartAPI = {
getChartInstance: () => chartInstance,
refreshChart: () => {
if (chartInstance) {
chartInstance.update();
}
},
exportChartData: () => {
return chartInstance ? chartInstance.data : null;
}
};
</script>
</body>
</html>