diff --git a/public/static/css/phone-image-templates.css b/public/static/css/phone-image-templates.css index 40d52f6..89e1aa5 100644 --- a/public/static/css/phone-image-templates.css +++ b/public/static/css/phone-image-templates.css @@ -563,6 +563,13 @@ Content Flow Code Blocks (中间栏代码块样式) 仅用于编辑区中间栏, 不影响渲染页面 ============================================ */ + +/* 表格美化样式(html2canvas兼容) */ +.page-content table { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 13px; } +.page-content th { background-color: #f0f0f0; font-weight: bold; padding: 8px 10px; border: 1px solid #ddd; text-align: left; } +.page-content td { padding: 8px 10px; border: 1px solid #ddd; } +.page-content tr:nth-child(even) td { background-color: #f9f9f9; } + .content-flow pre { background: #f5f5f5; border: 1px solid #e0e0e0; diff --git a/public/static/js/phone-image.js b/public/static/js/phone-image.js index a0b44a3..4577028 100644 --- a/public/static/js/phone-image.js +++ b/public/static/js/phone-image.js @@ -249,6 +249,11 @@ var PhoneImageEngine = (function () { block.type = tagName; block.estimatedHeight = estimateHeadingHeight(tagName, $el.text()); break; + case 'table': + block.type = 'table'; + block.html = $el[0].outerHTML; + block.estimatedHeight = estimateTableHeight($el); + break; case 'img': block.estimatedHeight = estimateImageHeight($el); block.$img = $el; @@ -341,6 +346,14 @@ var PhoneImageEngine = (function () { return estimateTextHeight(text, config.fontSize) + 10; } + /** + * 估算表格高度 + */ + function estimateTableHeight($table) { + var rows = $table.find('tr').length; + return rows * 36 + 20; // 36px per row (35px content + 1px border) + 20px margin + } + // ===== 分页核心算法 ===== /** @@ -436,6 +449,11 @@ var PhoneImageEngine = (function () { return [block]; } + // 表格块不拆分,保留原样 + if (block.type === 'table') { + return [block]; + } + // 文本类块:按句子拆分 var text = ''; var wrapperTag = 'p'; @@ -690,6 +708,59 @@ var PhoneImageEngine = (function () { return deferred.promise(); } + /** + * 将 #render-staging 中的表格转为图片 + * 使用 html2canvas 截图替换(与 convertCodeBlocks 模式相同) + * @returns {jQuery Deferred} resolves when all tables are converted + */ + function convertTables() { + var deferred = $.Deferred(); + var $staging = $('#render-staging'); + + var $tables = $staging.find('.page-content table'); + if ($tables.length === 0) { + deferred.resolve(); + return deferred.promise(); + } + + var idx = 0; + var total = $tables.length; + + function convertNext() { + if (idx >= total) { + deferred.resolve(); + return; + } + + var $table = $tables.eq(idx); + html2canvas($table[0], { + scale: 2, + useCORS: true, + backgroundColor: '#ffffff', + logging: false + }).then(function (canvas) { + var imgData = canvas.toDataURL('image/jpeg', 0.92); + var $img = $(''); + $img.attr('src', imgData); + $img.css({ + maxWidth: '100%', + height: 'auto', + display: 'block', + margin: '10px 0' + }); + $table.replaceWith($img); + idx++; + convertNext(); + }).catch(function () { + idx++; + convertNext(); + }); + } + + convertNext(); + return deferred.promise(); + } + /** * 渲染到隐藏区域 → 截图 → 显示缩略图 * @param {Object} sizeConfig @@ -716,6 +787,8 @@ var PhoneImageEngine = (function () { $staging.css({ visibility: 'visible' }); // 先转换代码块为图片 convertCodeBlocks().then(function () { + return convertTables(); + }).then(function () { var $pageElems = $staging.find('.phone-image-page'); if ($pageElems.length === 0) { $staging.css({ visibility: 'hidden' }); @@ -758,47 +831,49 @@ var PhoneImageEngine = (function () { captureNext(); }).catch(function () { - // 代码块转换失败,继续截图流程 - var $pageElems = $staging.find('.phone-image-page'); - if ($pageElems.length === 0) { - $staging.css({ visibility: 'hidden' }); - displayThumbnails([], sizeConfig); - deferred.resolve(pages); - return; - } - - var thumbnailDataUrls = []; - var idx = 0; - var total = $pageElems.length; - - function captureNext() { - if (idx >= total) { - $staging.empty(); + // 代码块转换失败,继续转换表格并截图流程 + convertTables().then(function () { + var $pageElems = $staging.find('.phone-image-page'); + if ($pageElems.length === 0) { $staging.css({ visibility: 'hidden' }); - displayThumbnails(thumbnailDataUrls, sizeConfig); + displayThumbnails([], sizeConfig); deferred.resolve(pages); return; } - var $elem = $pageElems.eq(idx); - html2canvas($elem[0], { - scale: 1, - useCORS: true, - backgroundColor: '#ffffff', - width: $elem.outerWidth(), - height: $elem.outerHeight(), - logging: false - }).then(function (canvas) { - thumbnailDataUrls.push(canvas.toDataURL('image/jpeg', 0.85)); - idx++; - captureNext(); - }).catch(function (err) { - $staging.css({ visibility: 'hidden' }); - deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err); - }); - } + var thumbnailDataUrls = []; + var idx = 0; + var total = $pageElems.length; - captureNext(); + function captureNext() { + if (idx >= total) { + $staging.empty(); + $staging.css({ visibility: 'hidden' }); + displayThumbnails(thumbnailDataUrls, sizeConfig); + deferred.resolve(pages); + return; + } + + var $elem = $pageElems.eq(idx); + html2canvas($elem[0], { + scale: 1, + useCORS: true, + backgroundColor: '#ffffff', + width: $elem.outerWidth(), + height: $elem.outerHeight(), + logging: false + }).then(function (canvas) { + thumbnailDataUrls.push(canvas.toDataURL('image/jpeg', 0.85)); + idx++; + captureNext(); + }).catch(function (err) { + $staging.css({ visibility: 'hidden' }); + deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err); + }); + } + + captureNext(); + }); }); }); @@ -876,6 +951,8 @@ var PhoneImageEngine = (function () { $staging.css({ visibility: 'visible' }); // 先转换代码块为图片 convertCodeBlocks().then(function () { + return convertTables(); + }).then(function () { var $pageElems = $staging.find('.phone-image-page'); if ($pageElems.length === 0) { $staging.css({ visibility: 'hidden' }); @@ -914,44 +991,46 @@ var PhoneImageEngine = (function () { captureNext(); }).catch(function () { - // 代码块转换失败,继续截图流程 - var $pageElems = $staging.find('.phone-image-page'); - if ($pageElems.length === 0) { - $staging.css({ visibility: 'hidden' }); - deferred.reject('没有可渲染的页面'); - return; - } - - var canvases = []; - var idx = 0; - var total = $pageElems.length; - - function captureNext() { - if (idx >= total) { - $staging.empty(); + // 代码块转换失败,继续转换表格并截图流程 + convertTables().then(function () { + var $pageElems = $staging.find('.phone-image-page'); + if ($pageElems.length === 0) { $staging.css({ visibility: 'hidden' }); - deferred.resolve(canvases); + deferred.reject('没有可渲染的页面'); return; } - html2canvas($pageElems.eq(idx)[0], { - scale: 2, - useCORS: true, - backgroundColor: '#ffffff', - width: $pageElems.eq(idx).outerWidth(), - height: $pageElems.eq(idx).outerHeight(), - logging: false - }).then(function (canvas) { - canvases.push(canvas); - idx++; - captureNext(); - }).catch(function (err) { - $staging.css({ visibility: 'hidden' }); - deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err); - }); - } + var canvases = []; + var idx = 0; + var total = $pageElems.length; - captureNext(); + function captureNext() { + if (idx >= total) { + $staging.empty(); + $staging.css({ visibility: 'hidden' }); + deferred.resolve(canvases); + return; + } + + html2canvas($pageElems.eq(idx)[0], { + scale: 2, + useCORS: true, + backgroundColor: '#ffffff', + width: $pageElems.eq(idx).outerWidth(), + height: $pageElems.eq(idx).outerHeight(), + logging: false + }).then(function (canvas) { + canvases.push(canvas); + idx++; + captureNext(); + }).catch(function (err) { + $staging.css({ visibility: 'hidden' }); + deferred.reject('截图失败(第' + (idx + 1) + '页): ' + err); + }); + } + + captureNext(); + }); }); });