diff --git a/src/controller/WorkbenchNavController.cpp b/src/controller/WorkbenchNavController.cpp index 4739b3b..2fabdcb 100644 --- a/src/controller/WorkbenchNavController.cpp +++ b/src/controller/WorkbenchNavController.cpp @@ -1,5 +1,7 @@ #include "WorkbenchNavController.hpp" +#include + #include "dto/NavDto.hpp" namespace geopro::controller { @@ -10,8 +12,8 @@ using data::Workspace; WorkbenchNavController::WorkbenchNavController(data::IProjectRepository& repo, QObject* parent) : QObject(parent), repo_(repo) {} -namespace { // RAII:进入公共导航操作时置忙(驱动等待光标),任何返回路径都复位——保证 busyChanged 配平。 +// 命名(非匿名)以匹配 controller 的 friend 声明,从而在析构时排空挂起的勾选请求。 struct BusyGuard { WorkbenchNavController* self; bool* busy; @@ -20,11 +22,16 @@ struct BusyGuard { emit self->busyChanged(true); } ~BusyGuard() { + WorkbenchNavController* ctrl = self; // 取本地副本:lambda 不能捕获成员 *busy = false; - emit self->busyChanged(false); + emit ctrl->busyChanged(false); + // 触发源是延迟合并发射,可能落在嵌套事件循环里:用队列调用在调用栈/嵌套循环展开后再排空, + // 那时 busy_ 已可靠为 false,重放才会真正执行(lambda 捕获的 ctrl 生命周期与应用一致,安全)。 + if (ctrl->checkedTmsPending_) + QMetaObject::invokeMethod( + ctrl, [ctrl] { ctrl->drainPendingCheckedTms(); }, Qt::QueuedConnection); } }; -} // namespace void WorkbenchNavController::start() { if (busy_) return; @@ -51,6 +58,10 @@ void WorkbenchNavController::loadProjectsAndStructure() { } lastProjects_ = ps.value.rows; tmExceptionCache_.clear(); + currentParentId_.clear(); // 切项目/工作空间重置选中态(spec §6) + currentParentConfType_ = 0; + checkedTmsPending_ = false; // 丢弃跨项目的陈旧挂起重放 + pendingCheckedTms_.clear(); QString curP; if (!ps.value.rows.empty()) { const auto& first = ps.value.rows.front(); @@ -107,6 +118,10 @@ void WorkbenchNavController::switchProject(const QString& projectId) { } lastStructNodes_ = st.value; tmExceptionCache_.clear(); + currentParentId_.clear(); // 切项目/工作空间重置选中态(spec §6) + currentParentConfType_ = 0; + checkedTmsPending_ = false; // 丢弃跨项目的陈旧挂起重放 + pendingCheckedTms_.clear(); emit structureLoaded(QString::fromStdString(currentProjectName_), st.value); } @@ -166,7 +181,11 @@ void WorkbenchNavController::loadMoreFiles() { } void WorkbenchNavController::setCheckedTms(const QStringList& tmObjectIds) { - if (busy_) return; + if (busy_) { // 触发源是延迟合并发射,可能落在别的同步操作的嵌套事件循环里: + pendingCheckedTms_ = tmObjectIds; // 不丢弃,记下最新一次请求,待空闲重放 + checkedTmsPending_ = true; + return; + } BusyGuard guard(this, &busy_); auto nameOf = [this](const std::string& id) -> std::string { for (const auto& n : lastStructNodes_) @@ -198,6 +217,12 @@ void WorkbenchNavController::setCheckedTms(const QStringList& tmObjectIds) { emit exceptionTreeLoaded(groups, total); } +void WorkbenchNavController::drainPendingCheckedTms() { + if (busy_ || !checkedTmsPending_) return; + checkedTmsPending_ = false; // 先清标志再重放,避免重入自旋 + setCheckedTms(pendingCheckedTms_); // 此时 busy_=false,会正常执行 +} + void WorkbenchNavController::selectDataset(const QString& dsObjectId) { if (dsObjectId.isEmpty() || busy_) return; BusyGuard guard(this, &busy_); diff --git a/src/controller/WorkbenchNavController.hpp b/src/controller/WorkbenchNavController.hpp index 2f28103..136fe78 100644 --- a/src/controller/WorkbenchNavController.hpp +++ b/src/controller/WorkbenchNavController.hpp @@ -45,10 +45,14 @@ signals: void loadFailed(const QString& stage, const QString& message); private: + friend struct BusyGuard; // 允许在 guard 析构时排空挂起的勾选请求 void loadProjectsAndStructure(); // start + switchWorkspace 共用 + void drainPendingCheckedTms(); // 空闲后重放最近一次被挂起的勾选集 data::IProjectRepository& repo_; bool busy_ = false; + bool checkedTmsPending_ = false; + QStringList pendingCheckedTms_; std::vector lastProjects_; std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_; std::string currentParentId_;