feat(controller): WorkbenchNavController 导航状态机

This commit is contained in:
gaozheng 2026-06-09 11:44:31 +08:00
parent fa4bbf08b3
commit f4ca9bcd38
4 changed files with 158 additions and 0 deletions

View File

@ -12,4 +12,5 @@ add_subdirectory(core)
add_subdirectory(data) add_subdirectory(data)
add_subdirectory(net) add_subdirectory(net)
add_subdirectory(render) add_subdirectory(render)
add_subdirectory(controller)
add_subdirectory(app) add_subdirectory(app)

View File

@ -0,0 +1,7 @@
find_package(Qt6 COMPONENTS Core REQUIRED)
add_library(geopro_controller STATIC
WorkbenchNavController.cpp)
target_include_directories(geopro_controller PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(geopro_controller PUBLIC geopro_data Qt6::Core)
target_compile_features(geopro_controller PUBLIC cxx_std_17)
set_target_properties(geopro_controller PROPERTIES AUTOMOC ON AUTOUIC OFF AUTORCC OFF)

View File

@ -0,0 +1,108 @@
#include "WorkbenchNavController.hpp"
namespace geopro::controller {
using data::ProjectSummary;
using data::Workspace;
WorkbenchNavController::WorkbenchNavController(data::IProjectRepository& repo, QObject* parent)
: QObject(parent), repo_(repo) {}
void WorkbenchNavController::start() {
emit busyChanged(true);
const auto ws = repo_.listWorkspaces();
if (!ws.ok) {
emit busyChanged(false);
emit loadFailed(QStringLiteral("workspaces"), QString::fromStdString(ws.error));
return;
}
QString cur;
for (const auto& w : ws.value)
if (w.isCurrent) cur = QString::fromStdString(w.id);
if (cur.isEmpty() && !ws.value.empty()) cur = QString::fromStdString(ws.value.front().id);
currentWorkspaceId_ = cur.toStdString();
emit workspacesLoaded(ws.value, cur);
loadProjectsAndStructure();
emit busyChanged(false);
}
void WorkbenchNavController::loadProjectsAndStructure() {
const auto ps = repo_.listProjects(std::string());
if (!ps.ok) {
emit loadFailed(QStringLiteral("projects"), QString::fromStdString(ps.error));
return;
}
lastProjects_ = ps.value;
QString curP;
if (!ps.value.empty()) {
const auto& first = ps.value.front();
curP = QString::fromStdString(first.id);
currentProjectId_ = first.id;
currentProjectName_ = first.name;
currentCrsCode_ = first.crsCode;
} else {
currentProjectId_.clear();
currentProjectName_.clear();
currentCrsCode_.clear();
}
emit projectsLoaded(ps.value, curP);
if (curP.isEmpty()) {
emit structureLoaded(QString(), {}); // 暂无项目 → 空树
return;
}
const auto st = repo_.loadStructure(currentProjectId_);
if (!st.ok) {
emit loadFailed(QStringLiteral("structure"), QString::fromStdString(st.error));
return;
}
emit structureLoaded(QString::fromStdString(currentProjectName_), st.value);
}
void WorkbenchNavController::switchWorkspace(const QString& tenantId) {
if (tenantId.isEmpty()) return;
emit busyChanged(true);
const auto r = repo_.switchWorkspace(tenantId.toStdString());
if (!r.ok) {
emit busyChanged(false);
emit loadFailed(QStringLiteral("switchWorkspace"), QString::fromStdString(r.error));
return;
}
currentWorkspaceId_ = tenantId.toStdString();
loadProjectsAndStructure();
emit busyChanged(false);
}
void WorkbenchNavController::switchProject(const QString& projectId) {
if (projectId.isEmpty()) return;
emit busyChanged(true);
currentProjectId_ = projectId.toStdString();
for (const auto& p : lastProjects_)
if (p.id == currentProjectId_) {
currentProjectName_ = p.name;
currentCrsCode_ = p.crsCode;
}
const auto st = repo_.loadStructure(currentProjectId_);
if (!st.ok) {
emit busyChanged(false);
emit loadFailed(QStringLiteral("structure"), QString::fromStdString(st.error));
return;
}
emit structureLoaded(QString::fromStdString(currentProjectName_), st.value);
emit busyChanged(false);
}
void WorkbenchNavController::selectTm(const QString& tmObjectId) {
if (tmObjectId.isEmpty()) return;
emit busyChanged(true);
const auto ds = repo_.loadDatasetsOfTm(tmObjectId.toStdString());
emit busyChanged(false);
if (!ds.ok) {
emit loadFailed(QStringLiteral("datasets"), QString::fromStdString(ds.error));
return;
}
emit datasetsLoaded(tmObjectId, ds.value);
}
} // namespace geopro::controller

View File

@ -0,0 +1,42 @@
#pragma once
#include <QObject>
#include <QString>
#include <string>
#include <vector>
#include "repo/IProjectRepository.hpp"
namespace geopro::controller {
// 导航状态机:编排 IProjectRepository持有当前 空间/项目 状态,经信号驱动 UI。不持有 widget。
class WorkbenchNavController : public QObject {
Q_OBJECT
public:
explicit WorkbenchNavController(data::IProjectRepository& repo, QObject* parent = nullptr);
void start(); // 启动:拉空间 → 项目 → 结构
QString currentCrsCode() const { return QString::fromStdString(currentCrsCode_); }
public slots:
void switchWorkspace(const QString& tenantId);
void switchProject(const QString& projectId);
void selectTm(const QString& tmObjectId);
signals:
void busyChanged(bool busy);
void workspacesLoaded(const std::vector<geopro::data::Workspace>& list, const QString& currentId);
void projectsLoaded(const std::vector<geopro::data::ProjectSummary>& list, const QString& currentId);
void structureLoaded(const QString& projectName, const std::vector<geopro::data::StructNode>& nodes);
void datasetsLoaded(const QString& tmObjectId, const std::vector<geopro::data::DsNode>& list);
void loadFailed(const QString& stage, const QString& message);
private:
void loadProjectsAndStructure(); // start + switchWorkspace 共用
data::IProjectRepository& repo_;
std::vector<data::ProjectSummary> lastProjects_;
std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_;
};
} // namespace geopro::controller