支持段落删除与移动排序,修复导出残留与外键约束

- 前端:段落列表/配置区/手动编辑区新增上移、下移、删除按钮,hover 显示
- 前端:移动和删除操作后自动保存到后端,修正 canDeleteBlock 判定逻辑
- 后端:保存段落时级联删除关联的 generation_logs 再删段落
- 后端:导出时清理未被引用的标题段落及其内容,避免已删段落残留在 Word 中
- 后端:删除模板时级联清理 generation_logs/documents/paragraphs
This commit is contained in:
zwt13703
2026-07-03 17:13:02 +08:00
parent a796301ae8
commit 1369d87afb
3 changed files with 208 additions and 7 deletions
+49
View File
@@ -32,6 +32,43 @@ def _delete_block(block):
parent.remove(element)
def _delete_heading_section(heading: Paragraph):
blocks = [heading]
current = heading._element.getnext()
while current is not None:
if isinstance(current, CT_P):
para = Paragraph(current, heading._parent)
if _is_heading(para):
break
blocks.append(para)
elif isinstance(current, CT_Tbl):
blocks.append(Table(current, heading._parent))
current = current.getnext()
for block in blocks:
_delete_block(block)
def _remove_unreferenced_headings(document: DocumentObject, referenced_anchors: set[str]):
headings_to_remove: list[Paragraph] = []
found_first_heading = False
pre_heading_blocks: list = []
print(f"[EXPORT] referenced_anchors: {referenced_anchors}")
for block in _iter_block_items(document):
if isinstance(block, Paragraph) and _is_heading(block):
found_first_heading = True
text = block.text.strip()
if text not in referenced_anchors:
print(f"[EXPORT] Unreferenced heading found, will remove: '{text}'")
headings_to_remove.append(block)
elif not found_first_heading:
pre_heading_blocks.append(block)
for heading in headings_to_remove:
_delete_heading_section(heading)
if not referenced_anchors:
for block in pre_heading_blocks:
_delete_block(block)
def _clear_paragraph(paragraph: Paragraph):
element = paragraph._element
for child in list(element):
@@ -332,10 +369,22 @@ def _replace_section_group(
def export_document_bytes(template_bytes: bytes, logs: list[dict]) -> bytes:
document = Document(BytesIO(template_bytes))
referenced_anchors: set[str] = set()
for item in logs:
for key in ("anchor_title", "title"):
val = (item.get(key) or "").strip()
if val:
referenced_anchors.add(val)
print(f"[EXPORT] logs count={len(logs)}, anchor_titles={[(l.get('anchor_title'), l.get('title')) for l in logs]}")
last_heading_element = None
for group in _group_logs(logs):
last_heading_element = _replace_section_group(document, group, last_heading_element)
_remove_unreferenced_headings(document, referenced_anchors)
output = BytesIO()
document.save(output)
return output.getvalue()