761 lines
30 KiB
Python
761 lines
30 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
测试结果视图,用于查看和筛选测试结果
|
||
"""
|
||
|
||
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||
QPushButton, QTableWidget, QTableWidgetItem,
|
||
QHeaderView, QLineEdit, QComboBox, QDateTimeEdit, QMessageBox, QGridLayout,
|
||
QFileDialog, QProgressDialog, QApplication)
|
||
from PyQt5.QtCore import Qt, QDateTime, QSize
|
||
import requests
|
||
from config import HTTP_API_BASE_URL
|
||
import datetime
|
||
import openpyxl
|
||
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
|
||
from openpyxl.utils import get_column_letter
|
||
|
||
|
||
class TestResultView(QWidget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.api_base_url = HTTP_API_BASE_URL
|
||
|
||
# 分页参数
|
||
self.current_page = 1
|
||
self.page_size = 20
|
||
self.total_count = 0
|
||
|
||
# 所有数据(从后端获取)
|
||
self.all_data = []
|
||
self.filtered_data = []
|
||
|
||
self.init_ui()
|
||
self.load_data()
|
||
|
||
def init_ui(self):
|
||
"""初始化界面"""
|
||
layout = QVBoxLayout(self)
|
||
layout.setContentsMargins(10, 10, 10, 10)
|
||
layout.setSpacing(10)
|
||
|
||
|
||
# 筛选栏
|
||
self._create_filter_bar(layout)
|
||
|
||
# 数据表格
|
||
self._create_table(layout)
|
||
|
||
# 分页控制栏
|
||
self._create_pagination_bar(layout)
|
||
|
||
def _create_filter_bar(self, parent_layout):
|
||
"""创建筛选栏(自适应1行或2行)"""
|
||
# 创建筛选容器
|
||
self.filter_container = QWidget()
|
||
self.filter_layout = QGridLayout(self.filter_container)
|
||
self.filter_layout.setSpacing(10)
|
||
self.filter_layout.setContentsMargins(0, 0, 0, 0)
|
||
|
||
# 样品编号
|
||
sn_label = QLabel("样品编号:")
|
||
self.sn_input = QLineEdit()
|
||
self.sn_input.setPlaceholderText("请输入编号")
|
||
self.sn_input.setFixedWidth(150)
|
||
|
||
# 工单
|
||
workorder_label = QLabel("工单:")
|
||
self.workorder_input = QLineEdit()
|
||
self.workorder_input.setPlaceholderText("请输入测试工单")
|
||
self.workorder_input.setFixedWidth(200)
|
||
|
||
# 测试人员
|
||
inspector_label = QLabel("测试人员:")
|
||
self.inspector_input = QLineEdit()
|
||
self.inspector_input.setPlaceholderText("请输入测试人员")
|
||
self.inspector_input.setFixedWidth(150)
|
||
|
||
# 开始时间
|
||
self.start_datetime_edit = QDateTimeEdit()
|
||
self.start_datetime_edit.setCalendarPopup(True)
|
||
self.start_datetime_edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
|
||
self.start_datetime_edit.setDateTime(QDateTime.currentDateTime().addMonths(-1))
|
||
|
||
# 至
|
||
to_label = QLabel("至")
|
||
|
||
# 结束时间
|
||
self.end_datetime_edit = QDateTimeEdit()
|
||
self.end_datetime_edit.setCalendarPopup(True)
|
||
self.end_datetime_edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
|
||
self.end_datetime_edit.setDateTime(QDateTime.currentDateTime())
|
||
|
||
# 查询按钮
|
||
search_btn = QPushButton("查询")
|
||
search_btn.clicked.connect(self.on_search)
|
||
search_btn.setFixedWidth(80)
|
||
|
||
# 导出结果按钮
|
||
export_btn = QPushButton("导出结果")
|
||
export_btn.clicked.connect(self.on_export)
|
||
export_btn.setFixedWidth(100)
|
||
|
||
# 将所有控件存储为列表,方便动态调整布局
|
||
self.filter_widgets = [
|
||
(sn_label, self.sn_input),
|
||
(workorder_label, self.workorder_input),
|
||
(inspector_label, self.inspector_input),
|
||
(self.start_datetime_edit, to_label, self.end_datetime_edit),
|
||
(search_btn, export_btn)
|
||
]
|
||
|
||
# 初始化为2行布局
|
||
self._arrange_filters_two_rows()
|
||
|
||
parent_layout.addWidget(self.filter_container)
|
||
|
||
def _arrange_filters_one_row(self):
|
||
"""将筛选条件排列为1行"""
|
||
# 清空现有布局
|
||
for i in reversed(range(self.filter_layout.count())):
|
||
widget = self.filter_layout.itemAt(i).widget()
|
||
if widget:
|
||
self.filter_layout.removeWidget(widget)
|
||
|
||
# 1行布局:所有控件排在同一行
|
||
col = 0
|
||
|
||
# 样品编号
|
||
self.filter_layout.addWidget(self.filter_widgets[0][0], 0, col) # Label
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[0][1], 0, col) # Input
|
||
col += 1
|
||
|
||
# 工单
|
||
self.filter_layout.addWidget(self.filter_widgets[1][0], 0, col)
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[1][1], 0, col)
|
||
col += 1
|
||
|
||
# 测试人员
|
||
self.filter_layout.addWidget(self.filter_widgets[2][0], 0, col)
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[2][1], 0, col)
|
||
col += 1
|
||
|
||
# 时间范围
|
||
self.filter_layout.addWidget(self.filter_widgets[3][0], 0, col) # start_datetime
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[3][1], 0, col) # "至"
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[3][2], 0, col) # end_datetime
|
||
col += 1
|
||
|
||
# 按钮
|
||
self.filter_layout.addWidget(self.filter_widgets[4][0], 0, col) # 查询
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[4][1], 0, col) # 导出
|
||
col += 1
|
||
|
||
# 添加弹性空间
|
||
self.filter_layout.setColumnStretch(col, 1)
|
||
|
||
def _arrange_filters_two_rows(self):
|
||
"""将筛选条件排列为2行"""
|
||
# 清空现有布局
|
||
for i in reversed(range(self.filter_layout.count())):
|
||
widget = self.filter_layout.itemAt(i).widget()
|
||
if widget:
|
||
self.filter_layout.removeWidget(widget)
|
||
|
||
# 第一行:样品编号、工单、测试人员
|
||
col = 0
|
||
self.filter_layout.addWidget(self.filter_widgets[0][0], 0, col) # 样品编号Label
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[0][1], 0, col) # 样品编号Input
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[1][0], 0, col) # 工单Label
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[1][1], 0, col) # 工单Input
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[2][0], 0, col) # 测试人员Label
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[2][1], 0, col) # 测试人员Input
|
||
col += 1
|
||
self.filter_layout.setColumnStretch(col, 1) # 弹性空间
|
||
|
||
# 第二行:时间范围和按钮
|
||
col = 0
|
||
self.filter_layout.addWidget(self.filter_widgets[3][0], 1, col) # start_datetime
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[3][1], 1, col) # "至"
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[3][2], 1, col) # end_datetime
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[4][0], 1, col) # 查询按钮
|
||
col += 1
|
||
self.filter_layout.addWidget(self.filter_widgets[4][1], 1, col) # 导出按钮
|
||
col += 1
|
||
self.filter_layout.setColumnStretch(col, 1) # 弹性空间
|
||
|
||
def _create_table(self, parent_layout):
|
||
"""创建数据表格"""
|
||
self.table = QTableWidget()
|
||
self.table.setColumnCount(18)
|
||
self.table.setHorizontalHeaderLabels([
|
||
"序号", "样品编号", "项目代码", "项目阶段", "项目方案", "测试周次",
|
||
"工单名称", "产品序列号", "测试设备", "测试通道", "跌落方向",
|
||
"跌落高度(mm)", "跌落速度(次/Min)", "样品跌落次数", "方向当前次数",
|
||
"木板累计跌落次数", "试验开始时间", "试验结束时间"
|
||
])
|
||
|
||
# 表格样式
|
||
self.table.setSelectionBehavior(QTableWidget.SelectRows)
|
||
self.table.setSelectionMode(QTableWidget.SingleSelection)
|
||
self.table.setAlternatingRowColors(True)
|
||
self.table.horizontalHeader().setStretchLastSection(True)
|
||
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
|
||
self.table.verticalHeader().setVisible(False)
|
||
self.table.setEditTriggers(QTableWidget.NoEditTriggers)
|
||
|
||
parent_layout.addWidget(self.table)
|
||
|
||
def _create_pagination_bar(self, parent_layout):
|
||
"""创建分页控制栏"""
|
||
pager_layout = QHBoxLayout()
|
||
pager_layout.setSpacing(10)
|
||
|
||
self.page_info_label = QLabel("")
|
||
pager_layout.addWidget(self.page_info_label)
|
||
pager_layout.addStretch()
|
||
|
||
# 每页行数选择
|
||
page_size_label = QLabel("每页行数:")
|
||
pager_layout.addWidget(page_size_label)
|
||
self.page_size_combo = QComboBox()
|
||
self.page_size_combo.addItems(["10", "20", "50", "100"])
|
||
self.page_size_combo.setCurrentText(str(self.page_size))
|
||
self.page_size_combo.currentTextChanged.connect(self.on_page_size_changed)
|
||
pager_layout.addWidget(self.page_size_combo)
|
||
|
||
# 上一页按钮
|
||
self.prev_btn = QPushButton("上一页")
|
||
self.prev_btn.clicked.connect(self.go_prev_page)
|
||
pager_layout.addWidget(self.prev_btn)
|
||
|
||
# 页码跳转
|
||
goto_label = QLabel("跳转至")
|
||
pager_layout.addWidget(goto_label)
|
||
|
||
self.page_input = QLineEdit()
|
||
self.page_input.setFixedWidth(60)
|
||
self.page_input.setPlaceholderText("页码")
|
||
self.page_input.setAlignment(Qt.AlignCenter)
|
||
self.page_input.setStyleSheet("""
|
||
QLineEdit {
|
||
padding: 4px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 3px;
|
||
}
|
||
QLineEdit:focus {
|
||
border: 1px solid #3A84FF;
|
||
}
|
||
""")
|
||
# 按回车键跳转
|
||
self.page_input.returnPressed.connect(self.go_to_page)
|
||
pager_layout.addWidget(self.page_input)
|
||
|
||
goto_page_label = QLabel("页")
|
||
pager_layout.addWidget(goto_page_label)
|
||
|
||
# 跳转按钮
|
||
self.goto_btn = QPushButton("跳转")
|
||
self.goto_btn.setFixedWidth(60)
|
||
self.goto_btn.clicked.connect(self.go_to_page)
|
||
self.goto_btn.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #3A84FF;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 3px;
|
||
padding: 4px 8px;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #2B6FE6;
|
||
}
|
||
QPushButton:pressed {
|
||
background-color: #1F5BD1;
|
||
}
|
||
QPushButton:disabled {
|
||
background-color: #cccccc;
|
||
}
|
||
""")
|
||
pager_layout.addWidget(self.goto_btn)
|
||
|
||
# 下一页按钮
|
||
self.next_btn = QPushButton("下一页")
|
||
self.next_btn.clicked.connect(self.go_next_page)
|
||
pager_layout.addWidget(self.next_btn)
|
||
|
||
parent_layout.addLayout(pager_layout)
|
||
|
||
def resizeEvent(self, event):
|
||
"""窗口大小变化时自动调整筛选栏布局"""
|
||
super().resizeEvent(event)
|
||
|
||
if hasattr(self, 'filter_container'):
|
||
# 获取当前窗口宽度
|
||
width = self.width()
|
||
|
||
# 根据宽度阈值决定布局:
|
||
# 大于1400px时显示1行,否则显示2行
|
||
if width > 1400:
|
||
if not hasattr(self, '_current_layout') or self._current_layout != 'one_row':
|
||
self._arrange_filters_one_row()
|
||
self._current_layout = 'one_row'
|
||
else:
|
||
if not hasattr(self, '_current_layout') or self._current_layout != 'two_rows':
|
||
self._arrange_filters_two_rows()
|
||
self._current_layout = 'two_rows'
|
||
|
||
def on_search(self):
|
||
"""查询按钮点击事件"""
|
||
self.current_page = 1
|
||
self.load_data()
|
||
|
||
def load_data(self):
|
||
"""从后端加载数据"""
|
||
try:
|
||
# 构建请求URL
|
||
url = f"{self.api_base_url}/dbTableAccess"
|
||
|
||
# 构建查询参数
|
||
params = {
|
||
'table': 'testReqResult',
|
||
'pageSize': self.page_size,
|
||
'currentPage': self.current_page,
|
||
}
|
||
|
||
# 添加时间筛选参数(后端处理)- 格式:yyyy/MM/dd HH:mm:ss
|
||
start_dt = self.start_datetime_edit.dateTime().toString("yyyy/MM/dd HH:mm:ss")
|
||
end_dt = self.end_datetime_edit.dateTime().toString("yyyy/MM/dd HH:mm:ss")
|
||
params['date1'] = start_dt
|
||
params['date2'] = end_dt
|
||
|
||
# 添加样品编号筛选(后端处理)
|
||
sn_filter = self.sn_input.text().strip()
|
||
if sn_filter:
|
||
params['SN'] = sn_filter
|
||
|
||
# 添加工单筛选(后端处理)
|
||
workorder_filter = self.workorder_input.text().strip()
|
||
if workorder_filter:
|
||
params['dutWorkOrder'] = workorder_filter
|
||
|
||
print(f"[测试结果] 请求参数: {params}") # 调试输出
|
||
|
||
# 调用后端API
|
||
response = requests.get(url, params=params, timeout=10)
|
||
response.raise_for_status()
|
||
result = response.json()
|
||
|
||
print(f"[测试结果] 返回数据量: {result.get('length', 0)}, 总数: {result.get('totalCount', 0)}") # 调试输出
|
||
|
||
if 'data' in result:
|
||
self.all_data = result['data']
|
||
self.total_count = result.get('totalCount', len(self.all_data))
|
||
|
||
# 应用前端筛选(测试人员)
|
||
self.apply_frontend_filter()
|
||
else:
|
||
QMessageBox.warning(self, "错误", "获取数据失败")
|
||
|
||
except Exception as e:
|
||
print(f"加载测试结果数据失败: {e}")
|
||
QMessageBox.warning(self, "错误", f"加载数据失败:{str(e)}")
|
||
|
||
def apply_frontend_filter(self):
|
||
"""应用前端筛选(测试人员)
|
||
注意:样品编号、工单已由后端筛选,这里不再处理
|
||
"""
|
||
inspector_filter = self.inspector_input.text().strip().lower()
|
||
|
||
# 筛选数据
|
||
filtered = []
|
||
for item in self.all_data:
|
||
# 测试人员筛选 - 使用 tester 字段
|
||
if inspector_filter:
|
||
inspector = str(item.get('tester', '')).lower()
|
||
if inspector_filter not in inspector:
|
||
continue
|
||
|
||
filtered.append(item)
|
||
|
||
self.filtered_data = filtered
|
||
print(f"[测试结果] 筛选后数据量: {len(filtered)}") # 调试输出
|
||
self.refresh_table()
|
||
self.update_pagination_info()
|
||
|
||
def _format_cell_value(self, value):
|
||
"""格式化单元格值,将 None/null/空字符串显示为空白"""
|
||
if value is None or value == 'None' or value == 'null' or str(value).strip() == '':
|
||
return ''
|
||
return str(value)
|
||
|
||
def refresh_table(self):
|
||
"""刷新表格显示"""
|
||
self.table.setRowCount(len(self.filtered_data))
|
||
|
||
for idx, item in enumerate(self.filtered_data):
|
||
# 序号 - 使用后端返回的 mysequence
|
||
self.table.setItem(idx, 0, QTableWidgetItem(self._format_cell_value(item.get('mysequence', idx + 1))))
|
||
|
||
# 样品编号 - 使用 SN
|
||
dut_sn = self._format_cell_value(item.get('SN'))
|
||
self.table.setItem(idx, 1, QTableWidgetItem(dut_sn))
|
||
|
||
# 项目代码 - 使用 dutProject
|
||
self.table.setItem(idx, 2, QTableWidgetItem(self._format_cell_value(item.get('dutProject'))))
|
||
|
||
# 项目阶段 - 使用 dutPhase
|
||
self.table.setItem(idx, 3, QTableWidgetItem(self._format_cell_value(item.get('dutPhase'))))
|
||
|
||
# 项目方案 - 使用 dutProjectType
|
||
self.table.setItem(idx, 4, QTableWidgetItem(self._format_cell_value(item.get('dutProjectType'))))
|
||
|
||
# 测试周次 - 使用 dutWeeks
|
||
self.table.setItem(idx, 5, QTableWidgetItem(self._format_cell_value(item.get('dutWeeks'))))
|
||
|
||
# 工单名称 - 使用 dutWorkOrder
|
||
self.table.setItem(idx, 6, QTableWidgetItem(self._format_cell_value(item.get('dutWorkOrder'))))
|
||
|
||
# 产品序列号 - 蓝色显示 SN
|
||
product_sn = self._format_cell_value(item.get('SN'))
|
||
product_sn_item = QTableWidgetItem(product_sn)
|
||
if product_sn: # 只有有值时才设置蓝色
|
||
product_sn_item.setForeground(Qt.blue)
|
||
self.table.setItem(idx, 7, product_sn_item)
|
||
|
||
# 测试设备 - 使用 machine_sn
|
||
self.table.setItem(idx, 8, QTableWidgetItem(self._format_cell_value(item.get('machine_sn'))))
|
||
|
||
# 测试通道 - 使用 station_no
|
||
self.table.setItem(idx, 9, QTableWidgetItem(self._format_cell_value(item.get('station_no'))))
|
||
|
||
# 跌落方向 - 使用 dropDirection
|
||
self.table.setItem(idx, 10, QTableWidgetItem(self._format_cell_value(item.get('dropDirection'))))
|
||
|
||
# 跌落高度(mm) - 使用 dropHeight_int
|
||
self.table.setItem(idx, 11, QTableWidgetItem(self._format_cell_value(item.get('dropHeight_int'))))
|
||
|
||
# 跌落速度(次/Min) - 使用 dropSpeed
|
||
self.table.setItem(idx, 12, QTableWidgetItem(self._format_cell_value(item.get('dropSpeed'))))
|
||
|
||
# 样品跌落次数 - 使用 dropCycles
|
||
self.table.setItem(idx, 13, QTableWidgetItem(self._format_cell_value(item.get('dropCycles'))))
|
||
|
||
# 方向当前次数 - 使用 itemCurrentCycles
|
||
self.table.setItem(idx, 14, QTableWidgetItem(self._format_cell_value(item.get('itemCurrentCycles'))))
|
||
|
||
# 木板累计跌落次数 - 使用 stationDropCycles
|
||
self.table.setItem(idx, 15, QTableWidgetItem(self._format_cell_value(item.get('stationDropCycles'))))
|
||
|
||
# 试验开始时间 - 使用 startTime
|
||
self.table.setItem(idx, 16, QTableWidgetItem(self._format_cell_value(item.get('startTime'))))
|
||
|
||
# 试验结束时间 - 使用 endTime
|
||
self.table.setItem(idx, 17, QTableWidgetItem(self._format_cell_value(item.get('endTime'))))
|
||
|
||
self.table.resizeColumnsToContents()
|
||
|
||
def update_pagination_info(self):
|
||
"""更新分页信息"""
|
||
total_pages = max(1, (self.total_count + self.page_size - 1) // self.page_size)
|
||
self.page_info_label.setText(
|
||
f"共 {self.total_count} 条,第 {self.current_page}/{total_pages} 页,每页 {self.page_size} 条"
|
||
)
|
||
self.prev_btn.setEnabled(self.current_page > 1)
|
||
self.next_btn.setEnabled(self.current_page < total_pages)
|
||
|
||
def on_page_size_changed(self, text):
|
||
"""每页行数改变"""
|
||
try:
|
||
self.page_size = int(text)
|
||
except Exception:
|
||
self.page_size = 10
|
||
self.current_page = 1
|
||
self.load_data()
|
||
|
||
def go_prev_page(self):
|
||
"""上一页"""
|
||
if self.current_page > 1:
|
||
self.current_page -= 1
|
||
self.load_data()
|
||
|
||
def go_next_page(self):
|
||
"""下一页"""
|
||
total_pages = max(1, (self.total_count + self.page_size - 1) // self.page_size)
|
||
if self.current_page < total_pages:
|
||
self.current_page += 1
|
||
self.load_data()
|
||
|
||
def go_to_page(self):
|
||
"""跳转到指定页码"""
|
||
try:
|
||
# 获取输入的页码
|
||
page_text = self.page_input.text().strip()
|
||
|
||
if not page_text:
|
||
QMessageBox.warning(self, "提示", "请输入页码")
|
||
return
|
||
|
||
# 尝试转换为整数
|
||
try:
|
||
target_page = int(page_text)
|
||
except ValueError:
|
||
QMessageBox.warning(self, "错误", "页码必须是整数")
|
||
self.page_input.clear()
|
||
return
|
||
|
||
# 计算总页数
|
||
total_pages = max(1, (self.total_count + self.page_size - 1) // self.page_size)
|
||
|
||
# 检查页码有效性
|
||
if target_page < 1:
|
||
QMessageBox.warning(self, "错误", f"页码不能小于 1")
|
||
self.page_input.clear()
|
||
return
|
||
|
||
if target_page > total_pages:
|
||
QMessageBox.warning(
|
||
self,
|
||
"错误",
|
||
f"页码不能大于总页数 {total_pages}"
|
||
)
|
||
self.page_input.clear()
|
||
return
|
||
|
||
# 如果已经在当前页,不需要重复加载
|
||
if target_page == self.current_page:
|
||
QMessageBox.information(self, "提示", f"当前已在第 {target_page} 页")
|
||
self.page_input.clear()
|
||
return
|
||
|
||
# 跳转到目标页
|
||
self.current_page = target_page
|
||
self.page_input.clear()
|
||
self.load_data()
|
||
|
||
except Exception as e:
|
||
print(f"页码跳转失败: {e}")
|
||
QMessageBox.critical(self, "错误", f"跳转失败:{str(e)}")
|
||
self.page_input.clear()
|
||
|
||
def on_export(self):
|
||
"""导出结果到Excel"""
|
||
try:
|
||
# 获取满足筛选条件的总数
|
||
total_count = self.total_count
|
||
if total_count == 0:
|
||
QMessageBox.information(self, "提示", "没有数据可以导出")
|
||
return
|
||
|
||
# 生成默认文件名:testResult+日期
|
||
default_filename = f"testResult_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||
|
||
# 显示另存为对话框
|
||
file_path, _ = QFileDialog.getSaveFileName(
|
||
self,
|
||
"导出测试结果",
|
||
default_filename,
|
||
"Excel Files (*.xlsx);;All Files (*)"
|
||
)
|
||
|
||
if not file_path:
|
||
return # 用户取消
|
||
|
||
# 确保文件后缀名
|
||
if not file_path.endswith('.xlsx'):
|
||
file_path += '.xlsx'
|
||
|
||
# 显示进度对话框
|
||
progress = QProgressDialog("正在导出数据...", "取消", 0, 100, self)
|
||
progress.setWindowModality(Qt.WindowModal)
|
||
progress.setWindowTitle("导出进度")
|
||
progress.setMinimumDuration(0)
|
||
progress.setValue(0)
|
||
QApplication.processEvents()
|
||
|
||
# 分批获取策略:每次最多获取500条数据
|
||
batch_size = 500
|
||
total_batches = (total_count + batch_size - 1) // batch_size
|
||
|
||
# 创建Excel工作簿
|
||
wb = openpyxl.Workbook()
|
||
ws = wb.active
|
||
ws.title = "测试结果"
|
||
|
||
# 设置表头
|
||
headers = [
|
||
"序号", "样品编号", "项目代码", "项目阶段", "项目方案", "测试周次",
|
||
"工单名称", "产品序列号", "测试设备", "测试通道", "跌落方向",
|
||
"跌落高度(mm)", "跌落速度(次/Min)", "样品跌落次数", "方向当前次数",
|
||
"木板累计跌落次数", "试验开始时间", "试验结束时间"
|
||
]
|
||
ws.append(headers)
|
||
|
||
# 设置表头样式
|
||
header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
|
||
header_font = Font(bold=True, color="FFFFFF", size=11)
|
||
header_alignment = Alignment(horizontal="center", vertical="center")
|
||
|
||
for col_num, header in enumerate(headers, 1):
|
||
cell = ws.cell(row=1, column=col_num)
|
||
cell.fill = header_fill
|
||
cell.font = header_font
|
||
cell.alignment = header_alignment
|
||
|
||
# 分批获取并写入数据
|
||
all_data = []
|
||
for batch_num in range(total_batches):
|
||
if progress.wasCanceled():
|
||
QMessageBox.information(self, "提示", "导出已取消")
|
||
return
|
||
|
||
# 更新进度
|
||
progress_value = int((batch_num / total_batches) * 80) # 0-80%用于数据获取
|
||
progress.setValue(progress_value)
|
||
progress.setLabelText(f"正在获取数据... ({batch_num + 1}/{total_batches})")
|
||
QApplication.processEvents()
|
||
|
||
# 获取当前批次数据
|
||
batch_data = self._fetch_batch_data(batch_num + 1, batch_size)
|
||
if batch_data:
|
||
all_data.extend(batch_data)
|
||
|
||
# 应用前端筛选(测试人员)
|
||
progress.setValue(85)
|
||
progress.setLabelText("正在筛选数据...")
|
||
QApplication.processEvents()
|
||
|
||
filtered_data = self._filter_data_by_inspector(all_data)
|
||
|
||
# 写入数据到Excel
|
||
progress.setValue(90)
|
||
progress.setLabelText(f"正在写入Excel... 共{len(filtered_data)}条数据")
|
||
QApplication.processEvents()
|
||
|
||
for idx, item in enumerate(filtered_data):
|
||
row_data = [
|
||
self._format_cell_value(item.get('mysequence', idx + 1)),
|
||
self._format_cell_value(item.get('SN')),
|
||
self._format_cell_value(item.get('dutProject')),
|
||
self._format_cell_value(item.get('dutPhase')),
|
||
self._format_cell_value(item.get('dutProjectType')),
|
||
self._format_cell_value(item.get('dutWeeks')),
|
||
self._format_cell_value(item.get('dutWorkOrder')),
|
||
self._format_cell_value(item.get('SN')),
|
||
self._format_cell_value(item.get('machine_sn')),
|
||
self._format_cell_value(item.get('station_no')),
|
||
self._format_cell_value(item.get('dropDirection')),
|
||
self._format_cell_value(item.get('dropHeight_int')),
|
||
self._format_cell_value(item.get('dropSpeed')),
|
||
self._format_cell_value(item.get('dropCycles')),
|
||
self._format_cell_value(item.get('itemCurrentCycles')),
|
||
self._format_cell_value(item.get('stationDropCycles')),
|
||
self._format_cell_value(item.get('startTime')),
|
||
self._format_cell_value(item.get('endTime'))
|
||
]
|
||
ws.append(row_data)
|
||
|
||
# 每100行更新一次进度
|
||
if (idx + 1) % 100 == 0:
|
||
progress_value = 90 + int((idx + 1) / len(filtered_data) * 8) # 90-98%
|
||
progress.setValue(progress_value)
|
||
QApplication.processEvents()
|
||
|
||
# 设置列宽
|
||
for col_num in range(1, len(headers) + 1):
|
||
column_letter = get_column_letter(col_num)
|
||
ws.column_dimensions[column_letter].width = 15
|
||
|
||
# 保存文件
|
||
progress.setValue(99)
|
||
progress.setLabelText("正在保存文件...")
|
||
QApplication.processEvents()
|
||
|
||
wb.save(file_path)
|
||
|
||
progress.setValue(100)
|
||
progress.close()
|
||
|
||
QMessageBox.information(
|
||
self,
|
||
"成功",
|
||
f"导出成功!\n共导出 {len(filtered_data)} 条数据\n文件保存在:{file_path}"
|
||
)
|
||
|
||
except Exception as e:
|
||
print(f"导出失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
QMessageBox.critical(self, "错误", f"导出失败:{str(e)}")
|
||
|
||
def _fetch_batch_data(self, page, page_size):
|
||
"""获取指定页的数据"""
|
||
try:
|
||
url = f"{self.api_base_url}/dbTableAccess"
|
||
|
||
# 构建请求参数(与当前筛选条件一致)
|
||
params = {
|
||
'table': 'testReqResult',
|
||
'pageSize': page_size,
|
||
'currentPage': page,
|
||
}
|
||
|
||
# 添加时间筛选
|
||
start_dt = self.start_datetime_edit.dateTime().toString("yyyy/MM/dd HH:mm:ss")
|
||
end_dt = self.end_datetime_edit.dateTime().toString("yyyy/MM/dd HH:mm:ss")
|
||
params['date1'] = start_dt
|
||
params['date2'] = end_dt
|
||
|
||
# 添加样品编号筛选
|
||
sn_filter = self.sn_input.text().strip()
|
||
if sn_filter:
|
||
params['SN'] = sn_filter
|
||
|
||
# 添加工单筛选
|
||
workorder_filter = self.workorder_input.text().strip()
|
||
if workorder_filter:
|
||
params['dutWorkOrder'] = workorder_filter
|
||
|
||
# 调用后端API
|
||
response = requests.get(url, params=params, timeout=30)
|
||
response.raise_for_status()
|
||
result = response.json()
|
||
|
||
if 'data' in result:
|
||
return result['data']
|
||
return []
|
||
|
||
except Exception as e:
|
||
print(f"获取第{page}页数据失败: {e}")
|
||
return []
|
||
|
||
def _filter_data_by_inspector(self, data):
|
||
"""根据测试人员筛选数据(前端筛选)"""
|
||
inspector_filter = self.inspector_input.text().strip().lower()
|
||
|
||
if not inspector_filter:
|
||
return data
|
||
|
||
filtered = []
|
||
for item in data:
|
||
inspector = str(item.get('tester', '')).lower()
|
||
if inspector_filter in inspector:
|
||
filtered.append(item)
|
||
|
||
return filtered
|