mirror of
https://gitee.com/ulthon/ulthon_information.git
synced 2026-07-10 02:32:47 +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) {
|
if (userConfig) {
|
||||||
$.extend(config, 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
|
// 渲染到DOM
|
||||||
renderToDOM(sizeConfig);
|
renderToDOM(sizeConfig);
|
||||||
|
|
||||||
|
// 渲染内容流(中间栏)
|
||||||
|
renderContentFlow(blocks);
|
||||||
|
|
||||||
|
// 渲染逐页对齐指示器
|
||||||
|
renderAlignmentToggles();
|
||||||
|
|
||||||
return pages;
|
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;
|
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,
|
switchSize: switchSize,
|
||||||
getConfig: getConfig,
|
getConfig: getConfig,
|
||||||
getPages: getPages,
|
getPages: getPages,
|
||||||
setPageAlignment: setPageAlignment
|
setPageAlignment: setPageAlignment,
|
||||||
|
renderContentFlow: renderContentFlow,
|
||||||
|
insertPageBreak: insertPageBreak,
|
||||||
|
removePageBreak: removePageBreak,
|
||||||
|
renderAlignmentToggles: renderAlignmentToggles,
|
||||||
|
exportLongImage: exportLongImage
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user