diff --git a/Static/Procedure.qss b/Static/Procedure.qss index ede668f..f93afdd 100644 --- a/Static/Procedure.qss +++ b/Static/Procedure.qss @@ -2,9 +2,9 @@ /* ==================== 全局样式 ==================== */ QWidget { - background-color: #F5F7FA; - color: #374151; - font-family: "PingFangSC-Regular", "Microsoft YaHei", "Segoe UI", Arial, sans-serif; + background-color: #F8FAFC; + color: #1F2937; + font-family: "PingFangSC-Medium", "Microsoft YaHei", "Segoe UI", Arial, sans-serif; font-size: 14px; selection-background-color: #3B82F6; selection-color: #FFFFFF; @@ -34,30 +34,38 @@ QToolBar::separator { margin: 8px 6px; } -/* 工具栏按钮 */ +/* ==================== 工具栏按钮样式 - 与变量表按钮一致 ==================== */ +/* 工具栏按钮基础样式 */ QToolBar QToolButton { - background-color: #FFFFFF; - border: 1px solid #E5E7EB; - border-radius: 8px; - padding: 12px 16px; - margin: 4px 2px; - color: #374151; - font-size: 13px; - font-weight: 500; + color: #047857; + font-size: 15px; + font-weight: 600; + padding: 8px 16px; + background-color: #ECFDF5; + border-radius: 6px; + border: 1px solid #BBF7D0; + margin: 2px; min-width: 80px; - min-height: 36px; + min-height: 32px; + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; } QToolBar QToolButton:hover { - background-color: #F9FAFB; - border-color: #D1D5DB; - color: #1F2937; + background-color: #D1FAE5; + border-color: #86EFAC; + color: #065F46; } QToolBar QToolButton:pressed { - background-color: #F3F4F6; - border-color: #9CA3AF; - color: #111827; + background-color: #A7F3D0; + border-color: #6EE7B7; + color: #064E3B; +} + +QToolBar QToolButton:disabled { + background-color: #F9FAFB; + color: #9CA3AF; + border-color: #E5E7EB; } /* ==================== 标签页样式 ==================== */ @@ -390,7 +398,7 @@ QLineEdit { background-color: #FFFFFF; border: 2px solid #E5E7EB; border-radius: 8px; - padding: 12px 16px; + padding: 4px 8px; color: #374151; font-size: 14px; font-weight: 400; @@ -406,11 +414,34 @@ QLineEdit:hover { border-color: #9CA3AF; } +/* 规程编辑器中的小型输入框 */ +QLineEdit#procedureNameEdit, +QLineEdit#procedureNumberEdit, +QLineEdit#procedureTypeEdit, +QLineEdit#procedureDescriptionEdit { + padding: 4px 8px; + font-size: 12px; + font-family: "Microsoft YaHei", "PingFangSC-Regular", sans-serif; + border: 1px solid #E5E7EB; + border-radius: 4px; + min-height: 22px; + line-height: 1.3; + color: #374151; +} + +QLineEdit#procedureNameEdit:focus, +QLineEdit#procedureNumberEdit:focus, +QLineEdit#procedureTypeEdit:focus, +QLineEdit#procedureDescriptionEdit:focus { + border-color: #3B82F6; + background-color: #FEFEFF; +} + QTextEdit { background-color: #FFFFFF; border: 2px solid #E5E7EB; border-radius: 8px; - padding: 16px; + /* padding: 16px; */ color: #374151; font-size: 14px; line-height: 1.5; @@ -1218,4 +1249,798 @@ QLabel#statsChange[trend="down"] { QLabel#statsChange[trend="stable"] { background-color: #F3F4F6; color: #6B7280; +} + +/* ==================== 规程编辑器专用样式 ==================== */ +/* 基本信息标题 */ +QLabel#basicInfoTitle { + font-size: 12px; + font-weight: bold; + color: #606266; +} + +/* 步骤列表标题 */ +QLabel#stepsTitle { + font-size: 12px; + font-weight: bold; + color: #606266; +} + +/* 字段标签样式 */ +QLabel[fieldLabel="true"] { + font-size: 11px; + font-weight: bold; + color: #909399; +} + +/* 添加步骤按钮 */ +QPushButton#addStepButton { + font-size: 11px; + padding: 4px 8px; + background-color: #10B981; + color: white; + border: none; + border-radius: 4px; + font-weight: 600; +} + +QPushButton#addStepButton:hover { + background-color: #059669; +} + +QPushButton#addStepButton:pressed { + background-color: #047857; +} + +/* 删除步骤按钮 */ +QPushButton#deleteStepButton { + font-size: 11px; + padding: 4px 8px; + background-color: #EF4444; + color: white; + border: none; + border-radius: 4px; + font-weight: 600; +} + +QPushButton#deleteStepButton:hover { + background-color: #DC2626; +} + +QPushButton#deleteStepButton:pressed { + background-color: #B91C1C; +} + +/* 保存规程按钮 */ +QPushButton#saveProcedureButton { + font-size: 11px; + padding: 6px 12px; + background-color: #3B82F6; + color: white; + border: none; + border-radius: 4px; + font-weight: 600; +} + +QPushButton#saveProcedureButton:hover { + background-color: #2563EB; +} + +QPushButton#saveProcedureButton:pressed { + background-color: #1D4ED8; +} + +/* 取消编辑按钮 */ +QPushButton#cancelEditButton { + font-size: 11px; + padding: 6px 12px; + background-color: #6B7280; + color: white; + border: none; + border-radius: 4px; + font-weight: 600; +} + +QPushButton#cancelEditButton:hover { + background-color: #4B5563; +} + +QPushButton#cancelEditButton:pressed { + background-color: #374151; +} + +/* 规程编辑器步骤表格 */ +QTableWidget#stepsTable { + gridline-color: #EBEEF5; + font-size: 11px; + border: 1px solid #DCDFE6; + border-radius: 4px; + background-color: #FFFFFF; + alternate-background-color: #F9FAFC; +} + +QTableWidget#stepsTable::item { + padding: 4px; + border-bottom: 1px solid #EBEEF5; +} + +QTableWidget#stepsTable::item:selected { + background-color: #ECF5FF; + color: #409EFF; +} + +QTableWidget#stepsTable QHeaderView::section { + background-color: #F5F7FA; + color: #606266; + padding: 6px; + border: none; + border-right: 1px solid #EBEEF5; + border-bottom: 1px solid #DCDFE6; + font-weight: bold; + font-size: 11px; +} + +/* ==================== 步骤执行器样式 ==================== */ +/* 超紧凑信息标签 */ +QLabel#ultraCompactInfoLabel { + font-size: 11px; + font-weight: bold; + color: #606266; +} + +/* 超紧凑信息值 */ +QLabel#ultraCompactInfoValue { + font-size: 11px; + color: #374151; +} + +/* 执行状态标签 */ +QLabel#executionStatusLabel { + font-weight: bold; +} + +/* 执行状态 - 成功 */ +QLabel#executionStatusLabel[status="success"] { + color: #059669; +} + +/* 执行状态 - 错误 */ +QLabel#executionStatusLabel[status="error"] { + color: #DC2626; +} + +/* 执行状态 - 警告 */ +QLabel#executionStatusLabel[status="warning"] { + color: #D97706; +} + +/* 执行状态 - 信息 */ +QLabel#executionStatusLabel[status="info"] { + color: #2563EB; +} + +/* ==================== 规程管理界面按钮样式 ==================== */ +/* 卡片标题 */ +QLabel#cardTitle { + font-size: 16px; + font-weight: 600; + color: #1F2937; + padding: 0 0 12px 0; + border-bottom: 2px solid #E5E7EB; + margin-bottom: 16px; +} + +/* 规程统计标签 */ +QLabel#procedureCountLabel { + color: #6B7280; + font-size: 13px; + font-weight: 500; + padding: 6px 12px; + background-color: #F3F4F6; + border-radius: 16px; +} + +/* 搜索框 */ +QLineEdit#searchEdit { + padding: 8px 12px; + font-size: 14px; + border-radius: 6px; + border: 2px solid #E5E7EB; + background-color: #FFFFFF; +} + +QLineEdit#searchEdit:focus { + border-color: #3B82F6; + background-color: #FEFEFF; +} + +/* 类型过滤器 */ +QComboBox#typeFilter { + padding: 8px 12px; + font-size: 14px; + border-radius: 6px; + border: 2px solid #E5E7EB; + background-color: #FFFFFF; + min-width: 120px; +} + +QComboBox#typeFilter:focus { + border-color: #3B82F6; +} + +/* 添加分类按钮 */ +QPushButton#addCategoryBtn { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #3B82F6, stop:1 #2563EB); + border: 1px solid #2563EB; + color: #FFFFFF; + font-size: 13px; + font-weight: 600; + padding: 8px 16px; + border-radius: 6px; + min-height: 32px; +} + +QPushButton#addCategoryBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2563EB, stop:1 #1D4ED8); + border-color: #1D4ED8; +} + +QPushButton#addCategoryBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #1D4ED8, stop:1 #1E40AF); + border-color: #1E40AF; +} + +/* 删除分类按钮 */ +QPushButton#deleteCategoryBtn { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #EF4444, stop:1 #DC2626); + border: 1px solid #DC2626; + color: #FFFFFF; + font-size: 13px; + font-weight: 600; + padding: 8px 16px; + border-radius: 6px; + min-height: 32px; +} + +QPushButton#deleteCategoryBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #DC2626, stop:1 #B91C1C); + border-color: #B91C1C; +} + +QPushButton#deleteCategoryBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #B91C1C, stop:1 #991B1B); + border-color: #991B1B; +} + +/* 导入规程按钮 */ +QPushButton#importBtn { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #10B981, stop:1 #059669); + border: 1px solid #059669; + color: #FFFFFF; + font-size: 13px; + font-weight: 600; + padding: 8px 16px; + border-radius: 6px; + min-height: 32px; +} + +QPushButton#importBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #059669, stop:1 #047857); + border-color: #047857; +} + +QPushButton#importBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #047857, stop:1 #065F46); + border-color: #065F46; +} + +/* 打开规程按钮 */ +QPushButton#openBtn { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #3B82F6, stop:1 #2563EB); + border: 1px solid #2563EB; + color: #FFFFFF; + font-size: 13px; + font-weight: 600; + padding: 8px 16px; + border-radius: 6px; + min-height: 32px; +} + +QPushButton#openBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2563EB, stop:1 #1D4ED8); + border-color: #1D4ED8; +} + +QPushButton#openBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #1D4ED8, stop:1 #1E40AF); + border-color: #1E40AF; +} + +/* 导出规程按钮 */ +QPushButton#exportBtn { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #F59E0B, stop:1 #D97706); + border: 1px solid #D97706; + color: #FFFFFF; + font-size: 13px; + font-weight: 600; + padding: 8px 16px; + border-radius: 6px; + min-height: 32px; +} + +QPushButton#exportBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #D97706, stop:1 #B45309); + border-color: #B45309; +} + +QPushButton#exportBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #B45309, stop:1 #92400E); + border-color: #92400E; +} + +/* 删除规程按钮 */ +QPushButton#deleteBtn { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #EF4444, stop:1 #DC2626); + border: 1px solid #DC2626; + color: #FFFFFF; + font-size: 13px; + font-weight: 600; + padding: 8px 16px; + border-radius: 6px; + min-height: 32px; +} + +QPushButton#deleteBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #DC2626, stop:1 #B91C1C); + border-color: #B91C1C; +} + +QPushButton#deleteBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #B91C1C, stop:1 #991B1B); + border-color: #991B1B; +} + +/* ==================== 历史查看器样式 ==================== */ +/* 历史记录表格 */ +QTableView#historyTable { + gridline-color: #d0d0d0; + background-color: #FFFFFF; + alternate-background-color: #F8FAFC; + selection-background-color: #EBF8FF; + selection-color: #1E40AF; + border: 1px solid #E5E7EB; + border-radius: 6px; + font-size: 13px; +} + +QTableView#historyTable::item { + padding: 8px 12px; + border: none; +} + +QTableView#historyTable::item:hover { + background-color: #F3F4F6; +} + +QTableView#historyTable::item:selected { + background-color: #EBF8FF; + color: #1E40AF; +} + +/* 步骤详情信息组 */ +QWidget#stepDetailInfoGroup { + background-color: #f8f9fa; + border: 1px solid #E5E7EB; + border-radius: 8px; + padding: 16px; +} + +/* 步骤描述文本 */ +QLineEdit#stepDescriptionText { + background-color: #f8f9fa; + border: 1px solid #E5E7EB; + border-radius: 4px; + padding: 8px; + font-size: 13px; + color: #374151; +} + +/* 执行结果文本 - 成功 */ +QLineEdit#stepResultText[status="success"] { + background-color: #d4edda; + border: 1px solid #c3e6cb; + border-radius: 4px; + padding: 8px; + font-size: 13px; + color: #155724; +} + +/* 执行结果文本 - 失败 */ +QLineEdit#stepResultText[status="error"] { + background-color: #f8d7da; + border: 1px solid #f5c6cb; + border-radius: 4px; + padding: 8px; + font-size: 13px; + color: #721c24; +} + +/* 执行结果文本 - 默认 */ +QLineEdit#stepResultText[status="default"] { + background-color: #fff3cd; + border: 1px solid #ffeaa7; + border-radius: 4px; + padding: 8px; + font-size: 13px; + color: #856404; +} + +/* ==================== 规程管理工具栏按钮样式 - 与变量表按钮一致 ==================== */ +/* 工具栏按钮基础样式 - 与变量表保持一致 */ +QToolBar QToolButton { + font-size: 14px; + font-weight: 600; + padding: 10px 16px; + border-radius: 8px; + border: 1px solid; + margin: 3px; + min-width: 90px; + min-height: 36px; + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; +} + +/* 导入规程按钮 - 绿色主题 (与变量表"开始通讯"一致) */ +#importToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #10B981, stop:1 #059669); + border-color: #059669; +} + +#importToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #059669, stop:1 #047857); + border-color: #047857; +} + +#importToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #047857, stop:1 #065F46); + border-color: #065F46; +} + +/* 添加分类按钮 - 蓝色主题 */ +#addCategoryToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #3B82F6, stop:1 #2563EB); + border-color: #2563EB; +} + +#addCategoryToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2563EB, stop:1 #1D4ED8); + border-color: #1D4ED8; +} + +#addCategoryToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #1D4ED8, stop:1 #1E40AF); + border-color: #1E40AF; +} + +/* 删除分类按钮 - 红色主题 */ +#deleteCategoryToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #EF4444, stop:1 #DC2626); + border-color: #DC2626; +} + +#deleteCategoryToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #DC2626, stop:1 #B91C1C); + border-color: #B91C1C; +} + +#deleteCategoryToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #B91C1C, stop:1 #991B1B); + border-color: #991B1B; +} + +/* 打开规程按钮 - 蓝色主题 */ +#openProcedureToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #3B82F6, stop:1 #2563EB); + border-color: #2563EB; +} + +#openProcedureToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2563EB, stop:1 #1D4ED8); + border-color: #1D4ED8; +} + +#openProcedureToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #1D4ED8, stop:1 #1E40AF); + border-color: #1E40AF; +} + +/* 删除规程按钮 - 红色主题 */ +#deleteProcedureToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #EF4444, stop:1 #DC2626); + border-color: #DC2626; +} + +#deleteProcedureToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #DC2626, stop:1 #B91C1C); + border-color: #B91C1C; +} + +#deleteProcedureToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #B91C1C, stop:1 #991B1B); + border-color: #991B1B; +} + +/* 历史记录按钮 - 紫色主题 */ +#historyToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #8B5CF6, stop:1 #7C3AED); + border-color: #7C3AED; +} + +#historyToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #7C3AED, stop:1 #6D28D9); + border-color: #6D28D9; +} + +#historyToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #6D28D9, stop:1 #5B21B6); + border-color: #5B21B6; +} + +/* 批量执行按钮 - 绿色主题 */ +#batchExecuteToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #10B981, stop:1 #059669); + border-color: #059669; +} + +#batchExecuteToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #059669, stop:1 #047857); + border-color: #047857; +} + +#batchExecuteToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #047857, stop:1 #065F46); + border-color: #065F46; +} + +/* 关键词管理按钮 - 紫色主题 */ +#keywordManageToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #8B5CF6, stop:1 #7C3AED); + border-color: #7C3AED; +} + +#keywordManageToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #7C3AED, stop:1 #6D28D9); + border-color: #6D28D9; +} + +#keywordManageToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #6D28D9, stop:1 #5B21B6); + border-color: #5B21B6; +} + +/* 导出规程按钮 - 橙色主题 */ +#exportProcedureToolBtn { + color: #FFFFFF; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #F59E0B, stop:1 #D97706); + border-color: #D97706; +} + +#exportProcedureToolBtn:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #D97706, stop:1 #B45309); + border-color: #B45309; +} + +#exportProcedureToolBtn:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #B45309, stop:1 #92400E); + border-color: #92400E; +}/ +* ==================== 规程管理工具栏按钮样式 - 遵循PyQt5规范 ==================== */ +/* 工具栏按钮基础样式 */ +QToolBar QToolButton { + font-size: 14px; + font-weight: 600; + padding: 10px 16px; + border-radius: 8px; + border: 1px solid #E5E7EB; + margin: 3px; + min-width: 90px; + min-height: 36px; + font-family: "Microsoft YaHei", sans-serif; + color: #FFFFFF; +} + +/* 导入规程按钮 - 绿色主题 */ +#importToolBtn { + background-color: #10B981; + border-color: #059669; +} + +#importToolBtn:hover { + background-color: #059669; + border-color: #047857; +} + +#importToolBtn:pressed { + background-color: #047857; + border-color: #065F46; +} + +/* 添加分类按钮 - 蓝色主题 */ +#addCategoryToolBtn { + background-color: #3B82F6; + border-color: #2563EB; +} + +#addCategoryToolBtn:hover { + background-color: #2563EB; + border-color: #1D4ED8; +} + +#addCategoryToolBtn:pressed { + background-color: #1D4ED8; + border-color: #1E40AF; +} + +/* 删除分类按钮 - 红色主题 */ +#deleteCategoryToolBtn { + background-color: #EF4444; + border-color: #DC2626; +} + +#deleteCategoryToolBtn:hover { + background-color: #DC2626; + border-color: #B91C1C; +} + +#deleteCategoryToolBtn:pressed { + background-color: #B91C1C; + border-color: #991B1B; +} + +/* 打开规程按钮 - 蓝色主题 */ +#openProcedureToolBtn { + background-color: #3B82F6; + border-color: #2563EB; +} + +#openProcedureToolBtn:hover { + background-color: #2563EB; + border-color: #1D4ED8; +} + +#openProcedureToolBtn:pressed { + background-color: #1D4ED8; + border-color: #1E40AF; +} + +/* 删除规程按钮 - 红色主题 */ +#deleteProcedureToolBtn { + background-color: #EF4444; + border-color: #DC2626; +} + +#deleteProcedureToolBtn:hover { + background-color: #DC2626; + border-color: #B91C1C; +} + +#deleteProcedureToolBtn:pressed { + background-color: #B91C1C; + border-color: #991B1B; +} + +/* 历史记录按钮 - 紫色主题 */ +#historyToolBtn { + background-color: #8B5CF6; + border-color: #7C3AED; +} + +#historyToolBtn:hover { + background-color: #7C3AED; + border-color: #6D28D9; +} + +#historyToolBtn:pressed { + background-color: #6D28D9; + border-color: #5B21B6; +} + +/* 批量执行按钮 - 绿色主题 */ +#batchExecuteToolBtn { + background-color: #10B981; + border-color: #059669; +} + +#batchExecuteToolBtn:hover { + background-color: #059669; + border-color: #047857; +} + +#batchExecuteToolBtn:pressed { + background-color: #047857; + border-color: #065F46; +} + +/* 关键词管理按钮 - 紫色主题 */ +#keywordManageToolBtn { + background-color: #8B5CF6; + border-color: #7C3AED; +} + +#keywordManageToolBtn:hover { + background-color: #7C3AED; + border-color: #6D28D9; +} + +#keywordManageToolBtn:pressed { + background-color: #6D28D9; + border-color: #5B21B6; +} + +/* 导出规程按钮 - 橙色主题 */ +#exportProcedureToolBtn { + background-color: #F59E0B; + border-color: #D97706; +} + +#exportProcedureToolBtn:hover { + background-color: #D97706; + border-color: #B45309; +} + +#exportProcedureToolBtn:pressed { + background-color: #B45309; + border-color: #92400E; } \ No newline at end of file diff --git a/UI/ProcedureManager/HistoryViewer.py b/UI/ProcedureManager/HistoryViewer.py index cae3793..2eed074 100644 --- a/UI/ProcedureManager/HistoryViewer.py +++ b/UI/ProcedureManager/HistoryViewer.py @@ -106,29 +106,7 @@ class HistoryViewerWidget(QWidget): table.setSelectionBehavior(QTableView.SelectRows) - table.setStyleSheet(""" - QTableView { - gridline-color: #d0d0d0; - background-color: white; - alternate-background-color: #f5f5f5; - selection-background-color: #0078d4; - selection-color: white; - } - QTableView::item { - padding: 5px; - border: none; - } - QTableView::item:selected { - background-color: #0078d4; - color: white; - } - QHeaderView::section { - background-color: #f0f0f0; - padding: 5px; - border: 1px solid #d0d0d0; - font-weight: bold; - } - """) + table.setObjectName("historyTable") def loadHistory(self): """加载历史记录""" @@ -451,15 +429,7 @@ class HistoryViewerWidget(QWidget): # 基本信息区域 infoLayout = QVBoxLayout() infoGroup = QWidget() - infoGroup.setStyleSheet(""" - QWidget { - background-color: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 5px; - padding: 10px; - margin: 5px; - } - """) + infoGroup.setObjectName("stepDetailInfoGroup") infoGroupLayout = QVBoxLayout() infoGroupLayout.addWidget(QLabel(f"步骤ID: {stepId}")) @@ -476,15 +446,7 @@ class HistoryViewerWidget(QWidget): descTextEdit = QLineEdit() descTextEdit.setText(stepDesc) descTextEdit.setReadOnly(True) - descTextEdit.setStyleSheet(""" - QLineEdit { - background-color: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 3px; - padding: 8px; - font-family: 'Consolas', monospace; - } - """) + descTextEdit.setObjectName("stepDescriptionText") layout.addWidget(descTextEdit) # 详细结果区域 @@ -495,38 +457,14 @@ class HistoryViewerWidget(QWidget): # 根据执行状态设置结果显示样式 if "成功" in execStatus: - resultTextEdit.setStyleSheet(""" - QLineEdit { - background-color: #d4edda; - border: 1px solid #c3e6cb; - border-radius: 3px; - padding: 8px; - color: #155724; - font-family: 'Consolas', monospace; - } - """) + resultTextEdit.setObjectName("stepResultText") + resultTextEdit.setProperty("status", "success") elif "失败" in execStatus: - resultTextEdit.setStyleSheet(""" - QLineEdit { - background-color: #f8d7da; - border: 1px solid #f5c6cb; - border-radius: 3px; - padding: 8px; - color: #721c24; - font-family: 'Consolas', monospace; - } - """) + resultTextEdit.setObjectName("stepResultText") + resultTextEdit.setProperty("status", "error") else: - resultTextEdit.setStyleSheet(""" - QLineEdit { - background-color: #fff3cd; - border: 1px solid #ffeaa7; - border-radius: 3px; - padding: 8px; - color: #856404; - font-family: 'Consolas', monospace; - } - """) + resultTextEdit.setObjectName("stepResultText") + resultTextEdit.setProperty("status", "default") layout.addWidget(resultTextEdit) diff --git a/UI/ProcedureManager/ProcedureEditor.py b/UI/ProcedureManager/ProcedureEditor.py index 88e23ce..5a4c11c 100644 --- a/UI/ProcedureManager/ProcedureEditor.py +++ b/UI/ProcedureManager/ProcedureEditor.py @@ -133,81 +133,815 @@ class ProcedureEditor(QWidget): self.createButtonSection(layout) def createBasicInfoSection(self, layout): - """创建基本信息区域""" + """创建基本信息区域 - 优化为紧凑布局""" info_group = QWidget() - info_group.setObjectName("basicInfoGroup") # 设置对象名称以便QSS选择器使用 + info_group.setObjectName("basicInfoGroup") + info_group.setMaximumHeight(80) # 限制基本信息区域的最大高度 + info_group.setMinimumHeight(80) layout.addWidget(info_group) - # 标题 - info_title = QLabel("基本信息") info_layout = QVBoxLayout() + info_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + info_layout.setSpacing(4) # 减小间距 info_group.setLayout(info_layout) + + # 标题 + info_title = QLabel("基本信息") + info_title.setObjectName("basicInfoTitle") info_layout.addWidget(info_title) + # 使用网格布局实现紧凑的两行布局 + grid_layout = QHBoxLayout() + grid_layout.setSpacing(16) + + # 第一列 + col1_layout = QHBoxLayout() + col1_layout.setSpacing(8) + + # 规程名称 + name_label = QLabel("规程名称:") + name_label.setFixedWidth(60) + name_label.setProperty("fieldLabel", True) + self.nameEdit = QLineEdit() + self.nameEdit.setPlaceholderText("输入规程名称") + self.nameEdit.setObjectName("procedureNameEdit") + self.nameEdit.setMaximumHeight(32) + col1_layout.addWidget(name_label) + col1_layout.addWidget(self.nameEdit) + + # 规程编号 + number_label = QLabel("规程编号:") + number_label.setFixedWidth(60) + number_label.setProperty("fieldLabel", True) + self.numberEdit = QLineEdit() + self.numberEdit.setPlaceholderText("输入规程编号") + self.numberEdit.setObjectName("procedureNumberEdit") + self.numberEdit.setMaximumHeight(32) + col1_layout.addWidget(number_label) + col1_layout.addWidget(self.numberEdit) + + grid_layout.addLayout(col1_layout) + + # 第二列 + col2_layout = QHBoxLayout() + col2_layout.setSpacing(8) + + # 规程类型 + type_label = QLabel("规程类型:") + type_label.setFixedWidth(60) + type_label.setProperty("fieldLabel", True) + self.typeEdit = QLineEdit() + self.typeEdit.setPlaceholderText("输入规程类型") + self.typeEdit.setObjectName("procedureTypeEdit") + self.typeEdit.setMaximumHeight(32) + col2_layout.addWidget(type_label) + col2_layout.addWidget(self.typeEdit) + + # 规程描述(简化为单行输入) + desc_label = QLabel("规程描述:") + desc_label.setFixedWidth(60) + desc_label.setProperty("fieldLabel", True) + self.descriptionEdit = QLineEdit() # 改为单行输入 + self.descriptionEdit.setPlaceholderText("输入规程描述") + self.descriptionEdit.setObjectName("procedureDescriptionEdit") + self.descriptionEdit.setMaximumHeight(32) + col2_layout.addWidget(desc_label) + col2_layout.addWidget(self.descriptionEdit) + + grid_layout.addLayout(col2_layout) + grid_layout.addStretch() # 添加弹性空间 + + info_layout.addLayout(grid_layout) + + def createStepsSection(self, layout): + """创建步骤表格区域 - 优化为最大化表格显示""" + steps_group = QWidget() + steps_group.setObjectName("stepsGroup") + layout.addWidget(steps_group) + + steps_layout = QVBoxLayout() + steps_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + steps_layout.setSpacing(4) # 减小间距 + steps_group.setLayout(steps_layout) + + # 紧凑的标题和按钮行 + header_layout = QHBoxLayout() + header_layout.setSpacing(8) + + # 标题 + steps_title = QLabel("步骤列表") + steps_title.setObjectName("stepsTitle") + header_layout.addWidget(steps_title) + + # 添加步骤按钮(紧凑样式) + add_step_btn = QPushButton("添加步骤") + add_step_btn.setIcon(qta.icon('fa5s.plus', color='white')) + add_step_btn.setObjectName("addStepButton") + add_step_btn.setMaximumHeight(28) # 减小按钮高度 + add_step_btn.clicked.connect(self.addStep) + header_layout.addWidget(add_step_btn) + + # 删除步骤按钮(紧凑样式) + delete_step_btn = QPushButton("删除步骤") + delete_step_btn.setIcon(qta.icon('fa5s.trash', color='white')) + delete_step_btn.setObjectName("deleteStepButton") + delete_step_btn.setMaximumHeight(28) # 减小按钮高度 + delete_step_btn.clicked.connect(self.deleteStep) + header_layout.addWidget(delete_step_btn) + + header_layout.addStretch() + steps_layout.addLayout(header_layout) + + # 步骤表格(最大化显示) + self.stepsTable = QTableWidget() + self.stepsTable.setObjectName("stepsTable") + self.stepsTable.setColumnCount(6) + self.stepsTable.setHorizontalHeaderLabels(['序号', '步骤ID', '操作', '类型', '预期值', '备注']) + + # 设置表格属性 + self.stepsTable.setSelectionBehavior(QAbstractItemView.SelectRows) + self.stepsTable.setEditTriggers(QAbstractItemView.AllEditTriggers) + self.stepsTable.setAlternatingRowColors(True) # 启用交替行颜色 + self.stepsTable.setShowGrid(True) # 显示网格线 + + # 优化表格样式 + + # 设置列宽 + header = self.stepsTable.horizontalHeader() + if header: + header.setSectionResizeMode(0, QHeaderView.ResizeToContents) # 序号 + header.setSectionResizeMode(1, QHeaderView.ResizeToContents) # 步骤ID + header.setSectionResizeMode(2, QHeaderView.Stretch) # 操作 + header.setSectionResizeMode(3, QHeaderView.ResizeToContents) # 类型 + header.setSectionResizeMode(4, QHeaderView.ResizeToContents) # 预期值 + header.setSectionResizeMode(5, QHeaderView.ResizeToContents) # 备注 + + # 设置最小列宽 + self.stepsTable.setColumnWidth(0, 50) # 序号 + self.stepsTable.setColumnWidth(1, 100) # 步骤ID + self.stepsTable.setColumnWidth(3, 70) # 类型 + self.stepsTable.setColumnWidth(4, 80) # 预期值 + self.stepsTable.setColumnWidth(5, 100) # 备注 + + # 添加右键菜单 + self.stepsTable.setContextMenuPolicy(Qt.CustomContextMenu) + self.stepsTable.customContextMenuRequested.connect(self.showContextMenu) + + steps_layout.addWidget(self.stepsTable) + + def createButtonSection(self, layout): + """创建紧凑的按钮区域""" + button_widget = QWidget() + button_widget.setMaximumHeight(40) # 限制按钮区域高度 + button_widget.setMinimumHeight(40) + + button_layout = QHBoxLayout() + button_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + button_layout.setSpacing(8) # 减小按钮间距 + button_widget.setLayout(button_layout) + + # 保存按钮(紧凑样式) + save_btn = QPushButton("保存规程") + save_btn.setIcon(qta.icon('fa5s.save', color='white')) + save_btn.setObjectName("saveProcedureButton") + save_btn.setMaximumHeight(32) # 减小按钮高度 + save_btn.clicked.connect(self.saveProcedure) + button_layout.addWidget(save_btn) + + # 取消按钮(紧凑样式) + cancel_btn = QPushButton("取消") + cancel_btn.setIcon(qta.icon('fa5s.times', color='red')) + cancel_btn.setObjectName("cancelEditButton") + cancel_btn.setMaximumHeight(32) # 减小按钮高度 + cancel_btn.clicked.connect(self.cancelEdit) + button_layout.addWidget(cancel_btn) + + button_layout.addStretch() # 将按钮推到左侧 + layout.addWidget(button_widget) + + def loadProcedureData(self): + """加载规程数据""" + if not self.procedureData: + print("警告:规程数据为空") + return + + print(f"加载规程数据: {self.procedureData.keys()}") + + # 加载基本信息 + procedure_info = self.procedureData.get('规程信息', {}) + self.nameEdit.setText(procedure_info.get('规程名称', '')) + self.numberEdit.setText(procedure_info.get('规程编号', '')) + self.typeEdit.setText(procedure_info.get('规程类型', '')) + + # 加载描述(可能在不同位置) + description = self.procedureData.get('description', '') + if not description: + # 尝试从测试用例信息中获取描述 + test_case_info = self.procedureData.get('测试用例信息', {}) + description = test_case_info.get('工况描述', '') + self.descriptionEdit.setText(description) # 修改为setText,因为现在是QLineEdit +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +规程编辑器 +支持编辑规程内容并保存到数据库 +""" + +import json +from datetime import datetime +from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, + QLineEdit, QTextEdit, QPushButton, QTableWidget, + QTableWidgetItem, QHeaderView, QMessageBox, + QDialog, QFormLayout, QSpinBox, QComboBox, + QDialogButtonBox, QAbstractItemView, QMenu) +from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtGui import QFont, QBrush, QColor +import qtawesome as qta + +class NoClearDelegate(QAbstractItemView): + """自定义委托,防止双击时清除内容""" + def createEditor(self, parent, option, index): + editor = super().createEditor(parent, option, index) + if hasattr(editor, 'setText'): + # 保持原有内容 + current_text = index.data() + if current_text: + editor.setText(current_text) + return editor + +class StepEditDialog(QDialog): + """步骤编辑对话框""" + + def __init__(self, step_data=None, parent=None): + super().__init__(parent) + self.setWindowTitle("编辑步骤") + self.setModal(True) + self.setMinimumWidth(500) + + self.step_data = step_data or {} + self.initUI() + + def initUI(self): + layout = QVBoxLayout() + self.setLayout(layout) + # 表单布局 form_layout = QFormLayout() + # 步骤ID + self.stepIdEdit = QLineEdit() + self.stepIdEdit.setText(self.step_data.get('stepId', '')) + form_layout.addRow("步骤ID:", self.stepIdEdit) + + # 操作(合并关键词和步骤描述) + self.operationEdit = QTextEdit() + self.operationEdit.setMaximumHeight(80) + self.operationEdit.setPlainText(self.step_data.get('operation', '')) + form_layout.addRow("操作:", self.operationEdit) + + # 操作类型(直接输入) + self.typeEdit = QLineEdit() + self.typeEdit.setText(self.step_data.get('type', 'SET')) + form_layout.addRow("操作类型:", self.typeEdit) + + # 预期值 + self.expectedValueEdit = QLineEdit() + self.expectedValueEdit.setText(str(self.step_data.get('expectedValue', ''))) + form_layout.addRow("预期值:", self.expectedValueEdit) + + # 备注 + self.remarkEdit = QTextEdit() + self.remarkEdit.setMaximumHeight(60) + self.remarkEdit.setPlainText(self.step_data.get('remark', '')) + form_layout.addRow("备注:", self.remarkEdit) + + # 序号 + self.orderSpin = QSpinBox() + self.orderSpin.setRange(1, 999) + self.orderSpin.setValue(self.step_data.get('order', 1)) + form_layout.addRow("序号:", self.orderSpin) + + layout.addLayout(form_layout) + + # 按钮 + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + layout.addWidget(button_box) + + def getStepData(self): + """获取步骤数据""" + return { + 'stepId': self.stepIdEdit.text(), + 'operation': self.operationEdit.toPlainText(), + 'type': self.typeEdit.text(), + 'expectedValue': self.expectedValueEdit.text(), + 'remark': self.remarkEdit.toPlainText(), + 'order': self.orderSpin.value() + } + +class ProcedureEditor(QWidget): + """规程编辑器""" + + procedureSaved = pyqtSignal(int) # 规程保存信号 + + def __init__(self, procedureData, procedureId, dbManager, parent=None): + super().__init__(parent) + self.procedureData = procedureData + self.procedureId = procedureId + self.dbManager = dbManager + self.parent = parent + + self.initUI() + self.loadProcedureData() + + def initUI(self): + """初始化界面""" + layout = QVBoxLayout() + self.setLayout(layout) + + # 标题 + title_label = QLabel("规程编辑器") + title_label.setObjectName("procedureEditorTitle") # 设置对象名称以便QSS选择器使用 + layout.addWidget(title_label) + + # 基本信息区域 + self.createBasicInfoSection(layout) + + # 步骤表格区域 + self.createStepsSection(layout) + + # 按钮区域 + self.createButtonSection(layout) + + def createBasicInfoSection(self, layout): + """创建基本信息区域 - 优化为紧凑布局""" + info_group = QWidget() + info_group.setObjectName("basicInfoGroup") + info_group.setMaximumHeight(80) # 限制基本信息区域的最大高度 + info_group.setMinimumHeight(80) + layout.addWidget(info_group) + + info_layout = QVBoxLayout() + info_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + info_layout.setSpacing(4) # 减小间距 + info_group.setLayout(info_layout) + + # 标题 + info_title = QLabel("基本信息") + info_title.setObjectName("basicInfoTitle") + info_layout.addWidget(info_title) + + # 使用网格布局实现紧凑的两行布局 + grid_layout = QHBoxLayout() + grid_layout.setSpacing(16) + + # 第一列 + col1_layout = QHBoxLayout() + col1_layout.setSpacing(8) + + # 规程名称 + name_label = QLabel("规程名称:") + name_label.setFixedWidth(60) + name_label.setProperty("fieldLabel", True) self.nameEdit = QLineEdit() self.nameEdit.setPlaceholderText("输入规程名称") - self.nameEdit.setObjectName("procedureNameEdit") # 设置对象名称以便QSS选择器使用 - form_layout.addRow("规程名称:", self.nameEdit) + self.nameEdit.setObjectName("procedureNameEdit") + self.nameEdit.setMaximumHeight(32) + col1_layout.addWidget(name_label) + col1_layout.addWidget(self.nameEdit) + # 规程编号 + number_label = QLabel("规程编号:") + number_label.setFixedWidth(60) + number_label.setProperty("fieldLabel", True) self.numberEdit = QLineEdit() self.numberEdit.setPlaceholderText("输入规程编号") - self.numberEdit.setObjectName("procedureNumberEdit") # 设置对象名称以便QSS选择器使用 - form_layout.addRow("规程编号:", self.numberEdit) + self.numberEdit.setObjectName("procedureNumberEdit") + self.numberEdit.setMaximumHeight(32) + col1_layout.addWidget(number_label) + col1_layout.addWidget(self.numberEdit) + grid_layout.addLayout(col1_layout) + + # 第二列 + col2_layout = QHBoxLayout() + col2_layout.setSpacing(8) + + # 规程类型 + type_label = QLabel("规程类型:") + type_label.setFixedWidth(60) + type_label.setProperty("fieldLabel", True) self.typeEdit = QLineEdit() self.typeEdit.setPlaceholderText("输入规程类型") - self.typeEdit.setObjectName("procedureTypeEdit") # 设置对象名称以便QSS选择器使用 - form_layout.addRow("规程类型:", self.typeEdit) + self.typeEdit.setObjectName("procedureTypeEdit") + self.typeEdit.setMaximumHeight(32) + col2_layout.addWidget(type_label) + col2_layout.addWidget(self.typeEdit) - self.descriptionEdit = QTextEdit() - self.descriptionEdit.setMaximumHeight(80) + # 规程描述(简化为单行输入) + desc_label = QLabel("规程描述:") + desc_label.setFixedWidth(60) + desc_label.setProperty("fieldLabel", True) + self.descriptionEdit = QLineEdit() # 改为单行输入 self.descriptionEdit.setPlaceholderText("输入规程描述") - self.descriptionEdit.setObjectName("procedureDescriptionEdit") # 设置对象名称以便QSS选择器使用 - form_layout.addRow("规程描述:", self.descriptionEdit) + self.descriptionEdit.setObjectName("procedureDescriptionEdit") + self.descriptionEdit.setMaximumHeight(32) + col2_layout.addWidget(desc_label) + col2_layout.addWidget(self.descriptionEdit) - info_layout.addLayout(form_layout) + grid_layout.addLayout(col2_layout) + grid_layout.addStretch() # 添加弹性空间 + + info_layout.addLayout(grid_layout) def createStepsSection(self, layout): - """创建步骤表格区域""" + """创建步骤表格区域 - 优化为最大化表格显示""" steps_group = QWidget() - steps_group.setObjectName("stepsGroup") # 设置对象名称以便QSS选择器使用 + steps_group.setObjectName("stepsGroup") layout.addWidget(steps_group) - # 标题和按钮 + steps_layout = QVBoxLayout() + steps_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + steps_layout.setSpacing(4) # 减小间距 + steps_group.setLayout(steps_layout) + + # 紧凑的标题和按钮行 header_layout = QHBoxLayout() + header_layout.setSpacing(8) + + # 标题 steps_title = QLabel("步骤列表") + steps_title.setObjectName("stepsTitle") header_layout.addWidget(steps_title) - # 添加步骤按钮 + # 添加步骤按钮(紧凑样式) add_step_btn = QPushButton("添加步骤") add_step_btn.setIcon(qta.icon('fa5s.plus', color='white')) + add_step_btn.setObjectName("addStepButton") + add_step_btn.setMaximumHeight(28) # 减小按钮高度 add_step_btn.clicked.connect(self.addStep) header_layout.addWidget(add_step_btn) - # 删除步骤按钮 + # 删除步骤按钮(紧凑样式) delete_step_btn = QPushButton("删除步骤") delete_step_btn.setIcon(qta.icon('fa5s.trash', color='white')) + delete_step_btn.setObjectName("deleteStepButton") + delete_step_btn.setMaximumHeight(28) # 减小按钮高度 delete_step_btn.clicked.connect(self.deleteStep) header_layout.addWidget(delete_step_btn) header_layout.addStretch() + steps_layout.addLayout(header_layout) + + # 步骤表格(最大化显示) + self.stepsTable = QTableWidget() + self.stepsTable.setObjectName("stepsTable") + self.stepsTable.setColumnCount(6) + self.stepsTable.setHorizontalHeaderLabels(['序号', '步骤ID', '操作', '类型', '预期值', '备注']) + + # 设置表格属性 + self.stepsTable.setSelectionBehavior(QAbstractItemView.SelectRows) + self.stepsTable.setEditTriggers(QAbstractItemView.AllEditTriggers) + self.stepsTable.setAlternatingRowColors(True) # 启用交替行颜色 + self.stepsTable.setShowGrid(True) # 显示网格线 + + # 优化表格样式 + + # 设置列宽 + header = self.stepsTable.horizontalHeader() + if header: + header.setSectionResizeMode(0, QHeaderView.ResizeToContents) # 序号 + header.setSectionResizeMode(1, QHeaderView.ResizeToContents) # 步骤ID + header.setSectionResizeMode(2, QHeaderView.Stretch) # 操作 + header.setSectionResizeMode(3, QHeaderView.ResizeToContents) # 类型 + header.setSectionResizeMode(4, QHeaderView.ResizeToContents) # 预期值 + header.setSectionResizeMode(5, QHeaderView.ResizeToContents) # 备注 + + # 设置最小列宽 + self.stepsTable.setColumnWidth(0, 50) # 序号 + self.stepsTable.setColumnWidth(1, 100) # 步骤ID + self.stepsTable.setColumnWidth(3, 70) # 类型 + self.stepsTable.setColumnWidth(4, 80) # 预期值 + self.stepsTable.setColumnWidth(5, 100) # 备注 + + # 添加右键菜单 + self.stepsTable.setContextMenuPolicy(Qt.CustomContextMenu) + self.stepsTable.customContextMenuRequested.connect(self.showContextMenu) + + steps_layout.addWidget(self.stepsTable) + + def createButtonSection(self, layout): + """创建紧凑的按钮区域""" + button_widget = QWidget() + button_widget.setMaximumHeight(40) # 限制按钮区域高度 + button_widget.setMinimumHeight(40) + + button_layout = QHBoxLayout() + button_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + button_layout.setSpacing(8) # 减小按钮间距 + button_widget.setLayout(button_layout) + + # 保存按钮(紧凑样式) + save_btn = QPushButton("保存规程") + save_btn.setIcon(qta.icon('fa5s.save', color='white')) + save_btn.setObjectName("saveProcedureButton") + save_btn.setMaximumHeight(32) # 减小按钮高度 + save_btn.clicked.connect(self.saveProcedure) + button_layout.addWidget(save_btn) + + # 取消按钮(紧凑样式) + cancel_btn = QPushButton("取消") + cancel_btn.setIcon(qta.icon('fa5s.times', color='red')) + cancel_btn.setObjectName("cancelEditButton") + cancel_btn.setMaximumHeight(32) # 减小按钮高度 + cancel_btn.clicked.connect(self.cancelEdit) + button_layout.addWidget(cancel_btn) + + button_layout.addStretch() # 将按钮推到左侧 + layout.addWidget(button_widget) + + def loadProcedureData(self): + """加载规程数据""" + if not self.procedureData: + print("警告:规程数据为空") + return + + print(f"加载规程数据: {self.procedureData.keys()}") + + # 加载基本信息 + procedure_info = self.procedureData.get('规程信息', {}) + self.nameEdit.setText(procedure_info.get('规程名称', '')) + self.numberEdit.setText(procedure_info.get('规程编号', '')) + self.typeEdit.setText(procedure_info.get('规程类型', '')) + +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +规程编辑器 +支持编辑规程内容并保存到数据库 +""" + +import json +from datetime import datetime +from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, + QLineEdit, QTextEdit, QPushButton, QTableWidget, + QTableWidgetItem, QHeaderView, QMessageBox, + QDialog, QFormLayout, QSpinBox, QComboBox, + QDialogButtonBox, QAbstractItemView, QMenu) +from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtGui import QFont, QBrush, QColor +import qtawesome as qta + +class NoClearDelegate(QAbstractItemView): + """自定义委托,防止双击时清除内容""" + def createEditor(self, parent, option, index): + editor = super().createEditor(parent, option, index) + if hasattr(editor, 'setText'): + # 保持原有内容 + current_text = index.data() + if current_text: + editor.setText(current_text) + return editor + +class StepEditDialog(QDialog): + """步骤编辑对话框""" + + def __init__(self, step_data=None, parent=None): + super().__init__(parent) + self.setWindowTitle("编辑步骤") + self.setModal(True) + self.setMinimumWidth(500) + + self.step_data = step_data or {} + self.initUI() + + def initUI(self): + layout = QVBoxLayout() + self.setLayout(layout) + + # 表单布局 + form_layout = QFormLayout() + + # 步骤ID + self.stepIdEdit = QLineEdit() + self.stepIdEdit.setText(self.step_data.get('stepId', '')) + form_layout.addRow("步骤ID:", self.stepIdEdit) + + # 操作(合并关键词和步骤描述) + self.operationEdit = QTextEdit() + self.operationEdit.setMaximumHeight(80) + self.operationEdit.setPlainText(self.step_data.get('operation', '')) + form_layout.addRow("操作:", self.operationEdit) + + # 操作类型(直接输入) + self.typeEdit = QLineEdit() + self.typeEdit.setText(self.step_data.get('type', 'SET')) + form_layout.addRow("操作类型:", self.typeEdit) + + # 预期值 + self.expectedValueEdit = QLineEdit() + self.expectedValueEdit.setText(str(self.step_data.get('expectedValue', ''))) + form_layout.addRow("预期值:", self.expectedValueEdit) + + # 备注 + self.remarkEdit = QTextEdit() + self.remarkEdit.setMaximumHeight(60) + self.remarkEdit.setPlainText(self.step_data.get('remark', '')) + form_layout.addRow("备注:", self.remarkEdit) + + # 序号 + self.orderSpin = QSpinBox() + self.orderSpin.setRange(1, 999) + self.orderSpin.setValue(self.step_data.get('order', 1)) + form_layout.addRow("序号:", self.orderSpin) + + layout.addLayout(form_layout) + + # 按钮 + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + layout.addWidget(button_box) + + def getStepData(self): + """获取步骤数据""" + return { + 'stepId': self.stepIdEdit.text(), + 'operation': self.operationEdit.toPlainText(), + 'type': self.typeEdit.text(), + 'expectedValue': self.expectedValueEdit.text(), + 'remark': self.remarkEdit.toPlainText(), + 'order': self.orderSpin.value() + } + +class ProcedureEditor(QWidget): + """规程编辑器""" + + procedureSaved = pyqtSignal(int) # 规程保存信号 + + def __init__(self, procedureData, procedureId, dbManager, parent=None): + super().__init__(parent) + self.procedureData = procedureData + self.procedureId = procedureId + self.dbManager = dbManager + self.parent = parent + + self.initUI() + self.loadProcedureData() + + def initUI(self): + """初始化界面""" + layout = QVBoxLayout() + self.setLayout(layout) + + # 标题 + title_label = QLabel("规程编辑器") + title_label.setObjectName("procedureEditorTitle") # 设置对象名称以便QSS选择器使用 + layout.addWidget(title_label) + + # 基本信息区域 + self.createBasicInfoSection(layout) + + # 步骤表格区域 + self.createStepsSection(layout) + + # 按钮区域 + self.createButtonSection(layout) + + def createBasicInfoSection(self, layout): + """创建基本信息区域 - 优化为紧凑布局""" + info_group = QWidget() + info_group.setObjectName("basicInfoGroup") + info_group.setMaximumHeight(80) # 限制基本信息区域的最大高度 + info_group.setMinimumHeight(80) + layout.addWidget(info_group) + + info_layout = QVBoxLayout() + info_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + info_layout.setSpacing(4) # 减小间距 + info_group.setLayout(info_layout) + + # 标题 + info_title = QLabel("基本信息") + info_title.setObjectName("basicInfoTitle") + info_layout.addWidget(info_title) + + # 使用网格布局实现紧凑的两行布局 + grid_layout = QHBoxLayout() + grid_layout.setSpacing(16) + + # 第一列 + col1_layout = QHBoxLayout() + col1_layout.setSpacing(8) + + # 规程名称 + name_label = QLabel("规程名称:") + name_label.setFixedWidth(60) + name_label.setProperty("fieldLabel", True) + self.nameEdit = QLineEdit() + self.nameEdit.setPlaceholderText("输入规程名称") + self.nameEdit.setObjectName("procedureNameEdit") + self.nameEdit.setMaximumHeight(32) + col1_layout.addWidget(name_label) + col1_layout.addWidget(self.nameEdit) + + # 规程编号 + number_label = QLabel("规程编号:") + number_label.setFixedWidth(60) + number_label.setProperty("fieldLabel", True) + self.numberEdit = QLineEdit() + self.numberEdit.setPlaceholderText("输入规程编号") + self.numberEdit.setObjectName("procedureNumberEdit") + self.numberEdit.setMaximumHeight(32) + col1_layout.addWidget(number_label) + col1_layout.addWidget(self.numberEdit) + + grid_layout.addLayout(col1_layout) + + # 第二列 + col2_layout = QHBoxLayout() + col2_layout.setSpacing(8) + + # 规程类型 + type_label = QLabel("规程类型:") + type_label.setFixedWidth(60) + type_label.setProperty("fieldLabel", True) + self.typeEdit = QLineEdit() + self.typeEdit.setPlaceholderText("输入规程类型") + self.typeEdit.setObjectName("procedureTypeEdit") + self.typeEdit.setMaximumHeight(32) + col2_layout.addWidget(type_label) + col2_layout.addWidget(self.typeEdit) + + # 规程描述(简化为单行输入) + desc_label = QLabel("规程描述:") + desc_label.setFixedWidth(60) + desc_label.setProperty("fieldLabel", True) + self.descriptionEdit = QLineEdit() # 改为单行输入 + self.descriptionEdit.setPlaceholderText("输入规程描述") + self.descriptionEdit.setObjectName("procedureDescriptionEdit") + self.descriptionEdit.setMaximumHeight(32) + col2_layout.addWidget(desc_label) + col2_layout.addWidget(self.descriptionEdit) + + grid_layout.addLayout(col2_layout) + grid_layout.addStretch() # 添加弹性空间 + + info_layout.addLayout(grid_layout) + + def createStepsSection(self, layout): + """创建步骤表格区域 - 优化为最大化表格显示""" + steps_group = QWidget() + steps_group.setObjectName("stepsGroup") + layout.addWidget(steps_group) + steps_layout = QVBoxLayout() + steps_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + steps_layout.setSpacing(4) # 减小间距 steps_group.setLayout(steps_layout) + + # 紧凑的标题和按钮行 + header_layout = QHBoxLayout() + header_layout.setSpacing(8) + + # 标题 + steps_title = QLabel("步骤列表") + steps_title.setObjectName("stepsTitle") + header_layout.addWidget(steps_title) + + # 添加步骤按钮(紧凑样式) + add_step_btn = QPushButton("添加步骤") + add_step_btn.setIcon(qta.icon('fa5s.plus', color='white')) + add_step_btn.setObjectName("addStepButton") + add_step_btn.setMaximumHeight(28) # 减小按钮高度 + add_step_btn.clicked.connect(self.addStep) + header_layout.addWidget(add_step_btn) + + # 删除步骤按钮(紧凑样式) + delete_step_btn = QPushButton("删除步骤") + delete_step_btn.setIcon(qta.icon('fa5s.trash', color='white')) + delete_step_btn.setObjectName("deleteStepButton") + delete_step_btn.setMaximumHeight(28) # 减小按钮高度 + delete_step_btn.clicked.connect(self.deleteStep) + header_layout.addWidget(delete_step_btn) + + header_layout.addStretch() steps_layout.addLayout(header_layout) - # 步骤表格 + # 步骤表格(最大化显示) self.stepsTable = QTableWidget() - self.stepsTable.setObjectName("stepsTable") # 设置对象名称以便QSS选择器使用 + self.stepsTable.setObjectName("stepsTable") self.stepsTable.setColumnCount(6) self.stepsTable.setHorizontalHeaderLabels(['序号', '步骤ID', '操作', '类型', '预期值', '备注']) # 设置表格属性 self.stepsTable.setSelectionBehavior(QAbstractItemView.SelectRows) - self.stepsTable.setEditTriggers(QAbstractItemView.AllEditTriggers) # 允许所有方式编辑 - self.stepsTable.setAlternatingRowColors(False) + self.stepsTable.setEditTriggers(QAbstractItemView.AllEditTriggers) + self.stepsTable.setAlternatingRowColors(True) # 启用交替行颜色 + self.stepsTable.setShowGrid(True) # 显示网格线 + + # 优化表格样式 # 设置列宽 header = self.stepsTable.horizontalHeader() @@ -219,15 +953,12 @@ class ProcedureEditor(QWidget): header.setSectionResizeMode(4, QHeaderView.ResizeToContents) # 预期值 header.setSectionResizeMode(5, QHeaderView.ResizeToContents) # 备注 - # 设置最小列宽,确保表头显示完整 - self.stepsTable.setColumnWidth(0, 60) # 序号 - self.stepsTable.setColumnWidth(1, 120) # 步骤ID - self.stepsTable.setColumnWidth(3, 80) # 类型 - self.stepsTable.setColumnWidth(4, 100) # 预期值 - self.stepsTable.setColumnWidth(5, 120) # 备注 - - # 移除双击弹窗 - # self.stepsTable.cellDoubleClicked.connect(self.editStep) + # 设置最小列宽 + self.stepsTable.setColumnWidth(0, 50) # 序号 + self.stepsTable.setColumnWidth(1, 100) # 步骤ID + self.stepsTable.setColumnWidth(3, 70) # 类型 + self.stepsTable.setColumnWidth(4, 80) # 预期值 + self.stepsTable.setColumnWidth(5, 100) # 备注 # 添加右键菜单 self.stepsTable.setContextMenuPolicy(Qt.CustomContextMenu) @@ -236,25 +967,34 @@ class ProcedureEditor(QWidget): steps_layout.addWidget(self.stepsTable) def createButtonSection(self, layout): - """创建按钮区域""" + """创建紧凑的按钮区域""" + button_widget = QWidget() + button_widget.setMaximumHeight(40) # 限制按钮区域高度 + button_widget.setMinimumHeight(40) + button_layout = QHBoxLayout() + button_layout.setContentsMargins(8, 4, 8, 4) # 减小边距 + button_layout.setSpacing(8) # 减小按钮间距 + button_widget.setLayout(button_layout) - # 保存按钮 + # 保存按钮(紧凑样式) save_btn = QPushButton("保存规程") save_btn.setIcon(qta.icon('fa5s.save', color='white')) - save_btn.setObjectName("saveProcedureButton") # 设置对象名称以便QSS选择器使用 + save_btn.setObjectName("saveProcedureButton") + save_btn.setMaximumHeight(32) # 减小按钮高度 save_btn.clicked.connect(self.saveProcedure) button_layout.addWidget(save_btn) - # 取消按钮 + # 取消按钮(紧凑样式) cancel_btn = QPushButton("取消") cancel_btn.setIcon(qta.icon('fa5s.times', color='red')) - cancel_btn.setObjectName("cancelEditButton") # 设置对象名称以便QSS选择器使用 + cancel_btn.setObjectName("cancelEditButton") + cancel_btn.setMaximumHeight(32) # 减小按钮高度 cancel_btn.clicked.connect(self.cancelEdit) button_layout.addWidget(cancel_btn) - button_layout.addStretch() - layout.addLayout(button_layout) + button_layout.addStretch() # 将按钮推到左侧 + layout.addWidget(button_widget) def loadProcedureData(self): """加载规程数据""" @@ -276,7 +1016,7 @@ class ProcedureEditor(QWidget): # 尝试从测试用例信息中获取描述 test_case_info = self.procedureData.get('测试用例信息', {}) description = test_case_info.get('工况描述', '') - self.descriptionEdit.setPlainText(description) + self.descriptionEdit.setText(description) # 加载步骤数据 self.loadStepsData() @@ -593,7 +1333,7 @@ class ProcedureEditor(QWidget): '测试用例信息': { '测试用例': procedureInfo['procedureName'], '用例编号': procedureInfo['procedureNumber'], - '工况描述': self.descriptionEdit.toPlainText() + '工况描述': self.descriptionEdit.text() # 修改为text(),因为现在是QLineEdit }, '测试步骤': testSteps, 'updatedAt': datetime.now().isoformat() diff --git a/UI/ProcedureManager/ProcedureManager.py b/UI/ProcedureManager/ProcedureManager.py index 9a6193d..aefa2d5 100644 --- a/UI/ProcedureManager/ProcedureManager.py +++ b/UI/ProcedureManager/ProcedureManager.py @@ -2,12 +2,12 @@ import sys import os from PyQt5.QtCore import Qt, QTimer, QAbstractTableModel, QModelIndex, QPoint, QSize, pyqtSignal, QFile,QTextStream from PyQt5.QtGui import QBrush, QColor, QFont, QStandardItemModel, QStandardItem -from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView, +from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView, QToolButton, QPushButton, QVBoxLayout, QWidget, QHBoxLayout, QLabel, QCheckBox, QSpinBox, QMenu, QFileDialog, QTabWidget, QTabBar, QListWidget, QListWidgetItem, QDialog, QLineEdit, QFormLayout, QDialogButtonBox, QMessageBox, - QHeaderView, QToolBar, QAction, QStatusBar, QComboBox, QSplitter, QAbstractItemView) + QHeaderView, QToolBar, QAction, QStatusBar, QSplitter, QAbstractItemView) from docx import Document from docx.shared import Pt, RGBColor from docx.enum.text import WD_PARAGRAPH_ALIGNMENT @@ -80,13 +80,14 @@ class ProcedureManager(QMainWindow): self.setWindowTitle("规程管理系统") self.setGeometry(100, 100, 1200, 800) - # 创建工具栏 + # 创建工具栏 - 优化图标大小和布局 self.toolbar = QToolBar("主工具栏") - self.toolbar.setIconSize(QSize(32, 32)) - # 新增:在工具栏级别统一设置文字显示位置 - self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + self.toolbar.setIconSize(QSize(12, 12)) # 减小图标尺寸避免遮盖 + self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # 文字在图标旁边 + self.toolbar.setMaximumHeight(50) # 适当的工具栏高度 + self.toolbar.setContentsMargins(0, 0, 0, 0) # 移除边距 self.addToolBar(self.toolbar) - self.createActions() + self.createActions() # 添加状态栏 self.statusBar = QStatusBar() @@ -223,149 +224,307 @@ class ProcedureManager(QMainWindow): self.loadProcedures(categoryId) def initProcedureManagementTab(self): - """创建规程管理主标签页""" + """创建规程管理主标签页 - 现代化卡片式设计""" mainWidget = QWidget() + mainWidget.setStyleSheet("background-color: #F5F7FA;") + + # 主布局 - 优化边距去除空白 mainLayout = QHBoxLayout() + mainLayout.setSpacing(8) + mainLayout.setContentsMargins(8, 8, 8, 8) + + # 左侧分类卡片 + categoryCard = QWidget() + categoryCard.setObjectName("categoryCard") + categoryCard.setFixedWidth(280) + categoryCard.setMaximumHeight(600) + + categoryLayout = QVBoxLayout() + categoryLayout.setSpacing(16) + categoryLayout.setContentsMargins(0, 0, 0, 0) + + # 分类标题 + categoryTitle = QLabel("规程分类") + categoryTitle.setObjectName("cardTitle") + categoryLayout.addWidget(categoryTitle) + # 分类列表 self.categoryList = QListWidget() - self.categoryList.setFixedWidth(200) + self.categoryList.setObjectName("categoryList") self.categoryList.currentItemChanged.connect(self.categorySelected) # 设置分类列表接受拖放 self.categoryList.setAcceptDrops(True) self.categoryList.setDragDropMode(QAbstractItemView.DropOnly) - # 新增:设置分类列表右键菜单 + # 设置分类列表右键菜单 self.categoryList.setContextMenuPolicy(Qt.CustomContextMenu) - self.categoryList.customContextMenuRequested.connect(self.showCategoryContextMenu) # 新增 + self.categoryList.customContextMenuRequested.connect(self.showCategoryContextMenu) + + categoryLayout.addWidget(self.categoryList) + + # 分类操作按钮 + categoryButtonLayout = QHBoxLayout() + categoryButtonLayout.setSpacing(8) + + addCategoryBtn = QPushButton("添加分类") + addCategoryBtn.setObjectName("addCategoryBtn") + addCategoryBtn.clicked.connect(self.addCategory) + addCategoryBtn.setIcon(qta.icon('fa5s.plus', color='white')) + + deleteCategoryBtn = QPushButton("删除分类") + deleteCategoryBtn.setObjectName("deleteCategoryBtn") + deleteCategoryBtn.clicked.connect(self.deleteCurrentCategory) + deleteCategoryBtn.setIcon(qta.icon('fa5s.trash', color='white')) + + categoryButtonLayout.addWidget(addCategoryBtn) + categoryButtonLayout.addWidget(deleteCategoryBtn) + categoryLayout.addLayout(categoryButtonLayout) + + categoryCard.setLayout(categoryLayout) + + # 右侧规程卡片 + procedureCard = QWidget() + procedureCard.setObjectName("procedureCard") + + procedureLayout = QVBoxLayout() + procedureLayout.setSpacing(16) + procedureLayout.setContentsMargins(0, 0, 0, 0) + + # 规程标题和统计信息 + procedureHeaderLayout = QHBoxLayout() + + procedureTitle = QLabel("规程列表") + procedureTitle.setObjectName("cardTitle") + # 规程统计标签 + self.procedureCountLabel = QLabel("共 0 个规程") + self.procedureCountLabel.setObjectName("procedureCountLabel") + + procedureHeaderLayout.addWidget(procedureTitle) + procedureHeaderLayout.addStretch() + procedureHeaderLayout.addWidget(self.procedureCountLabel) + + procedureLayout.addLayout(procedureHeaderLayout) + + # 搜索和过滤器 + searchLayout = QHBoxLayout() + searchLayout.setSpacing(12) + + # 搜索框 + self.searchEdit = QLineEdit() + self.searchEdit.setObjectName("searchEdit") + self.searchEdit.setPlaceholderText("搜索规程名称或编号...") + self.searchEdit.textChanged.connect(self.filterProcedures) + + searchLayout.addWidget(self.searchEdit, 1) + + procedureLayout.addLayout(searchLayout) + + # 规程列表 self.procedureList = QListWidget() + self.procedureList.setObjectName("procedureList") # 设置规程列表支持拖拽 self.procedureList.setDragEnabled(True) self.procedureList.setDragDropMode(QAbstractItemView.DragOnly) self.procedureList.itemDoubleClicked.connect( lambda item: self.openProcedureInExecutor(item) ) - # 新增:设置规程列表右键菜单 + # 设置规程列表右键菜单 self.procedureList.setContextMenuPolicy(Qt.CustomContextMenu) - self.procedureList.customContextMenuRequested.connect(self.showProcedureContextMenu) # 新增 + self.procedureList.customContextMenuRequested.connect(self.showProcedureContextMenu) - # 新增:拖拽事件处理 + # 拖拽事件处理 self.procedureList.dragEnterEvent = self.procedureDragEnterEvent self.categoryList.dropEvent = self.categoryDropEvent - leftPanel = QWidget() - leftLayout = QVBoxLayout() - leftLayout.addWidget(QLabel("规程分类")) - leftLayout.addWidget(self.categoryList) - leftPanel.setLayout(leftLayout) - leftPanel.setFixedWidth(220) + procedureLayout.addWidget(self.procedureList) + + # 规程操作按钮 + procedureButtonLayout = QHBoxLayout() + procedureButtonLayout.setSpacing(8) + + importBtn = QPushButton("导入规程") + importBtn.setObjectName("importBtn") + importBtn.clicked.connect(self.importProcedure) + importBtn.setIcon(qta.icon('fa5s.file-import', color='white')) + + openBtn = QPushButton("打开规程") + openBtn.setObjectName("openBtn") + openBtn.clicked.connect(self.openProcedureInExecutor) + openBtn.setIcon(qta.icon('fa5s.folder-open', color='white')) + + exportBtn = QPushButton("导出规程") + exportBtn.setObjectName("exportBtn") + exportBtn.clicked.connect(self.exportSelectedProcedure) + exportBtn.setIcon(qta.icon('fa5s.file-export', color='white')) + + deleteBtn = QPushButton("删除规程") + deleteBtn.setObjectName("deleteBtn") + deleteBtn.clicked.connect(self.deleteSelectedProcedure) + deleteBtn.setIcon(qta.icon('fa5s.trash', color='white')) + + procedureButtonLayout.addWidget(importBtn) + procedureButtonLayout.addWidget(openBtn) + procedureButtonLayout.addWidget(exportBtn) + procedureButtonLayout.addWidget(deleteBtn) + procedureButtonLayout.addStretch() + + procedureLayout.addLayout(procedureButtonLayout) - rightPanel = QWidget() - rightLayout = QVBoxLayout() - rightLayout.addWidget(QLabel("规程列表")) - rightLayout.addWidget(self.procedureList) - rightPanel.setLayout(rightLayout) + procedureCard.setLayout(procedureLayout) - mainLayout.addWidget(leftPanel) - mainLayout.addWidget(rightPanel) + # 添加到主布局 + mainLayout.addWidget(categoryCard) + mainLayout.addWidget(procedureCard, 1) # 规程卡片占据剩余空间 mainWidget.setLayout(mainLayout) self.tabs.addTab(mainWidget, "规程管理") + def getButtonStyle(self, bg_color, border_color, hover_bg, hover_border, pressed_bg, pressed_border): + """生成统一的按钮样式""" + return f""" + QToolButton {{ + background-color: {bg_color}; + border: 1px solid {border_color}; + border-radius: 6px; + padding: 6px 12px; + color: #FFFFFF; + font-size: 13px; + font-weight: 600; + min-width: 80px; + min-height: 32px; + font-family: "Microsoft YaHei", sans-serif; + }} + QToolButton:hover {{ + background-color: {hover_bg}; + border-color: {hover_border}; + }} + QToolButton:pressed {{ + background-color: {pressed_bg}; + border-color: {pressed_border}; + }} + """ + def createActions(self): - self.importAction = QAction( - qta.icon('fa5s.file-import', color='green'), - "导入规程", - self - ) - self.importAction.setIconText("导入规程") - self.importAction.setShortcut("Ctrl+I") - self.importAction.setStatusTip("导入Excel规程文件") - self.importAction.triggered.connect(self.importProcedure) - self.toolbar.addAction(self.importAction) # 修改变量名 - - self.addCategoryAction = QAction( - qta.icon('fa5s.folder-plus', color='blue'), - "添加分类", - self - ) - self.addCategoryAction.setIconText("添加分类") - self.addCategoryAction.setStatusTip("添加新的分类") - self.addCategoryAction.triggered.connect(self.addCategory) - self.toolbar.addAction(self.addCategoryAction) # 修改变量名 - - self.deleteCategoryAction = QAction( - qta.icon('fa5s.folder-minus', color='red'), - "删除分类", - self - ) - self.deleteCategoryAction.setIconText("删除分类") - self.deleteCategoryAction.setStatusTip("删除当前分类") - self.deleteCategoryAction.triggered.connect(self.deleteCurrentCategory) - self.toolbar.addAction(self.deleteCategoryAction) # 修改变量名 - - self.deleteProcedureAction = QAction( - qta.icon('fa5s.trash', color='red'), - "删除规程", - self - ) - self.deleteProcedureAction.setIconText("删除规程") - self.deleteProcedureAction.setStatusTip("删除选中的规程") - self.deleteProcedureAction.triggered.connect(self.deleteSelectedProcedure) - self.toolbar.addAction(self.deleteProcedureAction) # 修改变量名 - - self.openProcedureAction = QAction( - qta.icon('fa5s.folder-open', color='orange'), - "打开规程", - self - ) - self.openProcedureAction.setIconText("打开规程") - self.openProcedureAction.setStatusTip("在步骤执行工具中打开选中的规程") - self.openProcedureAction.triggered.connect(self.openProcedureInExecutor) - self.toolbar.addAction(self.openProcedureAction) # 修改变量名 - - # 添加历史记录查看器动作 - self.historyAction = QAction( - qta.icon('fa5s.history', color='purple'), - "历史记录", - self - ) - self.historyAction.setIconText("历史记录") - self.historyAction.setStatusTip("查看历史执行记录") - self.historyAction.triggered.connect(self.openHistoryViewer) - self.toolbar.addAction(self.historyAction) - - # 添加批量执行规程动作 - self.batchExecuteAction = QAction( - qta.icon('fa5s.play-circle', color='green'), - "批量执行", - self - ) - self.batchExecuteAction.setIconText("批量执行") - self.batchExecuteAction.setStatusTip("按顺序批量执行当前分类中的所有规程") - self.batchExecuteAction.triggered.connect(self.batchExecuteProcedures) - self.toolbar.addAction(self.batchExecuteAction) - - # 添加关键词管理动作 - self.keywordManageAction = QAction( - qta.icon('fa5s.key', color='purple'), - "关键词管理", - self - ) - self.keywordManageAction.setIconText("关键词管理") - self.keywordManageAction.setStatusTip("管理执行步骤关键词字段库") - self.keywordManageAction.triggered.connect(self.openKeywordManager) - self.toolbar.addAction(self.keywordManageAction) - - # 添加导出规程动作 - self.exportProcedureAction = QAction( - qta.icon('fa5s.file-export', color='green'), - "导出规程", - self - ) - self.exportProcedureAction.setIconText("导出规程") - self.exportProcedureAction.setStatusTip("导出规程为Excel文件") - self.exportProcedureAction.triggered.connect(self.exportSelectedProcedure) - self.toolbar.addAction(self.exportProcedureAction) + # 导入规程按钮 - 绿色主题 + self.importBtn = QToolButton() + self.importBtn.setObjectName("importToolBtn") + self.importBtn.setIcon(qta.icon('fa5s.file-import', color='#FFFFFF')) + self.importBtn.setText("导入规程") + self.importBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.importBtn.setStatusTip("导入Excel规程文件") + self.importBtn.setStyleSheet(self.getButtonStyle( + "#10B981", "#059669", "#059669", "#047857", "#047857", "#065F46" + )) + self.importBtn.clicked.connect(self.importProcedure) + self.toolbar.addWidget(self.importBtn) + + # 添加分类按钮 - 蓝色主题 + self.addCategoryBtn = QToolButton() + self.addCategoryBtn.setObjectName("addCategoryToolBtn") + self.addCategoryBtn.setIcon(qta.icon('fa5s.folder-plus', color='#FFFFFF')) + self.addCategoryBtn.setText("添加分类") + self.addCategoryBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.addCategoryBtn.setStatusTip("添加新的分类") + self.addCategoryBtn.setStyleSheet(self.getButtonStyle( + "#3B82F6", "#2563EB", "#2563EB", "#1D4ED8", "#1D4ED8", "#1E40AF" + )) + self.addCategoryBtn.clicked.connect(self.addCategory) + self.toolbar.addWidget(self.addCategoryBtn) + + # 删除分类按钮 - 红色主题 + self.deleteCategoryBtn = QToolButton() + self.deleteCategoryBtn.setObjectName("deleteCategoryToolBtn") + self.deleteCategoryBtn.setIcon(qta.icon('fa5s.folder-minus', color='#FFFFFF')) + self.deleteCategoryBtn.setText("删除分类") + self.deleteCategoryBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.deleteCategoryBtn.setStatusTip("删除当前分类") + self.deleteCategoryBtn.setStyleSheet(self.getButtonStyle( + "#EF4444", "#DC2626", "#DC2626", "#B91C1C", "#B91C1C", "#991B1B" + )) + self.deleteCategoryBtn.clicked.connect(self.deleteCurrentCategory) + self.toolbar.addWidget(self.deleteCategoryBtn) + + self.toolbar.addSeparator() + + # 打开规程按钮 - 蓝色主题 + self.openProcedureBtn = QToolButton() + self.openProcedureBtn.setObjectName("openProcedureToolBtn") + self.openProcedureBtn.setIcon(qta.icon('fa5s.folder-open', color='#FFFFFF')) + self.openProcedureBtn.setText("打开规程") + self.openProcedureBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.openProcedureBtn.setStatusTip("在步骤执行工具中打开选中的规程") + self.openProcedureBtn.setStyleSheet(self.getButtonStyle( + "#3B82F6", "#2563EB", "#2563EB", "#1D4ED8", "#1D4ED8", "#1E40AF" + )) + self.openProcedureBtn.clicked.connect(self.openProcedureInExecutor) + self.toolbar.addWidget(self.openProcedureBtn) + + # 删除规程按钮 - 红色主题 + self.deleteProcedureBtn = QToolButton() + self.deleteProcedureBtn.setObjectName("deleteProcedureToolBtn") + self.deleteProcedureBtn.setIcon(qta.icon('fa5s.trash', color='#FFFFFF')) + self.deleteProcedureBtn.setText("删除规程") + self.deleteProcedureBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.deleteProcedureBtn.setStatusTip("删除选中的规程") + self.deleteProcedureBtn.setStyleSheet(self.getButtonStyle( + "#EF4444", "#DC2626", "#DC2626", "#B91C1C", "#B91C1C", "#991B1B" + )) + self.deleteProcedureBtn.clicked.connect(self.deleteSelectedProcedure) + self.toolbar.addWidget(self.deleteProcedureBtn) + + self.toolbar.addSeparator() + + # 历史记录按钮 - 紫色主题 + self.historyBtn = QToolButton() + self.historyBtn.setObjectName("historyToolBtn") + self.historyBtn.setIcon(qta.icon('fa5s.history', color='#FFFFFF')) + self.historyBtn.setText("历史记录") + self.historyBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.historyBtn.setStatusTip("查看历史执行记录") + self.historyBtn.setStyleSheet(self.getButtonStyle( + "#8B5CF6", "#7C3AED", "#7C3AED", "#6D28D9", "#6D28D9", "#5B21B6" + )) + self.historyBtn.clicked.connect(self.openHistoryViewer) + self.toolbar.addWidget(self.historyBtn) + + # 批量执行按钮 - 绿色主题 + self.batchExecuteBtn = QToolButton() + self.batchExecuteBtn.setObjectName("batchExecuteToolBtn") + self.batchExecuteBtn.setIcon(qta.icon('fa5s.play-circle', color='#FFFFFF')) + self.batchExecuteBtn.setText("批量执行") + self.batchExecuteBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.batchExecuteBtn.setStatusTip("按顺序批量执行当前分类中的所有规程") + self.batchExecuteBtn.setStyleSheet(self.getButtonStyle( + "#10B981", "#059669", "#059669", "#047857", "#047857", "#065F46" + )) + self.batchExecuteBtn.clicked.connect(self.batchExecuteProcedures) + self.toolbar.addWidget(self.batchExecuteBtn) + + # 关键词管理按钮 - 紫色主题 + self.keywordManageBtn = QToolButton() + self.keywordManageBtn.setObjectName("keywordManageToolBtn") + self.keywordManageBtn.setIcon(qta.icon('fa5s.key', color='#FFFFFF')) + self.keywordManageBtn.setText("关键词管理") + self.keywordManageBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.keywordManageBtn.setStatusTip("管理执行步骤关键词字段库") + self.keywordManageBtn.setStyleSheet(self.getButtonStyle( + "#8B5CF6", "#7C3AED", "#7C3AED", "#6D28D9", "#6D28D9", "#5B21B6" + )) + self.keywordManageBtn.clicked.connect(self.openKeywordManager) + self.toolbar.addWidget(self.keywordManageBtn) + + # 导出规程按钮 - 橙色主题 + self.exportProcedureBtn = QToolButton() + self.exportProcedureBtn.setObjectName("exportProcedureToolBtn") + self.exportProcedureBtn.setIcon(qta.icon('fa5s.file-export', color='#FFFFFF')) + self.exportProcedureBtn.setText("导出规程") + self.exportProcedureBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.exportProcedureBtn.setStatusTip("导出规程为Excel文件") + self.exportProcedureBtn.setStyleSheet(self.getButtonStyle( + "#F59E0B", "#D97706", "#D97706", "#B45309", "#B45309", "#92400E" + )) + self.exportProcedureBtn.clicked.connect(self.exportSelectedProcedure) + self.toolbar.addWidget(self.exportProcedureBtn) def loadCategories(self): self.categoryList.clear() @@ -384,11 +543,49 @@ class ProcedureManager(QMainWindow): self.procedureList.clear() procedures = self.db.getProcedures(categoryId) + # 存储原始数据用于过滤 + self.allProcedures = procedures + for procId, name, number, type, createdAt in procedures: item = QListWidgetItem(f"{name} ({number})") item.setData(Qt.UserRole, procId) item.setToolTip(f"类型: {type}\n创建时间: {createdAt}") self.procedureList.addItem(item) + + # 更新规程统计信息 + count = len(procedures) + if hasattr(self, 'procedureCountLabel'): + self.procedureCountLabel.setText(f"共 {count} 个规程") + + def filterProcedures(self): + """过滤规程列表""" + if not hasattr(self, 'allProcedures') or not hasattr(self, 'searchEdit'): + return + + searchText = self.searchEdit.text().lower() + + self.procedureList.clear() + filteredCount = 0 + + for procId, name, number, type, createdAt in self.allProcedures: + # 文本搜索过滤 + if searchText and searchText not in name.lower() and searchText not in number.lower(): + continue + + # 添加到列表 + item = QListWidgetItem(f"{name} ({number})") + item.setData(Qt.UserRole, procId) + item.setToolTip(f"类型: {type}\n创建时间: {createdAt}") + self.procedureList.addItem(item) + filteredCount += 1 + + # 更新统计信息 + if hasattr(self, 'procedureCountLabel'): + totalCount = len(self.allProcedures) + if filteredCount == totalCount: + self.procedureCountLabel.setText(f"共 {totalCount} 个规程") + else: + self.procedureCountLabel.setText(f"显示 {filteredCount} / {totalCount} 个规程") def categorySelected(self, currentItem, previousItem): if currentItem: @@ -562,16 +759,28 @@ class ProcedureManager(QMainWindow): def loadStylesheet(self): qssPath = "Static/Procedure.qss" try: - qssFile = QFile(qssPath) # 修改变量名 + # 尝试加载优化版QSS,如果不存在则使用原版QSS + optimizedQssPath = "Static/Procedure_optimized.qss" + qssFile = QFile(optimizedQssPath) if qssFile.exists(): qssFile.open(QFile.ReadOnly | QFile.Text) stream = QTextStream(qssFile) stream.setCodec("UTF-8") self.setStyleSheet(stream.readAll()) qssFile.close() - print(f"成功加载样式表: {qssPath}") + print("已加载优化版样式表") else: - print(f"警告:样式表文件不存在: {qssPath}") + # 回退到原始QSS + qssFile = QFile(qssPath) + if qssFile.exists(): + qssFile.open(QFile.ReadOnly | QFile.Text) + stream = QTextStream(qssFile) + stream.setCodec("UTF-8") + self.setStyleSheet(stream.readAll()) + qssFile.close() + print(f"成功加载样式表: {qssPath}") + # else: + # print(f"警告:样式表文件不存在: {qssPath}") except Exception as e: print(f"加载样式表失败: {str(e)}") diff --git a/UI/ProcedureManager/StepExecutor.py b/UI/ProcedureManager/StepExecutor.py index 98f0932..796d25c 100644 --- a/UI/ProcedureManager/StepExecutor.py +++ b/UI/ProcedureManager/StepExecutor.py @@ -81,18 +81,23 @@ class StepExecutor(QWidget): self.createTableSection(layout, testSteps) self.createControlSection(layout) self.createSettingsSection(layout) - + layout.setSpacing(0) self.setLayout(layout) self.createToolbar() def createInfoSection(self, layout): - """创建信息显示区域""" + """创建信息显示区域 - 超紧凑的两行布局""" # 创建主信息容器 infoContainer = QWidget() infoContainer.setObjectName("procedureInfoContainer") + infoContainer.setMaximumHeight(60) # 两行布局,每行约25像素 + infoContainer.setMinimumHeight(60) # 固定高度 - infoLayout = QVBoxLayout() - infoContainer.setLayout(infoLayout) + # 使用垂直布局包含两行 + mainInfoLayout = QVBoxLayout() + mainInfoLayout.setContentsMargins(4, 2, 4, 2) + mainInfoLayout.setSpacing(2) + infoContainer.setLayout(mainInfoLayout) # 兼容新旧数据结构 procedure_info = self.procedureData.get("规程信息", {}) @@ -108,51 +113,63 @@ class StepExecutor(QWidget): case_number = test_case_info.get("用例编号", "") or test_case_info.get("caseNumber", "") condition_description = test_case_info.get("工况描述", "") or test_case_info.get("conditionDescription", "") - # 第一行:规程基本信息(三个并排) - procedureRow = QHBoxLayout() - procedureRow.setSpacing(15) + # 第一行:规程名称和规程编号 + row1Layout = QHBoxLayout() + row1Layout.setContentsMargins(0, 0, 0, 0) + row1Layout.setSpacing(8) - # 规程名称 - nameGroup = self.createInfoGroup("规程名称", procedure_name, "procedureNameGroup", "procedureNameLabel") - procedureRow.addWidget(nameGroup) + nameGroup = self.createUltraCompactInfoGroup("规程名称", procedure_name) + numberGroup = self.createUltraCompactInfoGroup("规程编号", procedure_number) - # 规程编号 - numberGroup = self.createInfoGroup("规程编号", procedure_number, "procedureNumberGroup", "procedureNumberLabel") - procedureRow.addWidget(numberGroup) + row1Layout.addWidget(nameGroup) + row1Layout.addWidget(numberGroup) + row1Layout.addStretch() - # 规程类型 - typeGroup = self.createInfoGroup("规程类型", procedure_type, "procedureTypeGroup", "procedureTypeLabel") - procedureRow.addWidget(typeGroup) + # 第二行:规程类型和规程版本 + row2Layout = QHBoxLayout() + row2Layout.setContentsMargins(0, 0, 0, 0) + row2Layout.setSpacing(8) - procedureRow.addStretch() - infoLayout.addLayout(procedureRow) + typeGroup = self.createUltraCompactInfoGroup("规程类型", procedure_type) + versionGroup = self.createUltraCompactInfoGroup("规程版本", "IC") # 从截图看到的版本信息 - # 第二行:测试用例信息(两个并排) - testCaseRow = QHBoxLayout() - testCaseRow.setSpacing(15) + row2Layout.addWidget(typeGroup) + row2Layout.addWidget(versionGroup) + row2Layout.addStretch() - # 测试用例 - testCaseGroup = self.createInfoGroup("测试用例", test_case, "testCaseGroup", "testCaseLabel") - testCaseRow.addWidget(testCaseGroup) + # 添加两行到主布局 + mainInfoLayout.addLayout(row1Layout) + mainInfoLayout.addLayout(row2Layout) - # 用例编号 - caseNumberGroup = self.createInfoGroup("用例编号", case_number, "caseNumberGroup", "caseNumberLabel") - testCaseRow.addWidget(caseNumberGroup) + layout.addWidget(infoContainer) + layout.addSpacing(4) # 减小与表格的间距 + + def createCompactInfoGroup(self, label, value): + """创建紧凑型信息组件 - 水平布局,适合单行显示""" + group = QWidget() + group.setObjectName("compactInfoGroup") + group.setMaximumHeight(24) # 限制高度 - testCaseRow.addStretch() - infoLayout.addLayout(testCaseRow) + layout = QHBoxLayout() + layout.setContentsMargins(1, 0, 1, 0) # 极小的边距 + layout.setSpacing(2) # 减小间距 - # 第三行:工况描述(独占一行,因为可能较长) - if condition_description: - descriptionRow = QHBoxLayout() - descriptionGroup = self.createInfoGroup("工况描述", condition_description, "conditionDescriptionGroup", "conditionDescriptionLabel", isDescription=True) - descriptionRow.addWidget(descriptionGroup) - descriptionRow.addStretch() - infoLayout.addLayout(descriptionRow) + # 标签 + labelWidget = QLabel(f"{label}:") + labelWidget.setObjectName("compactInfoLabel") + labelWidget.setFixedWidth(50) # 减小固定宽度 + layout.addWidget(labelWidget) + + # 值 + valueWidget = QLabel(value if value else "未设置") + valueWidget.setObjectName("compactInfoValue") + valueWidget.setElideMode(Qt.ElideRight) # 文本过长时显示省略号 + valueWidget.setMaximumWidth(100) # 限制最大宽度 + layout.addWidget(valueWidget) + + group.setLayout(layout) + return group - layout.addWidget(infoContainer) - layout.addSpacing(20) - def createInfoGroup(self, label, value, groupObjectName, labelObjectName, isDescription=False): """创建信息分组组件""" groupWidget = QWidget() @@ -180,6 +197,64 @@ class StepExecutor(QWidget): return groupWidget + def createCompactInfoGroup(self, label, value): + """创建紧凑的信息组件 - 用于单行布局""" + groupWidget = QWidget() + groupWidget.setObjectName("compactInfoGroup") + + # 使用水平布局 + groupLayout = QHBoxLayout() + groupLayout.setContentsMargins(8, 2, 8, 2) + groupLayout.setSpacing(4) + groupWidget.setLayout(groupLayout) + + # 标签 + labelWidget = QLabel(f"{label}:") + labelWidget.setObjectName("compactInfoLabel") + labelWidget.setMinimumWidth(60) + groupLayout.addWidget(labelWidget) + + # 值 + valueWidget = QLabel(value if value else "未设置") + valueWidget.setObjectName("compactInfoValue") + valueWidget.setWordWrap(False) # 不换行以保持紧凑 + groupLayout.addWidget(valueWidget) + + return groupWidget + + def createUltraCompactInfoGroup(self, label, value): + """创建超紧凑的信息组件 - 用于两行布局""" + groupWidget = QWidget() + groupWidget.setObjectName("ultraCompactInfoGroup") + groupWidget.setMaximumHeight(25) # 限制每个组件的高度 + + # 使用水平布局 + groupLayout = QHBoxLayout() + groupLayout.setContentsMargins(2, 0, 2, 0) # 极小边距 + groupLayout.setSpacing(4) + groupWidget.setLayout(groupLayout) + + # 标签 + labelWidget = QLabel(f"{label}:") + labelWidget.setObjectName("ultraCompactInfoLabel") + labelWidget.setFixedWidth(60) # 固定标签宽度 + groupLayout.addWidget(labelWidget) + + # 值 + valueWidget = QLabel(value if value else "未设置") + valueWidget.setObjectName("ultraCompactInfoValue") + valueWidget.setObjectName("ultraCompactInfoValue") + valueWidget.setWordWrap(False) + valueWidget.setMaximumWidth(200) # 限制值的最大宽度 + + # 手动处理文本截断 + if value and len(value) > 25: # 如果文本过长,手动截断并添加省略号 + valueWidget.setText(value[:22] + "...") + + groupLayout.addWidget(valueWidget) + + return groupWidget + def createTableSection(self, layout, testSteps): """创建表格区域""" try: @@ -429,7 +504,21 @@ class StepExecutor(QWidget): def updateStatusDisplay(self, message, color="blue"): """更新状态显示""" self.statusLabel.setText(message) - self.statusLabel.setStyleSheet(f"color: {color}; font-weight: bold;") + self.statusLabel.setObjectName("executionStatusLabel") + + # 根据颜色设置状态属性 + if color == "#059669" or "green" in color.lower(): + self.statusLabel.setProperty("status", "success") + elif color == "#DC2626" or "red" in color.lower(): + self.statusLabel.setProperty("status", "error") + elif color == "#D97706" or "orange" in color.lower(): + self.statusLabel.setProperty("status", "warning") + else: + self.statusLabel.setProperty("status", "info") + + # 刷新样式 + self.statusLabel.style().unpolish(self.statusLabel) + self.statusLabel.style().polish(self.statusLabel) def startAutoExecute(self): """开始自动执行""" @@ -568,10 +657,18 @@ class StepExecutor(QWidget): if not stepInfo: return - # 计算需要的行高 - description = stepInfo.get('description', '') - result = stepInfo.get('result', '') - note = stepInfo.get('note', '') + # 计算需要的行高,确保所有值都不为None + description = stepInfo.get('description') or '' + result = stepInfo.get('result') or '' + note = stepInfo.get('note') or '' + + # 确保所有值都是字符串类型 + if not isinstance(description, str): + description = str(description) if description is not None else '' + if not isinstance(result, str): + result = str(result) if result is not None else '' + if not isinstance(note, str): + note = str(note) if note is not None else '' # 基于最长文本计算行高 max_text_length = max(len(description), len(result), len(note)) @@ -597,7 +694,14 @@ class StepExecutor(QWidget): def adjustAllRowHeights(self): """调整所有行的高度""" try: - for row in range(self.tableModel.rowCount()): + if not hasattr(self, 'tableModel') or self.tableModel is None: + return + + row_count = self.tableModel.rowCount() + if row_count <= 0: + return + + for row in range(row_count): self.adjustRowHeight(row) except Exception as e: print(f"调整所有行高时出错: {e}") diff --git a/bin.py b/bin.py index eafcec1..8f6c747 100644 --- a/bin.py +++ b/bin.py @@ -15,6 +15,7 @@ logging.getLogger('modbus_tk').setLevel(logging.ERROR) if __name__ == '__main__': app = QApplication(sys.argv) + print(1111111) app.setStyle(QStyleFactory.create('Fusion')) app.setStyleSheet(CommonHelper.readQss('Static/Main.qss') + CommonHelper.readQss('Static/profibus.qss') @@ -27,6 +28,7 @@ if __name__ == '__main__': regWidget = RegisterWidget() regWidget.show() else: + Globals._init() Client.initDB() ex = MainWindow()