feat(app): TopBar 升级为数据驱动类(工作空间/项目切换信号)

This commit is contained in:
gaozheng 2026-06-09 11:48:40 +08:00
parent f4ca9bcd38
commit 6e78e50b0b
3 changed files with 108 additions and 75 deletions

View File

@ -127,12 +127,10 @@ QWidget* buildMenuBar(QWidget* parent)
return mb; return mb;
} }
QWidget* buildTopToolBar(QWidget* parent) TopBar::TopBar(QWidget* parent) : QWidget(parent) {
{ setObjectName(QStringLiteral("appToolBar"));
auto* bar = new QWidget(parent); setFixedHeight(56);
bar->setObjectName(QStringLiteral("appToolBar")); setStyleSheet(QStringLiteral(
bar->setFixedHeight(56);
bar->setStyleSheet(QStringLiteral(
"#appToolBar { background:#FFFFFF; border-bottom:1px solid #E1E6EE; }" "#appToolBar { background:#FFFFFF; border-bottom:1px solid #E1E6EE; }"
"#topDivider { color:#E1E6EE; }" "#topDivider { color:#E1E6EE; }"
"#wsSwitcher { color:#1F2A3D; border:none; border-radius:8px; padding:8px 12px;" "#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; }" "#userName { color:#1F2A3D; font-size:13px; font-weight:600; }"
"#userRole { color:#8A93A3; font-size:11px; }")); "#userRole { color:#8A93A3; font-size:11px; }"));
auto* lay = new QHBoxLayout(bar); auto* lay = new QHBoxLayout(this);
lay->setContentsMargins(14, 0, 14, 0); lay->setContentsMargins(14, 0, 14, 0);
lay->setSpacing(0); lay->setSpacing(0);
// ── 工作空间切换(最左):显示当前空间,点击下拉切换 ── // 工作空间切换器(数据驱动;初始占位文本,待 setWorkspaces 填充)。
auto* wsBtn = new QToolButton(bar); wsBtn_ = new QToolButton(this);
wsBtn->setObjectName(QStringLiteral("wsSwitcher")); wsBtn_->setObjectName(QStringLiteral("wsSwitcher"));
wsBtn->setIcon(makeGlyph(Glyph::Workspace, QColor("#2D6CB5"), kWorkspaceIcon)); wsBtn_->setIcon(makeGlyph(Glyph::Workspace, QColor("#2D6CB5"), kWorkspaceIcon));
wsBtn->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon)); wsBtn_->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon));
wsBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); wsBtn_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
wsBtn->setPopupMode(QToolButton::InstantPopup); wsBtn_->setPopupMode(QToolButton::InstantPopup);
wsBtn->setCursor(Qt::PointingHandCursor); wsBtn_->setCursor(Qt::PointingHandCursor);
wsBtn_->setText(QStringLiteral("(加载中…)"));
auto* wsMenu = new QMenu(bar); wsBtn_->setMenu(new QMenu(wsBtn_));
auto* wsHeader = wsMenu->addAction(QStringLiteral("切换空间")); lay->addWidget(wsBtn_);
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);
lay->addSpacing(10); lay->addSpacing(10);
lay->addWidget(makeDivider(bar)); lay->addWidget(makeDivider(this));
lay->addSpacing(10); lay->addSpacing(10);
// ── 项目选择器(与工作空间切换同款样式:无边框 + 图标 + 文本 + 下拉)── // 项目切换器(数据驱动)。
auto* projBtn = new QToolButton(bar); projBtn_ = new QToolButton(this);
projBtn->setObjectName(QStringLiteral("wsSwitcher")); projBtn_->setObjectName(QStringLiteral("wsSwitcher"));
projBtn->setIcon(makeGlyph(Glyph::Folder, QColor("#2D6CB5"), kWorkspaceIcon)); projBtn_->setIcon(makeGlyph(Glyph::Folder, QColor("#2D6CB5"), kWorkspaceIcon));
projBtn->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon)); projBtn_->setIconSize(QSize(kWorkspaceIcon, kWorkspaceIcon));
projBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); projBtn_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
projBtn->setPopupMode(QToolButton::InstantPopup); projBtn_->setPopupMode(QToolButton::InstantPopup);
projBtn->setCursor(Qt::PointingHandCursor); projBtn_->setCursor(Qt::PointingHandCursor);
auto* projMenu = new QMenu(bar); projBtn_->setText(QStringLiteral("(加载中…)"));
auto* projHeader = projMenu->addAction(QStringLiteral("切换项目")); projBtn_->setMenu(new QMenu(projBtn_));
projHeader->setEnabled(false); lay->addWidget(projBtn_);
projMenu->addSeparator();
auto* projCur = projMenu->addAction(QStringLiteral("青海湖北岸勘探项目"));
projCur->setCheckable(true);
projCur->setChecked(true);
projBtn->setMenu(projMenu);
projBtn->setText(QStringLiteral("青海湖北岸勘探项目 青海·海北州 ▾"));
lay->addWidget(projBtn);
lay->addStretch(); lay->addStretch();
// ── 右侧:帮助 / 通知 / 设置(仅图标,悬停显示文本)── lay->addWidget(makeIconButton(this, Glyph::Help, QStringLiteral("帮助")));
lay->addWidget(makeIconButton(bar, Glyph::Help, QStringLiteral("帮助"))); lay->addWidget(makeIconButton(this, Glyph::Bell, QStringLiteral("通知")));
lay->addWidget(makeIconButton(bar, Glyph::Bell, QStringLiteral("通知"))); lay->addWidget(makeIconButton(this, Glyph::Gear, QStringLiteral("设置")));
lay->addWidget(makeIconButton(bar, Glyph::Gear, QStringLiteral("设置")));
lay->addSpacing(10); lay->addSpacing(10);
lay->addWidget(makeDivider(bar)); lay->addWidget(makeDivider(this));
lay->addSpacing(12); lay->addSpacing(12);
// ── 用户:圆形头像 + 姓名/职务 ── // 用户区(本轮静态)。
auto* avatar = new QLabel(QStringLiteral("ZL"), bar); auto* avatar = new QLabel(QStringLiteral("ZL"), this);
avatar->setObjectName(QStringLiteral("avatar")); avatar->setObjectName(QStringLiteral("avatar"));
avatar->setFixedSize(34, 34); avatar->setFixedSize(34, 34);
avatar->setAlignment(Qt::AlignCenter); avatar->setAlignment(Qt::AlignCenter);
lay->addWidget(avatar); lay->addWidget(avatar);
lay->addSpacing(8); lay->addSpacing(8);
auto* userBox = new QWidget(bar); auto* userBox = new QWidget(this);
auto* userLay = new QVBoxLayout(userBox); auto* userLay = new QVBoxLayout(userBox);
userLay->setContentsMargins(0, 0, 0, 0); userLay->setContentsMargins(0, 0, 0, 0);
userLay->setSpacing(0); userLay->setSpacing(0);
@ -231,8 +204,56 @@ QWidget* buildTopToolBar(QWidget* parent)
userLay->addWidget(userName); userLay->addWidget(userName);
userLay->addWidget(userRole); userLay->addWidget(userRole);
lay->addWidget(userBox); lay->addWidget(userBox);
}
return bar; void TopBar::setWorkspaces(const std::vector<data::Workspace>& 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<data::ProjectSummary>& 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 } // namespace geopro::app

