feat(phone-image): 增加翻页预览与无封面图排版样式

- 为手机截图生成器添加翻页功能,支持在生成前预览各页内容
- 增加无封面图时的排版样式,使用装饰线条和居中布局
- 改进图片处理逻辑,清除内联样式并展平嵌套包装元素
- 修复 models.dev 同步接口,支持 GET 请求获取缓存数据
- 优化网络请求,添加直连失败后的本地代理重试机制
This commit is contained in:
augushong
2026-05-01 16:31:26 +08:00
parent eab8cee8a8
commit 34fe255829
11 changed files with 334 additions and 27 deletions

View File

@@ -113,6 +113,19 @@ var PhoneImageEngine = (function () {
html = html.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '');
// 清除空段落
html = html.replace(/<p[^>]*>\s*<\/p>/gi, '');
// 处理图片: 清除内联样式, 强制 max-width:100%
html = html.replace(/<img([^>]*?)>/gi, function (match, attrs) {
// 移除 style, width, height 内联属性
attrs = attrs.replace(/\s*style\s*=\s*"[^"]*"/gi, '');
attrs = attrs.replace(/\s*style\s*=\s*'[^']*'/gi, '');
attrs = attrs.replace(/\s*width\s*=\s*"[^"]*"/gi, '');
attrs = attrs.replace(/\s*width\s*=\s*'[^']*'/gi, '');
attrs = attrs.replace(/\s*height\s*=\s*"[^"]*"/gi, '');
attrs = attrs.replace(/\s*height\s*=\s*'[^']*'/gi, '');
return '<img' + attrs + ' style="max-width:100%;height:auto;display:block;margin:10px 0;">';
});
return html;
}
@@ -125,6 +138,29 @@ var PhoneImageEngine = (function () {
if (!html) return blocks;
var $temp = $('<div>').html(html);
// 展平嵌套的包装元素 (div, section, article, main, header, footer)
// 将其内部内容提取为直接子元素
var wrapperTags = ['div', 'section', 'article', 'main', 'header', 'footer', 'span'];
var changed = true;
var maxIterations = 10;
while (changed && maxIterations > 0) {
changed = false;
maxIterations--;
$temp.children().each(function () {
var $el = $(this);
var tagName = $el.prop('tagName').toLowerCase();
if ($.inArray(tagName, wrapperTags) !== -1) {
// 将此包装元素的子内容替换自身
var $children = $el.children();
if ($children.length > 0) {
$el.replaceWith($children);
changed = true;
}
}
});
}
var children = $temp.children();
if (children.length === 0) {
@@ -161,6 +197,7 @@ var PhoneImageEngine = (function () {
case 'h4':
case 'h5':
case 'h6':
block.type = tagName;
block.estimatedHeight = estimateHeadingHeight(tagName, $el.text());
break;
case 'img':
@@ -380,34 +417,73 @@ var PhoneImageEngine = (function () {
// ===== 页面HTML生成 =====
/**
* 检查是否有有效的封面图(排除默认头像占位图)
*/
function hasValidPoster() {
if (!postData.poster) return false;
// 排除默认头像占位图
var defaultPatterns = ['/static/images/avatar.png', '/static/images/avatar.jpeg', '/static/images/avatar.jpg'];
for (var i = 0; i < defaultPatterns.length; i++) {
if (postData.poster.indexOf(defaultPatterns[i]) !== -1) return false;
}
return true;
}
/**
* 生成封面页HTML
*/
function generateCoverPage(sizeConfig) {
var html = '<div class="phone-image-page page-cover" style="width:' +
sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">';
var hasCover = hasValidPoster();
var html = '<div class="phone-image-page page-cover';
if (hasCover) {
html += ' has-cover-image';
} else {
html += ' no-cover-image';
}
html += '" style="width:' + sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">';
if (postData.poster) {
if (hasCover) {
html += '<img class="cover-image" src="' + postData.poster + '" alt="">';
html += '<div class="cover-title">' + escapeHtml(postData.title) + '</div>';
if (postData.desc) {
html += '<div class="cover-subtitle">' + escapeHtml(postData.desc) + '</div>';
}
html += '<div class="cover-meta">';
if (postData.category_name) {
html += escapeHtml(postData.category_name) + ' | ';
}
if (postData.author_name) {
html += escapeHtml(postData.author_name) + ' | ';
}
if (postData.create_time) {
html += postData.create_time;
}
html += '</div>';
} else {
// 无封面图: 大标题+摘要+装饰线条排版
html += '<div class="cover-decor-line cover-decor-top"></div>';
html += '<div class="cover-no-img-content">';
html += '<div class="cover-no-img-title">' + escapeHtml(postData.title) + '</div>';
html += '<div class="cover-decor-divider"></div>';
if (postData.desc) {
html += '<div class="cover-no-img-desc">' + escapeHtml(postData.desc) + '</div>';
}
html += '<div class="cover-no-img-meta">';
if (postData.category_name) {
html += '<span>' + escapeHtml(postData.category_name) + '</span>';
}
if (postData.author_name) {
html += '<span>' + escapeHtml(postData.author_name) + '</span>';
}
if (postData.create_time) {
html += '<span>' + postData.create_time + '</span>';
}
html += '</div>';
html += '</div>';
html += '<div class="cover-decor-line cover-decor-bottom"></div>';
}
html += '<div class="cover-title">' + escapeHtml(postData.title) + '</div>';
if (postData.desc) {
html += '<div class="cover-subtitle">' + escapeHtml(postData.desc) + '</div>';
}
html += '<div class="cover-meta">';
if (postData.category_name) {
html += escapeHtml(postData.category_name) + ' | ';
}
if (postData.author_name) {
html += escapeHtml(postData.author_name) + ' | ';
}
if (postData.create_time) {
html += postData.create_time;
}
html += '</div>';
html += '</div>';
return { type: 'cover', html: html };
@@ -470,6 +546,9 @@ var PhoneImageEngine = (function () {
// ===== DOM渲染 =====
// 当前预览页码 (0-based)
var currentPreviewPage = 0;
/**
* 渲染分页结果到DOM
*/
@@ -493,6 +572,55 @@ var PhoneImageEngine = (function () {
for (var i = 0; i < pages.length; i++) {
$container.append(pages[i].html);
}
// 确保当前页码有效
if (currentPreviewPage >= pages.length) {
currentPreviewPage = Math.max(0, pages.length - 1);
}
// 显示当前页, 隐藏其他页
showPreviewPage(currentPreviewPage);
}
/**
* 显示指定预览页
* @param {number} index 页码(0-based)
*/
function showPreviewPage(index) {
if (!$container || !$container.length) return;
var $allPages = $container.find('.phone-image-page');
$allPages.hide();
if (index >= 0 && index < $allPages.length) {
$allPages.eq(index).show();
currentPreviewPage = index;
}
// 更新页码显示
$container.trigger('pageChange', [currentPreviewPage, pages.length]);
}
/**
* 下一页
*/
function nextPage() {
if (currentPreviewPage < pages.length - 1) {
showPreviewPage(currentPreviewPage + 1);
}
}
/**
* 上一页
*/
function prevPage() {
if (currentPreviewPage > 0) {
showPreviewPage(currentPreviewPage - 1);
}
}
/**
* 获取当前预览页码
*/
function getCurrentPageIndex() {
return currentPreviewPage;
}
// ===== 图片生成 =====
@@ -512,24 +640,31 @@ var PhoneImageEngine = (function () {
return deferred.promise();
}
// 截图时需要所有页面都显示
$pages.show();
var index = 0;
var total = $pages.length;
function captureNext() {
if (index >= total) {
if (onProgress) onProgress(total, total, null);
// 恢复单页显示
showPreviewPage(currentPreviewPage);
deferred.resolve(canvases);
return;
}
var $page = $($pages[index]);
// 隐藏其他页, 只显示当前要截图的页
$pages.hide();
$pages.eq(index).show();
html2canvas($page[0], {
html2canvas($pages.eq(index)[0], {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
width: $page.outerWidth(),
height: $page.outerHeight(),
width: $pages.eq(index).outerWidth(),
height: $pages.eq(index).outerHeight(),
logging: false
}).then(function (canvas) {
canvases.push(canvas);
@@ -537,6 +672,8 @@ var PhoneImageEngine = (function () {
if (onProgress) onProgress(index, total, canvas);
captureNext();
}).catch(function (err) {
// 恢复单页显示
showPreviewPage(currentPreviewPage);
deferred.reject('截图失败(第' + (index + 1) + '页): ' + err);
});
}
@@ -731,6 +868,10 @@ var PhoneImageEngine = (function () {
getPages: getPages,
applyAiRecommendation: applyAiRecommendation,
applyAiContent: applyAiContent,
restoreOriginalContent: restoreOriginalContent
restoreOriginalContent: restoreOriginalContent,
nextPage: nextPage,
prevPage: prevPage,
showPreviewPage: showPreviewPage,
getCurrentPageIndex: getCurrentPageIndex
};
})();