添加报表功能
This commit is contained in:
parent
fe60d656b1
commit
b5420880f7
@ -123,7 +123,7 @@
|
||||
}
|
||||
},
|
||||
"electricity": {
|
||||
"auto_start": true,
|
||||
"auto_start": false,
|
||||
"interval_minutes": 30
|
||||
}
|
||||
}
|
||||
170
dao/report_dao.py
Normal file
170
dao/report_dao.py
Normal 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)
|
||||
BIN
db/jtDB.db
BIN
db/jtDB.db
Binary file not shown.
@ -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)
|
||||
|
||||
# 添加到主布局
|
||||
|
||||
@ -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
|
||||
Loading…
Reference in New Issue
Block a user