From 6e78e50b0bd2096ad178bb53f20c77a3b18d8988 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Tue, 9 Jun 2026 11:48:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(app):=20TopBar=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E4=B8=BA=E6=95=B0=E6=8D=AE=E9=A9=B1=E5=8A=A8=E7=B1=BB=EF=BC=88?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E7=A9=BA=E9=97=B4/=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=BF=A1=E5=8F=B7=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/TopBar.cpp | 147 ++++++++++++++++++++++++++------------------- src/app/TopBar.hpp | 34 +++++++---- src/app/main.cpp | 2 +- 3 files changed, 108 insertions(+), 75 deletions(-) diff --git a/src/app/TopBar.cpp b/src/app/TopBar.cpp index 57e08d9..97e35a3 100644 --- a/src/app/TopBar.cpp +++ b/src/app/TopBar.cpp @@ -127,12 +127,10 @@ QWidget* buildMenuBar(QWidget* parent) return mb; } -QWidget* buildTopToolBar(QWidget* parent) -{ - auto* bar = new QWidget(parent); - bar->setObjectName(QStringLiteral("appToolBar")); - bar->setFixedHeight(56); - bar->setStyleSheet(QStringLiteral( +TopBar::TopBar(QWidget* parent) : QWidget(parent) { + setObjectName(QStringLiteral("appToolBar")); + setFixedHeight(56); + setStyleSheet(QStringLiteral( "#appToolBar { background:#FFFFFF; border-bottom:1px solid #E1E6EE; }" "#topDivider { color:#E1E6EE; }" "#wsSwitcher { color:#1F2A3D; border:none; border-radius:8px; padding:8px 12px;" @@ -146,81 +144,56 @@ QWidget* buildTopToolBar(QWidget* parent) "#userName { color:#1F2A3D; font-size:13px; font-weight:600; }" "#userRole { color:#8A93A3; font-size:11px; }")); - auto* lay = new QHBoxLayout(bar); + auto* lay = new QHBoxLayout(this); lay->setContentsMargins(14, 0, 14, 0); lay->setSpacing(0); - // ── 工作空间切换(最左):显示当前空间,点击下拉切换 ── - auto* wsBtn = new QToolButton(bar); - wsBtn->setObjectName(QStringLiteral("wsSwitcher")); - wsBtn->setIcon(makeGlyph(Glyph::Workspace, QColor("#2D6CB5"), kWorkspaceIcon)); - wsBtn->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon)); - wsBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - wsBtn->setPopupMode(QToolButton::InstantPopup); - wsBtn->setCursor(Qt::PointingHandCursor); - - auto* wsMenu = new QMenu(bar); - auto* wsHeader = wsMenu->addAction(QStringLiteral("切换空间")); - wsHeader->setEnabled(false); - wsMenu->addSeparator(); - auto* wsGroup = new QActionGroup(bar); - wsGroup->setExclusive(true); - const QStringList spaces = {QStringLiteral("个人工作空间"), QStringLiteral("勘探一队"), - QStringLiteral("研究院共享")}; - for (const auto& s : spaces) { - auto* a = wsMenu->addAction(s); - a->setCheckable(true); - wsGroup->addAction(a); - if (s == spaces.front()) a->setChecked(true); - QObject::connect(a, &QAction::triggered, wsBtn, - [wsBtn, s]() { wsBtn->setText(s + QStringLiteral(" ▾")); }); - } - wsBtn->setMenu(wsMenu); - wsBtn->setText(spaces.front() + QStringLiteral(" ▾")); - lay->addWidget(wsBtn); + // 工作空间切换器(数据驱动;初始占位文本,待 setWorkspaces 填充)。 + wsBtn_ = new QToolButton(this); + wsBtn_->setObjectName(QStringLiteral("wsSwitcher")); + wsBtn_->setIcon(makeGlyph(Glyph::Workspace, QColor("#2D6CB5"), kWorkspaceIcon)); + wsBtn_->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon)); + wsBtn_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + wsBtn_->setPopupMode(QToolButton::InstantPopup); + wsBtn_->setCursor(Qt::PointingHandCursor); + wsBtn_->setText(QStringLiteral("(加载中…)")); + wsBtn_->setMenu(new QMenu(wsBtn_)); + lay->addWidget(wsBtn_); lay->addSpacing(10); - lay->addWidget(makeDivider(bar)); + lay->addWidget(makeDivider(this)); lay->addSpacing(10); - // ── 项目选择器(与工作空间切换同款样式:无边框 + 图标 + 文本 + 下拉)── - auto* projBtn = new QToolButton(bar); - projBtn->setObjectName(QStringLiteral("wsSwitcher")); - projBtn->setIcon(makeGlyph(Glyph::Folder, QColor("#2D6CB5"), kWorkspaceIcon)); - projBtn->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon)); - projBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - projBtn->setPopupMode(QToolButton::InstantPopup); - projBtn->setCursor(Qt::PointingHandCursor); - auto* projMenu = new QMenu(bar); - auto* projHeader = projMenu->addAction(QStringLiteral("切换项目")); - projHeader->setEnabled(false); - projMenu->addSeparator(); - auto* projCur = projMenu->addAction(QStringLiteral("青海湖北岸勘探项目")); - projCur->setCheckable(true); - projCur->setChecked(true); - projBtn->setMenu(projMenu); - projBtn->setText(QStringLiteral("青海湖北岸勘探项目 青海·海北州 ▾")); - lay->addWidget(projBtn); + // 项目切换器(数据驱动)。 + projBtn_ = new QToolButton(this); + projBtn_->setObjectName(QStringLiteral("wsSwitcher")); + projBtn_->setIcon(makeGlyph(Glyph::Folder, QColor("#2D6CB5"), kWorkspaceIcon)); + projBtn_->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon)); + projBtn_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + projBtn_->setPopupMode(QToolButton::InstantPopup); + projBtn_->setCursor(Qt::PointingHandCursor); + projBtn_->setText(QStringLiteral("(加载中…)")); + projBtn_->setMenu(new QMenu(projBtn_)); + lay->addWidget(projBtn_); lay->addStretch(); - // ── 右侧:帮助 / 通知 / 设置(仅图标,悬停显示文本)── - lay->addWidget(makeIconButton(bar, Glyph::Help, QStringLiteral("帮助"))); - lay->addWidget(makeIconButton(bar, Glyph::Bell, QStringLiteral("通知"))); - lay->addWidget(makeIconButton(bar, Glyph::Gear, QStringLiteral("设置"))); + lay->addWidget(makeIconButton(this, Glyph::Help, QStringLiteral("帮助"))); + lay->addWidget(makeIconButton(this, Glyph::Bell, QStringLiteral("通知"))); + lay->addWidget(makeIconButton(this, Glyph::Gear, QStringLiteral("设置"))); lay->addSpacing(10); - lay->addWidget(makeDivider(bar)); + lay->addWidget(makeDivider(this)); lay->addSpacing(12); - // ── 用户:圆形头像 + 姓名/职务 ── - auto* avatar = new QLabel(QStringLiteral("ZL"), bar); + // 用户区(本轮静态)。 + auto* avatar = new QLabel(QStringLiteral("ZL"), this); avatar->setObjectName(QStringLiteral("avatar")); avatar->setFixedSize(34, 34); avatar->setAlignment(Qt::AlignCenter); lay->addWidget(avatar); lay->addSpacing(8); - auto* userBox = new QWidget(bar); + auto* userBox = new QWidget(this); auto* userLay = new QVBoxLayout(userBox); userLay->setContentsMargins(0, 0, 0, 0); userLay->setSpacing(0); @@ -231,8 +204,56 @@ QWidget* buildTopToolBar(QWidget* parent) userLay->addWidget(userName); userLay->addWidget(userRole); lay->addWidget(userBox); +} - return bar; +void TopBar::setWorkspaces(const std::vector& list, const QString& currentId) { + auto* menu = new QMenu(wsBtn_); + auto* header = menu->addAction(QStringLiteral("切换空间")); + header->setEnabled(false); + menu->addSeparator(); + QString currentName; + for (const auto& w : list) { + const QString id = QString::fromStdString(w.id); + const QString name = QString::fromStdString(w.name); + auto* a = menu->addAction(name); + a->setCheckable(true); + a->setChecked(id == currentId); + if (id == currentId) currentName = name; + QObject::connect(a, &QAction::triggered, this, + [this, id]() { emit workspaceSwitchRequested(id); }); + } + if (list.empty()) { + auto* none = menu->addAction(QStringLiteral("(暂无空间)")); + none->setEnabled(false); + } + wsBtn_->setMenu(menu); + wsBtn_->setText((currentName.isEmpty() ? QStringLiteral("选择空间") : currentName) + + QStringLiteral(" ▾")); +} + +void TopBar::setProjects(const std::vector& list, const QString& currentId) { + auto* menu = new QMenu(projBtn_); + auto* header = menu->addAction(QStringLiteral("切换项目")); + header->setEnabled(false); + menu->addSeparator(); + QString currentName; + for (const auto& p : list) { + const QString id = QString::fromStdString(p.id); + const QString name = QString::fromStdString(p.name); + auto* a = menu->addAction(name); + a->setCheckable(true); + a->setChecked(id == currentId); + if (id == currentId) currentName = name; + QObject::connect(a, &QAction::triggered, this, + [this, id]() { emit projectSwitchRequested(id); }); + } + if (list.empty()) { + auto* none = menu->addAction(QStringLiteral("(暂无项目)")); + none->setEnabled(false); + } + projBtn_->setMenu(menu); + projBtn_->setText((currentName.isEmpty() ? QStringLiteral("选择项目") : currentName) + + QStringLiteral(" ▾")); } } // namespace geopro::app diff --git a/src/app/TopBar.hpp b/src/app/TopBar.hpp index 6f4a3e0..3937f9f 100644 --- a/src/app/TopBar.hpp +++ b/src/app/TopBar.hpp @@ -1,19 +1,31 @@ #pragma once +#include +#include +#include "repo/RepoTypes.hpp" -// 顶部应用区(对齐原型,静态视觉壳): -// - buildMenuBar:最上方的菜单栏(视图 / 项目管理 / 业务工具 / 设备,含多级子菜单)。 -// - buildTopToolBar:菜单栏下方的工具条(工作空间切换 + 项目选择 + 帮助/通知/设置 + 用户)。 -// 调用方将两者纵向堆叠后经 QMainWindow::setMenuWidget 挂到主窗口顶部。 -// 菜单/按钮当前为静态占位,后续接真实页面与数据。 - -class QWidget; +class QToolButton; namespace geopro::app { -// 顶部菜单栏(返回 QWidget*,内部是 QMenuBar;调用方放在最上一行)。 -QWidget* buildMenuBar(QWidget* parent = nullptr); +// 顶部菜单栏(静态,本轮不接真实页面)。 +QWidget* buildMenuBar(QWidget* parent); -// 菜单栏下方的工具条(工作空间/项目/帮助/通知/设置/用户)。 -QWidget* buildTopToolBar(QWidget* parent = nullptr); +// 顶部工具条:数据驱动的工作空间/项目切换器 + 右侧图标 + 用户区。 +class TopBar : public QWidget { + Q_OBJECT +public: + explicit TopBar(QWidget* parent = nullptr); + + void setWorkspaces(const std::vector& list, const QString& currentId); + void setProjects(const std::vector& list, const QString& currentId); + +signals: + void workspaceSwitchRequested(const QString& tenantId); + void projectSwitchRequested(const QString& projectId); + +private: + QToolButton* wsBtn_ = nullptr; + QToolButton* projBtn_ = nullptr; +}; } // namespace geopro::app diff --git a/src/app/main.cpp b/src/app/main.cpp index e77585c..2fee684 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -790,7 +790,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re topLayout->setContentsMargins(0, 0, 0, 0); topLayout->setSpacing(0); topLayout->addWidget(geopro::app::buildMenuBar(topChrome)); - topLayout->addWidget(geopro::app::buildTopToolBar(topChrome)); + topLayout->addWidget(new geopro::app::TopBar(topChrome)); window.setMenuWidget(topChrome); }