#include "TopBar.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "Glyphs.hpp" #include "Theme.hpp" #include #include #include #include #include namespace geopro::app { namespace { // ── 专业图标尺寸(统一放大;菜单栏字号亦同步加大)── constexpr int kToolIcon = 22; // 工具条右侧图标 constexpr int kWorkspaceIcon = 20; // 工作空间 / 项目图标 // 竖直分隔细线。 QFrame* makeDivider(QWidget* parent) { auto* line = new QFrame(parent); line->setObjectName(QStringLiteral("topDivider")); line->setFrameShape(QFrame::VLine); line->setFixedWidth(1); line->setFixedHeight(24); return line; } // 右侧图标按钮(Fluent ElaIconButton:自带图标字体 + 悬停 + 随主题着色)。 QWidget* makeIconButton(QWidget* parent, ElaIconType::IconName icon, const QString& tip) { auto* btn = new ElaIconButton(icon, kToolIcon, parent); btn->setToolTip(tip); btn->setCursor(Qt::PointingHandCursor); return btn; } // ── 四个菜单(结构对齐需求;叶子项当前为静态占位,后续接真实页面)── QMenu* buildViewMenu(QWidget* p) { auto* m = new ElaMenu(QStringLiteral("视图"), p); m->addAction(QStringLiteral("分析视图")); m->addAction(QStringLiteral("大屏视图")); return m; } QMenu* buildProjectMenu(QWidget* p) { auto* m = new ElaMenu(QStringLiteral("项目管理"), p); m->addAction(QStringLiteral("数据视图")); auto* cfg = m->addMenu(QStringLiteral("项目配置")); cfg->addAction(QStringLiteral("基本信息")); cfg->addAction(QStringLiteral("项目结构")); cfg->addAction(QStringLiteral("视图配置")); m->addAction(QStringLiteral("数据管理")); auto* biz = m->addMenu(QStringLiteral("业务管理")); biz->addAction(QStringLiteral("异常管理")); biz->addAction(QStringLiteral("异常体管理")); auto* mon = m->addMenu(QStringLiteral("在线监测")); mon->addAction(QStringLiteral("项目设备")); mon->addAction(QStringLiteral("在线任务管理")); auto* doc = m->addMenu(QStringLiteral("项目资料管理")); doc->addAction(QStringLiteral("项目资料管理")); doc->addAction(QStringLiteral("报告列表")); auto* tools = m->addMenu(QStringLiteral("工具组件")); tools->addAction(QStringLiteral("装置与脚本")); tools->addAction(QStringLiteral("色阶配置")); tools->addAction(QStringLiteral("异常类型管理")); tools->addAction(QStringLiteral("模型管理")); auto* exp = m->addMenu(QStringLiteral("批量导出")); exp->addAction(QStringLiteral("文件导出")); exp->addAction(QStringLiteral("报告导出")); auto* alarm = m->addMenu(QStringLiteral("告警管理")); alarm->addAction(QStringLiteral("设备告警")); alarm->addAction(QStringLiteral("告警查询")); m->addAction(QStringLiteral("自动任务")); m->addAction(QStringLiteral("模板管理")); return m; } QMenu* buildToolsMenu(QWidget* p) { auto* m = new ElaMenu(QStringLiteral("业务工具"), p); m->addAction(QStringLiteral("ERT 思维分析")); m->addAction(QStringLiteral("电法脚本与装置")); m->addAction(QStringLiteral("Geo 反演")); m->addAction(QStringLiteral("三维 GPR 综合分析")); return m; } QMenu* buildDeviceMenu(QWidget* p) { auto* m = new ElaMenu(QStringLiteral("设备"), p); m->addAction(QStringLiteral("连接设备")); m->addAction(QStringLiteral("设备管理")); return m; } } // namespace QWidget* buildMenuBar(QWidget* parent) { auto* mb = new ElaMenuBar(parent); mb->setObjectName(QStringLiteral("appMenuBar")); // ElaMenuBar 自绘 Fluent 外观并自动随 ElaTheme 明暗,不再写内联 QSS。 mb->addMenu(buildViewMenu(mb)); mb->addMenu(buildProjectMenu(mb)); mb->addMenu(buildToolsMenu(mb)); mb->addMenu(buildDeviceMenu(mb)); return mb; } TopBar::TopBar(QWidget* parent) : QWidget(parent) { setObjectName(QStringLiteral("appToolBar")); setFixedHeight(56); // 字号引用 Theme 排版令牌:工作空间切换器=title(15)、头像/用户名=body·label(13)、 // 角色名=caption(12)。原 11px 角色名上调到 12,去掉只差 1px 的糊层级。 // 切换器(ElaToolButton)/图标(ElaIconButton) 自绘 Fluent,不再写它们的 QSS。 // 仅保留:工具条底/分隔线、头像(圆形自定义)、用户名/角色。头像白字用 white 关键字(恒白)。 geopro::app::applyThemedStyleSheet( this, QStringLiteral( "#appToolBar { background:#FFFFFF; border-bottom:1px solid #E1E6EE; }" "#topDivider { color:#E1E6EE; }" "QToolButton::menu-indicator { image:none; }" "#avatar { background:#2D6CB5; color:white; border-radius:17px; font-weight:%2;" " font-size:%1px; }" "#userName { color:#1F2A3D; font-size:%3px; font-weight:%4; }" "#userRole { color:#8A93A3; font-size:%5px; }") .arg(type::kBody) .arg(type::kWeightBold) .arg(type::kLabel) .arg(type::kWeightSemibold) .arg(type::kCaption)); auto* lay = new QHBoxLayout(this); lay->setContentsMargins(14, 0, 14, 0); lay->setSpacing(0); // 工作空间切换器(Fluent ElaToolButton;数据驱动,初始占位文本待 setWorkspaces 填充)。 wsBtn_ = new ElaToolButton(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 ElaMenu(wsBtn_)); lay->addWidget(wsBtn_); lay->addSpacing(10); lay->addWidget(makeDivider(this)); lay->addSpacing(10); // 项目切换器(Fluent ElaToolButton;数据驱动)。 projBtn_ = new ElaToolButton(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 ElaMenu(projBtn_)); lay->addWidget(projBtn_); lay->addStretch(); lay->addWidget(makeIconButton(this, ElaIconType::CircleQuestion, QStringLiteral("帮助"))); lay->addWidget(makeIconButton(this, ElaIconType::Bell, QStringLiteral("通知"))); lay->addWidget(makeIconButton(this, ElaIconType::Gear, QStringLiteral("设置"))); lay->addSpacing(10); lay->addWidget(makeDivider(this)); lay->addSpacing(12); // 用户区:头像可点击 → 弹出菜单(退出登录)。 auto* avatar = new QToolButton(this); avatar->setObjectName(QStringLiteral("avatar")); avatar->setText(QStringLiteral("ZL")); avatar->setFixedSize(34, 34); avatar->setCursor(Qt::PointingHandCursor); avatar->setPopupMode(QToolButton::InstantPopup); auto* userMenu = new ElaMenu(avatar); QObject::connect(userMenu->addAction(QStringLiteral("退出登录")), &QAction::triggered, this, [this] { emit logoutRequested(); }); avatar->setMenu(userMenu); lay->addWidget(avatar); lay->addSpacing(8); auto* userBox = new QWidget(this); auto* userLay = new QVBoxLayout(userBox); userLay->setContentsMargins(0, 0, 0, 0); userLay->setSpacing(0); auto* userName = new QLabel(QStringLiteral("张磊"), userBox); userName->setObjectName(QStringLiteral("userName")); auto* userRole = new QLabel(QStringLiteral("高级工程师"), userBox); userRole->setObjectName(QStringLiteral("userRole")); userLay->addWidget(userName); userLay->addWidget(userRole); lay->addWidget(userBox); } void TopBar::setWorkspaces(const std::vector& list, const QString& currentId) { auto* menu = new ElaMenu(wsBtn_); auto* header = menu->addAction(QStringLiteral("切换空间")); header->setEnabled(false); menu->addSeparator(); auto* group = new QActionGroup(menu); group->setExclusive(true); // 互斥:只一个勾选,避免“多选” 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); group->addAction(a); if (id == currentId) currentName = name; QObject::connect(a, &QAction::triggered, this, [this, id, name]() { wsBtn_->setText(name + QStringLiteral(" ▾")); // 立即反馈 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, bool hasMore) { auto* menu = new ElaMenu(projBtn_); auto* header = menu->addAction(QStringLiteral("切换项目")); header->setEnabled(false); menu->addSeparator(); auto* group = new QActionGroup(menu); group->setExclusive(true); 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); group->addAction(a); if (id == currentId) currentName = name; QObject::connect(a, &QAction::triggered, this, [this, id, name]() { projBtn_->setText(name + QStringLiteral(" ▾")); emit projectSwitchRequested(id); }); } if (list.empty()) { auto* none = menu->addAction(QStringLiteral("(暂无项目)")); none->setEnabled(false); } if (hasMore) { menu->addSeparator(); auto* all = menu->addAction(QStringLiteral("全部项目…")); QObject::connect(all, &QAction::triggered, this, [this]() { emit allProjectsRequested(); }); } projBtn_->setMenu(menu); projBtn_->setText((currentName.isEmpty() ? QStringLiteral("选择项目") : currentName) + QStringLiteral(" ▾")); } void TopBar::setProjectButtonText(const QString& name) { projBtn_->setText(name + QStringLiteral(" ▾")); } } // namespace geopro::app