""" 地磅单管理Tab页面 """ from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QRadioButton, QButtonGroup, QDateTimeEdit, QTextEdit, QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, QMessageBox, QDialog, QFormLayout, QSplitter, QGroupBox, QDoubleSpinBox, QGridLayout ) from PyQt5.QtCore import Qt, QDateTime, pyqtSignal, QTimer from PyQt5.QtGui import QFont, QColor, QKeyEvent, QKeySequence from datetime import datetime from typing import Tuple from database import Database from utils.printer import Printer class EmptyDoubleSpinBox(QDoubleSpinBox): """自定义QDoubleSpinBox,当值为-1时显示为空""" def textFromValue(self, value: float) -> str: """重写文本显示方法,当值为-1时返回空字符串""" if value == -1: return "" return super().textFromValue(value) def valueFromText(self, text: str) -> float: """重写文本解析方法,当文本为空时返回-1""" if not text or text.strip() == "": return -1 return super().valueFromText(text) class EmptyDateTimeEdit(QDateTimeEdit): """自定义QDateTimeEdit,当日期时间为无效时显示为空""" def __init__(self, parent=None): super().__init__(parent) # 监听日期时间变化,自动更新显示 self.dateTimeChanged.connect(self._update_display) # 设置一个无效的默认日期时间 from PyQt5.QtCore import QDateTime super().setDateTime(QDateTime()) def _update_display(self): """更新显示,如果日期时间无效则显示为空""" try: if not self.dateTime().isValid(): line_edit = self.lineEdit() if line_edit: line_edit.setText("") except: pass def setDateTime(self, dateTime: QDateTime): """重写设置日期时间方法""" super().setDateTime(dateTime) # 延迟更新显示,确保控件已完全初始化 if not dateTime.isValid(): QTimer.singleShot(0, self._update_display) # 立即清空显示 line_edit = self.lineEdit() if line_edit: line_edit.setText("") def showEvent(self, event): """重写显示事件,确保在显示时也清空""" super().showEvent(event) # 延迟更新显示,确保控件已完全初始化 if not self.dateTime().isValid(): QTimer.singleShot(0, self._update_display) class WeighingTab(QWidget): """地磅单管理Tab""" def __init__(self, db: Database): super().__init__() self.db = db self.printer = Printer() self.init_ui() self.refresh_dropdowns() self.refresh_list() def init_ui(self): """初始化界面""" layout = QVBoxLayout() layout.setSpacing(10) layout.setContentsMargins(10, 10, 10, 10) # 创建分割器,用于调整上下两部分的大小 splitter = QSplitter(Qt.Vertical) # 上部分:地磅单填写模块(占据65%空间) form_widget = self.create_form_widget() splitter.addWidget(form_widget) # 下部分:地磅单记录列表(占据35%空间) list_widget = self.create_list_widget() splitter.addWidget(list_widget) # 设置分割比例(65:35) splitter.setSizes([650, 350]) layout.addWidget(splitter) self.setLayout(layout) def create_form_widget(self) -> QWidget: """创建地磅单填写表单""" widget = QWidget() layout = QVBoxLayout() layout.setSpacing(15) layout.setContentsMargins(15, 15, 15, 15) # 标题 title = QLabel("地磅单填写") title_font = QFont("微软雅黑", 18, QFont.Bold) title.setFont(title_font) layout.addWidget(title) # 表单区域 - 使用网格布局,一行放4个元素(2对字段) form_layout = QGridLayout() form_layout.setSpacing(15) form_layout.setColumnStretch(1, 1) # 第2列(控件列)可拉伸 form_layout.setColumnStretch(3, 1) # 第4列(控件列)可拉伸 # 创建控件 # 必填字段在前,非必填字段在后 # 必填字段label为红色,非必填字段label为黑色 row = 0 # 第一行:车号(必填)、货物名称(必填)、业务类型(必填)、供货单位(必填) # 车号(必填) vehicle_label = QLabel("车号 *:") vehicle_label.setFont(QFont("微软雅黑", 16)) vehicle_label.setTextFormat(Qt.RichText) self.vehicle_no_input = QLineEdit() self.vehicle_no_input.setMinimumHeight(40) self.vehicle_no_input.setFont(QFont("微软雅黑", 16)) form_layout.addWidget(vehicle_label, row, 0) form_layout.addWidget(self.vehicle_no_input, row, 1) # 货物名称(必填) cargo_label = QLabel("货物名称 *:") cargo_label.setFont(QFont("微软雅黑", 16)) cargo_label.setTextFormat(Qt.RichText) self.cargo_combo = QComboBox() self.cargo_combo.setMinimumHeight(40) self.cargo_combo.setFont(QFont("微软雅黑", 16)) self.cargo_combo.setEditable(False) form_layout.addWidget(cargo_label, row, 2) form_layout.addWidget(self.cargo_combo, row, 3) row += 1 # 第二行:业务类型(必填)、供货单位(必填)、收货单位(必填) # 业务类型(必填) business_label = QLabel("业务类型 *:") business_label.setFont(QFont("微软雅黑", 16)) business_label.setTextFormat(Qt.RichText) self.business_type_group = QButtonGroup() self.business_sale_radio = QRadioButton("出库") self.business_purchase_radio = QRadioButton("入库") self.business_sale_radio.setFont(QFont("微软雅黑", 16)) self.business_purchase_radio.setFont(QFont("微软雅黑", 16)) self.business_type_group.addButton(self.business_sale_radio, 0) self.business_type_group.addButton(self.business_purchase_radio, 1) # 不设置默认选中 business_layout = QHBoxLayout() business_layout.addWidget(self.business_sale_radio) business_layout.addWidget(self.business_purchase_radio) business_layout.addStretch() business_widget = QWidget() business_widget.setLayout(business_layout) form_layout.addWidget(business_label, row, 0) form_layout.addWidget(business_widget, row, 1) # 供货单位(必填) supplier_label = QLabel("供货单位 *:") supplier_label.setFont(QFont("微软雅黑", 16)) supplier_label.setTextFormat(Qt.RichText) self.supplier_combo = QComboBox() self.supplier_combo.setMinimumHeight(40) self.supplier_combo.setFont(QFont("微软雅黑", 16)) self.supplier_combo.setEditable(False) form_layout.addWidget(supplier_label, row, 2) form_layout.addWidget(self.supplier_combo, row, 3) row += 1 # 第三行:收货单位(必填)、毛重(kg)(必填)、毛重时间(必填)、皮重(kg)(必填) # 收货单位(必填) consignee_label = QLabel("收货单位 *:") consignee_label.setFont(QFont("微软雅黑", 16)) consignee_label.setTextFormat(Qt.RichText) self.consignee_combo = QComboBox() self.consignee_combo.setMinimumHeight(40) self.consignee_combo.setFont(QFont("微软雅黑", 16)) self.consignee_combo.setEditable(False) form_layout.addWidget(consignee_label, row, 0) form_layout.addWidget(self.consignee_combo, row, 1) # 毛重(kg)(必填) gross_weight_label = QLabel("毛重(kg) *:") gross_weight_label.setFont(QFont("微软雅黑", 16)) gross_weight_label.setTextFormat(Qt.RichText) self.gross_weight_input = EmptyDoubleSpinBox() self.gross_weight_input.setMinimumHeight(40) self.gross_weight_input.setFont(QFont("微软雅黑", 16)) self.gross_weight_input.setDecimals(2) self.gross_weight_input.setMinimum(-1) self.gross_weight_input.setMaximum(999999) self.gross_weight_input.setSpecialValueText("") self.gross_weight_input.setValue(-1) form_layout.addWidget(gross_weight_label, row, 2) form_layout.addWidget(self.gross_weight_input, row, 3) row += 1 # 第四行:毛重时间(必填)、皮重(kg)(必填)、皮重时间(必填) # 毛重时间(必填) gross_time_label = QLabel("毛重时间 *:") gross_time_label.setFont(QFont("微软雅黑", 16)) gross_time_label.setTextFormat(Qt.RichText) self.gross_weight_time = EmptyDateTimeEdit() self.gross_weight_time.setMinimumHeight(40) self.gross_weight_time.setFont(QFont("微软雅黑", 16)) self.gross_weight_time.setCalendarPopup(True) # 不设置默认时间,使用一个无效的日期时间表示未设置 self.gross_weight_time.setDateTime(QDateTime()) self.gross_weight_time.setDisplayFormat("yyyy-MM-dd HH:mm:ss") # 强制清空显示 if self.gross_weight_time.lineEdit(): self.gross_weight_time.lineEdit().setText("") QTimer.singleShot(0, lambda: self.gross_weight_time.lineEdit().setText("") if self.gross_weight_time.lineEdit() else None) # 创建获取当前时间按钮 gross_time_btn = QPushButton("获取当前时间") gross_time_btn.setMinimumHeight(40) gross_time_btn.setFont(QFont("微软雅黑", 14)) gross_time_btn.clicked.connect(lambda: self.gross_weight_time.setDateTime(QDateTime.currentDateTime())) gross_time_layout = QHBoxLayout() gross_time_layout.addWidget(self.gross_weight_time) gross_time_layout.addWidget(gross_time_btn) gross_time_layout.setContentsMargins(0, 0, 0, 0) gross_time_widget = QWidget() gross_time_widget.setLayout(gross_time_layout) form_layout.addWidget(gross_time_label, row, 0) form_layout.addWidget(gross_time_widget, row, 1) # 皮重(kg)(必填) tare_weight_label = QLabel("皮重(kg) *:") tare_weight_label.setFont(QFont("微软雅黑", 16)) tare_weight_label.setTextFormat(Qt.RichText) self.tare_weight_input = EmptyDoubleSpinBox() self.tare_weight_input.setMinimumHeight(40) self.tare_weight_input.setFont(QFont("微软雅黑", 16)) self.tare_weight_input.setDecimals(2) self.tare_weight_input.setMinimum(-1) self.tare_weight_input.setMaximum(999999) self.tare_weight_input.setSpecialValueText("") self.tare_weight_input.setValue(-1) form_layout.addWidget(tare_weight_label, row, 2) form_layout.addWidget(self.tare_weight_input, row, 3) row += 1 # 第五行:皮重时间(必填) # 皮重时间(必填) tare_time_label = QLabel("皮重时间 *:") tare_time_label.setFont(QFont("微软雅黑", 16)) tare_time_label.setTextFormat(Qt.RichText) self.tare_weight_time = EmptyDateTimeEdit() self.tare_weight_time.setMinimumHeight(40) self.tare_weight_time.setFont(QFont("微软雅黑", 16)) self.tare_weight_time.setCalendarPopup(True) # 不设置默认时间,使用一个无效的日期时间表示未设置 self.tare_weight_time.setDateTime(QDateTime()) self.tare_weight_time.setDisplayFormat("yyyy-MM-dd HH:mm:ss") # 强制清空显示 if self.tare_weight_time.lineEdit(): self.tare_weight_time.lineEdit().setText("") QTimer.singleShot(0, lambda: self.tare_weight_time.lineEdit().setText("") if self.tare_weight_time.lineEdit() else None) # 创建获取当前时间按钮 tare_time_btn = QPushButton("获取当前时间") tare_time_btn.setMinimumHeight(40) tare_time_btn.setFont(QFont("微软雅黑", 14)) tare_time_btn.clicked.connect(lambda: self.tare_weight_time.setDateTime(QDateTime.currentDateTime())) tare_time_layout = QHBoxLayout() tare_time_layout.addWidget(self.tare_weight_time) tare_time_layout.addWidget(tare_time_btn) tare_time_layout.setContentsMargins(0, 0, 0, 0) tare_time_widget = QWidget() tare_time_widget.setLayout(tare_time_layout) form_layout.addWidget(tare_time_label, row, 0) form_layout.addWidget(tare_time_widget, row, 1) row += 1 # 非必填字段开始 # 第六行:计划号(非必填)、批号(非必填)、扣杂(kg)、净重(kg)(自动计算,只读) # 计划号(非必填) plan_label = QLabel("计划号:") plan_label.setFont(QFont("微软雅黑", 16)) self.plan_no_input = QLineEdit() self.plan_no_input.setMinimumHeight(40) self.plan_no_input.setFont(QFont("微软雅黑", 16)) form_layout.addWidget(plan_label, row, 0) form_layout.addWidget(self.plan_no_input, row, 1) # 批号(非必填) batch_label = QLabel("批号:") batch_label.setFont(QFont("微软雅黑", 16)) self.batch_no_input = QLineEdit() self.batch_no_input.setMinimumHeight(40) self.batch_no_input.setFont(QFont("微软雅黑", 16)) form_layout.addWidget(batch_label, row, 2) form_layout.addWidget(self.batch_no_input, row, 3) row += 1 # 第七行:扣杂(kg)、净重(kg)(自动计算,只读)、计划量(kg)、已执行量(kg) # 扣杂(kg) deduction_label = QLabel("扣杂(kg):") deduction_label.setFont(QFont("微软雅黑", 16)) self.deduction_input = EmptyDoubleSpinBox() self.deduction_input.setMinimumHeight(40) self.deduction_input.setFont(QFont("微软雅黑", 16)) self.deduction_input.setDecimals(2) self.deduction_input.setMinimum(-1) self.deduction_input.setMaximum(999999) self.deduction_input.setSpecialValueText("") self.deduction_input.setValue(-1) self.deduction_input.valueChanged.connect(self.calculate_net_weight) self.gross_weight_input.valueChanged.connect(self.calculate_net_weight) self.tare_weight_input.valueChanged.connect(self.calculate_net_weight) form_layout.addWidget(deduction_label, row, 0) form_layout.addWidget(self.deduction_input, row, 1) # 净重(kg)(自动计算,只读) net_weight_label = QLabel("净重(kg):") net_weight_label.setFont(QFont("微软雅黑", 16)) self.net_weight_label = QLabel("0.000 kg") self.net_weight_label.setMinimumHeight(40) self.net_weight_label.setFont(QFont("微软雅黑", 16)) self.net_weight_label.setStyleSheet("background-color: #f0f0f0; padding: 5px; border: 1px solid #ccc;") form_layout.addWidget(net_weight_label, row, 2) form_layout.addWidget(self.net_weight_label, row, 3) row += 1 # 第八行:计划量(kg)、已执行量(kg) # 计划量(kg) planned_label = QLabel("计划量(kg):") planned_label.setFont(QFont("微软雅黑", 16)) self.planned_quantity_input = EmptyDoubleSpinBox() self.planned_quantity_input.setMinimumHeight(40) self.planned_quantity_input.setFont(QFont("微软雅黑", 16)) self.planned_quantity_input.setDecimals(2) self.planned_quantity_input.setMinimum(-1) self.planned_quantity_input.setMaximum(999999) self.planned_quantity_input.setSpecialValueText("") self.planned_quantity_input.setValue(-1) form_layout.addWidget(planned_label, row, 0) form_layout.addWidget(self.planned_quantity_input, row, 1) # 已执行量(kg) executed_label = QLabel("已执行量(kg):") executed_label.setFont(QFont("微软雅黑", 16)) self.executed_quantity_input = EmptyDoubleSpinBox() self.executed_quantity_input.setMinimumHeight(40) self.executed_quantity_input.setFont(QFont("微软雅黑", 16)) self.executed_quantity_input.setDecimals(2) self.executed_quantity_input.setMinimum(-1) self.executed_quantity_input.setMaximum(999999) self.executed_quantity_input.setSpecialValueText("") self.executed_quantity_input.setValue(-1) form_layout.addWidget(executed_label, row, 2) form_layout.addWidget(self.executed_quantity_input, row, 3) row += 1 # 第九行:备注(跨4列) remarks_label = QLabel("备注:") remarks_label.setFont(QFont("微软雅黑", 16)) self.remarks_input = QTextEdit() self.remarks_input.setMinimumHeight(80) self.remarks_input.setFont(QFont("微软雅黑", 16)) form_layout.addWidget(remarks_label, row, 0) form_layout.addWidget(self.remarks_input, row, 1, 1, 3) # 跨3列 # 按钮区域 row += 1 button_layout = QHBoxLayout() button_layout.addStretch() self.save_btn = QPushButton("保存") self.save_btn.setMinimumHeight(45) self.save_btn.setMinimumWidth(120) self.save_btn.setFont(QFont("微软雅黑", 16, QFont.Bold)) self.save_btn.setStyleSheet("background-color: #2196F3; color: white; border-radius: 5px;") self.save_btn.clicked.connect(self.save_weighing_slip) # 添加快捷键 self.save_btn.setShortcut(QKeySequence("Ctrl+S")) self.clear_btn = QPushButton("清空") self.clear_btn.setMinimumHeight(45) self.clear_btn.setMinimumWidth(120) self.clear_btn.setFont(QFont("微软雅黑", 16)) self.clear_btn.setStyleSheet("background-color: #757575; color: white; border-radius: 5px;") self.clear_btn.clicked.connect(self.clear_form) button_layout.addWidget(self.save_btn) button_layout.addWidget(self.clear_btn) button_layout.addStretch() button_widget = QWidget() button_widget.setLayout(button_layout) form_layout.addWidget(button_widget, row, 0, 1, 4) # 跨4列 # 将表单布局添加到主布局 layout.addLayout(form_layout) # 包装在GroupBox中 group_box = QGroupBox() group_box.setLayout(layout) return group_box def create_list_widget(self) -> QWidget: """创建地磅单记录列表""" widget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(10, 10, 10, 10) # 标题和刷新按钮区域 title_layout = QHBoxLayout() title = QLabel("地磅单记录列表") title_font = QFont("微软雅黑", 18, QFont.Bold) title.setFont(title_font) title_layout.addWidget(title) title_layout.addStretch() self.refresh_btn = QPushButton("刷新") self.refresh_btn.setMinimumHeight(40) self.refresh_btn.setMinimumWidth(100) self.refresh_btn.setFont(QFont("微软雅黑", 16)) self.refresh_btn.setStyleSheet(""" QPushButton { background-color: #607D8B; color: white; border-radius: 5px; border: none; } QPushButton:hover { background-color: #455A64; } QPushButton:pressed { background-color: #37474F; } QPushButton:disabled { background-color: #BDBDBD; color: #757575; } """) self.refresh_btn.setEnabled(True) # 确保按钮是启用状态 self.refresh_btn.clicked.connect(self.refresh_list) self.refresh_btn.setShortcut(QKeySequence("F5")) title_layout.addWidget(self.refresh_btn) layout.addLayout(title_layout) # 表格 self.table = QTableWidget() self.table.setColumnCount(7) self.table.setHorizontalHeaderLabels(["序号", "车号", "货物名称", "业务类型", "净重(kg)", "毛重时间", "操作"]) self.table.horizontalHeader().setFont(QFont("微软雅黑", 15, QFont.Bold)) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table.setFont(QFont("微软雅黑", 15)) self.table.setAlternatingRowColors(True) self.table.setSelectionBehavior(QTableWidget.SelectRows) layout.addWidget(self.table) widget.setLayout(layout) return widget def calculate_net_weight(self): """计算净重""" gross = self.gross_weight_input.value() if self.gross_weight_input.value() >= 0 else 0 tare = self.tare_weight_input.value() if self.tare_weight_input.value() >= 0 else 0 deduction = self.deduction_input.value() if self.deduction_input.value() >= 0 else 0 net = gross - tare - deduction self.net_weight_label.setText(f"{net:.2f} kg") def refresh_dropdowns(self): """刷新下拉列表""" # 刷新货物名称 self.cargo_combo.clear() self.cargo_combo.addItem("") # 添加一个空选项 cargos = self.db.get_all_cargos() for cargo in cargos: self.cargo_combo.addItem(cargo['name']) self.cargo_combo.setCurrentIndex(0) # 默认选中空选项 # 刷新公司名称(供货单位和收货单位共用) self.supplier_combo.clear() self.supplier_combo.addItem("") # 添加一个空选项 self.consignee_combo.clear() self.consignee_combo.addItem("") # 添加一个空选项 companies = self.db.get_all_companies() for company in companies: self.supplier_combo.addItem(company['name']) self.consignee_combo.addItem(company['name']) self.supplier_combo.setCurrentIndex(0) # 默认选中空选项 self.consignee_combo.setCurrentIndex(0) # 默认选中空选项 def refresh_list(self): """刷新地磅单记录列表""" slips = self.db.get_all_weighing_slips() self.table.setRowCount(len(slips)) for i, slip in enumerate(slips): # 序号 self.table.setItem(i, 0, QTableWidgetItem(str(i + 1))) # 车号 self.table.setItem(i, 1, QTableWidgetItem(slip['vehicle_no'])) # 货物名称 self.table.setItem(i, 2, QTableWidgetItem(slip['cargo_name'])) # 业务类型 self.table.setItem(i, 3, QTableWidgetItem(slip['business_type'])) # 净重 if slip['is_voided']: net_weight_item = QTableWidgetItem("[作废]") net_weight_item.setForeground(QColor(128, 128, 128)) else: net_weight_item = QTableWidgetItem(f"{slip['net_weight']:.2f}") self.table.setItem(i, 4, net_weight_item) # 毛重时间 self.table.setItem(i, 5, QTableWidgetItem(slip['gross_weight_time'])) # 操作按钮 btn_widget = QWidget() btn_layout = QHBoxLayout() btn_layout.setContentsMargins(5, 2, 5, 2) detail_btn = QPushButton("查看") detail_btn.setFont(QFont("微软雅黑", 14)) detail_btn.clicked.connect(lambda checked, sid=slip['id']: self.show_detail(sid)) preview_btn = QPushButton("打印") preview_btn.setFont(QFont("微软雅黑", 14)) preview_btn.clicked.connect(lambda checked, sid=slip['id']: self.preview_slip(sid)) void_btn = QPushButton("作废") void_btn.setFont(QFont("微软雅黑", 14)) void_btn.setStyleSheet("background-color: #f44336; color: white;") void_btn.clicked.connect(lambda checked, sid=slip['id']: self.void_slip(sid)) if slip['is_voided']: void_btn.setEnabled(False) void_btn.setText("已作废") btn_layout.addWidget(detail_btn) btn_layout.addWidget(preview_btn) btn_layout.addWidget(void_btn) btn_widget.setLayout(btn_layout) self.table.setCellWidget(i, 6, btn_widget) # 如果已作废,整行灰色显示 if slip['is_voided']: for j in range(self.table.columnCount()): item = self.table.item(i, j) if item: item.setBackground(QColor(240, 240, 240)) self.table.resizeRowsToContents() def validate_form(self) -> Tuple[bool, str]: """验证表单数据""" # 清除之前的错误样式 self.clear_error_styles() if not self.vehicle_no_input.text().strip(): self.set_error_style(self.vehicle_no_input) return False, "请填写车号" if self.cargo_combo.currentIndex() <= 0 or not self.cargo_combo.currentText().strip(): self.set_error_style(self.cargo_combo) return False, "请选择货物名称" if not self.business_type_group.checkedButton(): return False, "请选择业务类型" if self.supplier_combo.currentIndex() <= 0 or not self.supplier_combo.currentText().strip(): self.set_error_style(self.supplier_combo) return False, "请选择供货单位" if self.consignee_combo.currentIndex() <= 0 or not self.consignee_combo.currentText().strip(): self.set_error_style(self.consignee_combo) return False, "请选择收货单位" if self.gross_weight_input.value() < 0: self.set_error_style(self.gross_weight_input) return False, "请填写毛重" if self.gross_weight_input.value() <= 0: self.set_error_style(self.gross_weight_input) return False, "毛重必须大于0" if self.tare_weight_input.value() < 0: self.set_error_style(self.tare_weight_input) return False, "请填写皮重" if self.tare_weight_input.value() <= 0: self.set_error_style(self.tare_weight_input) return False, "皮重必须大于0" # 验证毛重时间:检查日期时间是否有效,同时检查文本是否为空 gross_time_text = self.gross_weight_time.lineEdit().text().strip() if self.gross_weight_time.lineEdit() else "" if not self.gross_weight_time.dateTime().isValid() or not gross_time_text: self.set_error_style(self.gross_weight_time) return False, "请填写毛重时间" # 验证皮重时间:检查日期时间是否有效,同时检查文本是否为空 tare_time_text = self.tare_weight_time.lineEdit().text().strip() if self.tare_weight_time.lineEdit() else "" if not self.tare_weight_time.dateTime().isValid() or not tare_time_text: self.set_error_style(self.tare_weight_time) return False, "请填写皮重时间" # 计算净重 gross = self.gross_weight_input.value() tare = self.tare_weight_input.value() deduction = self.deduction_input.value() if self.deduction_input.value() >= 0 else 0 net_weight = gross - tare - deduction if net_weight <= 0: return False, "净重必须大于0,请检查毛重、皮重和扣杂的数值" return True, "" def set_error_style(self, widget): """设置错误样式(红色边框)""" widget.setStyleSheet("border: 2px solid red;") def clear_error_styles(self): """清除所有错误样式""" self.vehicle_no_input.setStyleSheet("") self.cargo_combo.setStyleSheet("") self.supplier_combo.setStyleSheet("") self.consignee_combo.setStyleSheet("") self.gross_weight_input.setStyleSheet("") self.tare_weight_input.setStyleSheet("") self.gross_weight_time.setStyleSheet("") self.tare_weight_time.setStyleSheet("") def save_weighing_slip(self): """保存地磅单""" # 验证 valid, error_msg = self.validate_form() if not valid: QMessageBox.warning(self, "验证失败", error_msg) return # 准备数据 data = { 'vehicle_no': self.vehicle_no_input.text().strip(), 'plan_no': self.plan_no_input.text().strip(), 'cargo_name': self.cargo_combo.currentText(), 'business_type': self.business_type_group.checkedButton().text(), 'batch_no': self.batch_no_input.text().strip(), 'supplier': self.supplier_combo.currentText(), 'consignee': self.consignee_combo.currentText(), 'gross_weight': self.gross_weight_input.value(), 'gross_weight_time': self.gross_weight_time.dateTime().toString("yyyy-MM-dd HH:mm:ss") if self.gross_weight_time.dateTime().isValid() else "", 'tare_weight': self.tare_weight_input.value(), 'tare_weight_time': self.tare_weight_time.dateTime().toString("yyyy-MM-dd HH:mm:ss") if self.tare_weight_time.dateTime().isValid() else "", 'deduction': self.deduction_input.value() if self.deduction_input.value() >= 0 else 0, 'planned_quantity': self.planned_quantity_input.value() if self.planned_quantity_input.value() >= 0 else None, 'executed_quantity': self.executed_quantity_input.value() if self.executed_quantity_input.value() >= 0 else None, 'remarks': self.remarks_input.toPlainText().strip() } try: self.db.insert_weighing_slip(data) QMessageBox.information(self, "成功", "地磅单保存成功!") self.clear_error_styles() # 清除错误样式 self.clear_form() self.refresh_list() except Exception as e: QMessageBox.critical(self, "错误", f"保存失败:{str(e)}") def clear_form(self): """清空表单""" self.vehicle_no_input.clear() self.plan_no_input.clear() # 清空货物名称、供货单位、收货单位(设置为第一个空选项) if self.cargo_combo.count() > 0: self.cargo_combo.setCurrentIndex(0) if self.supplier_combo.count() > 0: self.supplier_combo.setCurrentIndex(0) if self.consignee_combo.count() > 0: self.consignee_combo.setCurrentIndex(0) # 不设置业务类型默认选中 if self.business_type_group.checkedButton(): self.business_type_group.setExclusive(False) self.business_type_group.checkedButton().setChecked(False) self.business_type_group.setExclusive(True) self.batch_no_input.clear() self.gross_weight_input.setValue(-1) # 不设置毛重时间默认值,使用无效日期时间 self.gross_weight_time.setDateTime(QDateTime()) if self.gross_weight_time.lineEdit(): self.gross_weight_time.lineEdit().setText("") # 延迟清空,确保显示被正确清空 QTimer.singleShot(0, lambda: self.gross_weight_time.lineEdit().setText("") if self.gross_weight_time.lineEdit() else None) self.tare_weight_input.setValue(-1) # 不设置皮重时间默认值,使用无效日期时间 self.tare_weight_time.setDateTime(QDateTime()) if self.tare_weight_time.lineEdit(): self.tare_weight_time.lineEdit().setText("") # 延迟清空,确保显示被正确清空 QTimer.singleShot(0, lambda: self.tare_weight_time.lineEdit().setText("") if self.tare_weight_time.lineEdit() else None) self.deduction_input.setValue(-1) self.net_weight_label.setText("0.00 kg") self.planned_quantity_input.setValue(-1) self.executed_quantity_input.setValue(-1) self.remarks_input.clear() def show_detail(self, slip_id: int): """显示地磅单详情""" slip = self.db.get_weighing_slip_by_id(slip_id) if not slip: QMessageBox.warning(self, "错误", "未找到该地磅单") return dialog = DetailDialog(slip, self.db, self) dialog.exec_() def preview_slip(self, slip_id: int): """预览地磅单""" slip = self.db.get_weighing_slip_by_id(slip_id) if not slip: QMessageBox.warning(self, "错误", "未找到该地磅单") return try: self.printer.preview_weighing_slip(slip) except Exception as e: QMessageBox.critical(self, "错误", f"预览失败:{str(e)}") def print_slip(self, slip_id: int): """打印地磅单""" slip = self.db.get_weighing_slip_by_id(slip_id) if not slip: QMessageBox.warning(self, "错误", "未找到该地磅单") return try: self.printer.print_weighing_slip(slip) QMessageBox.information(self, "成功", "打印任务已发送") except Exception as e: QMessageBox.critical(self, "错误", f"打印失败:{str(e)}") def void_slip(self, slip_id: int): """作废地磅单""" reply = QMessageBox.question( self, "确认", "确定要作废这条地磅单吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.Yes: if self.db.void_weighing_slip(slip_id): QMessageBox.information(self, "成功", "地磅单已作废") self.refresh_list() else: QMessageBox.warning(self, "错误", "作废失败") class DetailDialog(QDialog): """地磅单详情对话框""" def __init__(self, slip: dict, db: Database, parent=None): super().__init__(parent) self.slip = slip self.db = db self.printer = Printer() self.init_ui() def init_ui(self): """初始化对话框界面""" self.setWindowTitle("地磅单详情") self.setMinimumWidth(500) layout = QVBoxLayout() layout.setSpacing(10) layout.setContentsMargins(20, 20, 20, 20) # 显示详情信息(只读) form_layout = QFormLayout() form_layout.setSpacing(10) label_font = QFont("微软雅黑", 16) fields = [ ("车号", self.slip['vehicle_no']), ("计划号", self.slip.get('plan_no', '')), ("货物名称", self.slip['cargo_name']), ("业务类型", self.slip['business_type']), ("批号", self.slip.get('batch_no', '')), ("供货单位", self.slip['supplier']), ("收货单位", self.slip['consignee']), ("毛重(kg)", f"{self.slip['gross_weight']:.2f}"), ("毛重时间", self.slip['gross_weight_time']), ("皮重(kg)", f"{self.slip['tare_weight']:.2f}"), ("皮重时间", self.slip['tare_weight_time']), ("扣杂(kg)", f"{self.slip.get('deduction', 0):.2f}"), ("净重(kg)", f"{self.slip['net_weight']:.2f}"), ("计划量(kg)", f"{self.slip.get('planned_quantity', 0):.2f}" if self.slip.get('planned_quantity') else ""), ("已执行量(kg)", f"{self.slip.get('executed_quantity', 0):.2f}" if self.slip.get('executed_quantity') else ""), ("磅点", self.slip.get('weighing_point', '山峡地磅')), ("备注", self.slip.get('remarks', '')) ] for label, value in fields: label_widget = QLabel(f"{label}:") label_widget.setFont(label_font) value_label = QLabel(str(value) if value else "") value_label.setFont(QFont("微软雅黑", 15)) form_layout.addRow(label_widget, value_label) layout.addLayout(form_layout) # 按钮 button_layout = QHBoxLayout() button_layout.addStretch() preview_btn = QPushButton("预览") preview_btn.setMinimumHeight(40) preview_btn.setMinimumWidth(100) preview_btn.setFont(QFont("微软雅黑", 15)) preview_btn.clicked.connect(self.preview_slip) print_btn = QPushButton("打印") print_btn.setMinimumHeight(40) print_btn.setMinimumWidth(100) print_btn.setFont(QFont("微软雅黑", 15)) print_btn.clicked.connect(self.print_slip) close_btn = QPushButton("关闭") close_btn.setMinimumHeight(40) close_btn.setMinimumWidth(100) close_btn.setFont(QFont("微软雅黑", 15)) close_btn.clicked.connect(self.accept) button_layout.addWidget(preview_btn) button_layout.addWidget(print_btn) button_layout.addWidget(close_btn) layout.addLayout(button_layout) self.setLayout(layout) def preview_slip(self): """预览地磅单""" try: self.printer.preview_weighing_slip(self.slip) except Exception as e: QMessageBox.critical(self, "错误", f"预览失败:{str(e)}") def print_slip(self): """打印地磅单""" try: self.printer.print_weighing_slip(self.slip) QMessageBox.information(self, "成功", "打印任务已发送") except Exception as e: QMessageBox.critical(self, "错误", f"打印失败:{str(e)}")