diff --git a/config/app_config.json b/config/app_config.json index f49e74a..a33730b 100644 --- a/config/app_config.json +++ b/config/app_config.json @@ -123,7 +123,7 @@ } }, "electricity": { - "auto_start": true, + "auto_start": false, "interval_minutes": 30 } } \ No newline at end of file diff --git a/dao/report_dao.py b/dao/report_dao.py new file mode 100644 index 0000000..13f5460 --- /dev/null +++ b/dao/report_dao.py @@ -0,0 +1,170 @@ +import logging +from datetime import datetime +from utils.sql_utils import SQLUtils + +class ReportDAO: + """报表数据访问对象,处理报表相关的数据库操作""" + + def __init__(self): + """初始化数据访问对象""" + # 不再在初始化时创建数据库连接,而是在需要时创建 + pass + + def get_production_report(self, start_date, end_date, customer=None, material=None, spec=None): + """获取生产报表数据 + + Args: + start_date: 开始日期,格式为 'YYYY-MM-DD' + end_date: 结束日期,格式为 'YYYY-MM-DD' + customer: 客户名称,用于模糊查询,可选 + material: 材质,用于模糊查询,可选 + spec: 规格,用于模糊查询,可选 + + Returns: + list: 包含报表数据的字典列表 + """ + try: + # 构建SQL查询 + sql = """ + SELECT DATE(pack_time) AS 日期 + , customerexp AS 客户 + , t1.order_id AS 订单号 + , COUNT(DISTINCT t1.gc_note) AS '轴数' + , t2.cz AS '材质' + , t2.size AS '规格' + , ROUND(SUM(t1.net_weight), 2) AS '净重' + FROM wsbz_inspection_pack_data t1 + LEFT JOIN wsbz_order_info t2 ON t1.order_id = t2.ddmo + WHERE pack_time BETWEEN ? AND ? + """ + + params = [f"{start_date} 00:00:00", f"{end_date} 23:59:59"] + + # 添加可选的筛选条件 + if customer: + sql += " AND customerexp LIKE ?" + params.append(f"%{customer}%") + + if material: + sql += " AND t2.cz LIKE ?" + params.append(f"%{material}%") + + if spec: + sql += " AND t2.size LIKE ?" + params.append(f"%{spec}%") + + # 添加分组条件 + sql += " GROUP BY DATE(pack_time), t2.cz, t2.size, customerexp" + + # 执行查询 + with SQLUtils('sqlite', database='db/jtDB.db') as db: + db.cursor.execute(sql, params) + results = db.cursor.fetchall() + + # 获取列名 + columns = [desc[0] for desc in db.cursor.description] + + # 转换为字典列表 + data = [] + for row in results: + row_dict = {} + for i, col in enumerate(columns): + row_dict[col] = row[i] + data.append(row_dict) + + return data + + except Exception as e: + logging.error(f"获取生产报表数据失败: {str(e)}") + raise e + + def get_daily_report(self, date): + """获取指定日期的日报表 + + Args: + date: 日期,格式为 'YYYY-MM-DD' + + Returns: + list: 包含报表数据的字典列表 + """ + return self.get_production_report(date, date) + + def get_monthly_report(self, year, month): + """获取指定月份的月报表 + + Args: + year: 年份,如 2024 + month: 月份,如 8 + + Returns: + list: 包含报表数据的字典列表 + """ + # 计算月初和月末 + start_date = f"{year}-{month:02d}-01" + + # 计算月末日期 + if month == 12: + next_year = year + 1 + next_month = 1 + else: + next_year = year + next_month = month + 1 + + end_date = f"{next_year}-{next_month:02d}-01" + + # 使用日期范围查询 + return self.get_production_report(start_date, end_date) + + def get_yearly_report(self, year): + """获取指定年份的年报表 + + Args: + year: 年份,如 2024 + + Returns: + list: 包含报表数据的字典列表 + """ + start_date = f"{year}-01-01" + end_date = f"{year+1}-01-01" + + # 使用日期范围查询 + return self.get_production_report(start_date, end_date) + + def get_customer_report(self, start_date, end_date, customer): + """获取指定客户的报表 + + Args: + start_date: 开始日期,格式为 'YYYY-MM-DD' + end_date: 结束日期,格式为 'YYYY-MM-DD' + customer: 客户名称 + + Returns: + list: 包含报表数据的字典列表 + """ + return self.get_production_report(start_date, end_date, customer=customer) + + def get_material_report(self, start_date, end_date, material): + """获取指定材质的报表 + + Args: + start_date: 开始日期,格式为 'YYYY-MM-DD' + end_date: 结束日期,格式为 'YYYY-MM-DD' + material: 材质 + + Returns: + list: 包含报表数据的字典列表 + """ + return self.get_production_report(start_date, end_date, material=material) + + def get_spec_report(self, start_date, end_date, spec): + """获取指定规格的报表 + + Args: + start_date: 开始日期,格式为 'YYYY-MM-DD' + end_date: 结束日期,格式为 'YYYY-MM-DD' + spec: 规格 + + Returns: + list: 包含报表数据的字典列表 + """ + return self.get_production_report(start_date, end_date, spec=spec) diff --git a/db/jtDB.db b/db/jtDB.db index fcdd03a..ac20ea3 100644 Binary files a/db/jtDB.db and b/db/jtDB.db differ diff --git a/ui/report_dialog_ui.py b/ui/report_dialog_ui.py index d2b9d87..c8b56fd 100644 --- a/ui/report_dialog_ui.py +++ b/ui/report_dialog_ui.py @@ -1,7 +1,8 @@ from PySide6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLabel, QTableWidget, QTableWidgetItem, QHeaderView, - QPushButton, QComboBox, QFrame, QDateEdit + QPushButton, QComboBox, QFrame, QDateEdit, + QLineEdit ) from PySide6.QtCore import Qt, QDate from PySide6.QtGui import QFont @@ -37,27 +38,67 @@ class ReportDialogUI(QDialog): # 筛选条件容器 self.filter_frame = QFrame() self.filter_frame.setFrameShape(QFrame.StyledPanel) - self.filter_layout = QHBoxLayout(self.filter_frame) + self.filter_layout = QVBoxLayout(self.filter_frame) - # 日期选择 + # 第一行:日期选择 + self.date_row = QHBoxLayout() self.date_label = QLabel("日期范围:") self.date_label.setFont(self.normal_font) + self.date_label.setFixedWidth(80) self.start_date = QDateEdit() self.start_date.setFont(self.normal_font) self.start_date.setCalendarPopup(True) - self.start_date.setDate(QDate.currentDate()) + self.start_date.setDate(QDate.currentDate().addMonths(-1)) # 默认开始日期为一个月前 self.date_separator = QLabel("-") self.end_date = QDateEdit() self.end_date.setFont(self.normal_font) self.end_date.setCalendarPopup(True) self.end_date.setDate(QDate.currentDate()) - # 报表类型选择 - self.type_label = QLabel("报表类型:") - self.type_label.setFont(self.normal_font) - self.type_combo = QComboBox() - self.type_combo.setFont(self.normal_font) - self.type_combo.addItems(["日报表", "月报表", "年报表"]) + self.date_row.addWidget(self.date_label) + self.date_row.addWidget(self.start_date) + self.date_row.addWidget(self.date_separator) + self.date_row.addWidget(self.end_date) + self.date_row.addStretch() + + # 第二行:客户、材质、规格 + self.filter_row = QHBoxLayout() + + # 客户输入 + self.customer_label = QLabel("客户:") + self.customer_label.setFont(self.normal_font) + self.customer_label.setFixedWidth(80) + self.customer_edit = QLineEdit() + self.customer_edit.setFont(self.normal_font) + self.customer_edit.setPlaceholderText("输入客户名称") + + # 材质输入 + self.material_label = QLabel("材质:") + self.material_label.setFont(self.normal_font) + self.material_label.setFixedWidth(80) + self.material_edit = QLineEdit() + self.material_edit.setFont(self.normal_font) + self.material_edit.setPlaceholderText("输入材质") + + # 规格输入 + self.spec_label = QLabel("规格:") + self.spec_label.setFont(self.normal_font) + self.spec_label.setFixedWidth(80) + self.spec_edit = QLineEdit() + self.spec_edit.setFont(self.normal_font) + self.spec_edit.setPlaceholderText("输入规格") + + self.filter_row.addWidget(self.customer_label) + self.filter_row.addWidget(self.customer_edit) + self.filter_row.addSpacing(10) + self.filter_row.addWidget(self.material_label) + self.filter_row.addWidget(self.material_edit) + self.filter_row.addSpacing(10) + self.filter_row.addWidget(self.spec_label) + self.filter_row.addWidget(self.spec_edit) + + # 第三行:查询按钮 + self.button_row = QHBoxLayout() # 查询按钮 self.query_button = QPushButton("查询") @@ -74,17 +115,29 @@ class ReportDialogUI(QDialog): } """) - # 添加组件到筛选布局 - self.filter_layout.addWidget(self.date_label) - self.filter_layout.addWidget(self.start_date) - self.filter_layout.addWidget(self.date_separator) - self.filter_layout.addWidget(self.end_date) - self.filter_layout.addSpacing(20) - self.filter_layout.addWidget(self.type_label) - self.filter_layout.addWidget(self.type_combo) - self.filter_layout.addSpacing(20) - self.filter_layout.addWidget(self.query_button) - self.filter_layout.addStretch() + # 清空按钮 + self.clear_button = QPushButton("清空条件") + self.clear_button.setFont(self.normal_font) + self.clear_button.setStyleSheet(""" + QPushButton { + padding: 8px 16px; + background-color: #757575; + color: white; + border-radius: 4px; + } + QPushButton:hover { + background-color: #616161; + } + """) + + self.button_row.addStretch() + self.button_row.addWidget(self.clear_button) + self.button_row.addWidget(self.query_button) + + # 添加所有行到筛选布局 + self.filter_layout.addLayout(self.date_row) + self.filter_layout.addLayout(self.filter_row) + self.filter_layout.addLayout(self.button_row) # 添加到主布局 self.main_layout.addWidget(self.filter_frame) @@ -95,11 +148,10 @@ class ReportDialogUI(QDialog): self.report_table = QTableWidget() self.report_table.setFont(self.normal_font) - # 设置列 - self.report_table.setColumnCount(8) + # 设置列 - 根据SQL查询结果设置列 + self.report_table.setColumnCount(7) self.report_table.setHorizontalHeaderLabels([ - "日期", "工程号", "品名", "规格", - "生产数量", "合格数量", "不合格数量", "合格率" + "日期", "客户", "订单号", "轴数", "材质", "规格", "净重" ]) # 设置表格样式 @@ -118,7 +170,7 @@ class ReportDialogUI(QDialog): # 调整列宽 header = self.report_table.horizontalHeader() - for i in range(8): + for i in range(7): header.setSectionResizeMode(i, QHeaderView.Stretch) # 添加到主布局 diff --git a/widgets/report_dialog.py b/widgets/report_dialog.py index 7b4cf6d..407c659 100644 --- a/widgets/report_dialog.py +++ b/widgets/report_dialog.py @@ -1,14 +1,17 @@ -from PySide6.QtWidgets import QMessageBox +from PySide6.QtWidgets import QMessageBox, QFileDialog, QLineEdit, QTableWidgetItem from PySide6.QtCore import Qt from ui.report_dialog_ui import ReportDialogUI import pandas as pd from datetime import datetime import logging +import os +from dao.report_dao import ReportDAO class ReportDialog(ReportDialogUI): def __init__(self, parent=None): super().__init__(parent) self.init_signals() + self.current_data = [] # 存储当前查询的数据 def init_signals(self): """初始化信号连接""" @@ -21,34 +24,55 @@ class ReportDialog(ReportDialogUI): # 关闭按钮点击事件 self.close_button.clicked.connect(self.close) + # 清空条件按钮点击事件 + self.clear_button.clicked.connect(self.on_clear) + + def on_clear(self): + """清空条件按钮点击处理""" + # 重置日期为默认值 + self.start_date.setDate(datetime.now().date().addMonths(-1)) + self.end_date.setDate(datetime.now().date()) + + # 清空其他输入框 + self.customer_edit.clear() + self.material_edit.clear() + self.spec_edit.clear() + def on_query(self): """查询按钮点击处理""" try: # 获取查询条件 - start_date = self.start_date.date().toString(Qt.ISODate) - end_date = self.end_date.date().toString(Qt.ISODate) - report_type = self.type_combo.currentText() + start_date = self.start_date.date().toString("yyyy-MM-dd") + end_date = self.end_date.date().toString("yyyy-MM-dd") + customer = self.customer_edit.text().strip() + material = self.material_edit.text().strip() + spec = self.spec_edit.text().strip() - # TODO: 根据条件从数据库查询数据 - # 这里需要实现具体的查询逻辑 + # 使用ReportDAO获取数据 + report_dao = ReportDAO() + data = report_dao.get_production_report( + start_date=start_date, + end_date=end_date, + customer=customer if customer else None, + material=material if material else None, + spec=spec if spec else None + ) - # 示例数据 - data = [ - { - "日期": "2024-03-20", - "工程号": "GC001", - "品名": "产品A", - "规格": "规格1", - "生产数量": 100, - "合格数量": 95, - "不合格数量": 5, - "合格率": "95%" - } - ] + # 保存当前数据用于导出 + self.current_data = data # 更新表格显示 self.update_table(data) + # 显示查询结果数量 + if data: + if self.statusBar(): + self.statusBar().showMessage(f"查询到 {len(data)} 条记录") + else: + if self.statusBar(): + self.statusBar().showMessage("未查询到符合条件的记录") + QMessageBox.information(self, "提示", "未查询到符合条件的记录") + except Exception as e: logging.error(f"查询报表数据失败: {str(e)}") QMessageBox.warning(self, "错误", f"查询数据失败: {str(e)}") @@ -62,10 +86,17 @@ class ReportDialog(ReportDialogUI): # 清空表格 self.report_table.setRowCount(0) + if not data: + return + # 添加数据行 for row_idx, row_data in enumerate(data): self.report_table.insertRow(row_idx) - for col_idx, (key, value) in enumerate(row_data.items()): + + # 按照表头顺序添加数据 + headers = ["日期", "客户", "订单号", "轴数", "材质", "规格", "净重"] + for col_idx, header in enumerate(headers): + value = row_data.get(header, "") item = QTableWidgetItem(str(value)) item.setTextAlignment(Qt.AlignCenter) self.report_table.setItem(row_idx, col_idx, item) @@ -73,33 +104,41 @@ class ReportDialog(ReportDialogUI): def on_export(self): """导出按钮点击处理""" try: - # 获取表格数据 - data = [] - for row in range(self.report_table.rowCount()): - row_data = {} - for col in range(self.report_table.columnCount()): - header = self.report_table.horizontalHeaderItem(col).text() - item = self.report_table.item(row, col) - value = item.text() if item else "" - row_data[header] = value - data.append(row_data) - - if not data: + if not self.current_data: QMessageBox.warning(self, "警告", "没有数据可以导出") return # 创建DataFrame - df = pd.DataFrame(data) + df = pd.DataFrame(self.current_data) - # 生成文件名 - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = f"统计报表_{timestamp}.xlsx" + # 让用户选择保存路径 + default_name = f"统计报表_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" + filename, _ = QFileDialog.getSaveFileName( + self, "保存报表", default_name, "Excel 文件 (*.xlsx);;所有文件 (*)" + ) + + if not filename: # 用户取消了保存 + return + + # 确保文件名有正确的扩展名 + if not filename.endswith('.xlsx'): + filename += '.xlsx' # 导出到Excel df.to_excel(filename, index=False, engine='openpyxl') + # 显示成功消息并打开文件所在目录 QMessageBox.information(self, "成功", f"报表已导出到: {filename}") + # 打开文件所在目录 + os.system(f'explorer /select,"{os.path.normpath(filename)}"') + except Exception as e: logging.error(f"导出报表失败: {str(e)}") - QMessageBox.warning(self, "错误", f"导出报表失败: {str(e)}") \ No newline at end of file + QMessageBox.warning(self, "错误", f"导出报表失败: {str(e)}") + + def statusBar(self): + """获取状态栏,如果父窗口有状态栏则使用父窗口的状态栏""" + if self.parent() and hasattr(self.parent(), 'statusBar'): + return self.parent().statusBar() + return None \ No newline at end of file