qwen_agent/skills/common/data-dashboard/apps/multi-chart.html
2026-05-20 14:54:07 +08:00

179 lines
6.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multi Chart</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 24px; }
h1 { font-size: 22px; font-weight: 600; margin-bottom: 20px; text-align: center; }
.grid { display: grid; gap: 16px; }
.chart-card { border-radius: 12px; padding: 16px; border: 1px solid #e2e8f0; }
@media (max-width: 768px) { .grid { grid-template-columns: 1fr !important; } }
</style>
</head>
<body>
<h1 id="title"></h1>
<div class="grid" id="grid"></div>
<script>
(function () {
var charts = [];
var COLOR_PALETTE = ['#5470c6','#91cc75','#fac858','#ee6666','#73c0de','#3ba272','#fc8452','#9a60b4'];
function baseOption(title) {
return {
title: { text: title, left: 'center', top: 8, textStyle: { fontSize: 16, fontWeight: 600 } },
tooltip: { trigger: 'axis' },
legend: { top: 36 },
grid: { top: 80, left: 60, right: 30, bottom: 40 },
color: COLOR_PALETTE
};
}
function labelCfg(show) { return { show: show, position: 'top', fontSize: 11 }; }
var builders = {
line: function (d) {
var opt = baseOption(d.title);
opt.xAxis = { type: 'category', data: d.data.categories || [], boundaryGap: false };
opt.yAxis = { type: 'value' };
opt.series = (d.data.series || []).map(function (s) {
return { name: s.name, type: 'line', data: s.data, smooth: !!d.smooth,
stack: d.stacked ? 'total' : null, areaStyle: d.stacked ? {} : null,
label: labelCfg(!!d.show_label) };
});
return opt;
},
bar: function (d) {
var opt = baseOption(d.title);
opt.xAxis = { type: 'category', data: d.data.categories || [] };
opt.yAxis = { type: 'value' };
opt.series = (d.data.series || []).map(function (s) {
return { name: s.name, type: 'bar', data: s.data, stack: d.stacked ? 'total' : null,
label: labelCfg(!!d.show_label), barMaxWidth: 50 };
});
return opt;
},
pie: function (d) {
var opt = baseOption(d.title);
delete opt.grid;
opt.tooltip = { trigger: 'item', formatter: '{b}: {c} ({d}%)' };
var s0 = (d.data.series || [])[0] || {};
opt.series = [{
name: s0.name || d.title, type: 'pie', radius: ['40%','70%'], center: ['50%','55%'],
data: s0.data || [],
emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)' } },
label: { show: true, formatter: '{b}: {d}%' },
itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 2 }
}];
return opt;
},
radar: function (d) {
var opt = baseOption(d.title);
delete opt.grid;
opt.tooltip = { trigger: 'item' };
var cats = d.data.categories || [];
var series = d.data.series || [];
var maxVals = cats.map(function () { return 0; });
series.forEach(function (s) {
s.data.forEach(function (v, i) { if (i < maxVals.length && v > maxVals[i]) maxVals[i] = v; });
});
opt.radar = {
indicator: cats.map(function (c, i) { return { name: c, max: Math.round(maxVals[i] * 1.2) || 100 }; }),
center: ['50%','55%']
};
opt.series = [{
type: 'radar',
data: series.map(function (s) { return { value: s.data, name: s.name }; }),
areaStyle: { opacity: 0.15 }
}];
return opt;
},
scatter: function (d) {
var opt = baseOption(d.title);
opt.tooltip = { trigger: 'item', formatter: '{a}: ({c})' };
opt.xAxis = { type: 'value', scale: true };
opt.yAxis = { type: 'value', scale: true };
opt.series = (d.data.series || []).map(function (s) {
return { name: s.name, type: 'scatter', data: s.data, symbolSize: 10, label: labelCfg(!!d.show_label) };
});
return opt;
},
gauge: function (d) {
var opt = baseOption(d.title);
delete opt.grid;
opt.tooltip = { trigger: 'item' };
var s0 = (d.data.series || [])[0] || {};
var val = (s0.data && s0.data[0]) || 0;
if (typeof val !== 'number') val = 0;
opt.series = [{
type: 'gauge', center: ['50%','60%'], startAngle: 200, endAngle: -20,
min: 0, max: 100,
detail: { formatter: '{value}%', fontSize: 24, offsetCenter: [0,'60%'] },
data: [{ value: val, name: s0.name || d.title }],
axisLine: { lineStyle: { width: 20 } },
progress: { show: true, width: 20 },
pointer: { show: true }
}];
return opt;
}
};
function render(payload) {
var isDark = payload.theme === 'dark';
document.body.style.background = isDark ? '#1a1a2e' : '#f8fafc';
document.body.style.color = isDark ? '#e0e0e0' : '#0f172a';
document.getElementById('title').textContent = payload.title || 'Dashboard';
var grid = document.getElementById('grid');
var columns = payload.columns || 2;
grid.style.gridTemplateColumns = 'repeat(' + columns + ', 1fr)';
grid.innerHTML = '';
// Dispose previous charts
charts.forEach(function (c) { c.dispose(); });
charts = [];
var echartsTheme = isDark ? 'dark' : null;
var cardBg = isDark ? '#252547' : '#ffffff';
var borderColor = isDark ? '#3a3a5c' : '#e2e8f0';
(payload.charts || []).forEach(function (chartDef, idx) {
var card = document.createElement('div');
card.className = 'chart-card';
card.style.background = cardBg;
card.style.borderColor = borderColor;
var el = document.createElement('div');
el.id = 'chart_' + idx;
el.style.width = '100%';
el.style.height = chartDef.height || '350px';
card.appendChild(el);
grid.appendChild(card);
var c = echarts.init(el, echartsTheme);
var builder = builders[chartDef.chart_type] || builders.line;
c.setOption(builder(chartDef));
charts.push(c);
});
window.addEventListener('resize', function () {
charts.forEach(function (c) { c.resize(); });
});
}
window.addEventListener('message', function (event) {
var msg = event.data;
if (msg && msg.type === 'mcp-app-data') {
render(msg.payload);
}
});
window.parent.postMessage({ type: 'mcp-app-ready' }, '*');
})();
</script>
</body>
</html>