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>"
)