feat(phone-image): table-to-image conversion with beautified CSS styles

This commit is contained in:
augushong
2026-05-03 10:17:34 +08:00
parent d35172e14e
commit 6cff21d50f
2 changed files with 154 additions and 68 deletions

View File

@@ -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;

View File

@@ -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>');
$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();
});
});
});