feat: 双Y轴左右轴可独立命名 + 不同轴可设不同图表类型

- 双Y轴:左右两个轴都可单独命名(左轴名称/右轴名称输入框)
- 修复系列类型下拉不生效的 bug(seriesTypeOverrides 只写不读),
  现在每个系列可独立设置柱状/折线,结合轴分配实现'左轴柱状+右轴折线'
- 后端 /api/chart 新增 leftAxisName + seriesTypes 参数(POST/GET 均可)
This commit is contained in:
2026-08-19 13:19:28 +08:00
parent 68e4b72d46
commit 576024af76
5 changed files with 40 additions and 15 deletions
+3 -1
View File
@@ -47,7 +47,9 @@ Content-Type: application/json
| `splitStyle` | string | — | `"solid"` | 分割线样式:`solid` / `dashed` / `dotted` |
| `dualYAxis` | boolean | — | `false` | 启用双Y轴(左右量度不同) |
| `rightAxisSeries` | array | — | `null` | 右轴系列名列表,如 `["利润"]`(未列出的系列用左轴) |
| `rightAxisName` | string | — | `""` | 轴名称 |
| `leftAxisName` | string | — | `""` | 轴名称(如 `"销售额(元)"` |
| `rightAxisName` | string | — | `""` | 右轴名称(如 `"增长率(%)"` |
| `seriesTypes` | array | — | `null` | 每系列图表类型,按系列顺序对应,如 `["bar","line"]``bar`/`line`/`auto` |
| `width` | number | — | `800` | 图片宽度(px |
| `height` | number | — | `500` | 图片高度(px |
| `pixelRatio` | number | — | `2` | 像素倍率(越大越清晰) |
+1 -1
View File
@@ -11,7 +11,7 @@
- **柱状图** - 支持单系列/多系列对比
- **折线图** - 支持平滑曲线、面积填充
- **混合图** - 柱状图+折线图组合展示
- **双Y轴** - 一张图左右两个坐标轴,量度可不同,系列可指定左轴/右轴(含右轴名称
- **双Y轴** - 一张图左右两个坐标轴,量度可不同,左右轴可独立命名,系列可指定左轴/右轴且每种类型独立设置(柱状/折线
### 📋 表格功能
- **表格图片生成** - 根据数据生成精美的表格图片
+13 -5
View File
@@ -175,6 +175,7 @@ function updateChart() {
const smoothLine = document.getElementById('smoothLine').checked;
const enableSplit = document.getElementById('enableSplit').checked;
const dualAxis = document.getElementById('dualAxis').checked;
const leftAxisName = document.getElementById('leftAxisName').value;
const rightAxisName = document.getElementById('rightAxisName').value;
// 显示/隐藏分割配置
@@ -191,10 +192,17 @@ function updateChart() {
const name = parsedData.seriesNames[origIdx];
const data = parsedData.seriesData[name];
const color = seriesColors[origIdx] || palette[origIdx % palette.length];
let type = chartType === 'bar-line'
? (displayIdx % 2 === 0 ? 'bar' : 'line')
: chartType;
// 系列类型:优先用系列配置的独立覆盖(支持不同轴不同图表类型)
const override = window.seriesTypeOverrides && window.seriesTypeOverrides[origIdx];
let type;
if (override && override !== 'auto') {
type = override;
} else if (chartType === 'bar-line') {
type = displayIdx % 2 === 0 ? 'bar' : 'line';
} else {
type = chartType;
}
const seriesItem = {
name: name,
@@ -391,7 +399,7 @@ function updateChart() {
yAxis: dualAxis ? [
{
type: 'value',
name: '左轴',
name: leftAxisName || '左轴',
nameTextStyle: { color: textColor, fontSize: 11, padding: [0, 0, 0, 4] },
axisLine: { show: false },
axisTick: { show: false },
+7 -3
View File
@@ -105,9 +105,13 @@ C, 20, 30, 40</pre>
</div>
<div class="config-group" id="rightAxisGroup" style="display:none;">
<label>右轴名称(量度不同)</label>
<input type="text" id="rightAxisName" placeholder="如:利润(万元)" oninput="updateChart()">
<p class="hint-text">在下方「系列配置」中可指定每个系列用左轴/右轴</p>
<label>右轴名称(量度不同)</label>
<div class="res-input-row">
<input type="text" id="leftAxisName" placeholder="左轴,如:销售额" oninput="updateChart()">
<span class="res-times">/</span>
<input type="text" id="rightAxisName" placeholder="右轴,如:增长率(%)" oninput="updateChart()">
</div>
<p class="hint-text">在下方「系列配置」中可指定每个系列用左轴/右轴及图表类型(柱状/折线)</p>
</div>
</div>
+16 -5
View File
@@ -116,7 +116,9 @@ function buildChartOption(params) {
seriesColors: customColors = null,
dualYAxis = false,
rightAxisSeries = null,
rightAxisName = ''
leftAxisName = '',
rightAxisName = '',
seriesTypes = null
} = params;
const parsedData = parseData(data);
@@ -137,6 +139,11 @@ function buildChartOption(params) {
? (idx % 2 === 0 ? 'bar' : 'line')
: chartType;
// 每系列独立类型覆盖(seriesTypes 数组按系列顺序对应)
if (Array.isArray(seriesTypes) && seriesTypes[idx] && seriesTypes[idx] !== 'auto') {
type = seriesTypes[idx];
}
const seriesItem = {
name: name,
type: type,
@@ -280,7 +287,7 @@ function buildChartOption(params) {
yAxis: dualYAxis ? [
{
type: 'value',
name: '左轴',
name: leftAxisName || '左轴',
nameTextStyle: { color: textColor, fontSize: 11, padding: [0, 0, 0, 4] },
axisLine: { show: false },
axisTick: { show: false },
@@ -402,7 +409,9 @@ app.get('/api/chart', (req, res) => {
splitStyle: req.query.splitStyle || 'solid',
dualYAxis: req.query.dualYAxis === 'true',
rightAxisSeries: req.query.rightAxisSeries ? req.query.rightAxisSeries.split(',') : null,
leftAxisName: req.query.leftAxisName || '',
rightAxisName: req.query.rightAxisName || '',
seriesTypes: req.query.seriesTypes ? req.query.seriesTypes.split(',') : null,
width: parseInt(req.query.width) || 800,
height: parseInt(req.query.height) || 500,
format: req.query.format || 'png',
@@ -919,7 +928,7 @@ app.get('/api/health', (req, res) => {
res.json({
status: 'ok',
service: 'data-chart-tool',
version: '1.11.1',
version: '1.12.0',
endpoints: {
'POST /api/chart': '生成图表图片(JSON body',
'GET /api/chart': '生成图表图片(URL 参数)',
@@ -934,7 +943,7 @@ app.get('/api/health', (req, res) => {
app.get('/api/docs', (req, res) => {
res.json({
name: '数据可视化图表生成器 API',
version: '1.11.1',
version: '1.12.0',
endpoints: [
{
method: 'POST',
@@ -962,7 +971,9 @@ app.get('/api/docs', (req, res) => {
pixelRatio: { type: 'number', default: 2, description: '像素倍率(清晰度)' },
dualYAxis: { type: 'boolean', default: false, description: '是否启用双Y轴(左右量度不同)' },
rightAxisSeries: { type: 'array', default: null, description: '右轴系列名列表(如 ["利润"],指定哪些系列用右轴)' },
rightAxisName: { type: 'string', default: '', description: '轴名称' }
leftAxisName: { type: 'string', default: '', description: '轴名称' },
rightAxisName: { type: 'string', default: '', description: '右轴名称' },
seriesTypes: { type: 'array', default: null, description: '每系列图表类型(如 ["bar","line"],按系列顺序对应,bar/line/auto' }
},
returns: 'image/png',
example: {