feat: generate doc preview html

This commit is contained in:
zwt13703
2026-07-01 20:30:34 +08:00
parent 389b9c0c92
commit 611e089c9e
3 changed files with 78 additions and 11 deletions
+62 -7
View File
@@ -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 = ['<div class="doc-preview-content">']
html_parts = [f'<div class="doc-preview-content" style="{PAGE_STYLE}">']
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'<h2 data-block-id="{block_id}" style="font-size:16px;font-weight:600;margin:20px 0 10px;padding-bottom:6px;border-bottom:2px solid #1a1d24">{text}</h2>'
html = render_heading(block_id, text)
elif block_type == "table":
html = f'<div data-block-id="{block_id}" style="margin:12px 0;padding:8px;background:#f7f8fa;border:1px dashed #ccc;border-radius:4px;color:#5b626e">📊 {text[:80]}</div>'
html = render_table(block)
else:
html = f'<p data-block-id="{block_id}" style="text-indent:2em;margin:8px 0;line-height:1.8;text-align:justify">{text}</p>'
html = render_paragraph(block_id, text)
html_parts.append(html)
html_parts.append('</div>')
return "\n".join(html_parts)
def render_heading(block_id: str, text: str) -> str:
return f'<h2 data-block-id="{escape(block_id)}" style="{HEADING_STYLE}">{escape(text)}</h2>'
def render_paragraph(block_id: str, text: str) -> str:
return f'<p data-block-id="{escape(block_id)}" style="{PARAGRAPH_STYLE}">{escape(text)}</p>'
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'<th style="{TH_STYLE}">{header}</th>' for header in headers[:cols])
if len(headers) < cols:
header_html += "".join(f'<th style="{TH_STYLE}"></th>' for _ in range(cols - len(headers)))
row_html = "".join(f'<td style="{TD_STYLE}"></td>' for _ in range(cols))
body_html = "\n".join(f"<tr>{row_html}</tr>" for _ in range(body_rows))
return (
f'<table data-block-id="{block_id}" style="{TABLE_STYLE}">'
f"<thead><tr>{header_html}</tr></thead>"
f"<tbody>{body_html}</tbody>"
"</table>"
)
@@ -40,10 +40,10 @@
| 024 | 实现层级构建(parent_block_id+ 结构树 tree 输出 | 后端 | 0.25d | ✅ 已完成 |
| 025 | 将解析结果批量写入 template_block 表 | 后端 | 0.25d | ✅ 已完成 |
| | **HTML 预览生成** | | | |
| 026 | 标题 → `<h2 data-block-id>` 转换 + 居中加粗样式 | 后端 | 0.25d | ⏳ 未完成 |
| 027 | 段落 → `<p data-block-id>` 转换 + 首行缩进样式 | 后端 | 0.25d | ⏳ 未完成 |
| 028 | 表格 → `<table data-block-id>` 转换 + 边框样式 | 后端 | 0.25d | ⏳ 未完成 |
| 029 | 拼接完整 HTML 字符串(带内联 CSS | 后端 | 0.25d | ⏳ 未完成 |
| 026 | 标题 → `<h2 data-block-id>` 转换 + 居中加粗样式 | 后端 | 0.25d | ✅ 已完成 |
| 027 | 段落 → `<p data-block-id>` 转换 + 首行缩进样式 | 后端 | 0.25d | ✅ 已完成 |
| 028 | 表格 → `<table data-block-id>` 转换 + 边框样式 | 后端 | 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 | ⏳ 未完成 |
+12
View File
@@ -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. 标题输出为 `<h2 data-block-id>`,支持居中、加粗和下边框样式。
3. 段落输出为 `<p data-block-id>`,支持首行缩进和两端对齐。
4. 表格输出为真实 `<table data-block-id>`,包含表头、单元格边框和占位网格。
5. 增加 HTML 转义,避免 Word 文本中的特殊字符破坏预览结构。
6. 使用示例 blocks 验证标题、段落、表格 HTML 均生成正确。
- **执行结果**: 完成任务 026-029,HTML 预览生成服务已满足基础预览要求。