feat(typesetting): 页面品牌标识 - 数据链路+渲染函数+分页修正

- Post.php: phoneImage()传递siteName/siteLogo到模板
- phone_image.html: postData增加siteName/siteLogo字段
- phone-image.js: 新增generateBrandHeader()+preloadBrandLogo()
- phone-image.js: 3个页面生成函数调用品牌头部
- phone-image.js: contentAreaHeight扣减BRAND_HEADER_HEIGHT(36px)
- logo预转base64规避html2canvas CORS,过滤默认头像
This commit is contained in:
augushong
2026-05-17 11:12:56 +08:00
parent 0ce43ddd19
commit bf32bce922
3 changed files with 58 additions and 0 deletions

View File

@@ -380,6 +380,8 @@ class Post extends Common
View::assign('layoutContentHtml', $layoutContentHtml); View::assign('layoutContentHtml', $layoutContentHtml);
View::assign('layoutConfig', $layoutConfig); View::assign('layoutConfig', $layoutConfig);
View::assign('lastOutputId', $postOutput ? $postOutput->id : 0); View::assign('lastOutputId', $postOutput ? $postOutput->id : 0);
View::assign('siteName', \app\common\tools\Site::name() ?? '');
View::assign('siteLogo', \app\common\tools\Site::logo() ?? '');
return View::fetch(); return View::fetch();
} }

View File

@@ -112,6 +112,8 @@ var PhoneImageEngine = (function () {
content_html: '', content_html: '',
poster: '', poster: '',
author_name: '', author_name: '',
site_name: '',
site_logo: '',
create_time: '', create_time: '',
category_name: '' category_name: ''
}; };
@@ -148,11 +150,14 @@ var PhoneImageEngine = (function () {
postData.author_name = options.authorName || ''; postData.author_name = options.authorName || '';
postData.create_time = options.createTime || ''; postData.create_time = options.createTime || '';
postData.category_name = options.categoryName || ''; postData.category_name = options.categoryName || '';
postData.site_name = options.siteName || '';
postData.site_logo = options.siteLogo || '';
if (userConfig) { if (userConfig) {
$.extend(config, userConfig); $.extend(config, userConfig);
} }
applyFontScale(); applyFontScale();
preloadBrandLogo();
} }
/** /**
@@ -196,6 +201,8 @@ var PhoneImageEngine = (function () {
var sizeConfig = config.sizes[config.size] || config.sizes.xiaohongshu; var sizeConfig = config.sizes[config.size] || config.sizes.xiaohongshu;
var pageHeight = sizeConfig.height; var pageHeight = sizeConfig.height;
var contentAreaHeight = pageHeight - (config.contentPadding * 2); var contentAreaHeight = pageHeight - (config.contentPadding * 2);
var brandHeaderOffset = (postData.site_name || postData.site_logo) ? BRAND_HEADER_HEIGHT : 0;
contentAreaHeight -= brandHeaderOffset;
// 从预览区读取已预处理的内容 // 从预览区读取已预处理的内容
syncPreview(); syncPreview();
@@ -827,6 +834,50 @@ var PhoneImageEngine = (function () {
return true; return true;
} }
// ===== 品牌头部 =====
var BRAND_HEADER_HEIGHT = 36;
var brandLogoBase64 = null;
function generateBrandHeader() {
if (!postData.site_name && !postData.site_logo) return '';
var html = '<div class="brand-header" style="height:' + BRAND_HEADER_HEIGHT + 'px;">';
if (postData.site_logo) {
var logoSrc = brandLogoBase64 || postData.site_logo;
html += '<img class="brand-logo" src="' + escapeHtml(logoSrc) + '" alt="" onerror="this.style.display=\'none\'">';
}
if (postData.site_name) {
html += '<span class="brand-name">' + escapeHtml(postData.site_name) + '</span>';
}
html += '</div>';
return html;
}
function preloadBrandLogo() {
if (!postData.site_logo) return;
if (postData.site_logo.indexOf('avatar.png') !== -1) {
postData.site_logo = '';
return;
}
try {
var img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
canvas.getContext('2d').drawImage(img, 0, 0);
brandLogoBase64 = canvas.toDataURL('image/png');
};
img.src = postData.site_logo;
} catch(e) {
// base64 conversion failed, use original URL
}
}
/** /**
* 生成封面页HTML * 生成封面页HTML
*/ */
@@ -839,6 +890,7 @@ var PhoneImageEngine = (function () {
html += ' no-cover-image'; html += ' no-cover-image';
} }
html += '" style="width:' + sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">'; html += '" style="width:' + sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">';
html += generateBrandHeader();
if (hasCover) { if (hasCover) {
html += '<img class="cover-image" src="' + escapeHtml(postData.poster) + '" alt="" onerror="this.style.display=\'none\'">'; html += '<img class="cover-image" src="' + escapeHtml(postData.poster) + '" alt="" onerror="this.style.display=\'none\'">';
@@ -914,6 +966,7 @@ var PhoneImageEngine = (function () {
var html = '<div class="phone-image-page page-body' + valignClass + '" style="width:' + var html = '<div class="phone-image-page page-body' + valignClass + '" style="width:' +
sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">'; sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">';
html += generateBrandHeader();
// 页头(仅第一页内容页显示标题) // 页头(仅第一页内容页显示标题)
if (pageNum === 1) { if (pageNum === 1) {
@@ -955,6 +1008,7 @@ var PhoneImageEngine = (function () {
function generateSummaryPage(sizeConfig, totalPages) { function generateSummaryPage(sizeConfig, totalPages) {
var html = '<div class="phone-image-page page-summary" style="width:' + var html = '<div class="phone-image-page page-summary" style="width:' +
sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">'; sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">';
html += generateBrandHeader();
html += '<div class="summary-title">感谢阅读</div>'; html += '<div class="summary-title">感谢阅读</div>';
html += '<div class="summary-text">' + escapeHtml(postData.title) + '</div>'; html += '<div class="summary-text">' + escapeHtml(postData.title) + '</div>';

View File

@@ -307,6 +307,8 @@
contentHtml: $('#post-content-html').html(), contentHtml: $('#post-content-html').html(),
poster: <?php echo json_encode($post->poster ?? ''); ?>, poster: <?php echo json_encode($post->poster ?? ''); ?>,
authorName: <?php echo json_encode($post->author_name ?? ''); ?>, authorName: <?php echo json_encode($post->author_name ?? ''); ?>,
siteName: <?php echo json_encode($siteName ?? ''); ?>,
siteLogo: <?php echo json_encode($siteLogo ?? ''); ?>,
createTime: <?php echo json_encode($post->create_time_text ?? ''); ?>, createTime: <?php echo json_encode($post->create_time_text ?? ''); ?>,
categoryName: '' categoryName: ''
}; };