diff --git a/AGENTS.md b/AGENTS.md index 38f41d6..e0491fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,6 +73,7 @@ AJAX请求 -> JSON; 普通请求 -> 模板页面(common@tpl/success|error) Post --(M:N)--> Category (通过 post_category) Post --(M:N)--> Tag (通过 post_tag) Post --(1:N)--> PostComment, PostVisit +Post 字段含 cover_text: 封面文案,用于手机图片排版封面展示 Admin --(N:1)--> AdminGroup --(权限列表)--> AdminPermission 表前缀: ul_,时间戳: int(10),软删除: delete_time字段 ``` diff --git a/app/admin/controller/Post.php b/app/admin/controller/Post.php index 2f364ca..c41ed71 100644 --- a/app/admin/controller/Post.php +++ b/app/admin/controller/Post.php @@ -358,7 +358,24 @@ class Post extends Common if (empty($model_post)) { $this->error('文章不存在'); } + + // 查询已有的排版输出,获取排版内容副本 + $postOutput = \app\model\PostOutput::where('post_id', $id) + ->where('output_type', 'phone_image') + ->order('id', 'desc') + ->find(); + + // 排版内容副本:优先使用已保存的副本,否则使用原文 + $layoutContentHtml = $model_post->content_html; + $layoutConfig = []; + if ($postOutput && !empty($postOutput->config['content_html'])) { + $layoutContentHtml = $postOutput->config['content_html']; + $layoutConfig = $postOutput->config; + } + View::assign('post', $model_post); + View::assign('layoutContentHtml', $layoutContentHtml); + View::assign('layoutConfig', $layoutConfig); return View::fetch(); } @@ -395,6 +412,10 @@ class Post extends Common } $config = $data['config'] ?? []; + // 确保排版内容副本被保存到 config + if (isset($data['content_html']) && !isset($config['content_html'])) { + $config['content_html'] = $data['content_html']; + } $pages = $data['pages'] ?? []; $admin_id = session('admin_id') ?? 0; diff --git a/app/api/controller/Articles.php b/app/api/controller/Articles.php index 6de3d7e..aab21c4 100644 --- a/app/api/controller/Articles.php +++ b/app/api/controller/Articles.php @@ -154,6 +154,7 @@ class Articles extends BaseController 'jump_to_url_status' => 0, 'poster' => '', 'desc' => '', + 'cover_text' => '', 'author_name' => '', 'hits' => 0, 'is_top' => 0, diff --git a/public/static/js/phone-image.js b/public/static/js/phone-image.js index 5a7e78c..e0cd02f 100644 --- a/public/static/js/phone-image.js +++ b/public/static/js/phone-image.js @@ -12,23 +12,14 @@ var PhoneImageEngine = (function () { // ===== 配置 ===== var config = { - template: 'minimal', size: 'xiaohongshu', - font: 'source-han-sans', fontSize: 14, watermark: '', - // 尺寸映射 (渲染尺寸, 实际输出是2倍) + pageAlignments: {}, // key=页码(1-based), value='top'|'center' sizes: { xiaohongshu: { width: 540, height: 720 }, douyin: { width: 540, height: 960 } }, - // 字体映射 - fonts: { - 'source-han-sans': "'Source Han Sans', 'SourceHanSans-Normal', sans-serif", - 'alibaba-puhuiti': "'AlibabaPuHuiTi', sans-serif", - 'lxgw-wenkai': "'LXGWWenKai', cursive" - }, - // 页面内容边距 contentPadding: 20 }; @@ -37,6 +28,7 @@ var PhoneImageEngine = (function () { id: 0, title: '', desc: '', + coverText: '', // 封面文案 content_html: '', poster: '', author_name: '', @@ -47,18 +39,16 @@ var PhoneImageEngine = (function () { // ===== 分页结果 ===== var pages = []; - // ===== DOM引用 ===== - var $container = null; - /** * 初始化引擎 - * @param {Object} options - {postId, title, desc, contentHtml, poster, authorName, createTime, categoryName} - * @param {Object} userConfig - {template, size, font, fontSize, watermark} + * @param {Object} options - {postId, title, desc, coverText, contentHtml, poster, authorName, createTime, categoryName} + * @param {Object} userConfig - {size, fontSize, watermark} */ function init(options, userConfig) { postData.id = options.postId || 0; postData.title = options.title || ''; postData.desc = options.desc || ''; + postData.coverText = options.coverText || ''; postData.content_html = options.contentHtml || ''; postData.poster = options.poster || ''; postData.author_name = options.authorName || ''; @@ -68,12 +58,11 @@ var PhoneImageEngine = (function () { if (userConfig) { $.extend(config, userConfig); } - - $container = $('#phone-image-container'); } /** * 渲染排版预览 - 生成分页后的HTML + * 两遍遍历: 先算总页数, 再带页码生成 * @returns {Array} 页面数组,每项是 { type, html, pageNum } */ function render() { @@ -82,22 +71,31 @@ var PhoneImageEngine = (function () { var pageHeight = sizeConfig.height; var contentAreaHeight = pageHeight - (config.contentPadding * 2); - // 内容预处理 var cleanHtml = preprocessContent(postData.content_html); - - // 解析为块级元素 var blocks = parseHtmlToBlocks(cleanHtml); - // 生成封面页 + // 封面页 pages.push(generateCoverPage(sizeConfig)); // 内容分页 var contentPages = paginateContent(blocks, contentAreaHeight, sizeConfig); pages = pages.concat(contentPages); - // 生成尾页 + // 尾页 pages.push(generateSummaryPage(sizeConfig, pages.length)); + // 两遍遍历: 现在知道了总页数, 给每页补上 N/M 页码 + var totalPages = pages.length; + for (var i = 0; i < pages.length; i++) { + if (pages[i].type === 'content') { + // 在页脚中添加 N/M 格式页码 + pages[i].html = pages[i].html.replace( + '' + pages[i].pageNum + '', + '' + (i + 1) + '/' + totalPages + '' + ); + } + } + // 渲染到DOM renderToDOM(sizeConfig); @@ -191,6 +189,11 @@ var PhoneImageEngine = (function () { }; switch (tagName) { + case 'hr': + block.type = 'page-break'; + block.html = ''; + block.estimatedHeight = 0; + break; case 'h1': case 'h2': case 'h3': @@ -296,7 +299,7 @@ var PhoneImageEngine = (function () { /** * 将块级元素按高度累加,超过可用高度时分页 - * 支持超大块拆分(超长文本拆成多段) + * 支持