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

149 lines
5.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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: 16px; }
#chart { width: 100%; height: 100%; min-height: 360px; }
</style>
</head>
<body>
<div id="chart"></div>
<script>
(function () {
var chartInstance = null;
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 theme = payload.theme === 'dark' ? 'dark' : null;
var bgColor = payload.theme === 'dark' ? '#1a1a2e' : '#f8fafc';
document.body.style.background = bgColor;
var el = document.getElementById('chart');
if (chartInstance) chartInstance.dispose();
chartInstance = echarts.init(el, theme);
var builder = builders[payload.chart_type] || builders.line;
chartInstance.setOption(builder(payload));
window.addEventListener('resize', function () { chartInstance && chartInstance.resize(); });
}
/* MCP Apps postMessage protocol */
window.addEventListener('message', function (event) {
var msg = event.data;
if (msg && msg.type === 'mcp-app-data') {
render(msg.payload);
}
});
/* Signal readiness to host */
window.parent.postMessage({ type: 'mcp-app-ready' }, '*');
})();
</script>
</body>
</html>