#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 系统设置视图 """ from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGroupBox, QPushButton, QLineEdit, QComboBox, QCheckBox, QMessageBox, QTabWidget, QTableWidget, QTableWidgetItem, QHeaderView, QGridLayout, QScrollArea) from PyQt5.QtCore import Qt from viewmodels.system_settings_viewmodel import SystemSettingsViewModel from ui_utils.user_session import UserSession import json class SystemSettingsView(QWidget): def __init__(self): super().__init__() # 使用 ViewModel 管理数据(响应式) self.viewModel = SystemSettingsViewModel(self) # 获取用户角色 self.user_role = UserSession.get_role() # 检查权限 if not self._check_permission(): self._show_no_permission_ui() return self.init_ui() self._bind_signals() # 绑定响应式信号 self.viewModel.loadAll() # 加载所有数据 def _check_permission(self): """检查用户是否有权限访问系统设置""" return self.user_role in ['admin', 'super'] def _show_no_permission_ui(self): """显示无权限提示界面""" layout = QVBoxLayout(self) layout.setSpacing(15) title_label = QLabel("系统设置") title_label.setObjectName("title") layout.addWidget(title_label) # 无权限提示 tip_widget = QWidget() tip_layout = QVBoxLayout(tip_widget) tip_layout.setAlignment(Qt.AlignCenter) icon_label = QLabel("🔒") icon_label.setStyleSheet("font-size: 72px;") icon_label.setAlignment(Qt.AlignCenter) tip_layout.addWidget(icon_label) message_label = QLabel("您没有权限访问系统设置模块") message_label.setStyleSheet("font-size: 18px; color: #666; margin-top: 20px;") message_label.setAlignment(Qt.AlignCenter) tip_layout.addWidget(message_label) detail_label = QLabel("请联系管理员获取相应权限") detail_label.setStyleSheet("font-size: 14px; color: #999; margin-top: 10px;") detail_label.setAlignment(Qt.AlignCenter) tip_layout.addWidget(detail_label) layout.addWidget(tip_widget) def init_ui(self): """初始化界面(Tab 结构)""" layout = QVBoxLayout(self) layout.setSpacing(15) title_label = QLabel("系统设置") title_label.setObjectName("title") layout.addWidget(title_label) self.tab = QTabWidget() # 根据角色显示不同的Tab if self.user_role == 'super': # super 拥有所有4个模块 self.tab.addTab(self._init_machine_tab(), "机台设置") self.tab.addTab(self._init_phase_tab(), "项目阶段设置") self.tab.addTab(self._init_user_tab(), "用户及权限设置") self.tab.addTab(self._init_plc_tab(), "PLC设置") elif self.user_role == 'admin': # admin 只有机台设置和项目阶段设置 self.tab.addTab(self._init_machine_tab(), "机台设置") self.tab.addTab(self._init_phase_tab(), "项目阶段设置") layout.addWidget(self.tab) def _bind_signals(self): """绑定响应式信号 - 数据变化时自动刷新界面""" # 机台数据变化 -> 刷新机台表格 self.viewModel.machinesChanged.connect(self._refresh_machine_table) # 样品阶段数据变化 -> 刷新阶段表格 self.viewModel.phasesChanged.connect(self._refresh_phase_table) # 仅 super 角色才绑定 PLC 相关信号 if self.user_role == 'super': # 机台数据变化 -> 刷新 PLC 机台下拉框 self.viewModel.machinesChanged.connect(self._refresh_plc_machine_combo) # PLC 数据变化 -> 刷新 PLC 表单 self.viewModel.plcDataChanged.connect(self._refresh_plc_form) # 用户数据变化 -> 刷新用户表格 self.viewModel.usersChanged.connect(self._refresh_user_table) def _init_machine_tab(self): w = QWidget() v = QVBoxLayout(w) self.machine_table = QTableWidget() self.machine_table.setColumnCount(12) self.machine_table.setHorizontalHeaderLabels([ "序号","设备名称","设备编号","设备状态","PLC类型", "通道1","通道2","通道3","通道4","说明","串口","产品类型" ]) self.machine_table.setSelectionBehavior(QTableWidget.SelectRows) self.machine_table.setSelectionMode(QTableWidget.SingleSelection) self.machine_table.horizontalHeader().setStretchLastSection(True) self.machine_table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) self.machine_table.verticalHeader().setVisible(False) self.machine_table.setAlternatingRowColors(True) self.machine_table.setEditTriggers(QTableWidget.AllEditTriggers) v.addWidget(self.machine_table) hl = QHBoxLayout() add_btn = QPushButton("+ 增加") add_btn.clicked.connect(self._on_add_machine_row) save_btn = QPushButton("✓ 保存") save_btn.clicked.connect(self._on_save_machines) delete_btn = QPushButton("× 删除选中行") delete_btn.setStyleSheet("QPushButton { margin-left: 40px; background-color: #f44336; color: white; }") delete_btn.clicked.connect(self._on_delete_machine_row) hl.addWidget(add_btn) hl.addWidget(save_btn) hl.addWidget(delete_btn) hl.addStretch() v.addLayout(hl) return w def _init_phase_tab(self): w = QWidget() v = QVBoxLayout(w) self.phase_table = QTableWidget() self.phase_table.setColumnCount(4) self.phase_table.setHorizontalHeaderLabels(["序号","阶段名称","阶段标识","说明"]) self.phase_table.setSelectionBehavior(QTableWidget.SelectRows) self.phase_table.setSelectionMode(QTableWidget.SingleSelection) self.phase_table.horizontalHeader().setStretchLastSection(True) self.phase_table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) self.phase_table.verticalHeader().setVisible(False) self.phase_table.setAlternatingRowColors(True) self.phase_table.setEditTriggers(QTableWidget.AllEditTriggers) v.addWidget(self.phase_table) hl = QHBoxLayout() add_btn = QPushButton("+ 增加") add_btn.clicked.connect(self._on_add_phase_row) save_btn = QPushButton("✓ 保存") save_btn.clicked.connect(self._on_save_phases) delete_btn = QPushButton("× 删除选中行") delete_btn.setStyleSheet("QPushButton { margin-left: 40px; background-color: #f44336; color: white; }") delete_btn.clicked.connect(self._on_delete_phase_row) hl.addWidget(add_btn) hl.addWidget(save_btn) hl.addWidget(delete_btn) hl.addStretch() v.addLayout(hl) return w def _init_user_tab(self): w = QWidget() v = QVBoxLayout(w) self.user_table = QTableWidget() self.user_table.setColumnCount(7) self.user_table.setHorizontalHeaderLabels(["序号","用户名","用户角色","密码","userid","授权","说明"]) self.user_table.setSelectionBehavior(QTableWidget.SelectRows) self.user_table.setSelectionMode(QTableWidget.SingleSelection) self.user_table.horizontalHeader().setStretchLastSection(True) self.user_table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) self.user_table.verticalHeader().setVisible(False) self.user_table.setAlternatingRowColors(True) self.user_table.setEditTriggers(QTableWidget.AllEditTriggers) v.addWidget(self.user_table) hl = QHBoxLayout() add_btn = QPushButton("+ 增加") add_btn.clicked.connect(self._on_add_user_row) save_btn = QPushButton("✓ 保存") save_btn.clicked.connect(self._on_save_users) delete_btn = QPushButton("× 删除选中行") delete_btn.setStyleSheet("QPushButton { margin-left: 40px; background-color: #f44336; color: white; }") delete_btn.clicked.connect(self._on_delete_user_row) hl.addWidget(add_btn) hl.addWidget(save_btn) hl.addWidget(delete_btn) hl.addStretch() v.addLayout(hl) return w def _init_plc_tab(self): w = QWidget() v = QVBoxLayout(w) # 选择机台 top = QHBoxLayout() top.addWidget(QLabel("选择机台:")) self.plc_machine_combo = QComboBox() # 响应式绑定:下拉框变化 -> 更新 ViewModel 的当前机台 ID self.plc_machine_combo.currentIndexChanged.connect(self._on_plc_machine_changed) top.addWidget(self.plc_machine_combo) save_btn = QPushButton("✓ 保存到机台") save_btn.clicked.connect(self._on_save_plc_for_index) top.addWidget(save_btn) top.addStretch() v.addLayout(top) # 顶层 PLC 通用参数(一行两个字段) general = QGroupBox("PLC 通用参数") g = QGridLayout(general) g.addWidget(QLabel("站点地址(plcAddress):"), 0, 0) self.plc_address_input = QLineEdit() g.addWidget(self.plc_address_input, 0, 1) g.addWidget(QLabel("串口(com):"), 0, 2) self.plc_com_input = QLineEdit() g.addWidget(self.plc_com_input, 0, 3) g.addWidget(QLabel("串行参数(baudrate JSON):"), 1, 0) self.plc_baud_input = QLineEdit() g.addWidget(self.plc_baud_input, 1, 1) g.addWidget(QLabel("测试类型(testType):"), 1, 2) self.plc_testtype_input = QLineEdit() g.addWidget(self.plc_testtype_input, 1, 3) v.addWidget(general) # 通道编辑器(每通道使用网格布局,一行两个参数) self.ch_enable = {} self.ch_edits = {1:{},2:{},3:{},4:{}} # 将所有4个通道放在滚动区域 scroll = QScrollArea() scroll.setWidgetResizable(True) scroll_content = QWidget() scroll_layout = QVBoxLayout(scroll_content) def build_channel(ch_no): box = QGroupBox(f"通道{ch_no}") vl = QVBoxLayout(box) # 启用状态标签(只读显示) status_label = QLabel() status_label.setObjectName(f"ch{ch_no}_status") vl.addWidget(status_label) self.ch_enable[ch_no] = status_label # 存储标签而非复选框 # 使用网格布局,每行2个参数 grid = QGridLayout() # 每个参数项:标签 + 输入框 # 按照示例 JSON 字段,布局为 6 行 x 4 列(每行2对 label-input) fields = [ ("高度(height)", "height"), ("循环次数(cycles)", "cycles"), ("已完成(cyclesFinished)", "cyclesFinished"), ("开始(start)", "start"), ("停止(stop)", "stop"), ("暂停(pause)", "pause"), ("运行状态(runStatus)", "runStatus"), ("方向(direction)", "direction"), ("DUT基础(dutBasic)", "dutBasic"), ("DUT参数(dutParam)", "dutParam"), ("结果开关(resultSwitch)", "resultSwitch"), ("站点计数(stationResultCounter)", "stationResultCounter"), ] row = 0 col = 0 for label_text, key in fields: lb = QLabel(label_text) le = QLineEdit() grid.addWidget(lb, row, col) grid.addWidget(le, row, col+1) self.ch_edits[ch_no][key] = le # 每行2个参数,布局为 4 列:(label1, input1, label2, input2) col += 2 if col >= 4: col = 0 row += 1 vl.addLayout(grid) return box for ch in range(1,5): b = build_channel(ch) scroll_layout.addWidget(b) scroll.setWidget(scroll_content) v.addWidget(scroll) return w # ============ 响应式刷新方法 ============ def _refresh_machine_table(self): """响应式刷新机台表格(由 machinesChanged 信号触发)""" data = self.viewModel.machines self.machine_table.setRowCount(len(data)) for i, m in enumerate(data): self.machine_table.setItem(i, 0, QTableWidgetItem(str(i + 1))) self.machine_table.setItem(i, 1, QTableWidgetItem(m.get("label", ""))) self.machine_table.setItem(i, 2, QTableWidgetItem(m.get("SN", ""))) self.machine_table.setItem(i, 3, QTableWidgetItem(m.get("status", ""))) self.machine_table.setItem(i, 4, QTableWidgetItem(m.get("type", ""))) # 通道状态: 使用绿色复选框表示启用 cb1 = QCheckBox() cb1.setChecked(str(m.get("station1", "")).lower() in ("1","01","enabled")) cb1.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 5, cb1) cb2 = QCheckBox() cb2.setChecked(str(m.get("station2", "")).lower() in ("2","02","enabled")) cb2.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 6, cb2) cb3 = QCheckBox() cb3.setChecked(str(m.get("station3", "")).lower() in ("3","03","enabled")) cb3.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 7, cb3) cb4 = QCheckBox() cb4.setChecked(str(m.get("station4", "")).lower() in ("4","04","enabled")) cb4.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 8, cb4) self.machine_table.setItem(i, 9, QTableWidgetItem(m.get("description", ""))) self.machine_table.setItem(i, 10, QTableWidgetItem(m.get("com", ""))) self.machine_table.setItem(i, 11, QTableWidgetItem(m.get("testType", ""))) # 将 id 缓存到行的0列的 data 中 self.machine_table.item(i,0).setData(Qt.UserRole, m.get("id")) self.machine_table.resizeColumnsToContents() def _refresh_plc_machine_combo(self): """响应式刷新 PLC 机台下拉框(由 machinesChanged 信号触发)""" # 仅在 super 角色且控件存在时执行 if not hasattr(self, 'plc_machine_combo'): return machines = self.viewModel.machines # 临时阻断信号,避免填充过程中多次触发 self.plc_machine_combo.blockSignals(True) current_id = self.viewModel.currentMachineId self.plc_machine_combo.clear() selected_index = -1 for idx, m in enumerate(machines): self.plc_machine_combo.addItem(m.get("label",""), m.get("id")) if m.get("id") == current_id: selected_index = idx self.plc_machine_combo.blockSignals(False) # 恢复选中项 if selected_index >= 0: self.plc_machine_combo.setCurrentIndex(selected_index) elif self.plc_machine_combo.count() > 0: self.plc_machine_combo.setCurrentIndex(0) def _refresh_phase_table(self): """响应式刷新样品阶段表格(由 phasesChanged 信号触发)""" data = self.viewModel.phases self.phase_table.setRowCount(len(data)) for i, p in enumerate(data): self.phase_table.setItem(i, 0, QTableWidgetItem(str(i + 1))) self.phase_table.setItem(i, 1, QTableWidgetItem(p.get("name", ""))) self.phase_table.setItem(i, 2, QTableWidgetItem(p.get("PN", ""))) self.phase_table.setItem(i, 3, QTableWidgetItem(p.get("description", ""))) self.phase_table.item(i,0).setData(Qt.UserRole, p.get("id")) self.phase_table.resizeColumnsToContents() def _refresh_user_table(self): """响应式刷新用户表格(由 usersChanged 信号触发)""" # 仅在 super 角色且控件存在时执行 if not hasattr(self, 'user_table'): return data = self.viewModel.users self.user_table.setRowCount(len(data)) for i, u in enumerate(data): self.user_table.setItem(i, 0, QTableWidgetItem(str(i + 1))) self.user_table.setItem(i, 1, QTableWidgetItem(u.get("username", ""))) self.user_table.setItem(i, 2, QTableWidgetItem(u.get("role", ""))) self.user_table.setItem(i, 3, QTableWidgetItem(u.get("password", ""))) self.user_table.setItem(i, 4, QTableWidgetItem(u.get("userid", ""))) self.user_table.setItem(i, 5, QTableWidgetItem(u.get("auth", ""))) self.user_table.setItem(i, 6, QTableWidgetItem(u.get("description", ""))) self.user_table.item(i,0).setData(Qt.UserRole, u.get("id")) self.user_table.resizeColumnsToContents() def _refresh_plc_form(self): """响应式刷新 PLC 表单(由 plcDataChanged 信号触发)""" # 仅在 super 角色且控件存在时执行 if not hasattr(self, 'plc_address_input'): return print(f"Refreshing PLC form--更新PLC表单") plc = self.viewModel.plcData # 通用参数 self.plc_address_input.setText(str(plc.get("plcAddress", ""))) self.plc_com_input.setText(str(plc.get("com", ""))) self.plc_testtype_input.setText(str(plc.get("testType", ""))) # baudrate baud = plc.get("baudrate", {}) if isinstance(baud, dict): self.plc_baud_input.setText(json.dumps(baud, ensure_ascii=False)) else: self.plc_baud_input.setText(str(baud)) # 通道启用状态(只读显示) def set_status(ch_no, enabled): label = self.ch_enable[ch_no] if enabled: label.setText("✓ 已启用") label.setStyleSheet("color: green; font-weight: bold;") else: label.setText("✗ 未启用") label.setStyleSheet("color: gray;") set_status(1, plc.get("status1", False)) set_status(2, plc.get("status2", False)) set_status(3, plc.get("status3", False)) set_status(4, plc.get("status4", False)) # 通道参数 def fill_channel(ch_no, reg_data): if isinstance(reg_data, dict): data = reg_data else: data = {} for key, le in self.ch_edits[ch_no].items(): le.setText(str(data.get(key, ""))) fill_channel(1, plc.get("register01",{})) fill_channel(2, plc.get("register02",{})) fill_channel(3, plc.get("register03",{})) fill_channel(4, plc.get("register04",{})) def _on_plc_machine_changed(self): """下拉框切换机台时,更新 ViewModel 的当前机台 ID(响应式触发 PLC 数据刷新)""" idx = self.plc_machine_combo.currentIndex() if idx >= 0: print(f"Switching to machine index: {idx}") machine_id = self.plc_machine_combo.itemData(idx) self.viewModel.setCurrentMachineId(machine_id) # 设置后自动触发 plcDataChanged 信号 # ============ 原有事件处理(改为调用 ViewModel) ============ def _load_all_tabs(self): self._load_machine_table() self._load_phase_table() self._load_user_table() # 填充 PLC 机台选择 machines = self.model.load_machines() # 临时阻断信号,避免填充过程中多次触发 self.plc_machine_combo.blockSignals(True) self.plc_machine_combo.clear() for m in machines: # 用 label 作为显示文本,id 作为数据 self.plc_machine_combo.addItem(m.get("label",""), m.get("id")) self.plc_machine_combo.blockSignals(False) # 绑定信号(只绑定一次) try: self.plc_machine_combo.currentIndexChanged.disconnect() except: pass self.plc_machine_combo.currentIndexChanged.connect(self._on_load_plc_for_index) # 如果有机台,默认加载第一个 if self.plc_machine_combo.count() > 0: self.plc_machine_combo.setCurrentIndex(0) self._on_load_plc_for_index() # 机台设置 def _load_machine_table(self): data = self.model.load_machines() self.machine_table.setRowCount(len(data)) for i, m in enumerate(data): self.machine_table.setItem(i, 0, QTableWidgetItem(str(i + 1))) self.machine_table.setItem(i, 1, QTableWidgetItem(m.get("label", ""))) self.machine_table.setItem(i, 2, QTableWidgetItem(m.get("SN", ""))) self.machine_table.setItem(i, 3, QTableWidgetItem(m.get("status", ""))) self.machine_table.setItem(i, 4, QTableWidgetItem(m.get("type", ""))) # 通道状态: 使用绿色复选框表示启用 cb1 = QCheckBox() cb1.setChecked(str(m.get("status1", "")).lower() in ("1","true","enabled")) cb1.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 5, cb1) cb2 = QCheckBox() cb2.setChecked(str(m.get("status2", "")).lower() in ("1","true","enabled")) cb2.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 6, cb2) cb3 = QCheckBox() cb3.setChecked(str(m.get("status3", "")).lower() in ("1","true","enabled")) cb3.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 7, cb3) cb4 = QCheckBox() cb4.setChecked(str(m.get("status4", "")).lower() in ("1","true","enabled")) cb4.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(i, 8, cb4) self.machine_table.setItem(i, 9, QTableWidgetItem(m.get("description", ""))) self.machine_table.setItem(i, 10, QTableWidgetItem(m.get("com", ""))) # 将 id 缓存到行的第0列的 data 中 self.machine_table.item(i,0).setData(Qt.UserRole, m.get("id")) self.machine_table.resizeColumnsToContents() def _on_add_machine_row(self): r = self.machine_table.rowCount() self.machine_table.insertRow(r) self.machine_table.setItem(r, 0, QTableWidgetItem(str(r + 1))) self.machine_table.setItem(r, 1, QTableWidgetItem("")) self.machine_table.setItem(r, 2, QTableWidgetItem("")) self.machine_table.setItem(r, 3, QTableWidgetItem("")) self.machine_table.setItem(r, 4, QTableWidgetItem("")) # 通道启用复选框 - 绿色样式 cb1 = QCheckBox() cb1.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(r, 5, cb1) cb2 = QCheckBox() cb2.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(r, 6, cb2) cb3 = QCheckBox() cb3.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(r, 7, cb3) cb4 = QCheckBox() cb4.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }") self.machine_table.setCellWidget(r, 8, cb4) self.machine_table.setItem(r, 9, QTableWidgetItem("")) self.machine_table.setItem(r, 10, QTableWidgetItem("")) def _on_save_machines(self): """保存机台设置(调用 ViewModel) 重要:只传入机台基本信息和通道启用状态,不传入 PLC 参数 """ rows = self.machine_table.rowCount() to_save = [] for i in range(rows): def get(i,c): item = self.machine_table.item(i,c) return item.text().strip() if item else "" def get_cb(i,c,value): w = self.machine_table.cellWidget(i,c) try: return value if (w and isinstance(w, QCheckBox) and w.isChecked()) else "" except Exception: return "" # 只传入机台设置页的字段,不包含 PLC 相关字段 m = { "id": self.machine_table.item(i,0).data(Qt.UserRole), "label": get(i,1), "SN": get(i,2), "status": get(i,3), "type": get(i,4), "testType": get(i,11), # 产品类型 "station1": get_cb(i,5,"01"), # 通道启用状态 "station2": get_cb(i,6,"02"), "station3": get_cb(i,7,"03"), "station4": get_cb(i,8,"04"), "description": get(i,9), "com": get(i,10), # 不传入 PLC 相关字段,保持数据库中的原有值 # "station1", "station2", "station3", "station4", # "plcAddress", "register01", "register02", "register03", "register04", # "baudrate", "testType" } to_save.append(m) self.viewModel.saveMachines(to_save) # 调用 ViewModel,自动触发响应式刷新 QMessageBox.information(self, "保存成功", "机台设置已保存") def _on_delete_machine_row(self): """删除选中的机台行""" selected_rows = self.machine_table.selectionModel().selectedRows() if not selected_rows: QMessageBox.warning(self, "提示", "请先选中要删除的行") return # 确认删除 reply = QMessageBox.question( self, "确认删除", f"确定要删除选中的 {len(selected_rows)} 条机台记录吗?\n\n注意:此操作不可恢复!", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.No: return # 收集要删除的 ID machine_ids = [] for index in selected_rows: row = index.row() id_item = self.machine_table.item(row, 0) if id_item: machine_id = id_item.data(Qt.UserRole) if machine_id: machine_ids.append(machine_id) # 调用 ViewModel 删除 if machine_ids: success = self.viewModel.deleteMachines(machine_ids) if success: QMessageBox.information(self, "成功", f"已删除 {len(machine_ids)} 条机台记录") else: QMessageBox.warning(self, "错误", "删除失败,请重试") # 样品阶段设置(旧手动加载方法,已被响应式 _refresh_phase_table 取代) def _load_phase_table(self): """已废弃,使用响应式 _refresh_phase_table 代替""" pass def _on_add_phase_row(self): r = self.phase_table.rowCount() self.phase_table.insertRow(r) self.phase_table.setItem(r, 0, QTableWidgetItem(str(r + 1))) for c in range(1, 4): self.phase_table.setItem(r, c, QTableWidgetItem("")) def _on_save_phases(self): """保存样品阶段设置(调用 ViewModel)""" rows = self.phase_table.rowCount() phases = [] for i in range(rows): def get(i,c): item = self.phase_table.item(i,c) return item.text().strip() if item else "" p = { "id": self.phase_table.item(i,0).data(Qt.UserRole), "name": get(i,1), "PN": get(i,2), "description": get(i,3), } phases.append(p) self.viewModel.savePhases(phases) # 调用 ViewModel,自动触发响应式刷新 QMessageBox.information(self, "保存成功", "样品阶段设置已保存") def _on_delete_phase_row(self): """删除选中的阶段行""" selected_rows = self.phase_table.selectionModel().selectedRows() if not selected_rows: QMessageBox.warning(self, "提示", "请先选中要删除的行") return # 确认删除 reply = QMessageBox.question( self, "确认删除", f"确定要删除选中的 {len(selected_rows)} 条阶段记录吗?\n\n注意:此操作不可恢复!", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.No: return # 收集要删除的 ID phase_ids = [] for index in selected_rows: row = index.row() id_item = self.phase_table.item(row, 0) if id_item: phase_id = id_item.data(Qt.UserRole) if phase_id: phase_ids.append(phase_id) # 调用 ViewModel 删除 if phase_ids: success = self.viewModel.deletePhases(phase_ids) if success: QMessageBox.information(self, "成功", f"已删除 {len(phase_ids)} 条阶段记录") else: QMessageBox.warning(self, "错误", "删除失败,请重试") # 用户及权限设置(旧手动加载方法,已被响应式 _refresh_user_table 取代) def _load_user_table(self): """已废弃,使用响应式 _refresh_user_table 代替""" pass def _on_add_user_row(self): r = self.user_table.rowCount() self.user_table.insertRow(r) self.user_table.setItem(r, 0, QTableWidgetItem(str(r + 1))) for c in range(1, 7): self.user_table.setItem(r, c, QTableWidgetItem("")) def _on_save_users(self): """保存用户设置(调用 ViewModel)""" rows = self.user_table.rowCount() users = [] for i in range(rows): def get(i,c): item = self.user_table.item(i,c) return item.text().strip() if item else "" u = { "id": self.user_table.item(i,0).data(Qt.UserRole), "username": get(i,1), "role": get(i,2), "password": get(i,3), "userid": get(i,4), "auth": get(i,5), "description": get(i,6), } users.append(u) self.viewModel.saveUsers(users) # 调用 ViewModel,自动触发响应式刷新 QMessageBox.information(self, "保存成功", "用户及权限设置已保存") def _on_delete_user_row(self): """删除选中的用户行""" selected_rows = self.user_table.selectionModel().selectedRows() if not selected_rows: QMessageBox.warning(self, "提示", "请先选中要删除的行") return # 确认删除 reply = QMessageBox.question( self, "确认删除", f"确定要删除选中的 {len(selected_rows)} 条用户记录吗?\n\n注意:此操作不可恢复!", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.No: return # 收集要删除的 ID user_ids = [] for index in selected_rows: row = index.row() id_item = self.user_table.item(row, 0) if id_item: user_id = id_item.data(Qt.UserRole) if user_id: user_ids.append(user_id) # 调用 ViewModel 删除 if user_ids: success = self.viewModel.deleteUsers(user_ids) if success: QMessageBox.information(self, "成功", f"已删除 {len(user_ids)} 条用户记录") else: QMessageBox.warning(self, "错误", "删除失败,请重试") # PLC: 旧的手动加载方法(已被响应式 _refresh_plc_form 取代,保留用于兼容) def _on_load_plc_for_index(self): """已废弃,使用响应式 _refresh_plc_form 代替""" pass def _on_save_plc_for_index(self): """保存 PLC 设置(响应式,调用 ViewModel)""" # 收集每通道参数为 dict def collect(ch_no): payload = {} for key, le in self.ch_edits[ch_no].items(): val = le.text().strip() if val != "": payload[key] = val return payload # baudrate 从输入框解析,容错处理 baud_text = self.plc_baud_input.text().strip() try: baud_obj = json.loads(baud_text) if baud_text else {"baudrate": 19200, "stopbits": 1, "bytesize": 8, "parity": "E"} except Exception: baud_obj = {"baudrate": 19200, "stopbits": 1, "bytesize": 8, "parity": "E"} # 构建更新数据(register01~04 和 baudrate 都为 dict,ViewModel 会自动转换为 JSON 字符串) plc_updates = { "plcAddress": self.plc_address_input.text().strip(), "com": self.plc_com_input.text().strip(), "baudrate": baud_obj, # dict 格式 "testType": self.plc_testtype_input.text().strip(), # 通道启用状态不在此页修改,保持原值 "register01": collect(1), # dict 格式 "register02": collect(2), # dict 格式 "register03": collect(3), # dict 格式 "register04": collect(4), # dict 格式 } # 调用 ViewModel 保存(自动触发响应式刷新) success = self.viewModel.savePlcData(plc_updates) if success: QMessageBox.information(self, "保存成功", "PLC 设置已保存到机台") else: QMessageBox.warning(self, "保存失败", "请先选择机台")