mirror of
https://gitee.com/ulthon/ulthon_information.git
synced 2026-07-01 16:22:49 +08:00
feat(phone-image): add content flow, interactive breaks, per-page alignment and long image export
T9: renderContentFlow(), insertPageBreak(), removePageBreak(), renderAlignmentToggles(), exportLongImage() - Content flow renders blocks to #content-flow with interactive break-inserters - Page break markers have delete buttons - Per-page alignment toggle buttons on each content page - Long image export hides interactive elements before html2canvas capture - Event delegation with proper unbind/rebind to avoid duplicates
This commit is contained in:
@@ -58,6 +58,30 @@ var PhoneImageEngine = (function () {
|
||||
if (userConfig) {
|
||||
$.extend(config, userConfig);
|
||||
}
|
||||
|
||||
// 内容流事件委托(只绑定一次)
|
||||
$(document).off('click.phoneImage', '.break-inserter-btn');
|
||||
$(document).off('click.phoneImage', '.remove-break-btn');
|
||||
$(document).off('click.phoneImage', '.page-alignment-toggle');
|
||||
|
||||
$(document).on('click', '.break-inserter-btn', function () {
|
||||
var afterIndex = parseInt($(this).parent().data('after-index'), 10);
|
||||
insertPageBreak(afterIndex);
|
||||
});
|
||||
|
||||
$(document).on('click', '.remove-break-btn', function () {
|
||||
var index = parseInt($(this).data('index'), 10);
|
||||
removePageBreak(index);
|
||||
});
|
||||
|
||||
$(document).on('click', '.page-alignment-toggle', function () {
|
||||
var pageNum = parseInt($(this).data('page-num'), 10);
|
||||
var currentAlign = (config.pageAlignments && config.pageAlignments[pageNum]) || 'top';
|
||||
var newAlign = currentAlign === 'top' ? 'center' : 'top';
|
||||
|
||||
setPageAlignment(pageNum, newAlign);
|
||||
render();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,6 +123,12 @@ var PhoneImageEngine = (function () {
|
||||
// 渲染到DOM
|
||||
renderToDOM(sizeConfig);
|
||||
|
||||
// 渲染内容流(中间栏)
|
||||
renderContentFlow(blocks);
|
||||
|
||||
// 渲染逐页对齐指示器
|
||||
renderAlignmentToggles();
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
@@ -588,6 +618,122 @@ var PhoneImageEngine = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为每页渲染逐页对齐切换指示器
|
||||
*/
|
||||
function renderAlignmentToggles() {
|
||||
var $pages = $('#paginated-preview .phone-image-page');
|
||||
$pages.each(function (index) {
|
||||
var $page = $(this);
|
||||
|
||||
// 只给内容页添加(跳过封面页和总结页)
|
||||
if (!$page.hasClass('page-body')) return;
|
||||
|
||||
// 通过 pages 数组获取真正的 pageNum
|
||||
var pageNum = index;
|
||||
if (pages[index] && pages[index].pageNum) {
|
||||
pageNum = pages[index].pageNum;
|
||||
}
|
||||
|
||||
// 移除旧的 toggle
|
||||
$page.find('.page-alignment-toggle').remove();
|
||||
|
||||
// 当前对齐状态
|
||||
var currentAlign = (config.pageAlignments && config.pageAlignments[pageNum]) || 'top';
|
||||
var isActiveCenter = currentAlign === 'center';
|
||||
|
||||
var $toggle = $('<button class="page-alignment-toggle' + (isActiveCenter ? ' active-center' : '') + '" data-page-num="' + pageNum + '">' +
|
||||
(isActiveCenter ? '\u2195' : '\u2191') +
|
||||
'</button>');
|
||||
|
||||
$page.append($toggle);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容流到 #content-flow 容器
|
||||
* @param {Array} blocks - parseHtmlToBlocks 返回的块数组
|
||||
*/
|
||||
function renderContentFlow(blocks) {
|
||||
var $flow = $('#content-flow');
|
||||
if (!$flow.length) return;
|
||||
|
||||
$flow.empty();
|
||||
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
var block = blocks[i];
|
||||
|
||||
// 分页标记
|
||||
if (block.type === 'page-break') {
|
||||
var $marker = $('<div class="page-break-marker">' +
|
||||
'<span class="break-marker-label">-- 分页标记 --</span>' +
|
||||
'<button class="remove-break-btn" data-index="' + i + '" title="删除分页">×</button>' +
|
||||
'</div>');
|
||||
$flow.append($marker);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 内容块
|
||||
var $block = $('<div class="content-flow-block" data-index="' + i + '">' + block.html + '</div>');
|
||||
$flow.append($block);
|
||||
|
||||
// 块与块之间的分页插入区域(不在最后一个块之后添加)
|
||||
if (i < blocks.length - 1) {
|
||||
var $inserter = $('<div class="break-inserter" data-after-index="' + i + '">' +
|
||||
'<button class="break-inserter-btn" title="插入分页">+</button>' +
|
||||
'</div>');
|
||||
$flow.append($inserter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定位置插入分页标记
|
||||
* 修改 postData.content_html,在对应位置插入 <hr>
|
||||
* @param {number} blockIndex - 在哪个块之后插入 (对应 break-inserter 的 data-after-index)
|
||||
*/
|
||||
function insertPageBreak(blockIndex) {
|
||||
var cleanHtml = preprocessContent(postData.content_html);
|
||||
var $temp = $('<div>').html(cleanHtml);
|
||||
var children = $temp.children();
|
||||
|
||||
// 在 blockIndex 位置的元素之后插入 <hr>
|
||||
if (blockIndex >= 0 && blockIndex < children.length) {
|
||||
$(children[blockIndex]).after('<hr>');
|
||||
} else {
|
||||
$temp.append('<hr>');
|
||||
}
|
||||
|
||||
postData.content_html = $temp.html();
|
||||
|
||||
// 重新渲染
|
||||
render();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定位置的分页标记
|
||||
* @param {number} blockIndex - 分页标记的 data-index
|
||||
*/
|
||||
function removePageBreak(blockIndex) {
|
||||
// 统计这是第几个 page-break
|
||||
var cleanHtml = preprocessContent(postData.content_html);
|
||||
var $temp = $('<div>').html(cleanHtml);
|
||||
var hrElements = $temp.find('hr');
|
||||
|
||||
// 在 blocks 数组中找到这个 page-break 是第几个
|
||||
var breakOrder = 0;
|
||||
var currentBlocks = parseHtmlToBlocks(cleanHtml);
|
||||
for (var i = 0; i < currentBlocks.length && i < blockIndex; i++) {
|
||||
if (currentBlocks[i].type === 'page-break') breakOrder++;
|
||||
}
|
||||
|
||||
if (breakOrder < hrElements.length) {
|
||||
$(hrElements[breakOrder]).remove();
|
||||
postData.content_html = $temp.html();
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 图片生成 =====
|
||||
|
||||
/**
|
||||
@@ -723,6 +869,54 @@ var PhoneImageEngine = (function () {
|
||||
config.pageAlignments[pageNum] = align;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出内容流为长图
|
||||
*/
|
||||
function exportLongImage() {
|
||||
var deferred = $.Deferred();
|
||||
var $flow = $('#content-flow');
|
||||
if (!$flow.length) {
|
||||
deferred.reject('内容流容器不存在');
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
// 临时隐藏交互元素
|
||||
$flow.find('.break-inserter').hide();
|
||||
$flow.find('.page-break-marker').hide();
|
||||
|
||||
// 获取内容流的实际高度
|
||||
var flowWidth = $flow.outerWidth();
|
||||
var flowHeight = $flow[0].scrollHeight;
|
||||
|
||||
html2canvas($flow[0], {
|
||||
scale: 2,
|
||||
useCORS: true,
|
||||
backgroundColor: '#ffffff',
|
||||
width: flowWidth,
|
||||
height: flowHeight,
|
||||
logging: false
|
||||
}).then(function (canvas) {
|
||||
// 恢复交互元素
|
||||
$flow.find('.break-inserter').show();
|
||||
$flow.find('.page-break-marker').show();
|
||||
|
||||
// 触发下载
|
||||
var link = document.createElement('a');
|
||||
link.download = 'phone-image-long-' + Date.now() + '.png';
|
||||
link.href = canvas.toDataURL('image/png');
|
||||
link.click();
|
||||
|
||||
deferred.resolve(canvas);
|
||||
}).catch(function (err) {
|
||||
// 恢复交互元素
|
||||
$flow.find('.break-inserter').show();
|
||||
$flow.find('.page-break-marker').show();
|
||||
deferred.reject('长图导出失败: ' + err);
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
// ===== 工具方法 =====
|
||||
|
||||
/**
|
||||
@@ -759,6 +953,11 @@ var PhoneImageEngine = (function () {
|
||||
switchSize: switchSize,
|
||||
getConfig: getConfig,
|
||||
getPages: getPages,
|
||||
setPageAlignment: setPageAlignment
|
||||
setPageAlignment: setPageAlignment,
|
||||
renderContentFlow: renderContentFlow,
|
||||
insertPageBreak: insertPageBreak,
|
||||
removePageBreak: removePageBreak,
|
||||
renderAlignmentToggles: renderAlignmentToggles,
|
||||
exportLongImage: exportLongImage
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user