feat(phone-image): DOM分页預覽CSS+HTML基礎結構和數據模型

This commit is contained in:
augushong
2026-05-18 23:32:40 +08:00
parent 0782399709
commit bfeb1811be
3 changed files with 216 additions and 11 deletions

View File

@@ -1096,3 +1096,126 @@ body > .page-header-right .layui-btn:not(.layui-btn-primary):not(.layui-btn-norm
color: #888;
margin-right: 8px;
}
/* ============================================
DOM Page Container (DOM分页预览容器)
右侧分页预览区使用纯DOM渲染,不依赖截图
JS动态填充这些容器
============================================ */
/* --- DOM分页容器基础 --- */
.dom-page-container {
background: #ffffff;
overflow: hidden;
position: relative;
box-sizing: border-box;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 尺寸 - 复用size class模式 */
.size-xiaohongshu .dom-page-container {
width: 540px;
height: 720px;
}
.size-douyin .dom-page-container {
width: 540px;
height: 960px;
}
/* --- DOM分页品牌标识头 --- */
.dom-page-brand-header {
height: 36px;
display: flex;
align-items: center;
padding: 0 20px;
font-size: 13px;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
}
.dom-page-brand-header img {
height: 20px;
margin-right: 8px;
}
.dom-page-brand-header span {
color: #666;
}
/* --- DOM分页内容区域 --- */
.dom-page-content {
padding: 20px;
overflow: hidden;
box-sizing: border-box;
height: calc(100% - 36px - 30px);
font-size: calc(14px * var(--pi-font-scale, 1));
line-height: 1.8;
word-break: break-word;
}
/* DOM分页内容区对齐支持 (复用valign模式) */
.dom-page-content.valign-center {
display: flex;
flex-direction: column;
justify-content: center;
}
.dom-page-content.valign-bottom {
display: flex;
flex-direction: column;
justify-content: flex-end;
}
/* --- DOM分页页码 --- */
.dom-page-footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
padding: 6px 0;
font-size: 11px;
color: #999;
border-top: 1px solid #f5f5f5;
background: #fff;
}
/* --- 纯图片页 --- */
.dom-page-pure-image .dom-page-content {
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
.dom-page-pure-image .dom-page-content img {
max-width: 100%;
height: auto;
display: block;
}
/* ============================================
DOM分页内容区代码块样式
参照 #render-preview pre (L926-935)
============================================ */
.dom-page-content pre {
background: #f6f8fa;
border-radius: 6px;
padding: 12px 16px;
overflow-x: auto;
font-size: calc(13px * var(--pi-code-font-scale, 1));
line-height: 1.6;
margin: 10px 0;
}
.dom-page-content code {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: calc(13px * var(--pi-code-font-scale, 1));
}
.dom-page-content pre code {
background: none;
padding: 0;
border-radius: 0;
}

View File

@@ -100,9 +100,15 @@ var PhoneImageEngine = (function () {
},
contentPadding: 20,
fontScale: 1,
tableFontScale: 1
tableFontScale: 1,
useDomPreview: true
};
// ===== DOM分页预览状态 =====
var domPages = [];
var currentDomPageIndex = 0;
var totalDomPages = 0;
// ===== 文章数据 =====
var postData = {
id: 0,
@@ -248,19 +254,30 @@ var PhoneImageEngine = (function () {
}
// 渲染缩略图
renderThumbnails(sizeConfig).then(function() {
PhoneImageLogPanel.log('渲染完成,共 ' + pages.length + ' 页', 'success');
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);
}).catch(function(err) {
PhoneImageLogPanel.log('渲染失败: ' + err, 'error');
render._locked = false;
deferred.reject(err);
});
} 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;
@@ -1311,6 +1328,68 @@ var PhoneImageEngine = (function () {
return deferred.promise();
}
/**
* DOM分页预览 - 将pages[]转换为domPages格式
* 替代canvas截图方案直接操作DOM渲染
*/
function renderDomPages() {
var sizeConfig = config.sizes[config.size] || config.sizes.xiaohongshu;
// 清空domPages
domPages = [];
// 如果没有pages数据返回
if (!pages || pages.length === 0) {
return;
}
// 遍历pages创建domPage对象
for (var i = 0; i < pages.length; i++) {
var page = pages[i];
var domPage = {
html: page.html || '',
type: page.type || 'content',
pageNum: page.pageNum || (i + 1),
valign: (config.pageAlignments && config.pageAlignments[i]) || 'top',
isPureImage: page.type === 'pure-image'
};
domPages.push(domPage);
}
totalDomPages = domPages.length;
currentDomPageIndex = 0;
// 调用渲染
renderDomPageThumbnails(sizeConfig);
}
/**
* DOM分页预览 - 渲染所有页面的缩略图
* TODO: T3将实现完整逻辑
* 当前只清空容器并创建占位
* @param {Object} sizeConfig
*/
function renderDomPageThumbnails(sizeConfig) {
var $preview = $('#paginated-preview');
$preview.empty();
// 完整实现在T3中
}
/**
* DOM分页预览 - 刷新指定页面的对齐方式
* TODO: T3将实现完整逻辑
* @param {number} pageIndex - 页面索引(0-based)
*/
function refreshDomPage(pageIndex) {
var $containers = $('#paginated-preview .dom-page-container');
if (pageIndex >= 0 && pageIndex < $containers.length) {
var $content = $containers.eq(pageIndex).find('.dom-page-content');
$content.removeClass('valign-top valign-center valign-bottom');
var alignment = config.pageAlignments[pageIndex] || 'top';
$content.addClass('valign-' + alignment);
}
}
/**
* 初始化缩略图容器(流式渲染第一步:清空容器)
* @param {Object} sizeConfig
@@ -1735,6 +1814,9 @@ var PhoneImageEngine = (function () {
syncPreview: syncPreview,
setPageAlignment: setPageAlignment,
exportLongImage: exportLongImage,
renderDomPages: renderDomPages,
renderDomPageThumbnails: renderDomPageThumbnails,
refreshDomPage: refreshDomPage,
logPanel: PhoneImageLogPanel,
getContentHtml: function () {
if (window.phoneImageEditor) {

View File

@@ -261,16 +261,16 @@
<div id="render-preview"></div>
</div>
<!-- 右侧:分页排版预览 -->
<!-- 右侧:分页排版预览 (DOM分页渲染) -->
<div class="paginated-preview-area">
<div class="preview-header">
<span>画布预览</span>
<span>分页预览</span>
<div style="display:inline-flex;align-items:center;gap:8px;float:right;">
<button id="btn-reset-alignments" type="button" style="height:26px;font-size:12px;border:1px solid #d9d9d9;border-radius:3px;padding:0 8px;cursor:pointer;background:#fff;">重置</button>
</div>
</div>
<div class="preview-thumb-zone">
<div id="paginated-preview" class="preview-thumbnails"></div>
<div id="paginated-preview"></div>
</div>
<!-- 日志面板由 LogPanel.init() 动态插入 -->
</div>