添加报表功能

This commit is contained in:
zhu-mengmeng 2025-08-16 13:37:42 +08:00
parent fe60d656b1
commit b5420880f7
5 changed files with 325 additions and 64 deletions

View File

@ -123,7 +123,7 @@
}
},
"electricity": {
"auto_start": true,
"auto_start": false,
"interval_minutes": 30
}
}

170
dao/report_dao.py Normal file
View File

@ -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)

Binary file not shown.

View File

@ -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)
# 添加到主布局

View File

@ -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)}")
def statusBar(self):
"""获取状态栏,如果父窗口有状态栏则使用父窗口的状态栏"""
if self.parent() and hasattr(self.parent(), 'statusBar'):
return self.parent().statusBar()
return None