From 5ce732fe72f1a144db98cc8f18d43f00dce40c2e Mon Sep 17 00:00:00 2001 From: geht Date: Mon, 20 Apr 2026 10:04:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=89=93=E5=8D=B0=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E8=A1=8C=E9=AB=98=E4=BC=B0=E7=AE=97=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=96=87=E6=9C=AC=E6=8D=A2=E8=A1=8C?= =?UTF-8?q?=E4=BC=B0=E7=AE=97=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E8=A1=8C=E9=AB=98=E8=AE=A1=E7=AE=97=E4=BB=A5=E9=80=82=E5=BA=94?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E5=86=85=E5=AE=B9=E7=B1=BB=E5=9E=8B=EF=BC=8C?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E6=89=93=E5=8D=B0=E9=A2=84=E8=A7=88=E5=87=86?= =?UTF-8?q?=E7=A1=AE=E6=80=A7=E5=92=8C=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=E3=80=82=E5=A2=9E=E5=8A=A0=E9=98=B2=E8=A3=81=E5=88=87=E4=BD=99?= =?UTF-8?q?=E9=87=8F=E5=92=8C=E8=A1=8C=E9=AB=98=E5=BE=AE=E8=B0=83=E7=B3=BB?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E6=94=B9=E5=96=84=E5=88=86=E9=A1=B5=E5=A4=84?= =?UTF-8?q?=E7=90=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/native/core/printRenderer.ts | 79 +++++++++++++++---- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/jeecgboot-vue3/src/views/print/template/native/core/printRenderer.ts b/jeecgboot-vue3/src/views/print/template/native/core/printRenderer.ts index 6374ff3..837109c 100644 --- a/jeecgboot-vue3/src/views/print/template/native/core/printRenderer.ts +++ b/jeecgboot-vue3/src/views/print/template/native/core/printRenderer.ts @@ -157,7 +157,8 @@ async function renderTablePage( }), ) ).join(''); - return `${cells}`; + // 使用 min-height:长文本/二维码撑高行时以内容为准,避免固定 height 与实高不一致导致分页估算偶发偏差 + return `${cells}`; }), ) ).join(''); @@ -673,6 +674,46 @@ function resolveDetailTableChunkTopMm( const MM_TO_CSS_PX = 96 / 25.4; const CSS_PX_PER_MM = MM_TO_CSS_PX; +/** 版心底部保留的防裁切余量(mm)。过大则本页明明还能放 1~2 行却提前换页导致大块留白;过小易压分页缝 */ +const AUTO_PAGE_CHUNK_INSET_MM = 1.4; +/** 累计行高微调:略大于 1 防亚像素/线宽误差,不宜过大以免总行高膨胀、底部空白 */ +const AUTO_PAGE_ROW_HEIGHT_FIT_FACTOR = 1.03; + +/** 判断是否视为「全角级」占宽,用于换行行数估算(拉丁字母约 0.62em,汉字约 1em) */ +function isFullWidthWrapCodePoint(cp: number): boolean { + return ( + (cp >= 0x2e80 && cp <= 0x9fff) || // CJK/CJK 部首等 + (cp >= 0x3040 && cp <= 0x30ff) || // 假名 + (cp >= 0xac00 && cp <= 0xd7af) || // 谚文音节 + (cp >= 0xf900 && cp <= 0xfaff) || // 兼容汉字 + (cp >= 0xff00 && cp <= 0xffef) + ); // 全角ASCII +} + +/** + * 按列宽与字体估算换行宽度单位(拉丁≈0.62、CJK≈1),避免纯用字符数/拉丁 charWidth 低估中文行数导致偶发跨页裁切。 + */ +function estimateTextWrapLines(text: string, innerWpx: number, fontSize: number): number { + if (!text.length) { + return 1; + } + let units = 0; + for (const ch of text) { + const cp = ch.codePointAt(0)!; + if (isFullWidthWrapCodePoint(cp)) { + units += 1; + } else if (/\s/.test(ch)) { + units += 0.22; + } else { + units += 0.62; + } + } + units = Math.max(0.01, units); + // 每行可容纳的等效「全角」数:0.97 在防中文低估与避免行数虚高之间折中 + const unitsPerLine = Math.max(1.8, (innerWpx / Math.max(1, fontSize)) * 0.97); + return Math.max(1, Math.ceil(units / unitsPerLine)); +} + /** * 估算表格数据行在纸面上的高度(mm)。换行/二维码等会使实际行高远大于设计 rowHeight, * 若仍按固定 rowHeight 分页会导致整表落在同一 chunk、页数为 1 且预览溢出灰底。 @@ -681,6 +722,8 @@ function estimateTableBodyRowHeightMm(element: NativeTableElement, row: Record