新增:完全代码创建word表格,设置宽度、字体

This commit is contained in:
2026-02-03 16:50:58 +08:00
parent 4a1881bf32
commit 48a0fad7e4
43 changed files with 672 additions and 41 deletions

View File

@@ -1,13 +1,19 @@
import base64
import io
from typing import Any
from datetime import datetime
from docx.shared import Mm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from ninja.errors import HttpError
from ninja_extra import ControllerBase, api_controller, route
from django.db import transaction
from django.db.models import Q
from docxtpl import DocxTemplate
from docxtpl import DocxTemplate, InlineImage
from pathlib import Path
from utils.chen_response import ChenResponse
# 导入数据库ORM
from apps.project.models import Project, Contact, Abbreviation
from apps.project.models import Project, Contact, Abbreviation, ProjectSoftSummary
from apps.dict.models import Dict
# 导入工具函数
from utils.util import get_str_dict, get_list_dict, get_testType, get_ident, get_str_abbr
@@ -23,7 +29,7 @@ from apps.createSeiTaiDocument.extensions.logger import GenerateLogger
# 导入mixins-处理文档片段
from apps.createDocument.extensions.mixins import FragementToolsMixin
# 导入工具
from apps.createDocument.extensions.tools import demand_sort_by_designKey
from apps.createDocument.extensions.tools import demand_sort_by_designKey, set_table_border
# @api_controller("/generate", tags=['生成大纲文档'], auth=JWTAuth(), permissions=[IsAuthenticated])
@api_controller("/generate", tags=['生成大纲文档'])
@@ -286,6 +292,62 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
@route.get('/create/softComposition', url_name='create-softComposition')
@transaction.atomic
def create_softComposition(self, id: int):
# 首先判断是否包含 - 项目信息-软件概述
project_obj = get_object_or_404(Project, id=id)
input_path_2 = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '测评对象_2.docx'
doc = DocxTemplate(input_path_2)
soft_summary_qs = ProjectSoftSummary.objects.filter(project=project_obj)
if soft_summary_qs.exists():
data_qs = soft_summary_qs.first().data_schemas
if data_qs.exists():
# 如果存在则渲染此处
data_list = []
for data_obj in data_qs.all():
item_context: dict[str, Any] = {"fontnote": data_obj.fontnote, 'type': data_obj.type}
# 根据数据类型处理content字段
if data_obj.type == 'text':
item_context['content'] = data_obj.content
elif data_obj.type == 'table':
# 使用subdoc
subdoc = doc.new_subdoc()
rows = len(data_obj.content)
cols = len(data_obj.content[0])
table = subdoc.add_table(rows=rows, cols=cols, style='Table Grid')
# 设置边框
set_table_border(table)
# 单元格处理
for row in range(rows):
for col in range(cols):
cell = table.cell(row, col)
cell.text = data_obj.content[row][col]
# 第一行设置居中
if row == 0:
# 黑体设置
cell.text = ""
pa = cell.paragraphs[0]
run = pa.add_run(str(data_obj.content[row][col]))
run.font.name = '黑体'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
run.font.bold = False
pa.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 表格居中
table.alignment = WD_ALIGN_PARAGRAPH.CENTER
item_context['content'] = subdoc
elif data_obj.type == 'image':
base64_bytes = base64.b64decode(data_obj.content.replace("data:image/png;base64,", ""))
item_context['content'] = InlineImage(doc, io.BytesIO(base64_bytes), width=Mm(120))
data_list.append(item_context)
context = {
"datas": data_list,
}
doc.render(context)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '测评对象.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
# 原来文档片段或者初始内容
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '测评对象.docx'
doc = DocxTemplate(input_path)
replace, frag, rich_text_list = self._generate_frag(id, doc, '测评对象')

View File

@@ -1,4 +1,6 @@
from apps.project.models import TestDemand
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
def demand_sort_by_designKey(demand_obj: TestDemand) -> tuple[int, ...]:
"""仅限于测试项排序函数传入sorted函数的key里面"""
@@ -6,4 +8,32 @@ def demand_sort_by_designKey(demand_obj: TestDemand) -> tuple[int, ...]:
sort_tuple = tuple(int(part) for part in parts)
return sort_tuple
__all__ = ['demand_sort_by_designKey']
def set_table_border(table, **kwargs):
"""docx-设置表格上下左右边框"""
# 获取或创建表格属性
tbl_pr = table._tbl.tblPr
# 查找并移除现有的边框设置
existing_borders = tbl_pr.find(qn('w:tblBorders'))
if existing_borders is not None:
tbl_pr.remove(existing_borders)
# 创建新的边框元素
borders = OxmlElement('w:tblBorders')
# 只设置外边框top, left, bottom, right
# 不设置 insideV 和 insideH内部边框
for border_type in ['top', 'left', 'bottom', 'right']:
border_data = kwargs.get(border_type, {"sz": "12", "val": "single", "color": "#000000"})
border_elem = OxmlElement(f'w:{border_type}')
# 设置边框属性
border_elem.set(qn('w:val'), border_data.get('val', 'single')) # 线条类型
border_elem.set(qn('w:sz'), border_data.get('sz', '12')) # 线条粗细8代表1磅
border_elem.set(qn('w:color'), border_data.get('color', '#000000')) # 颜色
borders.append(border_elem) # type:ignore
# 将边框设置添加到表格属性中
tbl_pr.append(borders)
__all__ = ['demand_sort_by_designKey', 'set_table_border']