diff --git a/backend/app/services/html_generator.py b/backend/app/services/html_generator.py index b95942c..1bc6c8f 100644 --- a/backend/app/services/html_generator.py +++ b/backend/app/services/html_generator.py @@ -1,5 +1,25 @@ """HTML 预览生成服务""" -from typing import List, Dict, Any +from html import escape +from typing import Any, Dict, List + + +PAGE_STYLE = ( + "width:794px;min-height:1123px;margin:0 auto;background:#fff;" + "padding:72px 64px;box-sizing:border-box;color:#1a1d24;" + "font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;" + "font-size:14px;line-height:1.8;" +) + +HEADING_STYLE = ( + "font-size:16px;font-weight:700;text-align:center;margin:24px 0 14px;" + "padding-bottom:6px;border-bottom:2px solid #1a1d24;" +) + +PARAGRAPH_STYLE = "text-indent:2em;margin:8px 0;line-height:1.8;text-align:justify;" + +TABLE_STYLE = "width:100%;border-collapse:collapse;margin:14px 0;font-size:13px;" +TH_STYLE = "padding:7px 10px;border:1px solid #d8dbe2;background:#f0f1f3;text-align:left;font-weight:600;" +TD_STYLE = "padding:7px 10px;border:1px solid #d8dbe2;text-align:left;min-height:24px;" def generate_preview_html(blocks: List[Dict[str, Any]]) -> str: @@ -7,21 +27,56 @@ def generate_preview_html(blocks: List[Dict[str, Any]]) -> str: 将 block 列表转为带 data-block-id 的 HTML 预览字符串。 每个元素都会带上 data-block-id 属性供前端点击交互。 """ - html_parts = ['
'] + html_parts = [f'
'] for block in blocks: block_id = block.get("block_id", "") - block_type = block.get("type", "") - text = block.get("text", "") + block_type = block.get("type") or block.get("block_type", "") + text = block.get("text") or block.get("text_preview") or "" if block_type == "heading": - html = f'

{text}

' + html = render_heading(block_id, text) elif block_type == "table": - html = f'
📊 {text[:80]}
' + html = render_table(block) else: - html = f'

{text}

' + html = render_paragraph(block_id, text) html_parts.append(html) html_parts.append('
') return "\n".join(html_parts) + + +def render_heading(block_id: str, text: str) -> str: + return f'

{escape(text)}

' + + +def render_paragraph(block_id: str, text: str) -> str: + return f'

{escape(text)}

' + + +def render_table(block: Dict[str, Any]) -> str: + block_id = escape(block.get("block_id", "")) + headers = [escape(str(header)) for header in block.get("headers", []) if str(header).strip()] + rows = int(block.get("rows") or block.get("table_rows") or 1) + cols = int(block.get("cols") or block.get("table_cols") or max(len(headers), 1)) + cols = max(cols, len(headers), 1) + body_rows = max(rows - 1, 1) + + if not headers: + text = block.get("text") or block.get("text_preview") or "表格" + headers = [escape(str(text))] + ["" for _ in range(cols - 1)] + + header_html = "".join(f'{header}' for header in headers[:cols]) + if len(headers) < cols: + header_html += "".join(f'' for _ in range(cols - len(headers))) + + row_html = "".join(f'' for _ in range(cols)) + body_html = "\n".join(f"{row_html}" for _ in range(body_rows)) + + return ( + f'' + f"{header_html}" + f"{body_html}" + "
" + ) diff --git a/docs/AI文档模板系统-像素级任务清单-20260701.md b/docs/AI文档模板系统-像素级任务清单-20260701.md index 89c70f8..0e190f0 100644 --- a/docs/AI文档模板系统-像素级任务清单-20260701.md +++ b/docs/AI文档模板系统-像素级任务清单-20260701.md @@ -40,10 +40,10 @@ | 024 | 实现层级构建(parent_block_id)+ 结构树 tree 输出 | 后端 | 0.25d | ✅ 已完成 | | 025 | 将解析结果批量写入 template_block 表 | 后端 | 0.25d | ✅ 已完成 | | | **HTML 预览生成** | | | | -| 026 | 标题 → `

` 转换 + 居中加粗样式 | 后端 | 0.25d | ⏳ 未完成 | -| 027 | 段落 → `

` 转换 + 首行缩进样式 | 后端 | 0.25d | ⏳ 未完成 | -| 028 | 表格 → `` 转换 + 边框样式 | 后端 | 0.25d | ⏳ 未完成 | -| 029 | 拼接完整 HTML 字符串(带内联 CSS) | 后端 | 0.25d | ⏳ 未完成 | +| 026 | 标题 → `

` 转换 + 居中加粗样式 | 后端 | 0.25d | ✅ 已完成 | +| 027 | 段落 → `

` 转换 + 首行缩进样式 | 后端 | 0.25d | ✅ 已完成 | +| 028 | 表格 → `

` 转换 + 边框样式 | 后端 | 0.25d | ✅ 已完成 | +| 029 | 拼接完整 HTML 字符串(带内联 CSS) | 后端 | 0.25d | ✅ 已完成 | | | **查询 + 配置接口** | | | | | 030 | 实现 `GET /api/templates/{id}` 组装全部数据 | 后端 | 0.5d | ⏳ 未完成 | | 031 | 实现 `POST /api/templates/{id}/blocks/{blockId}/config` upsert | 后端 | 0.25d | ⏳ 未完成 | diff --git a/docs/tasks/task_detail_2026_07_01.md b/docs/tasks/task_detail_2026_07_01.md index 6edeea9..23faf7d 100644 --- a/docs/tasks/task_detail_2026_07_01.md +++ b/docs/tasks/task_detail_2026_07_01.md @@ -35,3 +35,15 @@ 5. 新增 `save_template_blocks`,上传模板后批量写入 `template_block`。 6. 使用临时 docx 验证标题、段落、表格混排时的顺序和父子关系。 - **执行结果**: 完成任务 019-025,上传模板时会解析 Word 并保存模板区域块。 + +## 会话 ID: 20260701-preview-html +- [2026-07-01 20:30:13] +- **执行原因**: 按任务清单继续完成 HTML 预览生成阶段 026-029。 +- **执行过程**: + 1. 重构 `html_generator.py`,为预览容器、标题、段落、表格定义内联样式。 + 2. 标题输出为 `

`,支持居中、加粗和下边框样式。 + 3. 段落输出为 `

`,支持首行缩进和两端对齐。 + 4. 表格输出为真实 `

`,包含表头、单元格边框和占位网格。 + 5. 增加 HTML 转义,避免 Word 文本中的特殊字符破坏预览结构。 + 6. 使用示例 blocks 验证标题、段落、表格 HTML 均生成正确。 +- **执行结果**: 完成任务 026-029,HTML 预览生成服务已满足基础预览要求。