From 9680fefbe33418f44178a2ebbb023036ef7d8619 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Wed, 10 Jun 2026 17:59:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(topbar):=20=E7=94=A8=E6=88=B7=E5=8C=BA?= =?UTF-8?q?=E6=8C=89=E6=A0=B7=E5=9B=BE=E9=87=8D=E5=81=9A(=E5=A4=B4?= =?UTF-8?q?=E5=83=8F=E7=AB=96=E7=9B=B4=E5=B1=85=E4=B8=AD+=E5=A7=93?= =?UTF-8?q?=E5=90=8D/=E8=81=8C=E5=8A=A1=E4=B8=A4=E8=A1=8C=E5=B7=A6?= =?UTF-8?q?=E5=AF=B9=E9=BD=90+=E4=B8=8B=E6=8B=89=E7=AE=AD=E5=A4=B4,?= =?UTF-8?q?=E6=95=B4=E5=9D=97=E5=8F=AF=E7=82=B9)=20+=20=E5=8A=A0=E5=AE=BD?= =?UTF-8?q?=E4=B8=8B=E6=8B=89=E8=8F=9C=E5=8D=95(=E8=B4=A6=E6=88=B7/?= =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E8=B5=84=E6=96=99/=E5=81=8F=E5=A5=BD?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE/API=E5=AF=86=E9=92=A5/=E9=80=80=E5=87=BA?= =?UTF-8?q?=E7=99=BB=E5=BD=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/TopBar.cpp | 75 ++++++++++++++++++++++++++++++++++++---------- src/app/TopBar.hpp | 7 +++++ 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/app/TopBar.cpp b/src/app/TopBar.cpp index 471675d..3bc82ad 100644 --- a/src/app/TopBar.cpp +++ b/src/app/TopBar.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -232,22 +234,65 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) { lay->addWidget(makeDivider(this)); lay->addSpacing(12); - // 用户区:头像(圆形图标) + 姓名·职务 同一行,整块可点击 → 退出登录菜单。 - // 用 QToolButton(图标+文字),而非 QPushButton+子布局——后者按空文本算尺寸会把内容挤成一团。 - auto* userBtn = new QToolButton(this); - userBtn->setObjectName(QStringLiteral("userBtn")); - userBtn->setIcon(QIcon(renderAvatar(QStringLiteral("ZL"), 30, - geopro::app::tokenColor("accent/primary"), Qt::white))); - userBtn->setIconSize(QSize(30, 30)); - userBtn->setText(QStringLiteral("张磊 · 高级工程师")); - userBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - userBtn->setPopupMode(QToolButton::InstantPopup); - userBtn->setCursor(Qt::PointingHandCursor); - auto* userMenu = new QMenu(userBtn); - QObject::connect(userMenu->addAction(QStringLiteral("退出登录")), &QAction::triggered, this, + // 用户区:头像(圆形,竖直居中) + 右侧 姓名(上)/职务(下) 左对齐 + 下拉箭头;整块可点 → 菜单。 + // 用普通 QWidget + eventFilter:QWidget 按子布局正确撑开(QPushButton 装布局会按空文字算尺寸挤成一团)。 + userRow_ = new QWidget(this); + userRow_->setObjectName(QStringLiteral("userBtn")); + userRow_->setAttribute(Qt::WA_StyledBackground, true); // 令 QSS 背景(hover)在 QWidget 上生效 + userRow_->setCursor(Qt::PointingHandCursor); + userRow_->installEventFilter(this); + auto* uLay = new QHBoxLayout(userRow_); + uLay->setContentsMargins(8, 3, 8, 3); + uLay->setSpacing(10); + + auto* avatar = new QLabel(userRow_); + avatar->setPixmap( + renderAvatar(QStringLiteral("ZL"), 34, geopro::app::tokenColor("accent/primary"), Qt::white)); + avatar->setFixedSize(34, 34); + avatar->setAttribute(Qt::WA_TransparentForMouseEvents); + uLay->addWidget(avatar, 0, Qt::AlignVCenter); + + auto* nameBox = new QWidget(userRow_); + nameBox->setAttribute(Qt::WA_TransparentForMouseEvents); + auto* nameLay = new QVBoxLayout(nameBox); + nameLay->setContentsMargins(0, 0, 0, 0); + nameLay->setSpacing(0); + auto* userName = new QLabel(QStringLiteral("张磊"), nameBox); + userName->setObjectName(QStringLiteral("userName")); + auto* userRole = new QLabel(QStringLiteral("高级工程师"), nameBox); + userRole->setObjectName(QStringLiteral("userRole")); + nameLay->addWidget(userName); + nameLay->addWidget(userRole); + uLay->addWidget(nameBox, 0, Qt::AlignVCenter); + + auto* chevronLbl = new QLabel(userRow_); + chevronLbl->setPixmap(QPixmap(geopro::app::writeChevronIcon(true, QColor("#7C8493"))) + .scaled(12, 12, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + chevronLbl->setAttribute(Qt::WA_TransparentForMouseEvents); + uLay->addWidget(chevronLbl, 0, Qt::AlignVCenter); + + // 下拉菜单(加宽):账户 / 个人资料 / 偏好设置 / API 密钥 / 退出登录。 + userMenu_ = new QMenu(this); + userMenu_->setMinimumWidth(200); + userMenu_->addAction(QStringLiteral("账户")); + userMenu_->addAction(QStringLiteral("个人资料")); + QObject::connect(userMenu_->addAction(QStringLiteral("偏好设置")), &QAction::triggered, this, + [this] { emit settingsRequested(); }); + userMenu_->addAction(QStringLiteral("API 密钥")); + userMenu_->addSeparator(); + QObject::connect(userMenu_->addAction(QStringLiteral("退出登录")), &QAction::triggered, this, [this] { emit logoutRequested(); }); - userBtn->setMenu(userMenu); - lay->addWidget(userBtn); + + lay->addWidget(userRow_); +} + +bool TopBar::eventFilter(QObject* obj, QEvent* event) { + if (obj == userRow_ && event->type() == QEvent::MouseButtonRelease) { + if (userMenu_) + userMenu_->exec(userRow_->mapToGlobal(QPoint(0, userRow_->height() + 2))); + return true; + } + return QWidget::eventFilter(obj, event); } void TopBar::setWorkspaces(const std::vector& list, const QString& currentId) { diff --git a/src/app/TopBar.hpp b/src/app/TopBar.hpp index f90445e..87315d0 100644 --- a/src/app/TopBar.hpp +++ b/src/app/TopBar.hpp @@ -4,6 +4,8 @@ #include "repo/RepoTypes.hpp" class QToolButton; +class QEvent; +class QMenu; namespace geopro::app { @@ -21,6 +23,9 @@ public: bool hasMore); void setProjectButtonText(const QString& name); // 弹窗切换项目后更新按钮文字 +protected: + bool eventFilter(QObject* obj, QEvent* event) override; // 用户区整块可点 → 弹菜单 + signals: void workspaceSwitchRequested(const QString& tenantId); void projectSwitchRequested(const QString& projectId); @@ -31,6 +36,8 @@ signals: private: QToolButton* wsBtn_ = nullptr; QToolButton* projBtn_ = nullptr; + QWidget* userRow_ = nullptr; // 用户区整块(头像+姓名/职务+箭头) + QMenu* userMenu_ = nullptr; // 用户下拉菜单 }; } // namespace geopro::app