chart-mcp-service
Version:
MCP服务,实现根据输入自动生成对应图表的功能
427 lines (403 loc) • 13 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChartGenerator = void 0;
class ChartGenerator {
constructor() {
this.charts = new Map();
this.chartTemplates = {
line: {
import: `import { Line, LineProps } from '@alife/bi-material-center-chart';`,
template: `const LineChart: React.FC = () => {
const data = [
{ month: '1月', value: 3, category: '系列1' },
{ month: '2月', value: 4, category: '系列1' },
{ month: '3月', value: 3.5, category: '系列1' },
{ month: '4月', value: 5, category: '系列1' },
{ month: '1月', value: 2, category: '系列2' },
{ month: '2月', value: 3, category: '系列2' },
{ month: '3月', value: 4, category: '系列2' },
{ month: '4月', value: 4.5, category: '系列2' },
];
const config: LineProps = {
data,
xField: 'month',
yField: 'value',
seriesField: 'category',
height: 500,
point: { size: 5 },
line: { style: { lineWidth: 2 } },
legend: { position: 'top' },
tooltip: { shared: true },
color: ['#1890ff', '#2fc25b']
};
return <Line {...config} />;
};`,
},
bar: {
import: `import { Bar, BarProps } from '@alife/bi-material-center-chart';`,
template: `const BarChart: React.FC = () => {
const data = [
{ category: 'A类', value: 100, group: '分组1' },
{ category: 'B类', value: 200, group: '分组1' },
{ category: 'C类', value: 150, group: '分组1' },
{ category: 'A类', value: 120, group: '分组2' },
{ category: 'B类', value: 180, group: '分组2' },
{ category: 'C类', value: 190, group: '分组2' },
];
const config: BarProps = {
data,
xField: 'category',
yField: 'value',
seriesField: 'group',
height: 500,
columnWidthRatio: 0.6,
legend: { position: 'top' },
tooltip: { shared: true },
color: ['#1890ff', '#2fc25b']
};
return <Bar {...config} />;
};`,
},
pie: {
import: `import { Pie, PieProps } from '@alife/bi-material-center-chart';`,
template: `const PieChart: React.FC = () => {
const data = [
{ type: 'A类', value: 27 },
{ type: 'B类', value: 25 },
{ type: 'C类', value: 18 },
{ type: 'D类', value: 15 },
{ type: 'E类', value: 10 },
{ type: '其他', value: 5 },
];
const config: PieProps = {
data,
angleField: 'value',
colorField: 'type',
height: 500,
radius: 0.8,
label: { type: 'outer' },
legend: { position: 'top' },
tooltip: { shared: true },
color: ['#1890ff', '#2fc25b', '#facc14', '#f759ab', '#13c2c2', '#52c41a']
};
return <Pie {...config} />;
};`,
},
area: {
import: `import { Area, AreaProps } from '@alife/bi-material-center-chart';`,
template: `const AreaChart: React.FC = () => {
const data = [
{ month: '1月', value: 3, category: '产品A' },
{ month: '2月', value: 4, category: '产品A' },
{ month: '3月', value: 3.5, category: '产品A' },
{ month: '4月', value: 5, category: '产品A' },
{ month: '1月', value: 2, category: '产品B' },
{ month: '2月', value: 3, category: '产品B' },
{ month: '3月', value: 4, category: '产品B' },
{ month: '4月', value: 4.5, category: '产品B' },
];
const config: AreaProps = {
data,
xField: 'month',
yField: 'value',
seriesField: 'category',
height: 500,
point: { size: 4 },
area: { style: { fillOpacity: 0.3 } },
legend: { position: 'top' },
tooltip: { shared: true },
color: ['#1890ff', '#2fc25b']
};
return <Area {...config} />;
};`,
},
scatter: {
import: `import { Scatter, ScatterProps } from '@alife/bi-material-center-chart';`,
template: `const ScatterChart: React.FC = () => {
const data = [
{ x: 10, y: 20, category: '类别A', value: 15 },
{ x: 15, y: 25, category: '类别A', value: 20 },
{ x: 20, y: 30, category: '类别B', value: 25 },
{ x: 25, y: 35, category: '类别B', value: 30 },
{ x: 30, y: 40, category: '类别C', value: 35 },
];
const config: ScatterProps = {
data,
xField: 'x',
yField: 'y',
seriesField: 'category',
height: 500,
point: { size: 4 },
legend: { position: 'top' },
tooltip: { shared: true },
color: ['#1890ff', '#2fc25b', '#facc14']
};
return <Scatter {...config} />;
};`,
},
radar: {
import: `import { Radar, RadarProps } from '@alife/bi-material-center-chart';`,
template: `const RadarChart: React.FC = () => {
const data = [
{ item: '设计', score: 70, product: '产品A' },
{ item: '开发', score: 60, product: '产品A' },
{ item: '营销', score: 50, product: '产品A' },
{ item: '技术', score: 40, product: '产品A' },
{ item: '设计', score: 60, product: '产品B' },
{ item: '开发', score: 70, product: '产品B' },
{ item: '营销', score: 40, product: '产品B' },
{ item: '技术', score: 60, product: '产品B' },
];
const config: RadarProps = {
data,
xField: 'item',
yField: 'score',
seriesField: 'product',
height: 500,
point: { size: 4 },
area: { style: { fillOpacity: 0.3 } },
legend: { position: 'top' },
tooltip: { shared: true },
color: ['#1890ff', '#2fc25b']
};
return <Radar {...config} />;
};`,
},
wordcloud: {
import: `import { WordCloud, WordCloudProps } from '@alife/bi-material-center-chart';`,
template: `const WordCloudChart: React.FC = () => {
const data = [
{ word: 'React', weight: 100 },
{ word: 'JavaScript', weight: 80 },
{ word: 'TypeScript', weight: 70 },
{ word: 'Vue', weight: 60 },
{ word: 'Angular', weight: 50 },
{ word: 'Node.js', weight: 40 },
{ word: 'Express', weight: 30 },
];
const config: WordCloudProps = {
data,
wordField: 'word',
weightField: 'weight',
height: 500,
wordStyle: {
fontSize: [12, 48],
rotation: [0, 0]
},
legend: { position: 'top' },
tooltip: { shared: true }
};
return <WordCloud {...config} />;
};`,
},
strip: {
import: `import { Strip, StripProps } from '@alife/bi-material-center-chart';`,
template: `const StripChart: React.FC = () => {
const data = [
{ x: '类别A', y: 10 },
{ x: '类别A', y: 12 },
{ x: '类别A', y: 14 },
{ x: '类别B', y: 8 },
{ x: '类别B', y: 15 },
{ x: '类别B', y: 20 },
{ x: '类别C', y: 5 },
{ x: '类别C', y: 18 },
];
const config: StripProps = {
data,
xField: 'x',
yField: 'y',
height: 500,
legend: { position: 'top' },
tooltip: { shared: true },
yAxis: { title: { text: '数值' } },
xAxis: { title: { text: '类别' } }
};
return <Strip {...config} />;
};`,
},
heatmap: {
import: `import { Heatmap, HeatmapProps } from '@alife/bi-material-center-chart';`,
template: `const HeatmapChart: React.FC = () => {
const data = [
{ xField: '星期一', yField: '上午', value: 10 },
{ xField: '星期一', yField: '下午', value: 20 },
{ xField: '星期二', yField: '上午', value: 15 },
{ xField: '星期二', yField: '下午', value: 25 },
{ xField: '星期三', yField: '上午', value: 12 },
{ xField: '星期三', yField: '下午', value: 18 },
];
const config: HeatmapProps = {
data,
xField: 'xField',
yField: 'yField',
colorField: 'value',
height: 500,
legend: { position: 'top' },
tooltip: { shared: true },
color: ['#BAE7FF', '#1890FF', '#0050B3']
};
return <Heatmap {...config} />;
};`,
},
sankey: {
import: `import { Sankey, SankeyProps } from '@alife/bi-material-center-chart';`,
template: `const SankeyChart: React.FC = () => {
const data = [
{ source: '访问', target: '首页', value: 100 },
{ source: '首页', target: '产品页', value: 60 },
{ source: '首页', target: '关于页', value: 40 },
{ source: '产品页', target: '购买', value: 30 },
{ source: '产品页', target: '离开', value: 30 },
{ source: '关于页', target: '联系', value: 20 },
{ source: '关于页', target: '离开', value: 20 },
];
const config: SankeyProps = {
data,
sourceField: 'source',
targetField: 'target',
weightField: 'value',
height: 500,
legend: { position: 'top' },
tooltip: { shared: true }
};
return <Sankey {...config} />;
};`,
},
treemap: {
import: `import { Treemap, TreemapProps } from '@alife/bi-material-center-chart';`,
template: `const TreemapChart: React.FC = () => {
const data = [
{ year: '2020', value: 100 },
{ year: '2021', value: 200 },
{ year: '2022', value: 150 },
{ year: '2023', value: 300 },
{ year: '2024', value: 250 },
];
const config: TreemapProps = {
data,
valueField: 'value',
colorField: 'year',
height: 500,
legend: { position: 'top' },
tooltip: { shared: true }
};
return <Treemap {...config} />;
};`,
},
};
}
async generateChartCode(type) {
const template = this.chartTemplates[type];
if (!template) {
throw new Error(`不支持的图表类型: ${type}`);
}
return `${template.import}
import React from 'react';
${template.template}
export default ${this.getComponentName(type)}Chart;`;
}
async createChart(id, type, title, config) {
if (this.charts.has(id)) {
throw new Error(`图表 ${id} 已存在`);
}
const code = await this.generateChartCode(type);
const chart = {
id,
type,
title,
config: config || this.getDefaultConfig(type),
code,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
this.charts.set(id, chart);
return chart;
}
async getChart(id) {
return this.charts.get(id) || null;
}
async listCharts() {
return Array.from(this.charts.values());
}
async updateChart(id, updates) {
const chart = this.charts.get(id);
if (!chart) {
throw new Error(`图表 ${id} 不存在`);
}
const updatedChart = {
...chart,
...updates,
updatedAt: new Date().toISOString(),
};
// 如果更新了类型,需要重新生成代码
if (updates.type && updates.type !== chart.type) {
updatedChart.code = await this.generateChartCode(updates.type);
}
this.charts.set(id, updatedChart);
return updatedChart;
}
async deleteChart(id) {
return this.charts.delete(id);
}
getComponentName(type) {
const components = {
line: "Line",
bar: "Bar",
pie: "Pie",
area: "Area",
scatter: "Scatter",
radar: "Radar",
heatmap: "Heatmap",
wordcloud: "WordCloud",
strip: "Strip",
sankey: "Sankey",
treemap: "Treemap",
};
return components[type] || "Bar";
}
getDefaultConfig(type) {
const defaults = {
line: {
xField: "month",
yField: "value",
seriesField: "category",
height: 500,
point: { size: 5 },
legend: { position: "top" },
tooltip: { shared: true },
},
bar: {
xField: "category",
yField: "value",
seriesField: "group",
height: 500,
columnWidthRatio: 0.6,
legend: { position: "top" },
tooltip: { shared: true },
},
pie: {
angleField: "value",
colorField: "type",
height: 500,
radius: 0.8,
label: { type: "outer" },
legend: { position: "top" },
tooltip: { shared: true },
},
area: {
xField: "month",
yField: "value",
seriesField: "category",
height: 500,
point: { size: 4 },
area: { style: { fillOpacity: 0.3 } },
legend: { position: "top" },
tooltip: { shared: true },
},
};
return defaults[type] || defaults.bar;
}
}
exports.ChartGenerator = ChartGenerator;
//# sourceMappingURL=chart-generator.js.map