/**
* 操作日志面板模块
* 提供实时日志显示,替代 layer.load() 遮罩
*/
var PhoneImageLogPanel = (function () {
var $panel = null;
var $body = null;
var MAX_ENTRIES = 500;
var entryCount = 0;
function formatTime() {
var d = new Date();
var h = (d.getHours() < 10 ? '0' : '') + d.getHours();
var m = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
var s = (d.getSeconds() < 10 ? '0' : '') + d.getSeconds();
return h + ':' + m + ':' + s;
}
function log(msg, level) {
if (!$body) return;
level = level || 'info';
var entryHtml = '
').css({
position: 'fixed',
left: '-9999px',
top: 0,
visibility: 'hidden'
}).appendTo('body');
}
}
/**
* 初始化引擎
* @param {Object} options - {postId, title, desc, coverText, contentHtml, poster, authorName, createTime, categoryName}
* @param {Object} userConfig - {size, fontSize, watermark}
*/
function init(options, userConfig) {
postData.id = options.postId || 0;
postData.title = options.title || '';
postData.desc = options.desc || '';
postData.coverText = options.coverText || '';
postData.content_html = options.contentHtml || '';
postData.poster = options.poster || '';
postData.author_name = options.authorName || '';
postData.create_time = options.createTime || '';
postData.category_name = options.categoryName || '';
postData.site_name = options.siteName || '';
postData.site_logo = options.siteLogo || '';
if (userConfig) {
$.extend(config, userConfig);
}
applyFontScale();
preloadBrandLogo();
}
/**
* 应用字号缩放到CSS变量
*/
function applyFontScale() {
var scale = config.fontScale || 1;
var tableScale = config.tableFontScale || 1;
var codeScale = config.codeFontScale || 1;
var preview = document.getElementById('render-preview');
if (preview) {
preview.style.setProperty('--pi-font-scale', scale);
preview.style.setProperty('--pi-table-font-scale', tableScale);
preview.style.setProperty('--pi-code-font-scale', codeScale);
}
var staging = document.getElementById('render-staging');
if (staging) {
staging.style.setProperty('--pi-font-scale', scale);
staging.style.setProperty('--pi-table-font-scale', tableScale);
staging.style.setProperty('--pi-code-font-scale', codeScale);
}
}
/**
* 渲染排版预览 - 生成分页后的HTML
* 管线: editorHtml -> parseHtmlToBlocks -> captureEditorBlocks -> generatePages -> renderThumbnails
* 带并发锁:防止多次 render 同时执行造成数据混乱
* @returns {jQuery Deferred} resolves with pages array
*/
function render() {
var deferred = $.Deferred();
// 防止并发渲染
if (render._locked) {
// 标记有待处理的渲染请求,当前渲染完成后会自动重试
PhoneImageLogPanel.log('渲染进行中,跳过本次请求', 'warn');
render._pending = true;
return deferred.reject('rendering').promise();
}
render._locked = true;
PhoneImageLogPanel.log('开始渲染...', 'info');
pages = [];
var sizeConfig = config.sizes[config.size] || config.sizes.xiaohongshu;
var pageHeight = sizeConfig.height;
var contentAreaHeight = pageHeight - (config.contentPadding * 2);
var brandHeaderOffset = (postData.site_name || postData.site_logo) ? BRAND_HEADER_HEIGHT : 0;
contentAreaHeight -= brandHeaderOffset;
// 从预览区读取已预处理的内容
syncPreview();
PhoneImageLogPanel.log('同步预览区内容', 'info');
var previewEl = document.getElementById('render-preview');
// 等待预览区内所有图片加载完成,否则 getBoundingClientRect 测高为0
PhoneImageLogPanel.log('等待图片加载...', 'info');
waitForImages(previewEl).then(async function () {
// 预渲染超长代码块为图片
await preRenderCodeBlocks(contentAreaHeight);
var cleanHtml = previewEl ? previewEl.innerHTML : '';
var blocks = parseHtmlToBlocks(cleanHtml);
PhoneImageLogPanel.log('解析内容: ' + blocks.length + ' 个块', 'info');
// 空内容检测
if (blocks.length === 0) {
$('#paginated-preview').html('
文章正文内容为空
');
render._locked = false;
return deferred.resolve([]).promise();
}
// 封面页
pages.push(generateCoverPage(sizeConfig));
PhoneImageLogPanel.log('生成封面页', 'info');
// 内容分页
PhoneImageLogPanel.log('分页计算中...', 'info');
captureEditorBlocks(cleanHtml, blocks, contentAreaHeight, sizeConfig).then(function(contentPages) {
pages = pages.concat(contentPages);
// 尾页
pages.push(generateSummaryPage(sizeConfig, pages.length));
PhoneImageLogPanel.log('生成尾页', 'info');
// 页码 N/M
var totalPages = pages.length;
for (var i = 0; i < pages.length; i++) {
if (pages[i].type === 'content') {
pages[i].html = pages[i].html.replace(
'
' + pages[i].pageNum + '',
'
' + (i + 1) + '/' + totalPages + ''
);
}
}
// 渲染缩略图
if (config.useDomPreview) {
renderDomPages();
PhoneImageLogPanel.log('DOM预览渲染完成,共 ' + pages.length + ' 页', 'success');
render._locked = false;
if (render._pending) {
render._pending = false;
render().catch(function() {});
}
deferred.resolve(pages);
} else {
renderThumbnails(sizeConfig).then(function() {
PhoneImageLogPanel.log('渲染完成,共 ' + pages.length + ' 页', 'success');
render._locked = false;
if (render._pending) {
render._pending = false;
render().catch(function() {});
}
deferred.resolve(pages);
}).catch(function(err) {
PhoneImageLogPanel.log('渲染失败: ' + err, 'error');
render._locked = false;
deferred.reject(err);
});
}
}).catch(function(err) {
PhoneImageLogPanel.log('分页计算失败: ' + err, 'error');
render._locked = false;
deferred.reject(err);
});
});
return deferred.promise();
}
/**
* 预处理HTML内容
*/
function preprocessContent(html) {
if (!html) return '';
html = html.replace(/