新增:静态、动态环境内容
This commit is contained in:
Binary file not shown.
@@ -4,16 +4,18 @@ from typing import Any
|
||||
from datetime import datetime
|
||||
from docx.shared import Mm
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_ALIGN_VERTICAL
|
||||
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, InlineImage
|
||||
from docxtpl import DocxTemplate, InlineImage, Subdoc
|
||||
from pathlib import Path
|
||||
from utils.chen_response import ChenResponse
|
||||
# 导入数据库ORM
|
||||
from apps.project.models import Project, Contact, Abbreviation, ProjectSoftSummary, StuctSortData
|
||||
from apps.project.models import Project, Contact, Abbreviation, ProjectSoftSummary, StuctSortData, StaticSoftItem, StaticSoftHardware, \
|
||||
DynamicSoftTable, DynamicHardwareTable
|
||||
from apps.dict.models import Dict
|
||||
# 导入工具函数
|
||||
from utils.util import get_str_dict, get_list_dict, get_testType, get_ident, get_str_abbr
|
||||
@@ -29,7 +31,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, set_table_border
|
||||
from apps.createDocument.extensions.tools import demand_sort_by_designKey, set_table_border_by_cell_position, set_cell_margins
|
||||
|
||||
# @api_controller("/generate", tags=['生成大纲文档'], auth=JWTAuth(), permissions=[IsAuthenticated])
|
||||
@api_controller("/generate", tags=['生成大纲文档'])
|
||||
@@ -312,9 +314,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
|
||||
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)
|
||||
table = subdoc.add_table(rows=rows, cols=cols)
|
||||
# 单元格处理
|
||||
for row in range(rows):
|
||||
for col in range(cols):
|
||||
@@ -330,8 +330,12 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
|
||||
run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
|
||||
run.font.bold = False
|
||||
pa.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
# 垂直居中
|
||||
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
|
||||
# 表格居中
|
||||
table.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
# 设置边框
|
||||
set_table_border_by_cell_position(table)
|
||||
item_context['content'] = subdoc
|
||||
elif data_obj.type == 'image':
|
||||
base64_bytes = base64.b64decode(data_obj.content.replace("data:image/png;base64,", ""))
|
||||
@@ -451,9 +455,85 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
|
||||
except PermissionError as e:
|
||||
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
|
||||
|
||||
# 通用生成静态软件项、静态硬件项、动态软件项、动态硬件信息的context,包含fontnote和table
|
||||
@classmethod
|
||||
def create_table_context(cls, table_data: list[list[str]], doc: DocxTemplate) -> Subdoc:
|
||||
"""注意:该函数会增加一列序号列"""
|
||||
subdoc = doc.new_subdoc()
|
||||
rows = len(table_data)
|
||||
cols = len(table_data[0]) + 1 # 多渲染序号列
|
||||
table = subdoc.add_table(rows=rows, cols=cols)
|
||||
# 单元格处理
|
||||
for row in range(rows):
|
||||
for col in range(cols):
|
||||
cell = table.cell(row, col) # 单元格数据
|
||||
# 设置边距 - 所有单元格
|
||||
set_cell_margins(cell, left=100, right=100, top=100, bottom=100)
|
||||
pa = cell.paragraphs[0]
|
||||
# 处理第一列 - 要居中
|
||||
if col == 0:
|
||||
if row == 0:
|
||||
cell.text = "序号"
|
||||
else:
|
||||
cell.text = str(row)
|
||||
pa.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
# 处理非第一列
|
||||
else:
|
||||
cell.text = table_data[row][col - 1]
|
||||
# 单独处理第一行
|
||||
for col in range(cols):
|
||||
cell = table.cell(0, col)
|
||||
cell.text = ""
|
||||
pa = cell.paragraphs[0]
|
||||
if col == 0:
|
||||
run = pa.add_run("序号")
|
||||
else:
|
||||
run = pa.add_run(str(table_data[0][col - 1]))
|
||||
run.font.name = '黑体'
|
||||
run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
|
||||
run.font.bold = False
|
||||
pa.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
# 设置序号列宽度 - 先自动调整为False然后设置True
|
||||
for cell in table.columns[0].cells:
|
||||
cell.width = Mm(15)
|
||||
pa = cell.paragraphs[0]
|
||||
pa.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
# 表格居中
|
||||
table.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
# 最后设置表格外边框
|
||||
set_table_border_by_cell_position(table)
|
||||
return subdoc
|
||||
|
||||
# 统一静态软件项、静态硬件项、动态软件项、动态硬件信息的word生成 - 模版模式
|
||||
@classmethod
|
||||
def uniform_static_dynamic_response(cls, id: int, filename: str, r_filename: str, model) -> ChenResponse | None:
|
||||
project_obj = get_object_or_404(Project, id=id)
|
||||
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / filename
|
||||
doc = DocxTemplate(input_path)
|
||||
qs = model.objects.filter(project=project_obj)
|
||||
if qs.exists():
|
||||
obj = qs.first()
|
||||
table_data = obj.table
|
||||
subdoc = cls.create_table_context(table_data, doc)
|
||||
context = {
|
||||
'fontnote': obj.fontnote,
|
||||
'table': subdoc,
|
||||
}
|
||||
doc.render(context, autoescape=True)
|
||||
try:
|
||||
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / r_filename)
|
||||
return ChenResponse(status=200, code=200, message="文档生成成功!")
|
||||
except PermissionError as e:
|
||||
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
|
||||
return None
|
||||
|
||||
# 静态软件项
|
||||
@route.get('/create/static_soft', url_name='create-static_soft')
|
||||
def create_static_soft(self, id: int):
|
||||
res = self.uniform_static_dynamic_response(id, '静态软件项_2.docx', '静态软件项.docx', StaticSoftItem)
|
||||
if res is not None:
|
||||
return res
|
||||
|
||||
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, '静态软件项')
|
||||
@@ -466,6 +546,10 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
|
||||
# 静态硬件和固件项
|
||||
@route.get('/create/static_hard', url_name='create-static_hard')
|
||||
def create_static_hard(self, id: int):
|
||||
res = self.uniform_static_dynamic_response(id, '静态硬件和固件项_2.docx', '静态硬件和固件项.docx', StaticSoftHardware)
|
||||
if res is not None:
|
||||
return res
|
||||
|
||||
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, '静态硬件和固件项')
|
||||
@@ -497,6 +581,10 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
|
||||
# 动态软件项
|
||||
@route.get('/create/dynamic_soft', url_name='create-dynamic_soft')
|
||||
def create_dynamic_soft(self, id: int):
|
||||
res = self.uniform_static_dynamic_response(id, '动态软件项_2.docx', '动态软件项.docx', DynamicSoftTable)
|
||||
if res is not None:
|
||||
return res
|
||||
|
||||
project_obj: Project = get_object_or_404(Project, id=id)
|
||||
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '动态软件项.docx'
|
||||
doc = DocxTemplate(input_path)
|
||||
@@ -508,9 +596,14 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
|
||||
}
|
||||
return create_dg_docx("动态软件项.docx", context, id)
|
||||
|
||||
# 动态软件项
|
||||
# 动态硬件项
|
||||
@route.get('/create/dynamic_hard', url_name='create-dynamic_hard')
|
||||
def create_dynamic_hard(self, id: int):
|
||||
res = self.uniform_static_dynamic_response(id, '动态硬件和固件项_2.docx',
|
||||
'动态硬件和固件项.docx', DynamicHardwareTable)
|
||||
if res is not None:
|
||||
return res
|
||||
|
||||
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, '动态硬件和固件项')
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
from apps.project.models import TestDemand
|
||||
from docx.oxml import OxmlElement
|
||||
from docx.oxml.ns import qn
|
||||
from docx.table import _Cell, Table
|
||||
|
||||
def demand_sort_by_designKey(demand_obj: TestDemand) -> tuple[int, ...]:
|
||||
"""仅限于测试项排序函数,传入sorted函数的key里面"""
|
||||
@@ -8,6 +9,30 @@ def demand_sort_by_designKey(demand_obj: TestDemand) -> tuple[int, ...]:
|
||||
sort_tuple = tuple(int(part) for part in parts)
|
||||
return sort_tuple
|
||||
|
||||
# 传入cell设置边框
|
||||
def set_cell_border(cell: _Cell, **kwargs):
|
||||
tc = cell._tc
|
||||
tcPr = tc.get_or_add_tcPr()
|
||||
|
||||
# 检查标签是否存在,如果没有找到,则创建一个
|
||||
tcBorders = tcPr.first_child_found_in("w:tcBorders")
|
||||
if tcBorders is None:
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
tcPr.append(tcBorders)
|
||||
|
||||
for border_type in ['left', 'top', 'right', 'bottom']:
|
||||
# 设置为固定的“黑色加粗”
|
||||
border_data = kwargs.get(border_type, {"sz": "6", "val": "single", "color": "#000000", "space": "0"})
|
||||
tag = 'w:{}'.format(border_type)
|
||||
element = tcBorders.find(qn(tag))
|
||||
if element is None:
|
||||
element = OxmlElement(tag)
|
||||
tcBorders.append(element)
|
||||
for key in ["sz", "val", "color", "space", "shadow"]:
|
||||
if key in border_data:
|
||||
element.set(qn('w:{}'.format(key)), str(border_data[key]))
|
||||
|
||||
# 弃用,请使用下面函数
|
||||
def set_table_border(table, **kwargs):
|
||||
"""docx-设置表格上下左右边框"""
|
||||
# 获取或创建表格属性
|
||||
@@ -21,7 +46,7 @@ def set_table_border(table, **kwargs):
|
||||
# 创建新的边框元素
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
|
||||
# 只设置外边框:top, left, bottom, right
|
||||
# 只设置外边框: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"})
|
||||
@@ -31,9 +56,87 @@ def set_table_border(table, **kwargs):
|
||||
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
|
||||
borders.append(border_elem) # type:ignore
|
||||
|
||||
# 将边框设置添加到表格属性中
|
||||
tbl_pr.append(borders)
|
||||
|
||||
__all__ = ['demand_sort_by_designKey', 'set_table_border']
|
||||
# ~~~新解决方案:传入table对象,遍历cell,判断cell是否在外层~~~
|
||||
def set_table_border_by_cell_position(table: Table):
|
||||
"""
|
||||
智能设置表格边框:外边框粗,内边框细。
|
||||
"""
|
||||
# 获取表格的总行数和总列数
|
||||
total_rows = len(table.rows)
|
||||
total_cols = len(table.columns)
|
||||
|
||||
for row_idx, row in enumerate(table.rows):
|
||||
for col_idx, cell in enumerate(row.cells):
|
||||
# 初始化边框参数字典
|
||||
border_kwargs = {}
|
||||
|
||||
# 1. 判断上边框:如果是第一行,则设置粗上边框,否则不设置(由上一行的下边框决定,或单独设置细线)
|
||||
if row_idx == 0:
|
||||
border_kwargs['top'] = {"sz": "12", "val": "single", "color": "#000000"}
|
||||
# 2. 判断下边框:如果是最后一行,则设置粗下边框
|
||||
if row_idx == total_rows - 1:
|
||||
border_kwargs['bottom'] = {"sz": "12", "val": "single", "color": "#000000"}
|
||||
# 3. 判断左边框:如果是第一列,则设置粗左边框
|
||||
if col_idx == 0:
|
||||
border_kwargs['left'] = {"sz": "12", "val": "single", "color": "#000000"}
|
||||
# 4. 判断右边框:如果是最后一列,则设置粗右边框
|
||||
if col_idx == total_cols - 1:
|
||||
border_kwargs['right'] = {"sz": "12", "val": "single", "color": "#000000"}
|
||||
|
||||
# 5. 设置内部网格线(细线)
|
||||
# 内部横线 (insideH): 所有单元格都需要,但最后一行不需要(已经是外边框)
|
||||
if row_idx < total_rows - 1:
|
||||
border_kwargs['insideH'] = {"sz": "6", "val": "single", "color": "#000000"}
|
||||
# 内部竖线 (insideV): 所有单元格都需要,但最后一列不需要(已经是外边框)
|
||||
if col_idx < total_cols - 1:
|
||||
border_kwargs['insideV'] = {"sz": "6", "val": "single", "color": "#000000"}
|
||||
|
||||
# 调用您已有的 set_cell_border 函数
|
||||
set_cell_border(cell, **border_kwargs)
|
||||
|
||||
# 设置cell的左右边距
|
||||
def set_cell_margins(cell: _Cell, **kwargs):
|
||||
"""
|
||||
设置单元格边距,确保在Office和WPS中均能生效。
|
||||
参数示例: set_cell_margins(cell, left=50, right=50, top=100, bottom=100)
|
||||
参数单位: 为二十分之一磅 (dxa, 1/1440英寸)。
|
||||
"""
|
||||
tc = cell._tc
|
||||
tcPr = tc.get_or_add_tcPr()
|
||||
|
||||
# 关键步骤1:检查或创建 w:tcMar 元素
|
||||
tcMar = tcPr.find(qn('w:tcMar'))
|
||||
if tcMar is None:
|
||||
tcMar = OxmlElement('w:tcMar')
|
||||
tcPr.append(tcMar)
|
||||
|
||||
# 关键步骤2:为每个指定的边距方向创建元素,并同时设置新旧两套属性以保证兼容性[2](@ref)
|
||||
# 定义映射:我们的参数名 -> (XML元素名, 备用的XML元素名)
|
||||
margin_map = {
|
||||
'left': ('left', 'start'),
|
||||
'right': ('right', 'end'),
|
||||
'top': ('top', None),
|
||||
'bottom': ('bottom', None)
|
||||
}
|
||||
|
||||
for margin_key, value in kwargs.items():
|
||||
if margin_key in margin_map:
|
||||
primary_tag, alternate_tag = margin_map[margin_key]
|
||||
tags_to_set = [primary_tag]
|
||||
if alternate_tag: # 如果存在备选标签(如left/start),则同时设置
|
||||
tags_to_set.append(alternate_tag)
|
||||
|
||||
for tag in tags_to_set:
|
||||
# 检查该边距元素是否已存在
|
||||
margin_element = tcMar.find(qn(f'w:{tag}'))
|
||||
if margin_element is None:
|
||||
margin_element = OxmlElement(f'w:{tag}')
|
||||
tcMar.append(margin_element) # type:ignore
|
||||
# 设置边距值和单位类型
|
||||
margin_element.set(qn('w:w'), str(value))
|
||||
margin_element.set(qn('w:type'), 'dxa')
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -14,9 +14,10 @@ from ninja.errors import HttpError
|
||||
from ninja import Query
|
||||
from utils.chen_response import ChenResponse
|
||||
from utils.chen_crud import create, multi_delete_project
|
||||
from apps.project.models import Project, Round, ProjectSoftSummary, StuctSortData
|
||||
from apps.project.models import Project, Round, ProjectSoftSummary, StuctSortData, StaticSoftItem, StaticSoftHardware, DynamicSoftTable, \
|
||||
DynamicHardwareTable
|
||||
from apps.project.schemas.project import ProjectRetrieveSchema, ProjectFilterSchema, ProjectCreateInput, \
|
||||
DeleteSchema, SoftSummarySchema, DataSchema
|
||||
DeleteSchema, SoftSummarySchema, DataSchema, StaticDynamicData
|
||||
from utils.util import get_str_dict
|
||||
# 时间处理模块
|
||||
from apps.project.tool.timeList import time_return_to
|
||||
@@ -277,6 +278,10 @@ class ProjectController(ControllerBase):
|
||||
all_status = {
|
||||
"soft_summary": False,
|
||||
"interface_image": False,
|
||||
"static_soft_item": False,
|
||||
"static_soft_hardware": False,
|
||||
"dynamic_soft_item": False,
|
||||
"dynamic_soft_hardware": False,
|
||||
}
|
||||
# 1.查看软件概述是否填写
|
||||
project_obj = self.get_project_by_id(id)
|
||||
@@ -289,6 +294,22 @@ class ProjectController(ControllerBase):
|
||||
image_qs = StuctSortData.objects.filter(project=project_obj)
|
||||
if image_qs.exists():
|
||||
all_status['interface_image'] = True
|
||||
# 3.查看静态软件项是否填写
|
||||
static_item_qs = StaticSoftItem.objects.filter(project=project_obj)
|
||||
if static_item_qs.exists():
|
||||
all_status['static_soft_item'] = True
|
||||
# 4.静态硬件项
|
||||
static_hardware_qs = StaticSoftHardware.objects.filter(project=project_obj)
|
||||
if static_hardware_qs.exists():
|
||||
all_status['static_soft_hardware'] = True
|
||||
# 5.动态软件项
|
||||
dynamic_soft_item_qs = DynamicSoftTable.objects.filter(project=project_obj)
|
||||
if dynamic_soft_item_qs.exists():
|
||||
all_status['dynamic_soft_item'] = True
|
||||
# 5.动态软件项
|
||||
dynamic_hardware_qs = DynamicHardwareTable.objects.filter(project=project_obj)
|
||||
if dynamic_hardware_qs.exists():
|
||||
all_status['dynamic_soft_hardware'] = True
|
||||
return ChenResponse(status=200, code=20000, data=all_status, message='查询成功')
|
||||
|
||||
@classmethod
|
||||
@@ -351,7 +372,7 @@ class ProjectController(ControllerBase):
|
||||
"content": item.content,
|
||||
"fontnote": item.fontnote,
|
||||
} for item in dataSchem_qs])
|
||||
return ChenResponse(status=200, code=25002, data=None)
|
||||
return ChenResponse(status=200, code=25002, data=[])
|
||||
|
||||
# ~~~接口图新增或修改~~~
|
||||
@route.post("/interface_image/")
|
||||
@@ -361,9 +382,7 @@ class ProjectController(ControllerBase):
|
||||
image_qs = StuctSortData.objects.filter(project=project_obj)
|
||||
if image_qs.exists():
|
||||
image_qs.delete()
|
||||
self.bulk_create_data_schemas(project_obj, [dataSchema])
|
||||
else:
|
||||
self.bulk_create_data_schemas(project_obj, [dataSchema])
|
||||
self.bulk_create_data_schemas(project_obj, [dataSchema])
|
||||
|
||||
# ~~~接口图-获取数据~~~
|
||||
@route.get("/get_interface_image/", response=DataSchema)
|
||||
@@ -380,3 +399,36 @@ class ProjectController(ControllerBase):
|
||||
"fontnote": image_obj.fontnote,
|
||||
})
|
||||
return ChenResponse(status=200, code=25002, data=None)
|
||||
|
||||
@classmethod
|
||||
def get_model_from_category(cls, category: str):
|
||||
mapDict = {
|
||||
'静态软件项': StaticSoftItem,
|
||||
'静态硬件项': StaticSoftHardware,
|
||||
'动态软件项': DynamicSoftTable,
|
||||
'动态硬件项': DynamicHardwareTable
|
||||
}
|
||||
return mapDict[category]
|
||||
|
||||
# ~~~静态软件项、静态硬件项、动态软件项、动态硬件项 - 获取~~~
|
||||
@route.get("/get_static_dynamic_items/")
|
||||
def get_static_dynamic_items(self, id: int, category: str):
|
||||
project_obj = self.get_project_by_id(id)
|
||||
item_qs = self.get_model_from_category(category).objects.filter(project=project_obj)
|
||||
if item_qs.exists():
|
||||
item_obj = item_qs.first()
|
||||
return ChenResponse(status=200, code=25001, data={"table": item_obj.table, "fontnote": item_obj.fontnote})
|
||||
return ChenResponse(status=200, code=25002, data=None)
|
||||
|
||||
# ~~~静态软件项、静态硬件项、动态软件项、动态硬件项 - 新增或修改~~~
|
||||
@route.post("/post_static_dynamic_item/")
|
||||
@transaction.atomic
|
||||
def post_static_dynamic_item(self, data: StaticDynamicData):
|
||||
print(data)
|
||||
project_obj = self.get_project_by_id(data.id)
|
||||
model = self.get_model_from_category(data.category)
|
||||
item_qs = model.objects.filter(project=project_obj)
|
||||
if item_qs.exists():
|
||||
# 如果存在则修改
|
||||
item_qs.delete()
|
||||
model.objects.create(project=project_obj, table=data.table, fontnote=data.fontnote)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# Generated by Django 6.0.2 on 2026-02-05 09:45
|
||||
|
||||
import apps.project.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0021_alter_stuctsortdata_project'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DynamicHardwareTable',
|
||||
fields=[
|
||||
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='dynamic_hardware', serialize=False, to='project.project', verbose_name='关联项目')),
|
||||
('table', models.JSONField(default=apps.project.models.default_json_value, help_text='储存表格二维数组', verbose_name='储存表格二维数组')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '动态硬件项表',
|
||||
'verbose_name_plural': '动态硬件项表',
|
||||
'db_table': 'project_dynamic_hardware',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DynamicSoftTable',
|
||||
fields=[
|
||||
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='dynamic_soft_item', serialize=False, to='project.project', verbose_name='关联项目')),
|
||||
('table', models.JSONField(default=apps.project.models.default_json_value, help_text='储存表格二维数组', verbose_name='储存表格二维数组')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '动态软件项表',
|
||||
'verbose_name_plural': '动态软件项表',
|
||||
'db_table': 'project_dynamic_soft_item',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StaticSoftHardware',
|
||||
fields=[
|
||||
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='static_hardware', serialize=False, to='project.project', verbose_name='关联项目')),
|
||||
('table', models.JSONField(default=apps.project.models.default_json_value, help_text='储存表格二维数组', verbose_name='储存表格二维数组')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '静态硬件项表',
|
||||
'verbose_name_plural': '静态硬件项表',
|
||||
'db_table': 'project_static_hardware',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StaticSoftItem',
|
||||
fields=[
|
||||
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='static_soft_item', serialize=False, to='project.project', verbose_name='关联项目')),
|
||||
('table', models.JSONField(default=apps.project.models.default_json_value, help_text='储存表格二维数组', verbose_name='储存表格二维数组')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '静态软件项表',
|
||||
'verbose_name_plural': '静态软件项表',
|
||||
'db_table': 'project_static_soft_item',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 6.0.2 on 2026-02-05 11:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0022_dynamichardwaretable_dynamicsofttable_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dynamichardwaretable',
|
||||
name='fontnote',
|
||||
field=models.CharField(default='', help_text='数据的题注说明', max_length=256, null=True, verbose_name='题注'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dynamicsofttable',
|
||||
name='fontnote',
|
||||
field=models.CharField(default='', help_text='数据的题注说明', max_length=256, null=True, verbose_name='题注'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='staticsofthardware',
|
||||
name='fontnote',
|
||||
field=models.CharField(default='', help_text='数据的题注说明', max_length=256, null=True, verbose_name='题注'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='staticsoftitem',
|
||||
name='fontnote',
|
||||
field=models.CharField(default='', help_text='数据的题注说明', max_length=256, null=True, verbose_name='题注'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 6.0.2 on 2026-02-05 17:48
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0023_dynamichardwaretable_fontnote_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ProjectDynamicDescription',
|
||||
fields=[
|
||||
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='dynamic_des', serialize=False, to='project.project', verbose_name='关联项目')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '动态环境描述',
|
||||
'verbose_name_plural': '动态环境描述',
|
||||
'db_table': 'project_dynamic_description',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='stuctsortdata',
|
||||
name='dynamic_description',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='data_schemas', to='project.projectdynamicdescription', verbose_name='所属动态环境描述'),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -466,6 +466,9 @@ class Contact(CoreModel):
|
||||
ordering = ('create_datetime',)
|
||||
|
||||
# ~~~~~2024年2月27日新增~~~~~
|
||||
def default_json_value():
|
||||
return ""
|
||||
|
||||
class Abbreviation(models.Model):
|
||||
objects = models.Manager()
|
||||
title = models.CharField(max_length=64, verbose_name="缩略语", help_text="缩略语")
|
||||
@@ -489,8 +492,64 @@ class ProjectSoftSummary(models.Model):
|
||||
verbose_name = "软件概述表"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def default_json_value():
|
||||
return ""
|
||||
# 一对一项目model:动态测试环境描述
|
||||
class ProjectDynamicDescription(models.Model):
|
||||
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="dynamic_des", on_delete=models.CASCADE,
|
||||
verbose_name="关联项目", help_text="关联项目")
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_dynamic_description'
|
||||
verbose_name = "动态环境描述"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# 一对一项目model:静态软件项表
|
||||
class StaticSoftItem(models.Model):
|
||||
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="static_soft_item", on_delete=models.CASCADE,
|
||||
verbose_name="关联项目", help_text="关联项目")
|
||||
table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value)
|
||||
fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明")
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_static_soft_item'
|
||||
verbose_name = "静态软件项表"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# 一对一项目model:静态硬件项表
|
||||
class StaticSoftHardware(models.Model):
|
||||
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="static_hardware", on_delete=models.CASCADE,
|
||||
verbose_name="关联项目", help_text="关联项目")
|
||||
table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value)
|
||||
fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明")
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_static_hardware'
|
||||
verbose_name = "静态硬件项表"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# 一对一项目model:动态软件项表
|
||||
class DynamicSoftTable(models.Model):
|
||||
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="dynamic_soft_item", on_delete=models.CASCADE,
|
||||
verbose_name="关联项目", help_text="关联项目")
|
||||
table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value)
|
||||
fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明")
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_dynamic_soft_item'
|
||||
verbose_name = "动态软件项表"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# 一对一项目model:动态硬件项
|
||||
class DynamicHardwareTable(models.Model):
|
||||
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="dynamic_hardware", on_delete=models.CASCADE,
|
||||
verbose_name="关联项目", help_text="关联项目")
|
||||
|
||||
table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value)
|
||||
fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明")
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_dynamic_hardware'
|
||||
verbose_name = "动态硬件项表"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# 结构化排序数据
|
||||
class StuctSortData(CoreModel):
|
||||
@@ -502,7 +561,12 @@ class StuctSortData(CoreModel):
|
||||
on_delete=models.CASCADE, null=True, blank=True)
|
||||
# 接口图
|
||||
project = models.OneToOneField(Project, db_constraint=False, related_name="data_schemas", on_delete=models.CASCADE, null=True, blank=True,
|
||||
verbose_name="该接口图所属的项目")
|
||||
verbose_name="该接口图所属的项目")
|
||||
# 动态环境描述
|
||||
dynamic_description = models.ForeignKey(ProjectDynamicDescription, db_constraint=False, related_name="data_schemas",
|
||||
verbose_name="所属动态环境描述",
|
||||
on_delete=models.CASCADE, null=True, blank=True)
|
||||
|
||||
type = models.CharField(
|
||||
max_length=20,
|
||||
choices=(('text', '文本'), ('table', '表格'), ('image', '图片')),
|
||||
|
||||
Binary file not shown.
@@ -54,4 +54,11 @@ class SoftSummarySchema(Schema):
|
||||
data: list[DataSchema]
|
||||
|
||||
# ~~~软件接口图~~~
|
||||
## 复用DataSchema
|
||||
## 复用DataSchema
|
||||
|
||||
# ~~~静态软件项、静态硬件项、动态软件项、动态硬件项~~~
|
||||
class StaticDynamicData(Schema):
|
||||
id: int
|
||||
category: str
|
||||
table: list[list[str]]
|
||||
fontnote: Optional[str] = ""
|
||||
|
||||
Reference in New Issue
Block a user