Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6c8de5820 | ||
|
|
5f9357a182 |
@@ -673,6 +673,7 @@ function generateTable() {
|
|||||||
theme: document.getElementById('tableTheme').value,
|
theme: document.getElementById('tableTheme').value,
|
||||||
fontSize: parseInt(document.getElementById('tableFontSize').value) || 14,
|
fontSize: parseInt(document.getElementById('tableFontSize').value) || 14,
|
||||||
stripeRows: document.getElementById('stripeRows').checked,
|
stripeRows: document.getElementById('stripeRows').checked,
|
||||||
|
borderStyle: document.getElementById('borderStyle').value,
|
||||||
pixelRatio: 2
|
pixelRatio: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+16
@@ -151,6 +151,22 @@ C, 20, 30, 40</pre>
|
|||||||
<label><input type="checkbox" id="stripeRows" checked onchange="updateTable()"> 斑马纹</label>
|
<label><input type="checkbox" id="stripeRows" checked onchange="updateTable()"> 斑马纹</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="config-group">
|
||||||
|
<label>边框样式</label>
|
||||||
|
<select id="borderStyle" onchange="updateTable()">
|
||||||
|
<option value="all">全部边框</option>
|
||||||
|
<option value="none">无边框</option>
|
||||||
|
<option value="no-outer">无外边线</option>
|
||||||
|
<option value="no-horizontal">无横线</option>
|
||||||
|
<option value="no-vertical">无竖线</option>
|
||||||
|
<option value="no-inner">无内部线</option>
|
||||||
|
<option value="no-inner-horizontal">无内部横线</option>
|
||||||
|
<option value="no-inner-vertical">无内部竖线</option>
|
||||||
|
<option value="outer-only">仅外边框</option>
|
||||||
|
<option value="header-only">仅表头分隔线</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 系列配置区 -->
|
<!-- 系列配置区 -->
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ const path = require('path');
|
|||||||
|
|
||||||
// 注册中文字体
|
// 注册中文字体
|
||||||
try {
|
try {
|
||||||
registerFont('/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', { family: 'Noto Sans CJK SC' });
|
const { GlobalFonts } = require('@napi-rs/canvas');
|
||||||
registerFont('/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc', { family: 'Noto Sans CJK SC', weight: 'bold' });
|
GlobalFonts.registerFromPath('/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', 'Noto Sans CJK SC');
|
||||||
|
GlobalFonts.registerFromPath('/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc', 'Noto Sans CJK SC');
|
||||||
|
console.log('中文字体注册成功');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('中文字体注册失败,将使用系统默认字体:', e.message);
|
console.warn('中文字体注册失败,将使用系统默认字体:', e.message);
|
||||||
}
|
}
|
||||||
@@ -655,7 +657,8 @@ function generateTableImage(params) {
|
|||||||
borderWidth = 1,
|
borderWidth = 1,
|
||||||
stripeRows = true,
|
stripeRows = true,
|
||||||
pixelRatio = 2,
|
pixelRatio = 2,
|
||||||
maxWidth = 1200
|
maxWidth = 1200,
|
||||||
|
borderStyle = 'all' // all, none, no-outer, no-horizontal, no-vertical, no-inner, no-inner-horizontal, no-inner-vertical, outer-only, header-only
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
const tableData = parseTableData(data);
|
const tableData = parseTableData(data);
|
||||||
@@ -763,35 +766,50 @@ function generateTableImage(params) {
|
|||||||
ctx.strokeStyle = themeConfig.borderColor;
|
ctx.strokeStyle = themeConfig.borderColor;
|
||||||
ctx.lineWidth = borderWidth;
|
ctx.lineWidth = borderWidth;
|
||||||
|
|
||||||
// 外边框(向内偏移半个线宽,确保边框完全在画布内)
|
// 边框样式控制
|
||||||
const offset = borderWidth / 2;
|
const drawOuter = !['none', 'no-outer', 'no-inner', 'no-inner-horizontal', 'no-inner-vertical', 'header-only'].includes(borderStyle);
|
||||||
ctx.strokeRect(offset, startY + offset, finalWidth - borderWidth, tableHeight - borderWidth);
|
const drawHorizontal = !['none', 'no-horizontal', 'no-inner', 'no-inner-horizontal', 'outer-only', 'header-only'].includes(borderStyle);
|
||||||
|
const drawVertical = !['none', 'no-vertical', 'no-inner', 'no-inner-vertical', 'outer-only'].includes(borderStyle);
|
||||||
|
const drawHeaderLine = !['none', 'no-horizontal', 'no-inner', 'no-inner-horizontal'].includes(borderStyle);
|
||||||
|
|
||||||
// 横线
|
// 外边框
|
||||||
ctx.beginPath();
|
if (drawOuter) {
|
||||||
ctx.moveTo(0, startY + headerHeight);
|
const offset = borderWidth / 2;
|
||||||
ctx.lineTo(finalWidth, startY + headerHeight);
|
ctx.strokeRect(offset, startY + offset, finalWidth - borderWidth, tableHeight - borderWidth);
|
||||||
ctx.stroke();
|
}
|
||||||
|
|
||||||
rows.forEach((_, rowIdx) => {
|
// 表头分隔线(横线)
|
||||||
const y = startY + headerHeight + (rowIdx + 1) * rowHeight;
|
if (drawHeaderLine) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(0, y);
|
ctx.moveTo(0, startY + headerHeight);
|
||||||
ctx.lineTo(finalWidth, y);
|
ctx.lineTo(finalWidth, startY + headerHeight);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// 数据行横线
|
||||||
|
if (drawHorizontal) {
|
||||||
|
rows.forEach((_, rowIdx) => {
|
||||||
|
const y = startY + headerHeight + (rowIdx + 1) * rowHeight;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, y);
|
||||||
|
ctx.lineTo(finalWidth, y);
|
||||||
|
ctx.stroke();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 竖线
|
// 竖线
|
||||||
x = 0;
|
if (drawVertical) {
|
||||||
colWidths.forEach((width, i) => {
|
x = 0;
|
||||||
if (i > 0) {
|
colWidths.forEach((width, i) => {
|
||||||
ctx.beginPath();
|
if (i > 0) {
|
||||||
ctx.moveTo(x, startY);
|
ctx.beginPath();
|
||||||
ctx.lineTo(x, startY + tableHeight);
|
ctx.moveTo(x, startY);
|
||||||
ctx.stroke();
|
ctx.lineTo(x, startY + tableHeight);
|
||||||
}
|
ctx.stroke();
|
||||||
x += width;
|
}
|
||||||
});
|
x += width;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
@@ -833,7 +851,8 @@ app.get('/api/table', (req, res) => {
|
|||||||
borderWidth: parseInt(req.query.borderWidth) || 1,
|
borderWidth: parseInt(req.query.borderWidth) || 1,
|
||||||
stripeRows: req.query.stripeRows !== 'false',
|
stripeRows: req.query.stripeRows !== 'false',
|
||||||
pixelRatio: parseInt(req.query.pixelRatio) || 2,
|
pixelRatio: parseInt(req.query.pixelRatio) || 2,
|
||||||
maxWidth: parseInt(req.query.maxWidth) || 1200
|
maxWidth: parseInt(req.query.maxWidth) || 1200,
|
||||||
|
borderStyle: req.query.borderStyle || 'all'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!params.data) {
|
if (!params.data) {
|
||||||
@@ -951,7 +970,8 @@ app.get('/api/docs', (req, res) => {
|
|||||||
borderWidth: { type: 'number', default: 1, description: '边框宽度' },
|
borderWidth: { type: 'number', default: 1, description: '边框宽度' },
|
||||||
stripeRows: { type: 'boolean', default: true, description: '是否斑马纹' },
|
stripeRows: { type: 'boolean', default: true, description: '是否斑马纹' },
|
||||||
pixelRatio: { type: 'number', default: 2, description: '像素倍率(清晰度)' },
|
pixelRatio: { type: 'number', default: 2, description: '像素倍率(清晰度)' },
|
||||||
maxWidth: { type: 'number', default: 1200, description: '最大宽度(px)' }
|
maxWidth: { type: 'number', default: 1200, description: '最大宽度(px)' },
|
||||||
|
borderStyle: { type: 'string', default: 'all', options: ['all', 'none', 'no-outer', 'no-horizontal', 'no-vertical', 'no-inner', 'no-inner-horizontal', 'no-inner-vertical', 'outer-only', 'header-only'], description: '边框样式' }
|
||||||
},
|
},
|
||||||
returns: 'image/png',
|
returns: 'image/png',
|
||||||
example: {
|
example: {
|
||||||
|
|||||||
Reference in New Issue
Block a user