View File

@ -1,19 +1,31 @@
#pragma once #pragma once
#include <QWidget>
#include <vector>
#include "repo/RepoTypes.hpp"
// 顶部应用区(对齐原型,静态视觉壳): class QToolButton;
// - buildMenuBar最上方的菜单栏视图 / 项目管理 / 业务工具 / 设备,含多级子菜单)。
// - buildTopToolBar菜单栏下方的工具条工作空间切换 + 项目选择 + 帮助/通知/设置 + 用户)。
// 调用方将两者纵向堆叠后经 QMainWindow::setMenuWidget 挂到主窗口顶部。
// 菜单/按钮当前为静态占位,后续接真实页面与数据。
class QWidget;
namespace geopro::app { 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<data::Workspace>& list, const QString& currentId);
void setProjects(const std::vector<data::ProjectSummary>& 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 } // namespace geopro::app

View File

@ -790,7 +790,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
topLayout->setContentsMargins(0, 0, 0, 0); topLayout->setContentsMargins(0, 0, 0, 0);
topLayout->setSpacing(0); topLayout->setSpacing(0);
topLayout->addWidget(geopro::app::buildMenuBar(topChrome)); topLayout->addWidget(geopro::app::buildMenuBar(topChrome));
topLayout->addWidget(geopro::app::buildTopToolBar(topChrome)); topLayout->addWidget(new geopro::app::TopBar(topChrome));
window.setMenuWidget(topChrome); window.setMenuWidget(topChrome);
} }