feat(nav): 项目列表弹窗(名称/类型过滤+分页+8列,点项目名切换)+ 下拉全部项目入口
This commit is contained in:
parent
ee8342f4bf
commit
b4824a6e4e
|
|
@ -23,7 +23,8 @@ add_executable(geopro_desktop WIN32
|
|||
panels/AnomalyListPanel.cpp
|
||||
panels/DatasetListPanel.cpp
|
||||
panels/ObjectTreePanel.cpp
|
||||
CentralScene.cpp)
|
||||
CentralScene.cpp
|
||||
ProjectListDialog.cpp)
|
||||
|
||||
target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
#include "ProjectListDialog.hpp"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QColor>
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QStringList>
|
||||
#include <QTableWidget>
|
||||
#include <QTableWidgetItem>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace geopro::app {
|
||||
namespace {
|
||||
QString statusText(int s) {
|
||||
switch (s) {
|
||||
case 1: return QStringLiteral("未开始");
|
||||
case 2: return QStringLiteral("进行中");
|
||||
default: return QString::number(s);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ProjectListDialog::ProjectListDialog(data::IProjectRepository& repo, QWidget* parent)
|
||||
: QDialog(parent), repo_(repo) {
|
||||
setWindowTitle(QStringLiteral("全部项目"));
|
||||
resize(980, 560);
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
|
||||
auto* filter = new QHBoxLayout();
|
||||
filter->addWidget(new QLabel(QStringLiteral("项目名称"), this));
|
||||
nameEdit_ = new QLineEdit(this);
|
||||
nameEdit_->setPlaceholderText(QStringLiteral("输入项目名称"));
|
||||
nameEdit_->setFixedWidth(200);
|
||||
filter->addWidget(nameEdit_);
|
||||
filter->addSpacing(8);
|
||||
filter->addWidget(new QLabel(QStringLiteral("项目类型"), this));
|
||||
typeCombo_ = new QComboBox(this);
|
||||
typeCombo_->setFixedWidth(160);
|
||||
filter->addWidget(typeCombo_);
|
||||
filter->addSpacing(8);
|
||||
auto* searchBtn = new QPushButton(QStringLiteral("搜索"), this);
|
||||
auto* resetBtn = new QPushButton(QStringLiteral("重置"), this);
|
||||
filter->addWidget(searchBtn);
|
||||
filter->addWidget(resetBtn);
|
||||
filter->addStretch();
|
||||
root->addLayout(filter);
|
||||
|
||||
table_ = new QTableWidget(this);
|
||||
table_->setColumnCount(8);
|
||||
table_->setHorizontalHeaderLabels(QStringList{
|
||||
QStringLiteral("序号"), QStringLiteral("项目名称"), QStringLiteral("项目编号"),
|
||||
QStringLiteral("项目状态"), QStringLiteral("项目类型"), QStringLiteral("业主单位"),
|
||||
QStringLiteral("负责人"), QStringLiteral("创建时间")});
|
||||
table_->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
table_->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
table_->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
table_->verticalHeader()->setVisible(false);
|
||||
table_->horizontalHeader()->setStretchLastSection(true);
|
||||
table_->setColumnWidth(0, 50);
|
||||
table_->setColumnWidth(1, 260);
|
||||
root->addWidget(table_, 1);
|
||||
|
||||
auto* bottom = new QHBoxLayout();
|
||||
pageLabel_ = new QLabel(this);
|
||||
bottom->addWidget(pageLabel_);
|
||||
bottom->addStretch();
|
||||
prevBtn_ = new QPushButton(QStringLiteral("上一页"), this);
|
||||
nextBtn_ = new QPushButton(QStringLiteral("下一页"), this);
|
||||
bottom->addWidget(prevBtn_);
|
||||
bottom->addWidget(nextBtn_);
|
||||
root->addLayout(bottom);
|
||||
|
||||
fillTypeFilter();
|
||||
|
||||
QObject::connect(searchBtn, &QPushButton::clicked, this, [this]() {
|
||||
pageNo_ = 1;
|
||||
query();
|
||||
});
|
||||
QObject::connect(resetBtn, &QPushButton::clicked, this, [this]() {
|
||||
nameEdit_->clear();
|
||||
typeCombo_->setCurrentIndex(0);
|
||||
pageNo_ = 1;
|
||||
query();
|
||||
});
|
||||
QObject::connect(prevBtn_, &QPushButton::clicked, this, [this]() {
|
||||
if (pageNo_ > 1) {
|
||||
--pageNo_;
|
||||
query();
|
||||
}
|
||||
});
|
||||
QObject::connect(nextBtn_, &QPushButton::clicked, this, [this]() {
|
||||
if (pageNo_ * pageSize_ < total_) {
|
||||
++pageNo_;
|
||||
query();
|
||||
}
|
||||
});
|
||||
QObject::connect(table_, &QTableWidget::cellClicked, this, [this](int row, int col) {
|
||||
if (col != 1) return; // 仅"项目名称"列触发切换
|
||||
auto* item = table_->item(row, 1);
|
||||
if (!item) return;
|
||||
const QString id = item->data(Qt::UserRole).toString();
|
||||
if (id.isEmpty()) return;
|
||||
emit projectChosen(id, item->text());
|
||||
accept();
|
||||
});
|
||||
|
||||
query();
|
||||
}
|
||||
|
||||
void ProjectListDialog::fillTypeFilter() {
|
||||
typeCombo_->addItem(QStringLiteral("全部类型"), QString());
|
||||
const auto r = repo_.listProjectTypes();
|
||||
if (!r.ok) return;
|
||||
for (const auto& t : r.value)
|
||||
typeCombo_->addItem(QString::fromStdString(t.name), QString::fromStdString(t.id));
|
||||
}
|
||||
|
||||
void ProjectListDialog::query() {
|
||||
const std::string name = nameEdit_->text().trimmed().toStdString();
|
||||
const std::string typeId = typeCombo_->currentData().toString().toStdString();
|
||||
const auto r = repo_.pageProjects(name, typeId, pageNo_, pageSize_);
|
||||
if (!r.ok) {
|
||||
table_->setRowCount(0);
|
||||
pageLabel_->setText(QStringLiteral("加载失败:%1").arg(QString::fromStdString(r.error)));
|
||||
prevBtn_->setEnabled(false);
|
||||
nextBtn_->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
total_ = r.value.total;
|
||||
const auto& rows = r.value.rows;
|
||||
table_->setRowCount(static_cast<int>(rows.size()));
|
||||
for (int i = 0; i < static_cast<int>(rows.size()); ++i) {
|
||||
const auto& p = rows[i];
|
||||
auto set = [&](int col, const QString& text) {
|
||||
table_->setItem(i, col, new QTableWidgetItem(text));
|
||||
};
|
||||
set(0, QString::number((pageNo_ - 1) * pageSize_ + i + 1));
|
||||
auto* nameItem = new QTableWidgetItem(QString::fromStdString(p.name));
|
||||
nameItem->setData(Qt::UserRole, QString::fromStdString(p.id));
|
||||
nameItem->setForeground(QColor("#2D6CB5"));
|
||||
table_->setItem(i, 1, nameItem);
|
||||
set(2, QString::fromStdString(p.code));
|
||||
set(3, statusText(p.status));
|
||||
set(4, QString::fromStdString(p.typeName));
|
||||
set(5, QString::fromStdString(p.ownerCompany));
|
||||
set(6, QString::fromStdString(p.responsiblePerson));
|
||||
set(7, QString::fromStdString(p.createTime));
|
||||
}
|
||||
const int pages = total_ > 0 ? (total_ + pageSize_ - 1) / pageSize_ : 1;
|
||||
pageLabel_->setText(QStringLiteral("共 %1 条 第 %2 / %3 页").arg(total_).arg(pageNo_).arg(pages));
|
||||
prevBtn_->setEnabled(pageNo_ > 1);
|
||||
nextBtn_->setEnabled(pageNo_ < pages);
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
#include <QDialog>
|
||||
|
||||
#include "repo/IProjectRepository.hpp"
|
||||
|
||||
class QLineEdit;
|
||||
class QComboBox;
|
||||
class QTableWidget;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
// 项目列表弹窗:名称/类型过滤 + 分页表格;点项目名 → 切换项目并关闭。
|
||||
class ProjectListDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProjectListDialog(data::IProjectRepository& repo, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void projectChosen(const QString& projectId, const QString& projectName);
|
||||
|
||||
private:
|
||||
void query();
|
||||
void fillTypeFilter();
|
||||
|
||||
data::IProjectRepository& repo_;
|
||||
QLineEdit* nameEdit_ = nullptr;
|
||||
QComboBox* typeCombo_ = nullptr;
|
||||
QTableWidget* table_ = nullptr;
|
||||
QLabel* pageLabel_ = nullptr;
|
||||
QPushButton* prevBtn_ = nullptr;
|
||||
QPushButton* nextBtn_ = nullptr;
|
||||
int pageNo_ = 1;
|
||||
int pageSize_ = 20;
|
||||
int total_ = 0;
|
||||
};
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
@ -236,7 +236,8 @@ void TopBar::setWorkspaces(const std::vector<data::Workspace>& list, const QStri
|
|||
QStringLiteral(" ▾"));
|
||||
}
|
||||
|
||||
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) {
|
||||
auto* menu = new QMenu(projBtn_);
|
||||
auto* header = menu->addAction(QStringLiteral("切换项目"));
|
||||
header->setEnabled(false);
|
||||
|
|
@ -261,9 +262,18 @@ void TopBar::setProjects(const std::vector<data::ProjectSummary>& list, const QS
|
|||
auto* none = menu->addAction(QStringLiteral("(暂无项目)"));
|
||||
none->setEnabled(false);
|
||||
}
|
||||
if (hasMore) {
|
||||
menu->addSeparator();
|
||||
auto* all = menu->addAction(QStringLiteral("全部项目…"));
|
||||
QObject::connect(all, &QAction::triggered, this, [this]() { emit allProjectsRequested(); });
|
||||
}
|
||||
projBtn_->setMenu(menu);
|
||||
projBtn_->setText((currentName.isEmpty() ? QStringLiteral("选择项目") : currentName) +
|
||||
QStringLiteral(" ▾"));
|
||||
}
|
||||
|
||||
void TopBar::setProjectButtonText(const QString& name) {
|
||||
projBtn_->setText(name + QStringLiteral(" ▾"));
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
|
|||
|
|
@ -17,11 +17,14 @@ public:
|
|||
explicit TopBar(QWidget* parent = nullptr);
|
||||
|
||||
void setWorkspaces(const std::vector<data::Workspace>& list, const QString& currentId);
|
||||
void setProjects(const std::vector<data::ProjectSummary>& list, const QString& currentId);
|
||||
void setProjects(const std::vector<data::ProjectSummary>& list, const QString& currentId,
|
||||
bool hasMore);
|
||||
void setProjectButtonText(const QString& name); // 弹窗切换项目后更新按钮文字
|
||||
|
||||
signals:
|
||||
void workspaceSwitchRequested(const QString& tenantId);
|
||||
void projectSwitchRequested(const QString& projectId);
|
||||
void allProjectsRequested(); // 点击"全部项目…"
|
||||
|
||||
private:
|
||||
QToolButton* wsBtn_ = nullptr;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
#include "Theme.hpp"
|
||||
#include "TopBar.hpp"
|
||||
#include "CentralScene.hpp"
|
||||
#include "ProjectListDialog.hpp"
|
||||
#include "WorkbenchNavController.hpp"
|
||||
#include "api/ApiProjectRepository.hpp"
|
||||
#include "panels/ObjectTreePanel.hpp"
|
||||
|
|
@ -138,6 +139,7 @@ constexpr const char* kWgs84 = "EPSG:4326";
|
|||
// 在给定 QMainWindow 上构建 M1 工作台。
|
||||
// repo 生命周期须覆盖到事件循环结束(由调用方保证)。
|
||||
void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& repo,
|
||||
geopro::data::IProjectRepository& projectRepo,
|
||||
geopro::controller::WorkbenchNavController& nav)
|
||||
{
|
||||
// ── 世界系:启动取一次 grid1 的 lat/lon,用中位数作 GeoLocalFrame 原点 ──
|
||||
|
|
@ -642,6 +644,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
&geopro::controller::WorkbenchNavController::switchWorkspace);
|
||||
QObject::connect(topBar, &geopro::app::TopBar::projectSwitchRequested, &nav,
|
||||
&geopro::controller::WorkbenchNavController::switchProject);
|
||||
QObject::connect(topBar, &geopro::app::TopBar::allProjectsRequested, &window,
|
||||
[&projectRepo, &nav, topBar, &window]() {
|
||||
auto* dlg = new geopro::app::ProjectListDialog(projectRepo, &window);
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
QObject::connect(dlg, &geopro::app::ProjectListDialog::projectChosen, &nav,
|
||||
[&nav, topBar](const QString& id, const QString& name) {
|
||||
topBar->setProjectButtonText(name);
|
||||
nav.switchProject(id);
|
||||
});
|
||||
dlg->exec();
|
||||
});
|
||||
QObject::connect(objectTree, &geopro::app::ObjectTreePanel::tmClicked, &nav,
|
||||
&geopro::controller::WorkbenchNavController::selectTm);
|
||||
|
||||
|
|
@ -651,7 +664,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
});
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::projectsLoaded, topBar,
|
||||
[topBar](const std::vector<geopro::data::ProjectSummary>& list,
|
||||
const QString& cur) { topBar->setProjects(list, cur); });
|
||||
const QString& cur, int total) {
|
||||
topBar->setProjects(list, cur, total > static_cast<int>(list.size()));
|
||||
});
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::structureLoaded, objectTree,
|
||||
[objectTree, datasetList, fileList, datasetTitle, datasetTabs](
|
||||
const QString& projectName,
|
||||
|
|
@ -772,7 +787,7 @@ int main(int argc, char* argv[])
|
|||
window.resize(1280, 800);
|
||||
window.setMinimumSize(1024, 680); // 防止停靠面板被压到不可用尺寸
|
||||
|
||||
buildWorkbench(window, repo, nav);
|
||||
buildWorkbench(window, repo, projectRepo, nav);
|
||||
window.show();
|
||||
|
||||
nav.start(); // 进入工作台后拉真实 空间/项目/结构
|
||||
|
|
|
|||
|
|
@ -42,15 +42,15 @@ void WorkbenchNavController::start() {
|
|||
}
|
||||
|
||||
void WorkbenchNavController::loadProjectsAndStructure() {
|
||||
const auto ps = repo_.listProjects(std::string());
|
||||
const auto ps = repo_.pageProjects(std::string(), std::string(), 1, 20);
|
||||
if (!ps.ok) {
|
||||
emit loadFailed(QStringLiteral("projects"), QString::fromStdString(ps.error));
|
||||
return;
|
||||
}
|
||||
lastProjects_ = ps.value;
|
||||
lastProjects_ = ps.value.rows;
|
||||
QString curP;
|
||||
if (!ps.value.empty()) {
|
||||
const auto& first = ps.value.front();
|
||||
if (!ps.value.rows.empty()) {
|
||||
const auto& first = ps.value.rows.front();
|
||||
curP = QString::fromStdString(first.id);
|
||||
currentProjectId_ = first.id;
|
||||
currentProjectName_ = first.name;
|
||||
|
|
@ -60,7 +60,7 @@ void WorkbenchNavController::loadProjectsAndStructure() {
|
|||
currentProjectName_.clear();
|
||||
currentCrsCode_.clear();
|
||||
}
|
||||
emit projectsLoaded(ps.value, curP);
|
||||
emit projectsLoaded(ps.value.rows, curP, ps.value.total);
|
||||
|
||||
if (curP.isEmpty()) {
|
||||
emit structureLoaded(QString(), {}); // 暂无项目 → 空树
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ public slots:
|
|||
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 projectsLoaded(const std::vector<geopro::data::ProjectSummary>& list,
|
||||
const QString& currentId, int total);
|
||||
void structureLoaded(const QString& projectName, const std::vector<geopro::data::StructNode>& nodes);
|
||||
void datasetsLoaded(const QString& tmObjectId, const std::vector<geopro::data::DsRow>& rows,
|
||||
int total, bool append);
|
||||
|
|
|
|||
|
|
@ -47,11 +47,22 @@ RepoResult<bool> ApiProjectRepository::switchWorkspace(const std::string& tenant
|
|||
return {true, true, {}};
|
||||
}
|
||||
|
||||
RepoResult<std::vector<ProjectSummary>> ApiProjectRepository::listProjects(const std::string&) {
|
||||
// 我的工作台项目列表(当前空间全部项目)。queryByUser 实测为空,故用此接口。
|
||||
const net::ApiResponse r = api_.get(QStringLiteral("/business/my/profile/queryProject"));
|
||||
if (!ok(r)) return {false, {}, errorOf(r, "listProjects failed")};
|
||||
return {true, dto::parseProjectList(r.data.value(QStringLiteral("value")).toArray()), {}};
|
||||
RepoResult<ProjectListPage> ApiProjectRepository::pageProjects(const std::string& nameFilter,
|
||||
const std::string& typeId, int pageNo,
|
||||
int pageSize) {
|
||||
QJsonObject body{{QStringLiteral("projectName"), QString::fromStdString(nameFilter)},
|
||||
{QStringLiteral("pageNo"), pageNo},
|
||||
{QStringLiteral("pageSize"), pageSize}};
|
||||
if (!typeId.empty()) body[QStringLiteral("projectTypeId")] = QString::fromStdString(typeId);
|
||||
const net::ApiResponse r = api_.postJson(QStringLiteral("/business/my/profile/project/page"), body);
|
||||
if (!ok(r)) return {false, {}, errorOf(r, "pageProjects failed")};
|
||||
return {true, dto::parseProjectPage(r.data), {}};
|
||||
}
|
||||
|
||||
RepoResult<std::vector<ProjectType>> ApiProjectRepository::listProjectTypes() {
|
||||
const net::ApiResponse r = api_.get(QStringLiteral("/business/project/type/list"));
|
||||
if (!ok(r)) return {false, {}, errorOf(r, "listProjectTypes failed")};
|
||||
return {true, dto::parseProjectTypes(r.data.value(QStringLiteral("value")).toArray()), {}};
|
||||
}
|
||||
|
||||
RepoResult<std::vector<StructNode>> ApiProjectRepository::loadStructure(const std::string& projectId) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ public:
|
|||
|
||||
RepoResult<std::vector<Workspace>> listWorkspaces() override;
|
||||
RepoResult<bool> switchWorkspace(const std::string& tenantId) override;
|
||||
RepoResult<std::vector<ProjectSummary>> listProjects(const std::string& lastProjectId) override;
|
||||
RepoResult<ProjectListPage> pageProjects(const std::string& nameFilter, const std::string& typeId,
|
||||
int pageNo, int pageSize) override;
|
||||
RepoResult<std::vector<ProjectType>> listProjectTypes() override;
|
||||
RepoResult<std::vector<StructNode>> loadStructure(const std::string& projectId) override;
|
||||
RepoResult<DsPage> loadTmRows(const std::string& projectId, const std::string& tmObjectId,
|
||||
int classifyType, int pageNo) override;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ ProjectSummary parseProjectItem(const QJsonObject& o) {
|
|||
p.typeName = str(o, "projectTypeName");
|
||||
p.crsCode = str(o, "referenceCRSCode");
|
||||
p.crsName = str(o, "referenceCRSName");
|
||||
p.code = str(o, "projectCode");
|
||||
p.projectTypeId = str(o, "projectTypeId");
|
||||
p.ownerCompany = str(o, "ownerCompanyName");
|
||||
p.responsiblePerson = str(o, "responsiblePersonName");
|
||||
p.createTime = str(o, "createTime");
|
||||
p.status = o.value(QStringLiteral("status")).toInt();
|
||||
return p;
|
||||
}
|
||||
|
|
@ -55,6 +60,23 @@ std::vector<ProjectSummary> parseProjectList(const QJsonArray& arr) {
|
|||
return out;
|
||||
}
|
||||
|
||||
ProjectListPage parseProjectPage(const QJsonObject& data) {
|
||||
ProjectListPage p;
|
||||
p.rows = parseProjectList(data.value(QStringLiteral("list")).toArray());
|
||||
p.total = data.value(QStringLiteral("total")).toInt();
|
||||
return p;
|
||||
}
|
||||
|
||||
std::vector<ProjectType> parseProjectTypes(const QJsonArray& arr) {
|
||||
std::vector<ProjectType> out;
|
||||
out.reserve(static_cast<size_t>(arr.size()));
|
||||
for (const QJsonValue& v : arr) {
|
||||
const QJsonObject o = v.toObject();
|
||||
out.push_back(ProjectType{str(o, "id"), str(o, "name")});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<StructNode> parseStructNodes(const QJsonArray& arr) {
|
||||
std::vector<StructNode> out;
|
||||
out.reserve(static_cast<size_t>(arr.size()));
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ ProjectPage parseProjects(const QJsonObject& data);
|
|||
// my/profile/queryProject 的 data["value"] 数组 → 模型(与 parseProjects 同字段映射)。
|
||||
std::vector<ProjectSummary> parseProjectList(const QJsonArray& arr);
|
||||
|
||||
// my/profile/project/page 的整个 data 对象 → ProjectListPage(rows + total)。
|
||||
ProjectListPage parseProjectPage(const QJsonObject& data);
|
||||
// project/type/list 的 data["value"] 数组 → 项目类型列表。
|
||||
std::vector<ProjectType> parseProjectTypes(const QJsonArray& arr);
|
||||
|
||||
// 结构扁平节点数组(queryProjectStruct 的 data["projectStructList"])→ 模型。
|
||||
std::vector<StructNode> parseStructNodes(const QJsonArray& arr);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,12 @@ public:
|
|||
virtual ~IProjectRepository() = default;
|
||||
virtual RepoResult<std::vector<Workspace>> listWorkspaces() = 0;
|
||||
virtual RepoResult<bool> switchWorkspace(const std::string& tenantId) = 0;
|
||||
virtual RepoResult<std::vector<ProjectSummary>> listProjects(const std::string& lastProjectId) = 0;
|
||||
// 项目分页:nameFilter 名称模糊(可空)、typeId 类型过滤(空=不限)、pageNo 从 1 起。
|
||||
virtual RepoResult<ProjectListPage> pageProjects(const std::string& nameFilter,
|
||||
const std::string& typeId, int pageNo,
|
||||
int pageSize) = 0;
|
||||
// 项目类型列表(弹窗类型过滤下拉)。
|
||||
virtual RepoResult<std::vector<ProjectType>> listProjectTypes() = 0;
|
||||
virtual RepoResult<std::vector<StructNode>> loadStructure(const std::string& projectId) = 0;
|
||||
// 按 TM 分页拉数据/文件行:classifyType 3=数据 1=文件;pageNo 从 1 起,pageSize 固定 100。
|
||||
virtual RepoResult<DsPage> loadTmRows(const std::string& projectId,
|
||||
|
|
|
|||
|
|
@ -18,8 +18,17 @@ struct Project { std::string id, name; std::vector<GsNode> gss; };
|
|||
// 工作空间(=企业租户/空间)。ownerType: 1 个人空间 2 企业空间。
|
||||
struct Workspace { std::string id, name; int ownerType = 0; bool isCurrent = false; };
|
||||
|
||||
// 项目摘要(列表用)。crsCode/crsName 为项目参考坐标系,下一轮替换硬编码 EPSG:4547。
|
||||
struct ProjectSummary { std::string id, name, typeName, crsCode, crsName; int status = 0; };
|
||||
// 项目摘要(下拉/弹窗列表用)。crsCode 供下一轮替换硬编码 EPSG:4547。
|
||||
struct ProjectSummary {
|
||||
std::string id, name, typeName, crsCode, crsName;
|
||||
std::string code, projectTypeId, ownerCompany, responsiblePerson, createTime;
|
||||
int status = 0;
|
||||
};
|
||||
|
||||
// 项目类型(弹窗类型过滤下拉用)。
|
||||
struct ProjectType { std::string id, name; };
|
||||
// 项目分页结果。
|
||||
struct ProjectListPage { std::vector<ProjectSummary> rows; int total = 0; };
|
||||
|
||||
// 项目结构扁平节点(仅 GS / TM)。客户端按 parentId 建树,叶子=TM。
|
||||
struct StructNode { std::string id, name, parentId, typeName, confCode; int type = 0; };
|
||||
|
|
|
|||
|
|
@ -173,3 +173,31 @@ TEST(NavDto, ParseDsRowsDataAndFile) {
|
|||
ASSERT_EQ(page.rows.size(), 1u);
|
||||
EXPECT_EQ(page.rows[0].dsName, "a");
|
||||
}
|
||||
|
||||
TEST(NavDto, ParseProjectItemFullFields) {
|
||||
const auto v = dto::parseProjectList(arrOf(R"([
|
||||
{"id":"p1","projectName":"演示","projectCode":"001","status":2,
|
||||
"projectTypeId":"t1","projectTypeName":"全量类型",
|
||||
"ownerCompanyName":"华南所","responsiblePersonName":"张三","createTime":"2026-01-01 00:00:00"}
|
||||
])"));
|
||||
ASSERT_EQ(v.size(), 1u);
|
||||
EXPECT_EQ(v[0].code, "001");
|
||||
EXPECT_EQ(v[0].status, 2);
|
||||
EXPECT_EQ(v[0].projectTypeId, "t1");
|
||||
EXPECT_EQ(v[0].typeName, "全量类型");
|
||||
EXPECT_EQ(v[0].ownerCompany, "华南所");
|
||||
EXPECT_EQ(v[0].responsiblePerson, "张三");
|
||||
EXPECT_EQ(v[0].createTime, "2026-01-01 00:00:00");
|
||||
}
|
||||
|
||||
TEST(NavDto, ParseProjectPageAndTypes) {
|
||||
const auto page =
|
||||
dto::parseProjectPage(objOf(R"({"total":20,"list":[{"id":"p1","projectName":"a"}]})"));
|
||||
EXPECT_EQ(page.total, 20);
|
||||
ASSERT_EQ(page.rows.size(), 1u);
|
||||
EXPECT_EQ(page.rows[0].name, "a");
|
||||
const auto types = dto::parseProjectTypes(arrOf(R"([{"id":"t1","name":"全量类型"}])"));
|
||||
ASSERT_EQ(types.size(), 1u);
|
||||
EXPECT_EQ(types[0].id, "t1");
|
||||
EXPECT_EQ(types[0].name, "全量类型");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue