feat: 新增饼图/雷达图图表类型
- 单图模式图表类型新增:饼图(环形占比,第一列=名称/第一系列=数值)、雷达图(多维度对比,第一列=维度/每系列=多边形) - 多图合并的每张图也可选饼图/雷达图 - 后端 /api/chart 与 /api/combine 的 chartType 支持 pie/radar - 饼图/雷达图自动隐藏坐标轴/网格/双轴等不适用配置
This commit is contained in:
@@ -32,7 +32,7 @@ Content-Type: application/json
|
|||||||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||||||
|------|------|:----:|--------|------|
|
|------|------|:----:|--------|------|
|
||||||
| `data` | string | ✅ | — | CSV 格式数据,`\n` 换行,第一行表头,第一列横坐标 |
|
| `data` | string | ✅ | — | CSV 格式数据,`\n` 换行,第一行表头,第一列横坐标 |
|
||||||
| `chartType` | string | — | `bar` | 图表类型:`bar` / `line` / `bar-line` |
|
| `chartType` | string | — | `bar` | 图表类型:`bar` / `line` / `bar-line` / `pie` / `radar` |
|
||||||
| `title` | string | — | `""` | 图表标题 |
|
| `title` | string | — | `""` | 图表标题 |
|
||||||
| `theme` | string | — | `default` | 主题风格:`default` / `dark` / `macarons` / `gradient` / `retro` |
|
| `theme` | string | — | `default` | 主题风格:`default` / `dark` / `macarons` / `gradient` / `retro` |
|
||||||
| `showLegend` | boolean | — | `true` | 显示图例 |
|
| `showLegend` | boolean | — | `true` | 显示图例 |
|
||||||
@@ -181,7 +181,7 @@ Content-Type: application/json
|
|||||||
|
|
||||||
> 兼容旧参数:`chart1` + `chart2` 仍可传(等价于 `charts: [chart1, chart2]`)。
|
> 兼容旧参数:`chart1` + `chart2` 仍可传(等价于 `charts: [chart1, chart2]`)。
|
||||||
|
|
||||||
每个子图配置支持:`data`(CSV)、`chartType`(bar/line/bar-line)、`title`、`theme`、`showLegend`、`showGrid`、`showLabel`、`stackMode`、`smoothLine`、`width`、`height`。
|
每个子图配置支持:`data`(CSV)、`chartType`(bar/line/bar-line/pie/radar)、`title`、`theme`、`showLegend`、`showGrid`、`showLabel`、`stackMode`、`smoothLine`、`width`、`height`。
|
||||||
|
|
||||||
### curl 示例
|
### curl 示例
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
- **柱状图** - 支持单系列/多系列对比
|
- **柱状图** - 支持单系列/多系列对比
|
||||||
- **折线图** - 支持平滑曲线、面积填充
|
- **折线图** - 支持平滑曲线、面积填充
|
||||||
- **混合图** - 柱状图+折线图组合展示
|
- **混合图** - 柱状图+折线图组合展示
|
||||||
|
- **饼图** - 环形占比展示(第一列=名称,第一系列=数值)
|
||||||
|
- **雷达图** - 多维度对比(第一列=维度,每个系列=一个多边形)
|
||||||
- **双Y轴** - 一张图左右两个坐标轴,量度可不同,左右轴可独立命名,系列可指定左轴/右轴且每种类型独立设置(柱状/折线)
|
- **双Y轴** - 一张图左右两个坐标轴,量度可不同,左右轴可独立命名,系列可指定左轴/右轴且每种类型独立设置(柱状/折线)
|
||||||
|
|
||||||
### 📋 表格功能
|
### 📋 表格功能
|
||||||
|
|||||||
@@ -157,6 +157,91 @@ function initChart() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 饼图/雷达图 option 构建 =====
|
||||||
|
function buildPieRadarOption(chartType) {
|
||||||
|
if (!parsedData || parsedData.seriesNames.length === 0) return null;
|
||||||
|
const title = document.getElementById('chartTitle').value;
|
||||||
|
const theme = document.getElementById('themeStyle').value;
|
||||||
|
const showLegend = document.getElementById('showLegend').checked;
|
||||||
|
const showLabel = document.getElementById('showLabel').checked;
|
||||||
|
const bgColor = theme === 'dark' ? '#1a1a2e' : '#ffffff';
|
||||||
|
const textColor = theme === 'dark' ? '#e0e0e0' : '#333333';
|
||||||
|
const palette = colorPalettes[theme] || colorPalettes.default;
|
||||||
|
const colors = seriesOrder.map((origIdx, di) => seriesColors[origIdx] || palette[di % palette.length]);
|
||||||
|
|
||||||
|
const baseTitle = title ? {
|
||||||
|
text: title, left: 'center', top: 10,
|
||||||
|
textStyle: { color: textColor, fontSize: 18, fontWeight: 600 }
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
if (chartType === 'pie') {
|
||||||
|
// 饼图:第一列=名称,第一个系列=数值
|
||||||
|
const sName = parsedData.seriesNames[0];
|
||||||
|
const data = parsedData.seriesData[sName];
|
||||||
|
const pieData = parsedData.categories.map((c, i) => ({ name: c, value: data[i] || 0 }));
|
||||||
|
return {
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
title: baseTitle,
|
||||||
|
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
|
||||||
|
legend: showLegend ? {
|
||||||
|
orient: 'vertical', left: 'left', top: title ? 50 : 20,
|
||||||
|
textStyle: { color: textColor, fontSize: 12 }
|
||||||
|
} : { show: false },
|
||||||
|
color: colors,
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['38%', '68%'],
|
||||||
|
center: ['52%', '55%'],
|
||||||
|
avoidLabelOverlap: true,
|
||||||
|
itemStyle: { borderRadius: 6, borderColor: bgColor, borderWidth: 2 },
|
||||||
|
label: { show: showLabel, formatter: '{b}: {d}%', color: textColor },
|
||||||
|
labelLine: { show: showLabel },
|
||||||
|
data: pieData
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 雷达图:第一列=维度名,每个系列=一个雷达多边形
|
||||||
|
const indicator = parsedData.categories.map(c => {
|
||||||
|
let max = 0;
|
||||||
|
parsedData.seriesNames.forEach(n => {
|
||||||
|
parsedData.seriesData[n].forEach(v => { if (v > max) max = v; });
|
||||||
|
});
|
||||||
|
return { name: c, max: Math.ceil(max * 1.2) || 100 };
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
title: baseTitle,
|
||||||
|
tooltip: { trigger: 'item' },
|
||||||
|
legend: showLegend ? {
|
||||||
|
orient: 'horizontal', left: 'center', top: title ? 48 : 15,
|
||||||
|
textStyle: { color: textColor, fontSize: 12 }
|
||||||
|
} : { show: false },
|
||||||
|
color: colors,
|
||||||
|
radar: {
|
||||||
|
indicator,
|
||||||
|
radius: '62%',
|
||||||
|
center: ['50%', '55%'],
|
||||||
|
splitNumber: 5,
|
||||||
|
axisName: { color: textColor, fontSize: 12 },
|
||||||
|
splitLine: { lineStyle: { color: theme === 'dark' ? '#444' : '#ddd' } },
|
||||||
|
splitArea: {
|
||||||
|
areaStyle: {
|
||||||
|
color: theme === 'dark' ? ['rgba(79,195,247,0.03)', 'rgba(79,195,247,0.06)'] : ['rgba(79,70,229,0.03)', 'rgba(79,70,229,0.06)']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: { lineStyle: { color: theme === 'dark' ? '#444' : '#ddd' } }
|
||||||
|
},
|
||||||
|
series: parsedData.seriesNames.map(n => ({
|
||||||
|
type: 'radar',
|
||||||
|
name: n,
|
||||||
|
data: [{ value: parsedData.seriesData[n], name: n }],
|
||||||
|
symbolSize: 4,
|
||||||
|
areaStyle: { opacity: 0.15 }
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===== 更新图表 =====
|
// ===== 更新图表 =====
|
||||||
function updateChart() {
|
function updateChart() {
|
||||||
if (!parsedData || !chartInstance) return;
|
if (!parsedData || !chartInstance) return;
|
||||||
@@ -174,6 +259,16 @@ function updateChart() {
|
|||||||
const leftAxisName = document.getElementById('leftAxisName').value;
|
const leftAxisName = document.getElementById('leftAxisName').value;
|
||||||
const rightAxisName = document.getElementById('rightAxisName').value;
|
const rightAxisName = document.getElementById('rightAxisName').value;
|
||||||
|
|
||||||
|
// 饼图/雷达图:走专用构建逻辑(无坐标轴/网格,不支持双轴/堆叠/分割)
|
||||||
|
if (chartType === 'pie' || chartType === 'radar') {
|
||||||
|
const option = buildPieRadarOption(chartType);
|
||||||
|
if (option) {
|
||||||
|
chartInstance.setOption(option, true);
|
||||||
|
refreshPreview();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 显示/隐藏分割配置
|
// 显示/隐藏分割配置
|
||||||
document.getElementById('splitConfig').style.display = enableSplit ? 'block' : 'none';
|
document.getElementById('splitConfig').style.display = enableSplit ? 'block' : 'none';
|
||||||
|
|
||||||
@@ -1058,6 +1153,66 @@ function buildCombineChartOption(dataText, cfg) {
|
|||||||
const axisLineColor = theme === 'dark' ? '#444' : '#ddd';
|
const axisLineColor = theme === 'dark' ? '#444' : '#ddd';
|
||||||
const palette = colorPalettes[theme] || colorPalettes.default;
|
const palette = colorPalettes[theme] || colorPalettes.default;
|
||||||
|
|
||||||
|
// 饼图/雷达图:专用构建逻辑
|
||||||
|
if (chartType === 'pie' || chartType === 'radar') {
|
||||||
|
if (chartType === 'pie') {
|
||||||
|
const sName = parsed.seriesNames[0];
|
||||||
|
const data = parsed.seriesData[sName];
|
||||||
|
const pieData = parsed.categories.map((c, i) => ({ name: c, value: data[i] || 0 }));
|
||||||
|
return {
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
title: title ? { text: title, left: 'center', top: 10, textStyle: { color: textColor, fontSize: 16, fontWeight: 600 } } : undefined,
|
||||||
|
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
|
||||||
|
legend: showLegend ? { orient: 'vertical', left: 'left', top: title ? 40 : 10, textStyle: { color: textColor, fontSize: 12 } } : { show: false },
|
||||||
|
color: palette,
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['38%', '68%'],
|
||||||
|
center: ['55%', '55%'],
|
||||||
|
itemStyle: { borderRadius: 6, borderColor: bgColor, borderWidth: 2 },
|
||||||
|
label: { show: showLabel, formatter: '{b}: {d}%', color: textColor },
|
||||||
|
labelLine: { show: showLabel },
|
||||||
|
data: pieData
|
||||||
|
}],
|
||||||
|
animation: false
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const indicator = parsed.categories.map(c => {
|
||||||
|
let max = 0;
|
||||||
|
parsed.seriesNames.forEach(n => { parsed.seriesData[n].forEach(v => { if (v > max) max = v; }); });
|
||||||
|
return { name: c, max: Math.ceil(max * 1.2) || 100 };
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
title: title ? { text: title, left: 'center', top: 10, textStyle: { color: textColor, fontSize: 16, fontWeight: 600 } } : undefined,
|
||||||
|
tooltip: { trigger: 'item' },
|
||||||
|
legend: showLegend ? { orient: 'horizontal', left: 'center', top: title ? 38 : 10, textStyle: { color: textColor, fontSize: 12 } } : { show: false },
|
||||||
|
color: palette,
|
||||||
|
radar: {
|
||||||
|
indicator,
|
||||||
|
radius: '62%',
|
||||||
|
center: ['50%', '55%'],
|
||||||
|
splitNumber: 5,
|
||||||
|
axisName: { color: textColor, fontSize: 11 },
|
||||||
|
splitLine: { lineStyle: { color: theme === 'dark' ? '#444' : '#ddd' } },
|
||||||
|
splitArea: {
|
||||||
|
areaStyle: {
|
||||||
|
color: theme === 'dark' ? ['rgba(79,195,247,0.03)', 'rgba(79,195,247,0.06)'] : ['rgba(79,70,229,0.03)', 'rgba(79,70,229,0.06)']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: { lineStyle: { color: theme === 'dark' ? '#444' : '#ddd' } }
|
||||||
|
},
|
||||||
|
series: parsed.seriesNames.map(n => ({
|
||||||
|
type: 'radar', name: n,
|
||||||
|
data: [{ value: parsed.seriesData[n], name: n }],
|
||||||
|
symbolSize: 4,
|
||||||
|
areaStyle: { opacity: 0.15 }
|
||||||
|
})),
|
||||||
|
animation: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const series = parsed.seriesNames.map((name, idx) => {
|
const series = parsed.seriesNames.map((name, idx) => {
|
||||||
const color = palette[idx % palette.length];
|
const color = palette[idx % palette.length];
|
||||||
let type = chartType === 'bar-line' ? (idx % 2 === 0 ? 'bar' : 'line') : chartType;
|
let type = chartType === 'bar-line' ? (idx % 2 === 0 ? 'bar' : 'line') : chartType;
|
||||||
@@ -1177,7 +1332,7 @@ function initCombineCharts() {
|
|||||||
renderCombineCharts();
|
renderCombineCharts();
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMBINE_TYPES = [['bar', '柱状图'], ['line', '折线图'], ['bar-line', '柱状图+折线图混合']];
|
const COMBINE_TYPES = [['bar', '柱状图'], ['line', '折线图'], ['bar-line', '柱状图+折线图混合'], ['pie', '饼图'], ['radar', '雷达图']];
|
||||||
const COMBINE_THEMES = [['default', '默认'], ['dark', '深色'], ['macarons', '马卡龙'], ['gradient', '渐变'], ['retro', '复古'], ['ocean', '海洋'], ['forest', '森林'], ['sunset', '日落'], ['lavender', '薰衣草'], ['minimal', '极简'], ['cherry', '樱花'], ['midnight', '午夜'], ['gold', '金色'], ['coral', '珊瑚'], ['mint', '薄荷'], ['slate', '石板灰'], ['sky', '天空蓝'], ['rose', '玫瑰红'], ['amber', '琥珀黄'], ['emerald', '翡翠绿'], ['indigo', '靛蓝色'], ['stone', '石灰白']];
|
const COMBINE_THEMES = [['default', '默认'], ['dark', '深色'], ['macarons', '马卡龙'], ['gradient', '渐变'], ['retro', '复古'], ['ocean', '海洋'], ['forest', '森林'], ['sunset', '日落'], ['lavender', '薰衣草'], ['minimal', '极简'], ['cherry', '樱花'], ['midnight', '午夜'], ['gold', '金色'], ['coral', '珊瑚'], ['mint', '薄荷'], ['slate', '石板灰'], ['sky', '天空蓝'], ['rose', '玫瑰红'], ['amber', '琥珀黄'], ['emerald', '翡翠绿'], ['indigo', '靛蓝色'], ['stone', '石灰白']];
|
||||||
|
|
||||||
function escHtml(str) {
|
function escHtml(str) {
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ C, 20, 30, 40</pre>
|
|||||||
<option value="bar">柱状图</option>
|
<option value="bar">柱状图</option>
|
||||||
<option value="line">折线图</option>
|
<option value="line">折线图</option>
|
||||||
<option value="bar-line">柱状图+折线图混合</option>
|
<option value="bar-line">柱状图+折线图混合</option>
|
||||||
|
<option value="pie">饼图</option>
|
||||||
|
<option value="radar">雷达图</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,72 @@ function buildChartOption(params) {
|
|||||||
const textColor = theme === 'dark' ? '#e0e0e0' : '#333333';
|
const textColor = theme === 'dark' ? '#e0e0e0' : '#333333';
|
||||||
const axisLineColor = theme === 'dark' ? '#444' : '#ddd';
|
const axisLineColor = theme === 'dark' ? '#444' : '#ddd';
|
||||||
|
|
||||||
|
// 饼图/雷达图:专用构建逻辑(无坐标轴/网格)
|
||||||
|
if (chartType === 'pie' || chartType === 'radar') {
|
||||||
|
const bgC = theme === 'dark' ? '#1a1a2e' : '#ffffff';
|
||||||
|
const txtC = theme === 'dark' ? '#e0e0e0' : '#333333';
|
||||||
|
const tTitle = title ? { text: title, left: 'center', top: 15, textStyle: { color: txtC, fontSize: 18, fontWeight: 600 } } : undefined;
|
||||||
|
if (chartType === 'pie') {
|
||||||
|
// 饼图:第一列=名称,第一个系列=数值
|
||||||
|
const sName = parsedData.seriesNames[0];
|
||||||
|
const data = parsedData.seriesData[sName];
|
||||||
|
const pieData = parsedData.categories.map((c, i) => ({ name: c, value: data[i] || 0 }));
|
||||||
|
return {
|
||||||
|
backgroundColor: bgC,
|
||||||
|
title: tTitle,
|
||||||
|
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
|
||||||
|
legend: showLegend ? { orient: 'vertical', left: 'left', top: title ? 50 : 20, textStyle: { color: txtC, fontSize: 12 } } : { show: false },
|
||||||
|
color: seriesColorsArr,
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['38%', '68%'],
|
||||||
|
center: ['52%', '55%'],
|
||||||
|
itemStyle: { borderRadius: 6, borderColor: bgC, borderWidth: 2 },
|
||||||
|
label: { show: showLabel, formatter: '{b}: {d}%', color: txtC },
|
||||||
|
labelLine: { show: showLabel },
|
||||||
|
data: pieData
|
||||||
|
}],
|
||||||
|
animation: false
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 雷达图:第一列=维度名,每个系列=一个雷达多边形
|
||||||
|
const indicator = parsedData.categories.map(c => {
|
||||||
|
let max = 0;
|
||||||
|
parsedData.seriesNames.forEach(n => { parsedData.seriesData[n].forEach(v => { if (v > max) max = v; }); });
|
||||||
|
return { name: c, max: Math.ceil(max * 1.2) || 100 };
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
backgroundColor: bgC,
|
||||||
|
title: tTitle,
|
||||||
|
tooltip: { trigger: 'item' },
|
||||||
|
legend: showLegend ? { orient: 'horizontal', left: 'center', top: title ? 48 : 15, textStyle: { color: txtC, fontSize: 12 } } : { show: false },
|
||||||
|
color: seriesColorsArr,
|
||||||
|
radar: {
|
||||||
|
indicator,
|
||||||
|
radius: '62%',
|
||||||
|
center: ['50%', '55%'],
|
||||||
|
splitNumber: 5,
|
||||||
|
axisName: { color: txtC, fontSize: 12 },
|
||||||
|
splitLine: { lineStyle: { color: theme === 'dark' ? '#444' : '#ddd' } },
|
||||||
|
splitArea: {
|
||||||
|
areaStyle: {
|
||||||
|
color: theme === 'dark' ? ['rgba(79,195,247,0.03)', 'rgba(79,195,247,0.06)'] : ['rgba(79,70,229,0.03)', 'rgba(79,70,229,0.06)']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: { lineStyle: { color: theme === 'dark' ? '#444' : '#ddd' } }
|
||||||
|
},
|
||||||
|
series: parsedData.seriesNames.map(n => ({
|
||||||
|
type: 'radar',
|
||||||
|
name: n,
|
||||||
|
data: [{ value: parsedData.seriesData[n], name: n }],
|
||||||
|
symbolSize: 4,
|
||||||
|
areaStyle: { opacity: 0.15 }
|
||||||
|
})),
|
||||||
|
animation: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 构建系列
|
// 构建系列
|
||||||
const series = parsedData.seriesNames.map((name, idx) => {
|
const series = parsedData.seriesNames.map((name, idx) => {
|
||||||
const dataArr = parsedData.seriesData[name];
|
const dataArr = parsedData.seriesData[name];
|
||||||
@@ -928,7 +994,7 @@ app.get('/api/health', (req, res) => {
|
|||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
service: 'data-chart-tool',
|
service: 'data-chart-tool',
|
||||||
version: '1.13.1',
|
version: '1.14.0',
|
||||||
endpoints: {
|
endpoints: {
|
||||||
'POST /api/chart': '生成图表图片(JSON body)',
|
'POST /api/chart': '生成图表图片(JSON body)',
|
||||||
'GET /api/chart': '生成图表图片(URL 参数)',
|
'GET /api/chart': '生成图表图片(URL 参数)',
|
||||||
@@ -943,7 +1009,7 @@ app.get('/api/health', (req, res) => {
|
|||||||
app.get('/api/docs', (req, res) => {
|
app.get('/api/docs', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
name: '数据可视化图表生成器 API',
|
name: '数据可视化图表生成器 API',
|
||||||
version: '1.13.1',
|
version: '1.14.0',
|
||||||
endpoints: [
|
endpoints: [
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -952,7 +1018,7 @@ app.get('/api/docs', (req, res) => {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
params: {
|
params: {
|
||||||
data: { type: 'string', required: true, description: 'CSV 格式数据(第一行表头,第一列横坐标)' },
|
data: { type: 'string', required: true, description: 'CSV 格式数据(第一行表头,第一列横坐标)' },
|
||||||
chartType: { type: 'string', default: 'bar', options: ['bar', 'line', 'bar-line'], description: '图表类型' },
|
chartType: { type: 'string', default: 'bar', options: ['bar', 'line', 'bar-line', 'pie', 'radar'], description: '图表类型' },
|
||||||
title: { type: 'string', default: '', description: '图表标题' },
|
title: { type: 'string', default: '', description: '图表标题' },
|
||||||
theme: { type: 'string', default: 'default', options: ['default', 'dark', 'macarons', 'gradient', 'retro', 'ocean', 'forest', 'sunset', 'lavender', 'minimal', 'cherry', 'midnight', 'gold', 'coral', 'mint'], description: '主题风格' },
|
theme: { type: 'string', default: 'default', options: ['default', 'dark', 'macarons', 'gradient', 'retro', 'ocean', 'forest', 'sunset', 'lavender', 'minimal', 'cherry', 'midnight', 'gold', 'coral', 'mint'], description: '主题风格' },
|
||||||
showLegend: { type: 'boolean', default: true, description: '是否显示图例' },
|
showLegend: { type: 'boolean', default: true, description: '是否显示图例' },
|
||||||
|
|||||||
Reference in New Issue
Block a user