"""材质牌号管理界面
可视化维护 material_grades 表(牌号 / 标准号 / 材料形态 / 阀门零件类别 / 元素 / 性能范围)。
作为模态对话框从主窗口导航栏打开,内部通过 MaterialGradeController 完成 CRUD。
UI 复用通用基类 ``LookupTableManagerView``(列定义驱动 + 共享控件),
本模块仅声明列/字段定义与「载入示例」扩展动作,避免重复造轮子。
字段维度说明:
- 材料形态:category_large / category_medium(铸件类/锻件类/板材类/棒材类)
- 阀门零件类别:category(body/stem/disc/common),用于打印对话框分组
- 元素性能:element + standard_min/max + actual_min/max + decimal_places
"""
from __future__ import annotations
from typing import Any
from PySide6.QtWidgets import QLabel, QMessageBox
from certflow.controllers.material_grade_controller import MaterialGradeController
from certflow.utils.logger import logger
from certflow.views.bases.lookup_table_manager_view import (
ColumnSpec,
FieldSpec,
LookupTableManagerView,
)
# 表格列定义:(标题, 模型字段, 是否可编辑展示, 字段类型)
_COLUMNS = [
ColumnSpec("ID", "id", False),
ColumnSpec("牌号", "grade"),
ColumnSpec("标准号", "standard_code"),
ColumnSpec("材料大类", "category_large"),
ColumnSpec("材料中类", "category_medium"),
ColumnSpec("零件类别", "category"),
ColumnSpec("类型", "kind"),
ColumnSpec("元素/性能", "element"),
ColumnSpec("标准最小", "standard_min", field_type="float"),
ColumnSpec("标准最大", "standard_max", field_type="float"),
ColumnSpec("小数位", "decimal_places", field_type="int"),
ColumnSpec("虚拟最小", "actual_min", field_type="float"),
ColumnSpec("虚拟最大", "actual_max", field_type="float"),
ColumnSpec("启用", "is_active", field_type="bool"),
]
# 编辑表单字段定义
_EDIT_FIELDS = [
FieldSpec("grade", "牌号 *", required=True, placeholder="如 WCB, CF8, 304"),
FieldSpec("standard_code", "标准号 *", required=True, placeholder="如 GB/T12229-2005"),
FieldSpec("element", "元素/性能 *", required=True, placeholder="如 碳(C)%"),
FieldSpec(
"category_large", "材料大类", empty_as_none=True, placeholder="铸件类/锻件类/板材类/棒材类"
),
FieldSpec("category_medium", "材料中类", empty_as_none=True, placeholder="普通碳钢/低温钢"),
FieldSpec("standard_min", "标准最小", field_type="float", empty_as_none=True),
FieldSpec("standard_max", "标准最大", field_type="float", empty_as_none=True),
FieldSpec("decimal_places", "小数位", field_type="int", default=2),
FieldSpec("actual_min", "虚拟最小", field_type="float", empty_as_none=True),
FieldSpec("actual_max", "虚拟最大", field_type="float", empty_as_none=True),
FieldSpec(
"category", "零件类别", field_type="choice", choices=["", "common", "body", "stem", "disc"]
),
FieldSpec("kind", "数据类型", field_type="choice", choices=["chemical", "mechanical"]),
FieldSpec("is_active", "启用", field_type="bool", default=True),
]
[文档]
class MaterialGradeManagerView(LookupTableManagerView):
"""材质牌号管理对话框(通用基类 + 材质牌号专属配置)"""
def __init__(
self,
db_manager: Any = None,
session: Any = None,
dict_key: str | None = "material_grades",
parent: Any = None,
) -> None:
"""初始化管理对话框
Args:
db_manager: 数据库管理器(优先);传入后控制器持有独立会话并自行提交
session: 外部会话(与 db_manager 二选一)
dict_key: 字典键名(``dictionary.<key>.csv``),缺省 ``material_grades``
parent: 父窗口
"""
if db_manager is not None:
controller = MaterialGradeController(db_manager=db_manager)
else:
controller = MaterialGradeController(db_session=session)
# 先占位,避免基类 __init__ 末尾调用 refresh() 时状态栏尚未创建
self._status_label: QLabel | None = None
super().__init__(
controller=controller,
columns=_COLUMNS,
edit_fields=_EDIT_FIELDS,
title="材质牌号",
window_title="材质牌号管理",
extra_actions=[("📥 载入示例", self._on_seed, False)],
dict_key=dict_key,
table_key="material_grade",
parent=parent,
)
# C3:数据质量状态栏——揭示「启用但范围缺失」的记录数(缺失范围会让报告列恒空)
self._status_label = QLabel("")
self._status_label.setStyleSheet("font-size: 11px;")
layout = self.layout()
assert layout is not None # 基类 __init__ 已设置 QVBoxLayout 为顶层布局
layout.addWidget(self._status_label)
self._update_status()
def _update_status(self) -> None:
"""刷新数据质量状态栏(总数/启用/缺失范围)。"""
if self._status_label is None: # 基类 __init__ 期间 refresh() 早于状态栏创建
return
try:
summary = self._controller.get_data_quality_summary()
except Exception as e: # 防御:状态栏失败不影响主功能
logger.debug(f"读取材质数据质量概览失败(忽略): {e}")
return
incomplete = summary["incomplete"]
text = (
f"共 {summary['total']} 条 启用 {summary['active']} "
f"停用 {summary['inactive']} "
f"⚠ 范围缺失 {incomplete}"
)
color = "#c0392b" if incomplete else "#2e7d32"
self._status_label.setStyleSheet(f"font-size: 11px; color: {color};")
self._status_label.setText(text)
[文档]
def refresh(self) -> None:
"""重载表格后同步更新数据质量状态栏。"""
super().refresh()
self._update_status()
def _on_seed(self) -> None:
"""载入常见牌号示例(化学成分 + 力学性能),幂等"""
try:
count = self._controller.seed_common_grades()
except Exception as e:
logger.error(f"载入示例失败: {e}")
QMessageBox.critical(self, "错误", f"载入示例失败: {e}")
return
if count == 0:
QMessageBox.information(self, "提示", "库中已有数据,未重复写入。")
else:
QMessageBox.information(self, "成功", f"已载入 {count} 条示例数据。")
self.refresh()