From 61e06dd789c031186b5a35b8677cac930cdf7c3e Mon Sep 17 00:00:00 2001 From: DXC Date: Mon, 29 Jun 2026 15:15:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=AD=A5=E9=AA=A4=E5=8D=81=E4=B8=89=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gui/panels/step13_report_panel.py | 206 ++++++++++++++++++-------- 1 file changed, 144 insertions(+), 62 deletions(-) diff --git a/src/gui/panels/step13_report_panel.py b/src/gui/panels/step13_report_panel.py index bc455f5..4a80424 100644 --- a/src/gui/panels/step13_report_panel.py +++ b/src/gui/panels/step13_report_panel.py @@ -99,110 +99,186 @@ class Step13ReportPanel(QWidget): self.init_ui() def init_ui(self): + # 1. 确保全局主题类被导入 + from src.gui.styles import ModernStylesheet + layout = QVBoxLayout() - layout.setContentsMargins(10, 10, 10, 10) - layout.setSpacing(10) + layout.setContentsMargins(24, 24, 24, 24) + layout.setSpacing(20) + + # 重新定义全局绝美样式 + common_lineedit_css = "QLineEdit { padding: 4px 8px; border: 1px solid #C0C0C0; border-radius: 4px; min-height: 24px; background-color: #FFFFFF; }" + common_btn_css = """ + QPushButton { background-color: #F8F9FA; border: 1px solid #D1D5DB; border-radius: 4px; padding: 4px 12px; color: #374151; } + QPushButton:hover { background-color: #F3F4F6; border-color: #9CA3AF; } + """ + + # 【核心黑科技】:定义标准行闭包,强制统一 120px 标签和 10px 间距 + def create_standard_row(label_text, widget, btn=None): + row = QHBoxLayout() + row.setContentsMargins(0, 0, 0, 0) + row.setSpacing(10) + label = QLabel(label_text) + label.setMinimumWidth(120) + label.setMaximumWidth(120) + row.addWidget(label) + row.addWidget(widget, 1) + if btn: + row.addWidget(btn) + return row intro = QLabel( - "根据工作目录下的可视化结果(14_visualization 等)生成 Word 分析报告。" + "💡 提示:根据工作目录下的可视化结果(14_visualization 等)自动生成 Word 分析报告。\n" "需已存在可视化图表;AI 分析通过 Ollama 或 Minimax 调用云端/本地服务。" ) intro.setWordWrap(True) - intro.setStyleSheet( - f"color: {ModernStylesheet.COLORS.get('text_secondary', '#666')};" - ) + intro.setStyleSheet(f""" + QLabel {{ + color: {ModernStylesheet.COLORS.get('primary', '#0078D7')}; + background-color: {ModernStylesheet.COLORS.get('selected', '#E5F3FF')}; + border: 1px solid {ModernStylesheet.COLORS.get('border_light', '#D1D5DB')}; + border-radius: 6px; + padding: 10px 14px; + margin-bottom: 8px; + }} + """) layout.addWidget(intro) - # ── 路径 ────────────────────────────────────────────────────────────── - path_group = QGroupBox("路径") - path_form = QFormLayout() + # ========================================== + # 卡片 1:报告路径配置 + # ========================================== + path_group = QGroupBox("📁 报告路径配置") + path_layout = QVBoxLayout() + path_layout.setSpacing(16) + path_layout.setContentsMargins(20, 24, 20, 20) - wd_row = QHBoxLayout() + # 工作目录 (只读) self.work_dir_edit = QLineEdit() self.work_dir_edit.setPlaceholderText("流程工作目录(含 14_visualization)…") self.work_dir_edit.setReadOnly(True) - wd_browse = QPushButton("浏览…") - wd_browse.clicked.connect(self.browse_work_dir) - wd_browse.setVisible(False) - sync_btn = QPushButton("同步主窗口工作目录") - sync_btn.clicked.connect(self.sync_work_dir_from_main) - sync_btn.setVisible(False) - wd_row.addWidget(self.work_dir_edit, 1) - wd_row.addWidget(wd_browse) - wd_row.addWidget(sync_btn) - path_form.addRow("工作目录:", wd_row) + self.work_dir_edit.setStyleSheet(common_lineedit_css) + path_layout.addLayout(create_standard_row("工作目录:", self.work_dir_edit)) - out_row = QHBoxLayout() + # 报告输出目录 self.output_dir_edit = QLineEdit() self.output_dir_edit.setPlaceholderText("留空则保存到 工作目录/14_visualization") - out_browse = QPushButton("浏览…") - out_browse.clicked.connect(self.browse_output_dir) - out_row.addWidget(self.output_dir_edit, 1) - out_row.addWidget(out_browse) - path_form.addRow("报告输出目录:", out_row) + self.output_dir_edit.setStyleSheet(common_lineedit_css) + out_browse = QPushButton("浏览...") + out_browse.setStyleSheet(common_btn_css) + out_browse.setMaximumWidth(80) + out_browse.clicked.connect(self.browse_output_dir) + + path_layout.addLayout(create_standard_row("报告输出目录:", self.output_dir_edit, out_browse)) + + # 报告标题 self.report_title_edit = QLineEdit() self.report_title_edit.setText("水质参数反演分析报告") - path_form.addRow("报告标题:", self.report_title_edit) + self.report_title_edit.setStyleSheet(common_lineedit_css) + path_layout.addLayout(create_standard_row("报告标题:", self.report_title_edit)) - path_group.setLayout(path_form) + path_group.setLayout(path_layout) layout.addWidget(path_group) - # ── AI 分析 ─────────────────────────────────────────────────────────── - ai_group = QGroupBox("AI 分析") + # ========================================== + # 卡片 2:AI 分析配置 + # ========================================== + ai_group = QGroupBox("🤖 AI 分析配置") ai_layout = QVBoxLayout() + ai_layout.setSpacing(16) + ai_layout.setContentsMargins(20, 24, 20, 20) + + # 启用复选框,采用占位符顶齐 120px + check_row = QHBoxLayout() + check_row.setContentsMargins(0, 0, 0, 0) + check_row.setSpacing(10) + placeholder = QLabel("") + placeholder.setMinimumWidth(120) + placeholder.setMaximumWidth(120) + check_row.addWidget(placeholder) self.enable_ai_cb = QCheckBox("启用 AI 图表解读与综合总结") - self.enable_ai_cb.setChecked( - os.environ.get("ENABLE_AI_ANALYSIS", "1") not in {"0", "false", "False"} - ) - ai_layout.addWidget(self.enable_ai_cb) + self.enable_ai_cb.setChecked(os.environ.get("ENABLE_AI_ANALYSIS", "1") not in {"0", "false", "False"}) + check_row.addWidget(self.enable_ai_cb) + check_row.addStretch() + ai_layout.addLayout(check_row) - # 只读提示行:当前引擎 + 配置按钮 + # AI 引擎状态行 ai_status_row = QHBoxLayout() - ai_status_row.addWidget(QLabel("当前 AI 引擎:")) + ai_status_row.setContentsMargins(0, 0, 0, 0) + ai_status_row.setSpacing(10) + engine_label = QLabel("当前 AI 引擎:") + engine_label.setMinimumWidth(120) + engine_label.setMaximumWidth(120) + ai_status_row.addWidget(engine_label) + self._ai_label = QLabel() - self._ai_label.setStyleSheet("color: #0078d4; font-weight: bold;") + self._ai_label.setStyleSheet("color: #0078D7; font-weight: bold; font-size: 13px;") ai_status_row.addWidget(self._ai_label) ai_status_row.addStretch(1) + open_settings_btn = QPushButton("高级配置...") + open_settings_btn.setStyleSheet(common_btn_css) open_settings_btn.clicked.connect(self._open_ai_settings) ai_status_row.addWidget(open_settings_btn) + ai_layout.addLayout(ai_status_row) ai_group.setLayout(ai_layout) layout.addWidget(ai_group) - # ── 按钮 ────────────────────────────────────────────────────────────── - btn_row = QHBoxLayout() - self.generate_btn = QPushButton("生成 Word 报告") - self.generate_btn.setStyleSheet( - ModernStylesheet.get_button_stylesheet("success") - ) - self.generate_btn.clicked.connect(self.on_generate_clicked) - btn_row.addWidget(self.generate_btn) - btn_row.addStretch() - layout.addLayout(btn_row) + # ========================================== + # 卡片 3:输出与执行 + # ========================================== + execute_group = QGroupBox("🚀 输出与执行") + execute_layout = QVBoxLayout() + execute_layout.setSpacing(12) + execute_layout.setContentsMargins(20, 24, 20, 20) - # ── 进度条 + 状态文案 ──────────────────────────────────────────────── - progress_row = QHBoxLayout() - self.progress_label = QLabel("就绪") - self.progress_label.setMinimumWidth(220) - progress_row.addWidget(self.progress_label) + # 进度状态呈现(使用独一无二的 ID 选择器,彻底击碎并无视任何全局白块背景样式!) + self.progress_label = QLabel("⏳ 准备就绪") + self.progress_label.setObjectName("step13_progress_lbl") + self.progress_label.setStyleSheet(""" + QLabel#step13_progress_lbl { + background-color: transparent; + background: transparent; + border: none; + color: #4A5568; + font-size: 13px; + font-weight: 500; + } + """) + execute_layout.addWidget(self.progress_label) + + # 现代极简细线条进度条 self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) - self.progress_bar.setTextVisible(True) - progress_row.addWidget(self.progress_bar, 1) - layout.addLayout(progress_row) + self.progress_bar.setTextVisible(False) # 【核心】关闭原生文字,彻底干掉 Windows 丑陋白框! + self.progress_bar.setFixedHeight(8) # 压低高度,高级感拉满 + self.progress_bar.setStyleSheet(""" + QProgressBar { border: none; border-radius: 4px; background-color: #E2E8F0; } + QProgressBar::chunk { background-color: #0078D7; border-radius: 4px; } + """) + execute_layout.addWidget(self.progress_bar) + + action_layout = QHBoxLayout() + action_layout.addStretch() + + self.generate_btn = QPushButton("独立运行步骤") + self.generate_btn.setStyleSheet(ModernStylesheet.get_button_stylesheet("primary")) + self.generate_btn.setMinimumWidth(140) + self.generate_btn.clicked.connect(self.on_generate_clicked) + action_layout.addWidget(self.generate_btn) + + execute_layout.addLayout(action_layout) + execute_group.setLayout(execute_layout) + layout.addWidget(execute_group) layout.addStretch() self.setLayout(layout) - # 刷新引擎提示文字 self._refresh_ai_label() - - # 初次构建时尝试同步主窗口工作目录 self._auto_pull_work_dir() def _refresh_ai_label(self): @@ -215,6 +291,11 @@ class Step13ReportPanel(QWidget): def _open_ai_settings(self): """弹出全局 AI 设置对话框,保存后刷新提示标签。""" dlg = AISettingsDialog(self) + # 【强力注入】强制将配置弹窗内的标签与编组框背景抹为透明,全面恢复高级灰度质感! + dlg.setStyleSheet(""" + QLabel { background-color: transparent; background: transparent; border: none; } + QGroupBox { background-color: transparent; } + """) if dlg.exec_() == dlg.Accepted: self._refresh_ai_label() @@ -333,21 +414,22 @@ class Step13ReportPanel(QWidget): print(f"[{level}] {msg}") def _on_progress(self, pct: int, text: str): - """接收 ReportWorkerThread.progress(int, str) —— 主线程槽。""" + """接收后台进度的槽函数 —— 主线程。""" self.progress_bar.setValue(int(pct)) - self.progress_label.setText(text or f"{pct}%") + # 将百分比和文案完美融合进透明标签 + self.progress_label.setText(f"⏳ {text} ({pct}%)" if text else f"⏳ 正在生成... ({pct}%)") def _on_finished(self, path: str): """报告生成成功 —— 主线程槽。""" self.progress_bar.setValue(100) - self.progress_label.setText("完成") + self.progress_label.setText("✅ 分析报告生成成功!") self.generate_btn.setEnabled(True) QMessageBox.information(self, "完成", f"报告已生成:\n{path}") self._forward_log(f"Word 报告已保存: {path}", "info") def _on_error(self, err: str): """报告生成异常 —— 主线程槽。""" - self.progress_label.setText("失败") + self.progress_label.setText("❌ 报告生成失败") self.generate_btn.setEnabled(True) QMessageBox.critical(self, "失败", f"报告生成失败:\n{err[:800]}") self._forward_log(err, "error") \ No newline at end of file