#pragma once #include #include #include #include #include #include #include #include "repo/RepoTypes.hpp" namespace geopro::data { class IAsyncProjectRepository; class NavRequest; } // namespace geopro::data namespace geopro::controller { // 导航状态机:编排 IAsyncProjectRepository(异步句柄),持有当前 空间/项目 状态,经信号驱动 UI。 // 不持有 widget。abort-and-replace + 句柄身份比对保证迟到信号被丢弃(spec §5.0)。 // busyChanged 语义:「是否存在任一在飞句柄」(去抖:值变才发)。 class WorkbenchNavController : public QObject { Q_OBJECT public: explicit WorkbenchNavController(data::IAsyncProjectRepository& repo, QObject* parent = nullptr); ~WorkbenchNavController() override; // 退出契约:abort 所有在飞句柄 void start(); // 启动:拉空间 → 项目 → 结构(依赖链) QString currentCrsCode() const { return QString::fromStdString(currentCrsCode_); } QString currentProjectId() const { return QString::fromStdString(currentProjectId_); } public slots: void switchWorkspace(const QString& tenantId); void switchProject(const QString& projectId); void selectObject(const QString& objectId, int confType); // 单击对象→DS列表+对象详情(并发) void setCheckedTms(const QStringList& tmObjectIds); // 勾选叶子集→异常树(并发,带缓存) void selectDataset(const QString& dsObjectId); // 单击DS→数据集动态表单 void loadMoreData(); void loadMoreFiles(); void deleteObject(const QString& objectId, int confType); // 删除GS/TM→成功后刷新结构 void deleteDataset(const QString& dsObjectId); // 删除DS→成功后刷新当前TM数据集列表 // 右键「异常详情」:GS→收集其下全部 TM 子孙;TM→自身;复用 setCheckedTms 拉取并发射异常树。 void showObjectExceptions(const QString& objectId, int confType); signals: void busyChanged(bool busy); void workspacesLoaded(const std::vector& list, const QString& currentId); void projectsLoaded(const std::vector& list, const QString& currentId, int total); void structureLoaded(const QString& projectName, const std::vector& nodes); void datasetsLoaded(const QString& tmObjectId, const std::vector& rows, int total, bool append); void filesLoaded(const QString& tmObjectId, const std::vector& rows, int total, bool append); void objectDetailLoaded(const QString& title, const geopro::data::DynamicForm& form); void exceptionTreeLoaded(const std::vector& groups, int exceptionCount); void datasetDetailLoaded(const geopro::data::DynamicForm& form); void loadFailed(const QString& stage, const QString& message); // 增删改结果(用于状态栏/toast 反馈;成功后控制器已自行触发相应刷新)。 void mutationSucceeded(const QString& message); void mutationFailed(const QString& message); private: // start / switchWorkspace 依赖链:拉项目 → 拉结构(续延,复用)。 void runProjectsAndStructure(); void abortAll(); // 退出/重置时 abort 所有在飞句柄 void resetSelectionState(); // 切项目/工作空间重置选中态(spec §6) void emitBusyIfChanged(); // 据「是否存在任一在飞句柄」去抖发 busyChanged bool anyInflight() const; // OR 所有在飞 QPointer / 集合 void assembleAndEmitExceptionTree(const QStringList& tmObjectIds); // 缓存命中后组装异常树 // 数据页树形分页:从 allDataRows_(一次取全的整棵)按「第一层节点(根)」切下一页, // 每页 kDataRootPageSize 个根 + 各自整棵子树;total=根总数。append=false 首页、true 加载更多。 void emitNextDataRootPage(bool append); data::IAsyncProjectRepository& repo_; bool lastBusy_ = false; // 在飞句柄(QPointer 防悬垂;身份比对用): QPointer startStepReq_; // start / switchWorkspace 依赖链当前在飞级 QPointer structReq_; // switchProject QPointer selDataReq_; // selectObject:data 行 QPointer selFileReq_; // selectObject:file 行 QPointer selDetailReq_; // selectObject:对象详情 QPointer moreFilesReq_; // loadMoreFiles(数据页改客户端按根分页,无在飞句柄) QPointer datasetReq_; QPointer mutateReq_; // 删除/增改(abort-and-replace 单路) std::vector> checkedInflight_; // setCheckedTms:未命中缓存的并发批 std::vector lastProjects_; std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_; std::string currentParentId_; int currentParentConfType_ = 0; std::vector lastStructNodes_; // tmId→name 解析 std::map> tmExceptionCache_; int dataPageNo_ = 0; int filePageNo_ = 0; int dataTotal_ = 0; // 数据页:根节点总数(树形分页单位) int fileTotal_ = 0; std::vector allDataRows_; // 当前 TM 一次取全的所有数据行(树形按根客户端分页用) int dataRootsShown_ = 0; // 已 emit 的根节点数(loadMoreData 续切) }; } // namespace geopro::controller