mirror of
https://gitee.com/ulthon/ulthon_information.git
synced 2026-07-10 01:12:48 +08:00
docs(phone-image): 产出排版功能架构文档
fix(phone-image): 修复分页标记丢失bug,消除双数据源问题 - 新增 getContentHtml() 和 updateConfig() 引擎API - 保存逻辑改用引擎内部 content_html,不再从DOM读取 - doRender 改用 updateConfig,配置变更不重置内容 - loadFromHistory 改用 init+render 全量初始化 - PHP/JS 配置字段对齐(移除template/font,新增pageAlignments)
This commit is contained in:
315
docs/phone-image-architecture.md
Normal file
315
docs/phone-image-architecture.md
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
# 手机图片排版功能 - 架构文档
|
||||||
|
|
||||||
|
> 基于源代码实际阅读编写,记录排版引擎的设计原理与数据流。
|
||||||
|
|
||||||
|
## 1. 渲染管线
|
||||||
|
|
||||||
|
排版引擎的核心是 `PhoneImageEngine`(IIFE 闭包,`public/static/js/phone-image.js`),纯前端运行,后端不参与渲染计算。
|
||||||
|
|
||||||
|
### 1.1 管线总览
|
||||||
|
|
||||||
|
```
|
||||||
|
用户触发 doRender()
|
||||||
|
|
|
||||||
|
v
|
||||||
|
PhoneImageEngine.init(options, userConfig) -- L80: 合并配置、注册事件委托
|
||||||
|
|
|
||||||
|
v
|
||||||
|
PhoneImageEngine.render() -- L129: 管线入口,带并发锁
|
||||||
|
|
|
||||||
|
+---> preprocessContent(content_html) -- L226: 清洗HTML(去script/style/iframe,规范化img)
|
||||||
|
+---> parseHtmlToBlocks(cleanHtml) -- L273: HTML -> 块级元素数组,含高度估算
|
||||||
|
+---> renderContentFlow(blocks) -- L1160: 将块渲染到中间栏 #content-flow
|
||||||
|
+---> convertFlowBlocksToImages(blocks) -- L1051: 表格/代码块 -> html2canvas截图 -> img标签替换
|
||||||
|
+---> measureBlockHeights(blocks) -- L1138: 从中间栏DOM读取实测高度,覆盖估算值
|
||||||
|
+---> generateCoverPage() -- L618: 生成封面页HTML
|
||||||
|
+---> paginateContent(blocks, areaHeight) -- L447: 分页算法,累加高度超限则换页
|
||||||
|
+---> generateSummaryPage() -- L730: 生成尾页HTML
|
||||||
|
+---> 页码补全 N/M -- L188: 二次遍历pages数组,替换占位页码
|
||||||
|
+---> renderThumbnails(sizeConfig) -- L967: 截图 -> 显示右侧缩略图
|
||||||
|
|
|
||||||
|
+---> doCapturePages({scale:1}) -- L840: staging隐藏区域渲染 + html2canvas串行截图
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 并发控制
|
||||||
|
|
||||||
|
`render()` 函数使用 `_locked` 标志防止并发渲染(`phone-image.js:133-138`)。渲染期间如果有新请求,设置 `_pending = true`,当前渲染完成后自动重试(`phone-image.js:202-207`)。`insertPageBreak` 和 `removePageBreak` 也有类似的 `_retryCount` 轮询机制(`phone-image.js:1198-1209`, `phone-image.js:1233-1244`)。
|
||||||
|
|
||||||
|
### 1.3 截图机制
|
||||||
|
|
||||||
|
截图在 `#render-staging` 隐藏区域进行(`phone-image.js:64-73`)。关键步骤:
|
||||||
|
|
||||||
|
1. 将所有页面HTML append到staging区域
|
||||||
|
2. 临时将 `visibility:hidden` 切为 `visible`(html2canvas 会跳过隐藏元素)
|
||||||
|
3. 串行逐页调用 html2canvas 截图(`runCaptureLoop`, L880)
|
||||||
|
4. 纯图片页优化:直接用 Image + Canvas 绘制,跳过 html2canvas(`phone-image.js:765-829`)
|
||||||
|
5. 截图完成后恢复 `visibility:hidden`
|
||||||
|
|
||||||
|
缩略图用 `scale:1`,保存用 `scale:2`(输出1080px宽度)。
|
||||||
|
|
||||||
|
### 1.4 表格/代码块的预转换
|
||||||
|
|
||||||
|
在分页之前,中间栏 `#content-flow` 里的 `<table>` 和 `<pre>` 块会被 html2canvas 截图并替换为 `<img data-converted="true">` 标签(`phone-image.js:1051-1132`)。转换结果用 `convertedBlockCache`(djb2哈希做key)缓存,避免重复截图。这是为了保证分页算法拿到准确的高度值,因为 html2canvas 无法在 staging 区域中直接处理复杂元素。
|
||||||
|
|
||||||
|
## 2. 数据流
|
||||||
|
|
||||||
|
### 2.1 HTML内容的三个来源(三源问题)
|
||||||
|
|
||||||
|
文章HTML内容在运行时存在于三个独立位置,彼此之间没有同步机制:
|
||||||
|
|
||||||
|
| 来源 | 位置 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| **DOM `#post-content-html`** | `phone_image.html:98` | 服务端渲染的隐藏div,`{$layoutContentHtml\|raw}` |
|
||||||
|
| **闭包 `postData.content_html`** | `phone-image.js:32` | IIFE闭包变量,init()时赋值 |
|
||||||
|
| **初始化属性 `postData.contentHtml`** | `phone_image.html:193` | 模板内联脚本中的局部对象属性 |
|
||||||
|
|
||||||
|
具体流向:
|
||||||
|
|
||||||
|
```
|
||||||
|
服务端 ($layoutContentHtml)
|
||||||
|
-> DOM #post-content-html (L98, display:none)
|
||||||
|
-> 模板脚本读取: $('#post-content-html').html() (L193)
|
||||||
|
-> 赋值给 postData.contentHtml (模板局部变量)
|
||||||
|
-> init({contentHtml: ...}) (L208)
|
||||||
|
-> 闭包 postData.content_html = options.contentHtml (phone-image.js:85)
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题**:分页标记操作(`insertPageBreak`/`removePageBreak`)只更新闭包内的 `postData.content_html`(`phone-image.js:1223`, `phone-image.js:1261`),不更新 DOM `#post-content-html` 和模板局部的 `postData.contentHtml`。而保存操作(`#btn-save`, `#btn-generate`)读取的是 `$('#post-content-html').html()`(`phone_image.html:267`, `phone_image.html:288`),这意味着分页标记在保存时会丢失。
|
||||||
|
|
||||||
|
### 2.2 完整数据流图
|
||||||
|
|
||||||
|
```
|
||||||
|
[数据库 Post.content_html]
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Post.php::phoneImage() (L355)
|
||||||
|
查询 PostOutput 取已保存的 content_html 副本
|
||||||
|
|
|
||||||
|
v
|
||||||
|
phone_image.html 模板渲染
|
||||||
|
- $layoutContentHtml (优先用已保存副本)
|
||||||
|
- $layoutConfig (已保存的配置)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
前端初始化 (phone_image.html:188-208)
|
||||||
|
1. 构造 postData 局部对象 (L188)
|
||||||
|
2. 恢复 savedConfig (L201)
|
||||||
|
3. PhoneImageEngine.init(postData, initConfig) (L208)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
渲染管线 (phone-image.js render())
|
||||||
|
- 读取 postData.content_html
|
||||||
|
- 生成 pages 数组
|
||||||
|
- 截图显示缩略图
|
||||||
|
|
|
||||||
|
v
|
||||||
|
用户交互
|
||||||
|
- 分页标记 -> 修改 postData.content_html (闭包内)
|
||||||
|
- 保存配置 -> POST savePostOutputConfig (content_html 取自 DOM)
|
||||||
|
- 生成保存 -> POST savePostOutput (content_html 取自 DOM)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
后端存储
|
||||||
|
- PostOutput.config (JSON字段,含 content_html 副本)
|
||||||
|
- PostOutputFile (每页图片文件记录)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 历史记录加载流
|
||||||
|
|
||||||
|
```
|
||||||
|
loadFromHistory(outputId) (phone_image.html:371)
|
||||||
|
-> GET loadPostOutputConfig?id=XXX
|
||||||
|
-> 更新表单控件 (size/fontSize/watermark)
|
||||||
|
-> 更新 postData.contentHtml (模板局部变量)
|
||||||
|
-> 更新 DOM #post-content-html
|
||||||
|
-> doRender(renderConfig) (含 pageAlignments)
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:`loadFromHistory` 更新的是模板局部的 `postData.contentHtml` 和 DOM,但没有直接更新闭包内的 `postData.content_html`。它依赖 `doRender -> init()` 的间接传递来同步。
|
||||||
|
|
||||||
|
## 3. 存储模型
|
||||||
|
|
||||||
|
### 3.1 数据库表
|
||||||
|
|
||||||
|
**`ul_post_output`** (模型: `app/model/PostOutput.php`)
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `id` | 主键 |
|
||||||
|
| `post_id` | 关联文章ID |
|
||||||
|
| `output_type` | 输出类型,固定为 `phone_image` |
|
||||||
|
| `config` | JSON字段,存储完整配置 + content_html 副本 |
|
||||||
|
| `status` | 0=生成中, 1=已完成, 2=失败 (常量定义 L20-22) |
|
||||||
|
| `page_count` | 页数 |
|
||||||
|
| `admin_id` | 操作管理员ID |
|
||||||
|
| `create_time` | 创建时间 (int) |
|
||||||
|
| `delete_time` | 软删除标记 (默认0,trait: SoftDelete) |
|
||||||
|
|
||||||
|
模型使用 `$json = ['config']` 自动序列化/反序列化配置(`PostOutput.php:18`)。
|
||||||
|
|
||||||
|
**`ul_post_output_file`** (模型: `app/model/PostOutputFile.php`)
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `id` | 主键 |
|
||||||
|
| `output_id` | 关联 post_output.id |
|
||||||
|
| `page` | 页码 (从1开始) |
|
||||||
|
| `file_path` | 相对路径,如 `/upload/post_output/20260511/5_1.jpg` |
|
||||||
|
| `file_url` | 访问URL (同 file_path) |
|
||||||
|
| `file_size` | 文件字节数 |
|
||||||
|
| `width` | 图片宽度 (px) |
|
||||||
|
| `height` | 图片高度 (px) |
|
||||||
|
|
||||||
|
关系: `PostOutput hasMany PostOutputFile`,`PostOutput belongsTo Post`。
|
||||||
|
|
||||||
|
### 3.2 config JSON 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"size": "xiaohongshu",
|
||||||
|
"fontSize": 14,
|
||||||
|
"watermark": "",
|
||||||
|
"content_html": "<p>文章HTML副本</p>",
|
||||||
|
"pageAlignments": { "1": "top", "2": "center" }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`content_html` 作为配置的一部分保存,使每次排版记录都携带当时的内容快照。这实现了排版内容与文章原文的解耦(`Post.php:369-376` 加载时会优先使用此副本)。
|
||||||
|
|
||||||
|
### 3.3 文件存储
|
||||||
|
|
||||||
|
图片保存到 `public/upload/post_output/{Ymd}/{outputId}_{page}.jpg`(`PhoneImage.php:87-92`)。ZIP 临时文件生成到 `runtime/temp/` 目录,请求结束后通过 `register_shutdown_function` 清理(`Post.php:570-575`)。
|
||||||
|
|
||||||
|
## 4. 配置体系
|
||||||
|
|
||||||
|
### 4.1 PHP 端配置字段定义
|
||||||
|
|
||||||
|
`PhoneImage::getConfigFields()` (L15-23) 定义了5个字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 选项/范围 | 默认值 |
|
||||||
|
|------|------|-----------|--------|
|
||||||
|
| `template` | select | minimal/magazine/mixed | minimal |
|
||||||
|
| `size` | select | xiaohongshu/douyin | xiaohongshu |
|
||||||
|
| `font` | select | source-han-sans/alibaba-puhuiti/lxgw-wenkai | source-han-sans |
|
||||||
|
| `fontSize` | number | 10-24 | 14 |
|
||||||
|
| `watermark` | text | - | '' |
|
||||||
|
|
||||||
|
### 4.2 JS 端实际使用的配置
|
||||||
|
|
||||||
|
`phone-image.js` 闭包内的 `config` 对象 (L14-24):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
config = {
|
||||||
|
size: 'xiaohongshu',
|
||||||
|
fontSize: 14,
|
||||||
|
watermark: '',
|
||||||
|
pageAlignments: {}, // 逐页对齐
|
||||||
|
sizes: { ... }, // 尺寸预设
|
||||||
|
contentPadding: 20
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 PHP/JS 字段不对齐
|
||||||
|
|
||||||
|
两端存在明显的字段定义差异:
|
||||||
|
|
||||||
|
| 问题 | PHP 定义 | JS 实际使用 |
|
||||||
|
|------|----------|-------------|
|
||||||
|
| `template` | PHP有定义 (3个模板选项) | JS完全不使用,无任何模板切换逻辑 |
|
||||||
|
| `font` | PHP有定义 (3个字体选项) | JS不使用,字体通过CSS引入,无切换机制 |
|
||||||
|
| `pageAlignments` | PHP未定义 | JS核心功能,保存在config中 |
|
||||||
|
| `contentPadding` | PHP未定义 | JS硬编码为20 (L23) |
|
||||||
|
|
||||||
|
`validateConfig()` (L29-50) 验证 `template`/`size`/`font`/`fontSize`,但 `pageAlignments` 不在验证范围内。前端发送的配置比PHP定义的字段多,PHP不做字段白名单过滤,全部存入JSON。
|
||||||
|
|
||||||
|
## 5. 已知问题
|
||||||
|
|
||||||
|
### 5.1 分页标记丢失(根因分析)
|
||||||
|
|
||||||
|
**现象**:用户通过中间栏的 "+" 按钮插入分页标记后,点击"保存"或"生成并保存",重新加载页面时分页标记消失。
|
||||||
|
|
||||||
|
**根因**:数据源不一致。
|
||||||
|
|
||||||
|
分页标记的插入流程 (`insertPageBreak`, L1198-1227):
|
||||||
|
1. 从闭包 `postData.content_html` 读取内容
|
||||||
|
2. 在目标位置插入 `<hr>` 标签
|
||||||
|
3. 写回闭包 `postData.content_html`(L1223)
|
||||||
|
|
||||||
|
保存操作的配置构建 (`phone_image.html:262-268`, `284-289`):
|
||||||
|
```javascript
|
||||||
|
content_html: $('#post-content-html').html() // 读取的是DOM元素
|
||||||
|
```
|
||||||
|
|
||||||
|
DOM `#post-content-html` 的内容在页面初始化后从未被 `insertPageBreak` 更新。因此保存时发送的 `content_html` 是初始值,不含 `<hr>` 分页标记。下次加载页面时,服务端用这个无标记的内容渲染,标记丢失。
|
||||||
|
|
||||||
|
### 5.2 保存数据量限制
|
||||||
|
|
||||||
|
`saveImages()` 有16MB的客户端检查(`phone-image.js:1317-1320`),但服务端没有对应的分块接收机制。大文章(超过约30页)的base64数据可能超出 PHP `post_max_size` 配置。
|
||||||
|
|
||||||
|
### 5.3 目录权限 0777
|
||||||
|
|
||||||
|
`PhoneImage.php:95` 和 `:140` 中 `mkdir` 使用 0777 权限,与项目其他位置的 anti-pattern 一致。
|
||||||
|
|
||||||
|
### 5.4 接口 `PostOutputManagerInterface` 未被充分利用
|
||||||
|
|
||||||
|
`PhoneImage` 实现了 `process()` 和 `getPreview()` 方法,但两者都返回空值(`PhoneImage.php:56-67`)。实际业务完全在控制器中直接调用 `PhoneImage` 的其他方法,绕过了接口定义的流程。接口仅作为类型约束存在。
|
||||||
|
|
||||||
|
### 5.5 `saveConfigOnly` 是静态方法但 `createOutput` 是实例方法
|
||||||
|
|
||||||
|
`PhoneImage::saveConfigOnly()` 是 static 方法(L223),而同类的 `createOutput()` 是实例方法(L193)。两者的实现几乎相同(都是 `PostOutput::create()`),但调用方式不同。控制器中 `savePostOutput` 通过实例调用 `createOutput`,`savePostOutputConfig` 通过静态调用 `saveConfigOnly`,风格不统一。
|
||||||
|
|
||||||
|
## 6. API 端点清单
|
||||||
|
|
||||||
|
所有端点位于 `app/admin/controller/Post.php`,通过 ThinkPHP 自动路由访问,URL前缀为 `/index.php/admin/post/`。
|
||||||
|
|
||||||
|
| 端点 | 方法 | 行号 | 说明 | 前端调用方 |
|
||||||
|
|------|------|------|------|-----------|
|
||||||
|
| `phoneImage/{id}` | GET | L355-383 | 排版操作页(渲染模板) | 页面跳转 |
|
||||||
|
| `savePostOutput` | POST (JSON) | L405-437 | 保存输出记录+图片 | `phone-image.js:1324` |
|
||||||
|
| `savePostOutputConfig` | POST (JSON) | L442-462 | 仅保存配置(不生成图片) | `phone_image.html:184` -> `saveConfig()` |
|
||||||
|
| `loadPostOutputConfig` | GET | L467-490 | 加载历史记录配置 | `phone_image.html:186` -> `loadFromHistory()` |
|
||||||
|
| `getOutputListJson` | GET | L495-515 | 获取输出记录列表 | `phone_image.html:185` -> 历史弹窗 |
|
||||||
|
| `deletePostOutput` | POST/GET | L520-531 | 删除输出记录及文件 | 未在前端模板中直接调用 |
|
||||||
|
| `regeneratePostOutput` | POST/GET | L536-552 | 删除旧记录,返回配置 | 未在前端模板中直接调用 |
|
||||||
|
| `downloadPostOutputZip/{id}` | GET | L557-579 | 打包下载ZIP | `phone_image.html:183` -> `#btn-download` |
|
||||||
|
| `postOutputList/{id}` | GET | L388-400 | 输出管理列表页 | 独立页面入口 |
|
||||||
|
|
||||||
|
### 请求/响应格式
|
||||||
|
|
||||||
|
**savePostOutput** 请求体:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"post_id": 123,
|
||||||
|
"output_type": "phone_image",
|
||||||
|
"config": { "size": "...", "fontSize": 14, ... },
|
||||||
|
"content_html": "<p>...</p>",
|
||||||
|
"pages": ["data:image/jpeg;base64,...", ...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**savePostOutputConfig** 请求体:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"post_id": 123,
|
||||||
|
"output_type": "phone_image",
|
||||||
|
"config": { "size": "...", "fontSize": 14, ... },
|
||||||
|
"content_html": "<p>...</p>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**通用响应格式**:
|
||||||
|
```json
|
||||||
|
{ "code": 0, "msg": "", "data": { "output_id": 456 } }
|
||||||
|
```
|
||||||
|
失败时 `code` 为 500。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档基于以下源文件的完整阅读:*
|
||||||
|
- `public/static/js/phone-image.js` (1487行)
|
||||||
|
- `view/admin/post/phone_image.html` (425行)
|
||||||
|
- `app/admin/controller/Post.php` (L350-579)
|
||||||
|
- `app/common/tools/PhoneImage.php` (234行)
|
||||||
|
- `app/common/tools/PostOutputManagerInterface.php` (28行)
|
||||||
|
- `app/model/PostOutput.php` (62行)
|
||||||
|
- `app/model/PostOutputFile.php` (26行)
|
||||||
@@ -1482,6 +1482,14 @@ var PhoneImageEngine = (function () {
|
|||||||
setPageAlignment: setPageAlignment,
|
setPageAlignment: setPageAlignment,
|
||||||
insertPageBreak: insertPageBreak,
|
insertPageBreak: insertPageBreak,
|
||||||
removePageBreak: removePageBreak,
|
removePageBreak: removePageBreak,
|
||||||
|
getContentHtml: function () {
|
||||||
|
return postData.content_html;
|
||||||
|
},
|
||||||
|
updateConfig: function (newConfig) {
|
||||||
|
if (newConfig) {
|
||||||
|
$.extend(config, newConfig);
|
||||||
|
}
|
||||||
|
},
|
||||||
exportLongImage: exportLongImage
|
exportLongImage: exportLongImage
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -227,15 +227,16 @@
|
|||||||
clearTimeout(renderTimer);
|
clearTimeout(renderTimer);
|
||||||
renderTimer = setTimeout(function() {
|
renderTimer = setTimeout(function() {
|
||||||
var fontSize = parseInt($('[name="fontSize"]').val()) || 14;
|
var fontSize = parseInt($('[name="fontSize"]').val()) || 14;
|
||||||
var initConfig = {
|
var newConfig = {
|
||||||
size: $('[name="size"]').val(),
|
size: $('[name="size"]').val(),
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
watermark: $('[name="watermark"]').val()
|
watermark: $('[name="watermark"]').val()
|
||||||
};
|
};
|
||||||
if (extraConfig) {
|
if (extraConfig) {
|
||||||
$.extend(initConfig, extraConfig);
|
$.extend(newConfig, extraConfig);
|
||||||
}
|
}
|
||||||
PhoneImageEngine.init(postData, initConfig);
|
// 关键修改:用 updateConfig 替代 init,不重置 postData
|
||||||
|
PhoneImageEngine.updateConfig(newConfig);
|
||||||
var loadIdx = layer.load();
|
var loadIdx = layer.load();
|
||||||
PhoneImageEngine.render().then(function(pages) {
|
PhoneImageEngine.render().then(function(pages) {
|
||||||
layer.close(loadIdx);
|
layer.close(loadIdx);
|
||||||
@@ -264,9 +265,10 @@
|
|||||||
size: $('[name="size"]').val(),
|
size: $('[name="size"]').val(),
|
||||||
fontSize: parseInt($('[name="fontSize"]').val()) || 14,
|
fontSize: parseInt($('[name="fontSize"]').val()) || 14,
|
||||||
watermark: $('[name="watermark"]').val(),
|
watermark: $('[name="watermark"]').val(),
|
||||||
content_html: $('#post-content-html').html()
|
content_html: PhoneImageEngine.getContentHtml()
|
||||||
}, saveConfigUrl).then(function (data) {
|
}, saveConfigUrl).then(function (data) {
|
||||||
if (data.output_id) lastOutputId = data.output_id;
|
if (data.output_id) lastOutputId = data.output_id;
|
||||||
|
$('#post-content-html').html(PhoneImageEngine.getContentHtml());
|
||||||
layer.msg('保存成功');
|
layer.msg('保存成功');
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
layer.msg('保存失败: ' + err);
|
layer.msg('保存失败: ' + err);
|
||||||
@@ -285,11 +287,12 @@
|
|||||||
size: $('[name="size"]').val(),
|
size: $('[name="size"]').val(),
|
||||||
fontSize: parseInt($('[name="fontSize"]').val()) || 14,
|
fontSize: parseInt($('[name="fontSize"]').val()) || 14,
|
||||||
watermark: $('[name="watermark"]').val(),
|
watermark: $('[name="watermark"]').val(),
|
||||||
content_html: $('#post-content-html').html()
|
content_html: PhoneImageEngine.getContentHtml()
|
||||||
}).then(function (data) {
|
}).then(function (data) {
|
||||||
if (data.output_id) {
|
if (data.output_id) {
|
||||||
lastOutputId = data.output_id;
|
lastOutputId = data.output_id;
|
||||||
}
|
}
|
||||||
|
$('#post-content-html').html(PhoneImageEngine.getContentHtml());
|
||||||
layer.msg('保存成功!');
|
layer.msg('保存成功!');
|
||||||
btn.prop('disabled', false).html('<i class="layui-icon layui-icon-picture"></i> 生成并保存');
|
btn.prop('disabled', false).html('<i class="layui-icon layui-icon-picture"></i> 生成并保存');
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
@@ -378,37 +381,45 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cfg = res.data.config || {};
|
var cfg = res.data.config || {};
|
||||||
// 更新表单控件
|
|
||||||
|
// 更新 postData 中的 contentHtml
|
||||||
|
if (res.data.content_html) {
|
||||||
|
postData.contentHtml = res.data.content_html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建历史配置
|
||||||
|
var historyConfig = {};
|
||||||
|
if (cfg.size) historyConfig.size = cfg.size;
|
||||||
|
if (cfg.fontSize || cfg.font_size) historyConfig.fontSize = cfg.fontSize || cfg.font_size;
|
||||||
|
if (cfg.watermark !== undefined) historyConfig.watermark = cfg.watermark;
|
||||||
|
if (cfg.pageAlignments) historyConfig.pageAlignments = cfg.pageAlignments;
|
||||||
|
|
||||||
|
// 同步表单控件
|
||||||
if (cfg.size) {
|
if (cfg.size) {
|
||||||
$('[name="size"]').val(cfg.size);
|
$('[name="size"]').val(cfg.size);
|
||||||
form.render('select');
|
form.render('select');
|
||||||
}
|
}
|
||||||
if (cfg.fontSize || cfg.font_size) {
|
if (historyConfig.fontSize) {
|
||||||
var fs = cfg.fontSize || cfg.font_size;
|
$('[name="fontSize"]').val(historyConfig.fontSize);
|
||||||
$('[name="fontSize"]').val(fs);
|
$('#fontSizeValue').text(historyConfig.fontSize + 'px');
|
||||||
$('#fontSizeValue').text(fs + 'px');
|
|
||||||
}
|
}
|
||||||
if (cfg.watermark !== undefined) {
|
if (cfg.watermark !== undefined) {
|
||||||
$('[name="watermark"]').val(cfg.watermark);
|
$('[name="watermark"]').val(cfg.watermark);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新内容HTML(如果有保存)
|
lastOutputId = outputId;
|
||||||
if (res.data.content_html) {
|
|
||||||
postData.contentHtml = res.data.content_html;
|
|
||||||
$('#post-content-html').html(res.data.content_html);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭历史弹窗
|
|
||||||
layer.closeAll();
|
layer.closeAll();
|
||||||
|
|
||||||
// 重新初始化引擎并渲染(恢复pageAlignments)
|
// 全量初始化引擎(加载历史是新内容,需要完整初始化)
|
||||||
lastOutputId = outputId;
|
PhoneImageEngine.init(postData, historyConfig);
|
||||||
var renderConfig = {};
|
var loadIdx3 = layer.load();
|
||||||
if (cfg.pageAlignments) {
|
PhoneImageEngine.render().then(function(pages) {
|
||||||
renderConfig.pageAlignments = cfg.pageAlignments;
|
layer.close(loadIdx3);
|
||||||
}
|
layer.msg('已加载历史配置,共 ' + pages.length + ' 页');
|
||||||
doRender(renderConfig);
|
}).catch(function(err) {
|
||||||
layer.msg('已加载历史配置');
|
layer.close(loadIdx3);
|
||||||
|
if (err !== 'rendering') layer.msg('渲染失败: ' + err);
|
||||||
|
});
|
||||||
}).fail(function () {
|
}).fail(function () {
|
||||||
layer.close(loadIdx2);
|
layer.close(loadIdx2);
|
||||||
layer.msg('加载历史配置失败');
|
layer.msg('加载历史配置失败');
|
||||||
|
|||||||
Reference in New Issue
Block a user