From 5f9357a182d142f55b9147a368915aac5c09eeff Mon Sep 17 00:00:00 2001 From: hz4th_coder Date: Fri, 17 Jul 2026 13:37:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E8=BE=B9=E6=A1=86=E6=A0=B7=E5=BC=8F=E6=8E=A7=E5=88=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持10种边框样式: - all: 全部边框(默认) - none: 无边框 - no-outer: 无外边线 - no-horizontal: 无横线 - no-vertical: 无竖线 - no-inner: 无内部线 - no-inner-horizontal: 无内部横线 - no-inner-vertical: 无内部竖线 - outer-only: 仅外边框 - header-only: 仅表头分隔线 前端和API均已支持 --- app.js | 1 + index.html | 16 +++++++++++++ server.js | 70 ++++++++++++++++++++++++++++++++++-------------------- 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/app.js b/app.js index 5261fed..6b40786 100644 --- a/app.js +++ b/app.js @@ -673,6 +673,7 @@ function generateTable() { theme: document.getElementById('tableTheme').value, fontSize: parseInt(document.getElementById('tableFontSize').value) || 14, stripeRows: document.getElementById('stripeRows').checked, + borderStyle: document.getElementById('borderStyle').value, pixelRatio: 2 }; diff --git a/index.html b/index.html index 6a7eec2..5406976 100644 --- a/index.html +++ b/index.html @@ -151,6 +151,22 @@ C, 20, 30, 40 + +
+ + +
diff --git a/server.js b/server.js index 027328e..6089e65 100644 --- a/server.js +++ b/server.js @@ -655,7 +655,8 @@ function generateTableImage(params) { borderWidth = 1, stripeRows = true, 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; const tableData = parseTableData(data); @@ -763,35 +764,50 @@ function generateTableImage(params) { ctx.strokeStyle = themeConfig.borderColor; ctx.lineWidth = borderWidth; - // 外边框(向内偏移半个线宽,确保边框完全在画布内) - const offset = borderWidth / 2; - ctx.strokeRect(offset, startY + offset, finalWidth - borderWidth, tableHeight - borderWidth); + // 边框样式控制 + const drawOuter = !['none', 'no-outer', 'no-inner', 'no-inner-horizontal', 'no-inner-vertical', 'header-only'].includes(borderStyle); + 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(); - ctx.moveTo(0, startY + headerHeight); - ctx.lineTo(finalWidth, startY + headerHeight); - ctx.stroke(); + // 外边框 + if (drawOuter) { + const offset = borderWidth / 2; + ctx.strokeRect(offset, startY + offset, finalWidth - borderWidth, tableHeight - borderWidth); + } - rows.forEach((_, rowIdx) => { - const y = startY + headerHeight + (rowIdx + 1) * rowHeight; + // 表头分隔线(横线) + if (drawHeaderLine) { ctx.beginPath(); - ctx.moveTo(0, y); - ctx.lineTo(finalWidth, y); + ctx.moveTo(0, startY + headerHeight); + ctx.lineTo(finalWidth, startY + headerHeight); 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; - colWidths.forEach((width, i) => { - if (i > 0) { - ctx.beginPath(); - ctx.moveTo(x, startY); - ctx.lineTo(x, startY + tableHeight); - ctx.stroke(); - } - x += width; - }); + if (drawVertical) { + x = 0; + colWidths.forEach((width, i) => { + if (i > 0) { + ctx.beginPath(); + ctx.moveTo(x, startY); + ctx.lineTo(x, startY + tableHeight); + ctx.stroke(); + } + x += width; + }); + } return canvas; } @@ -833,7 +849,8 @@ app.get('/api/table', (req, res) => { borderWidth: parseInt(req.query.borderWidth) || 1, stripeRows: req.query.stripeRows !== 'false', 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) { @@ -951,7 +968,8 @@ app.get('/api/docs', (req, res) => { borderWidth: { type: 'number', default: 1, description: '边框宽度' }, stripeRows: { type: 'boolean', default: true, 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', example: {