Files
dtm-py-all/UI/views/test_result_view.py

761 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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