feat(ela): 菜单全换 ElaMenu/ElaMenuBar(根治圆角露直角) + 登出功能

- TopBar: 4 主菜单/子菜单/切换器菜单 → ElaMenu, 菜单栏 → ElaMenuBar(自绘透明圆角弹窗+随主题);
  去掉 #appMenuBar 内联 QSS; Theme.cpp 删除 QMenuBar/QMenu QSS(否则 border-radius 仍露直角)
- 登出: 头像改可点击 QToolButton + ElaMenu「退出登录」→ logoutRequested 信号;
  main 接信号 → forgetSession() 清凭证 + QProcess 重启回登录页(撤销之前的 GEOPRO_FORCE_LOGIN 补丁)
This commit is contained in:
gaozheng 2026-06-10 09:12:05 +08:00
parent 4a785ede88
commit 57c452e2d3
4 changed files with 33 additions and 54 deletions

View File

@ -250,40 +250,8 @@ QStatusBar QLabel {
padding: 0 4px; padding: 0 4px;
} }
/* ── 菜单栏 / 菜单(按需出现时也与主题一致)────────────────── */ /* ── 菜单栏 / 菜单:已全部改用 ElaMenuBar/ElaMenu(自绘透明圆角弹窗+随主题)
QMenuBar { QMenuBar/QMenu QSS border-radius */
background: #EDF1F7;
color: #1F2A3D;
border-bottom: 1px solid #D5DBE5;
}
QMenuBar::item {
background: transparent;
padding: 6px 12px;
border-radius: 6px;
}
QMenuBar::item:selected {
background: #DCE6F4;
}
QMenu {
background: #FFFFFF;
color: #1F2A3D;
border: 1px solid #D5DBE5;
border-radius: 8px;
padding: 6px;
}
QMenu::item {
padding: 6px 24px 6px 14px;
border-radius: 6px;
}
QMenu::item:selected {
background: #DCE9F8;
color: #1B3D67;
}
QMenu::separator {
height: 1px;
background: #E1E6EE;
margin: 6px 8px;
}
/* ── 下拉框(按需出现时也与主题一致)──────────────────────── */ /* ── 下拉框(按需出现时也与主题一致)──────────────────────── */
QComboBox { QComboBox {

View File

@ -16,6 +16,9 @@
#include "Glyphs.hpp" #include "Glyphs.hpp"
#include "Theme.hpp" #include "Theme.hpp"
#include <ElaMenu.h>
#include <ElaMenuBar.h>
namespace geopro::app { namespace geopro::app {
namespace { namespace {
@ -51,7 +54,7 @@ QToolButton* makeIconButton(QWidget* parent, Glyph g, const QString& tip)
// ── 四个菜单(结构对齐需求;叶子项当前为静态占位,后续接真实页面)── // ── 四个菜单(结构对齐需求;叶子项当前为静态占位,后续接真实页面)──
QMenu* buildViewMenu(QWidget* p) QMenu* buildViewMenu(QWidget* p)
{ {
auto* m = new QMenu(QStringLiteral("视图"), p); auto* m = new ElaMenu(QStringLiteral("视图"), p);
m->addAction(QStringLiteral("分析视图")); m->addAction(QStringLiteral("分析视图"));
m->addAction(QStringLiteral("大屏视图")); m->addAction(QStringLiteral("大屏视图"));
return m; return m;
@ -59,7 +62,7 @@ QMenu* buildViewMenu(QWidget* p)
QMenu* buildProjectMenu(QWidget* p) QMenu* buildProjectMenu(QWidget* p)
{ {
auto* m = new QMenu(QStringLiteral("项目管理"), p); auto* m = new ElaMenu(QStringLiteral("项目管理"), p);
m->addAction(QStringLiteral("数据视图")); m->addAction(QStringLiteral("数据视图"));
auto* cfg = m->addMenu(QStringLiteral("项目配置")); auto* cfg = m->addMenu(QStringLiteral("项目配置"));
cfg->addAction(QStringLiteral("基本信息")); cfg->addAction(QStringLiteral("基本信息"));
@ -93,7 +96,7 @@ QMenu* buildProjectMenu(QWidget* p)
QMenu* buildToolsMenu(QWidget* p) QMenu* buildToolsMenu(QWidget* p)
{ {
auto* m = new QMenu(QStringLiteral("业务工具"), p); auto* m = new ElaMenu(QStringLiteral("业务工具"), p);
m->addAction(QStringLiteral("ERT 思维分析")); m->addAction(QStringLiteral("ERT 思维分析"));
m->addAction(QStringLiteral("电法脚本与装置")); m->addAction(QStringLiteral("电法脚本与装置"));
m->addAction(QStringLiteral("Geo 反演")); m->addAction(QStringLiteral("Geo 反演"));
@ -103,7 +106,7 @@ QMenu* buildToolsMenu(QWidget* p)
QMenu* buildDeviceMenu(QWidget* p) QMenu* buildDeviceMenu(QWidget* p)
{ {
auto* m = new QMenu(QStringLiteral("设备"), p); auto* m = new ElaMenu(QStringLiteral("设备"), p);
m->addAction(QStringLiteral("连接设备")); m->addAction(QStringLiteral("连接设备"));
m->addAction(QStringLiteral("设备管理")); m->addAction(QStringLiteral("设备管理"));
return m; return m;
@ -113,16 +116,9 @@ QMenu* buildDeviceMenu(QWidget* p)
QWidget* buildMenuBar(QWidget* parent) QWidget* buildMenuBar(QWidget* parent)
{ {
auto* mb = new QMenuBar(parent); auto* mb = new ElaMenuBar(parent);
mb->setObjectName(QStringLiteral("appMenuBar")); mb->setObjectName(QStringLiteral("appMenuBar"));
// 自带样式(覆盖全局),加大字号/内边距,专业观感。 // ElaMenuBar 自绘 Fluent 外观并自动随 ElaTheme 明暗,不再写内联 QSS。
geopro::app::applyThemedStyleSheet(
mb, QStringLiteral(
"#appMenuBar { background:#FFFFFF; border-bottom:1px solid #EEF1F5; padding:2px 8px; }"
"#appMenuBar::item { padding:7px 14px; border-radius:6px; font-size:%1px; color:#1F2A3D; }"
"#appMenuBar::item:selected { background:#EAF1FB; color:#2D6CB5; }"
"#appMenuBar::item:pressed { background:#DCE6F4; }")
.arg(type::kBody));
mb->addMenu(buildViewMenu(mb)); mb->addMenu(buildViewMenu(mb));
mb->addMenu(buildProjectMenu(mb)); mb->addMenu(buildProjectMenu(mb));
mb->addMenu(buildToolsMenu(mb)); mb->addMenu(buildToolsMenu(mb));
@ -169,7 +165,7 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
wsBtn_->setPopupMode(QToolButton::InstantPopup); wsBtn_->setPopupMode(QToolButton::InstantPopup);
wsBtn_->setCursor(Qt::PointingHandCursor); wsBtn_->setCursor(Qt::PointingHandCursor);
wsBtn_->setText(QStringLiteral("正在加载工作空间…")); wsBtn_->setText(QStringLiteral("正在加载工作空间…"));
wsBtn_->setMenu(new QMenu(wsBtn_)); wsBtn_->setMenu(new ElaMenu(wsBtn_));
lay->addWidget(wsBtn_); lay->addWidget(wsBtn_);
lay->addSpacing(10); lay->addSpacing(10);
@ -185,7 +181,7 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
projBtn_->setPopupMode(QToolButton::InstantPopup); projBtn_->setPopupMode(QToolButton::InstantPopup);
projBtn_->setCursor(Qt::PointingHandCursor); projBtn_->setCursor(Qt::PointingHandCursor);
projBtn_->setText(QStringLiteral("正在加载项目…")); projBtn_->setText(QStringLiteral("正在加载项目…"));
projBtn_->setMenu(new QMenu(projBtn_)); projBtn_->setMenu(new ElaMenu(projBtn_));
lay->addWidget(projBtn_); lay->addWidget(projBtn_);
lay->addStretch(); lay->addStretch();
@ -197,11 +193,17 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
lay->addWidget(makeDivider(this)); lay->addWidget(makeDivider(this));
lay->addSpacing(12); lay->addSpacing(12);
// 用户区(本轮静态)。 // 用户区:头像可点击 → 弹出菜单(退出登录)。
auto* avatar = new QLabel(QStringLiteral("ZL"), this); auto* avatar = new QToolButton(this);
avatar->setObjectName(QStringLiteral("avatar")); avatar->setObjectName(QStringLiteral("avatar"));
avatar->setText(QStringLiteral("ZL"));
avatar->setFixedSize(34, 34); avatar->setFixedSize(34, 34);
avatar->setAlignment(Qt::AlignCenter); 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->addWidget(avatar);
lay->addSpacing(8); lay->addSpacing(8);
@ -219,7 +221,7 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
} }
void TopBar::setWorkspaces(const std::vector<data::Workspace>& list, const QString& currentId) { void TopBar::setWorkspaces(const std::vector<data::Workspace>& list, const QString& currentId) {
auto* menu = new QMenu(wsBtn_); auto* menu = new ElaMenu(wsBtn_);
auto* header = menu->addAction(QStringLiteral("切换空间")); auto* header = menu->addAction(QStringLiteral("切换空间"));
header->setEnabled(false); header->setEnabled(false);
menu->addSeparator(); menu->addSeparator();
@ -250,7 +252,7 @@ void TopBar::setWorkspaces(const std::vector<data::Workspace>& list, const QStri
void TopBar::setProjects(const std::vector<data::ProjectSummary>& list, const QString& currentId, void TopBar::setProjects(const std::vector<data::ProjectSummary>& list, const QString& currentId,
bool hasMore) { bool hasMore) {
auto* menu = new QMenu(projBtn_); auto* menu = new ElaMenu(projBtn_);
auto* header = menu->addAction(QStringLiteral("切换项目")); auto* header = menu->addAction(QStringLiteral("切换项目"));
header->setEnabled(false); header->setEnabled(false);
menu->addSeparator(); menu->addSeparator();

View File

@ -25,6 +25,7 @@ signals:
void workspaceSwitchRequested(const QString& tenantId); void workspaceSwitchRequested(const QString& tenantId);
void projectSwitchRequested(const QString& projectId); void projectSwitchRequested(const QString& projectId);
void allProjectsRequested(); // 点击"全部项目…" void allProjectsRequested(); // 点击"全部项目…"
void logoutRequested(); // 头像菜单「退出登录」
private: private:
QToolButton* wsBtn_ = nullptr; QToolButton* wsBtn_ = nullptr;

View File

@ -37,6 +37,7 @@
#include <QListWidget> #include <QListWidget>
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QKeySequence> #include <QKeySequence>
#include <QProcess>
#include <QSettings> #include <QSettings>
#include <QShortcut> #include <QShortcut>
#include <QSignalBlocker> #include <QSignalBlocker>
@ -841,6 +842,13 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
&geopro::controller::WorkbenchNavController::switchWorkspace); &geopro::controller::WorkbenchNavController::switchWorkspace);
QObject::connect(topBar, &geopro::app::TopBar::projectSwitchRequested, &nav, QObject::connect(topBar, &geopro::app::TopBar::projectSwitchRequested, &nav,
&geopro::controller::WorkbenchNavController::switchProject); &geopro::controller::WorkbenchNavController::switchProject);
// 退出登录:清除记住的凭证(QtKeychain+QSettings) → 重启应用回到登录页。
QObject::connect(topBar, &geopro::app::TopBar::logoutRequested, &window, []() {
geopro::app::forgetSession();
QProcess::startDetached(QCoreApplication::applicationFilePath(),
QCoreApplication::arguments().mid(1));
qApp->quit();
});
QObject::connect(topBar, &geopro::app::TopBar::allProjectsRequested, &window, QObject::connect(topBar, &geopro::app::TopBar::allProjectsRequested, &window,
[&projectRepo, &nav, topBar, &window]() { [&projectRepo, &nav, topBar, &window]() {
auto* dlg = new geopro::app::ProjectListDialog(projectRepo, &window); auto* dlg = new geopro::app::ProjectListDialog(projectRepo, &window);