feat(nav): ds数据/文件页签创建时间显示 + 加载更多分页(loadTmRows分页+total)

This commit is contained in:
gaozheng 2026-06-09 15:29:42 +08:00
parent 7cdc7b8077
commit ee8342f4bf
12 changed files with 139 additions and 43 deletions

View File

@ -502,7 +502,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
// ── 单击左下数据列表的采集批次(DS) → 占位(真实剖面/反演渲染下一阶段接 dd 接口)── // ── 单击左下数据列表的采集批次(DS) → 占位(真实剖面/反演渲染下一阶段接 dd 接口)──
QObject::connect(datasetList, &QListWidget::itemClicked, datasetList, QObject::connect(datasetList, &QListWidget::itemClicked, datasetList,
[propLabel, detailRendererPtr, detailRenderWindowPtr](QListWidgetItem* item) { [propLabel, detailRendererPtr, detailRenderWindowPtr, &nav](QListWidgetItem* item) {
if (item->data(geopro::app::kDsLoadMoreRole).toBool()) {
nav.loadMoreData();
return;
}
const QString name = const QString name =
item->data(Qt::DisplayRole).toString().section('\n', 0, 0); item->data(Qt::DisplayRole).toString().section('\n', 0, 0);
detailRendererPtr->RemoveAllViewProps(); detailRendererPtr->RemoveAllViewProps();
@ -617,6 +621,23 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
} }
// ── 控制器 ↔ UI 信号接线(导航壳)────────────────────────────────────── // ── 控制器 ↔ UI 信号接线(导航壳)──────────────────────────────────────
// "加载更多"行:列表末尾若已加载数 < 总数,放一行可点击的"加载更多(已/共)"。
auto removeLoadMore = [](QListWidget* lw) {
if (lw->count() > 0 &&
lw->item(lw->count() - 1)->data(geopro::app::kDsLoadMoreRole).toBool())
delete lw->takeItem(lw->count() - 1);
};
auto addLoadMore = [](QListWidget* lw, int total) {
const int loaded = lw->count();
if (loaded < total) {
auto* m = new QListWidgetItem(
QStringLiteral("加载更多(%1/%2").arg(loaded).arg(total), lw);
m->setData(geopro::app::kDsLoadMoreRole, true);
m->setTextAlignment(Qt::AlignCenter);
m->setForeground(QColor("#2D6CB5"));
}
return loaded;
};
QObject::connect(topBar, &geopro::app::TopBar::workspaceSwitchRequested, &nav, QObject::connect(topBar, &geopro::app::TopBar::workspaceSwitchRequested, &nav,
&geopro::controller::WorkbenchNavController::switchWorkspace); &geopro::controller::WorkbenchNavController::switchWorkspace);
QObject::connect(topBar, &geopro::app::TopBar::projectSwitchRequested, &nav, QObject::connect(topBar, &geopro::app::TopBar::projectSwitchRequested, &nav,
@ -643,19 +664,31 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
datasetTabs->setTabText(1, QStringLiteral("文件")); datasetTabs->setTabText(1, QStringLiteral("文件"));
}); });
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::datasetsLoaded, datasetList, QObject::connect(&nav, &geopro::controller::WorkbenchNavController::datasetsLoaded, datasetList,
[datasetList, datasetTitle, datasetTabs]( [removeLoadMore, addLoadMore, datasetList, datasetTitle, datasetTabs](
const QString&, const std::vector<geopro::data::DsRow>& list) { const QString&, const std::vector<geopro::data::DsRow>& rows, int total,
geopro::app::populateDatasetList(datasetList, list); bool append) {
removeLoadMore(datasetList);
geopro::app::populateDatasetList(datasetList, rows, append);
const int loaded = addLoadMore(datasetList, total);
if (datasetTitle) datasetTitle->setText(QStringLiteral("数据集显示栏")); if (datasetTitle) datasetTitle->setText(QStringLiteral("数据集显示栏"));
datasetTabs->setTabText( datasetTabs->setTabText(
0, QStringLiteral("数据 (%1)").arg(static_cast<int>(list.size()))); 0, total > 0 ? QStringLiteral("数据 (%1/%2)").arg(loaded).arg(total)
: QStringLiteral("数据"));
}); });
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::filesLoaded, fileList, QObject::connect(&nav, &geopro::controller::WorkbenchNavController::filesLoaded, fileList,
[fileList, datasetTabs](const QString&, [removeLoadMore, addLoadMore, fileList, datasetTabs](
const std::vector<geopro::data::DsRow>& list) { const QString&, const std::vector<geopro::data::DsRow>& rows, int total,
geopro::app::populateFileList(fileList, list); bool append) {
removeLoadMore(fileList);
geopro::app::populateFileList(fileList, rows, append);
const int loaded = addLoadMore(fileList, total);
datasetTabs->setTabText( datasetTabs->setTabText(
1, QStringLiteral("文件 (%1)").arg(static_cast<int>(list.size()))); 1, total > 0 ? QStringLiteral("文件 (%1/%2)").arg(loaded).arg(total)
: QStringLiteral("文件"));
});
QObject::connect(fileList, &QListWidget::itemClicked, fileList,
[&nav](QListWidgetItem* item) {
if (item->data(geopro::app::kDsLoadMoreRole).toBool()) nav.loadMoreFiles();
}); });
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::loadFailed, objectTree, QObject::connect(&nav, &geopro::controller::WorkbenchNavController::loadFailed, objectTree,
[objectTree, &window](const QString& stage, const QString& msg) { [objectTree, &window](const QString& stage, const QString& msg) {

View File

@ -16,22 +16,25 @@ QString humanSize(long long b) {
} }
} // namespace } // namespace
void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows) { void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append) {
if (!list) return; if (!list) return;
list->clear(); if (!append) list->clear();
for (const auto& d : rows) { for (const auto& d : rows) {
QString text = QString::fromStdString(d.dsName); QString text = QString::fromStdString(d.dsName);
if (!d.typeName.empty()) text += QStringLiteral("\n%1").arg(QString::fromStdString(d.typeName)); QString sub = QString::fromStdString(d.createTime); // 名称下先创建时间
if (!d.typeName.empty())
sub += QStringLiteral(" · %1").arg(QString::fromStdString(d.typeName)); // 再跟类型
if (!sub.isEmpty()) text += QStringLiteral("\n%1").arg(sub);
auto* item = new QListWidgetItem(text, list); auto* item = new QListWidgetItem(text, list);
item->setData(kDsIdRole, QString::fromStdString(d.id)); item->setData(kDsIdRole, QString::fromStdString(d.id));
item->setData(kDsDdTypeRole, QString::fromStdString(d.ddCode)); item->setData(kDsDdTypeRole, QString::fromStdString(d.ddCode));
} }
} }
void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows) { void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append) {
if (!list) return; if (!list) return;
list->clear(); if (!append) list->clear();
if (rows.empty()) { if (!append && rows.empty()) {
auto* hint = new QListWidgetItem(QStringLiteral("(暂无文件)"), list); auto* hint = new QListWidgetItem(QStringLiteral("(暂无文件)"), list);
hint->setFlags(Qt::NoItemFlags); hint->setFlags(Qt::NoItemFlags);
hint->setForeground(QColor("#9AA6B6")); hint->setForeground(QColor("#9AA6B6"));
@ -41,7 +44,9 @@ void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>&
for (const auto& d : rows) { for (const auto& d : rows) {
const QString fname = const QString fname =
d.fileName.empty() ? QString::fromStdString(d.dsName) : QString::fromStdString(d.fileName); d.fileName.empty() ? QString::fromStdString(d.dsName) : QString::fromStdString(d.fileName);
const QString text = fname + QStringLiteral("\n%1").arg(humanSize(d.fileSize)); QString sub = QString::fromStdString(d.createTime); // 名称下先创建时间
sub += QStringLiteral(" · %1").arg(humanSize(d.fileSize)); // 再跟大小
const QString text = fname + QStringLiteral("\n%1").arg(sub);
auto* item = new QListWidgetItem(text, list); auto* item = new QListWidgetItem(text, list);
item->setData(kDsIdRole, QString::fromStdString(d.id)); item->setData(kDsIdRole, QString::fromStdString(d.id));
item->setData(kDsFileUrlRole, QString::fromStdString(d.fileUrl)); item->setData(kDsFileUrlRole, QString::fromStdString(d.fileUrl));

View File

@ -11,10 +11,11 @@ namespace geopro::app {
constexpr int kDsIdRole = 0x0100; // Qt::UserRole constexpr int kDsIdRole = 0x0100; // Qt::UserRole
constexpr int kDsDdTypeRole = 0x0101; // Qt::UserRole + 1 constexpr int kDsDdTypeRole = 0x0101; // Qt::UserRole + 1
constexpr int kDsFileUrlRole = 0x0102; // Qt::UserRole + 2文件下载 url备用 constexpr int kDsFileUrlRole = 0x0102; // Qt::UserRole + 2文件下载 url备用
constexpr int kDsLoadMoreRole = 0x0103; // 标记"加载更多"行
// 数据页签:每条 = dsName +类型名UserRole 存 dsId、+1 存 ddCode。 // 数据页签:每条 = dsName +类型名UserRole 存 dsId、+1 存 ddCode。
void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows); void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append);
// 文件页签:每条 = 文件名 +可读大小UserRole 存 dsId、+2 存文件 url。空时显示占位。 // 文件页签:每条 = 文件名 +可读大小UserRole 存 dsId、+2 存文件 url。空时显示占位。
void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows); void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append);
} // namespace geopro::app } // namespace geopro::app

View File

@ -106,20 +106,48 @@ void WorkbenchNavController::switchProject(const QString& projectId) {
void WorkbenchNavController::selectTm(const QString& tmObjectId) { void WorkbenchNavController::selectTm(const QString& tmObjectId) {
if (tmObjectId.isEmpty() || busy_) return; if (tmObjectId.isEmpty() || busy_) return;
BusyGuard guard(this, &busy_); BusyGuard guard(this, &busy_);
currentTmId_ = tmObjectId.toStdString();
const std::string pid = currentProjectId_; const std::string pid = currentProjectId_;
const std::string tm = tmObjectId.toStdString(); dataPageNo_ = 1;
const auto data = repo_.loadTmRows(pid, tm, 3); // 数据 filePageNo_ = 1;
if (!data.ok) { const auto d = repo_.loadTmRows(pid, currentTmId_, 3, dataPageNo_);
emit loadFailed(QStringLiteral("datasets"), QString::fromStdString(data.error)); if (!d.ok) {
emit loadFailed(QStringLiteral("datasets"), QString::fromStdString(d.error));
return; return;
} }
emit datasetsLoaded(tmObjectId, data.value); dataTotal_ = d.value.total;
const auto files = repo_.loadTmRows(pid, tm, 1); // 文件 emit datasetsLoaded(tmObjectId, d.value.rows, d.value.total, false);
if (!files.ok) { const auto f = repo_.loadTmRows(pid, currentTmId_, 1, filePageNo_);
emit loadFailed(QStringLiteral("files"), QString::fromStdString(files.error)); if (!f.ok) {
emit loadFailed(QStringLiteral("files"), QString::fromStdString(f.error));
return; return;
} }
emit filesLoaded(tmObjectId, files.value); fileTotal_ = f.value.total;
emit filesLoaded(tmObjectId, f.value.rows, f.value.total, false);
}
void WorkbenchNavController::loadMoreData() {
if (currentTmId_.empty() || busy_) return;
BusyGuard guard(this, &busy_);
const auto d = repo_.loadTmRows(currentProjectId_, currentTmId_, 3, ++dataPageNo_);
if (!d.ok) {
emit loadFailed(QStringLiteral("datasets"), QString::fromStdString(d.error));
return;
}
dataTotal_ = d.value.total;
emit datasetsLoaded(QString::fromStdString(currentTmId_), d.value.rows, d.value.total, true);
}
void WorkbenchNavController::loadMoreFiles() {
if (currentTmId_.empty() || busy_) return;
BusyGuard guard(this, &busy_);
const auto f = repo_.loadTmRows(currentProjectId_, currentTmId_, 1, ++filePageNo_);
if (!f.ok) {
emit loadFailed(QStringLiteral("files"), QString::fromStdString(f.error));
return;
}
fileTotal_ = f.value.total;
emit filesLoaded(QString::fromStdString(currentTmId_), f.value.rows, f.value.total, true);
} }
} // namespace geopro::controller } // namespace geopro::controller

View File

@ -22,14 +22,18 @@ public slots:
void switchWorkspace(const QString& tenantId); void switchWorkspace(const QString& tenantId);
void switchProject(const QString& projectId); void switchProject(const QString& projectId);
void selectTm(const QString& tmObjectId); void selectTm(const QString& tmObjectId);
void loadMoreData();
void loadMoreFiles();
signals: signals:
void busyChanged(bool busy); void busyChanged(bool busy);
void workspacesLoaded(const std::vector<geopro::data::Workspace>& list, const QString& currentId); 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);
void structureLoaded(const QString& projectName, const std::vector<geopro::data::StructNode>& nodes); void structureLoaded(const QString& projectName, const std::vector<geopro::data::StructNode>& nodes);
void datasetsLoaded(const QString& tmObjectId, const std::vector<geopro::data::DsRow>& list); void datasetsLoaded(const QString& tmObjectId, const std::vector<geopro::data::DsRow>& rows,
void filesLoaded(const QString& tmObjectId, const std::vector<geopro::data::DsRow>& list); int total, bool append);
void filesLoaded(const QString& tmObjectId, const std::vector<geopro::data::DsRow>& rows,
int total, bool append);
void loadFailed(const QString& stage, const QString& message); void loadFailed(const QString& stage, const QString& message);
private: private:
@ -39,6 +43,11 @@ private:
bool busy_ = false; bool busy_ = false;
std::vector<data::ProjectSummary> lastProjects_; std::vector<data::ProjectSummary> lastProjects_;
std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_; std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_;
std::string currentTmId_;
int dataPageNo_ = 0;
int filePageNo_ = 0;
int dataTotal_ = 0;
int fileTotal_ = 0;
}; };
} // namespace geopro::controller } // namespace geopro::controller

View File

@ -63,9 +63,9 @@ RepoResult<std::vector<StructNode>> ApiProjectRepository::loadStructure(const st
return {true, dto::parseStructNodes(r.data.value(QStringLiteral("value")).toArray()), {}}; return {true, dto::parseStructNodes(r.data.value(QStringLiteral("value")).toArray()), {}};
} }
RepoResult<std::vector<DsRow>> ApiProjectRepository::loadTmRows(const std::string& projectId, RepoResult<DsPage> ApiProjectRepository::loadTmRows(const std::string& projectId,
const std::string& tmObjectId, const std::string& tmObjectId, int classifyType,
int classifyType) { int pageNo) {
const QString path = (classifyType == 1) ? QStringLiteral("/business/dsObject/file/page") const QString path = (classifyType == 1) ? QStringLiteral("/business/dsObject/file/page")
: QStringLiteral("/business/dsObject/data/page"); : QStringLiteral("/business/dsObject/data/page");
const QJsonObject body{ const QJsonObject body{
@ -73,11 +73,11 @@ RepoResult<std::vector<DsRow>> ApiProjectRepository::loadTmRows(const std::strin
{QStringLiteral("structParentId"), QString::fromStdString(tmObjectId)}, {QStringLiteral("structParentId"), QString::fromStdString(tmObjectId)},
{QStringLiteral("structParentConfType"), 2}, {QStringLiteral("structParentConfType"), 2},
{QStringLiteral("classifyTypeList"), QJsonArray{classifyType}}, {QStringLiteral("classifyTypeList"), QJsonArray{classifyType}},
{QStringLiteral("pageNo"), 1}, {QStringLiteral("pageNo"), pageNo},
{QStringLiteral("pageSize"), 100}}; {QStringLiteral("pageSize"), 100}};
const net::ApiResponse r = api_.postJson(path, body); const net::ApiResponse r = api_.postJson(path, body);
if (!ok(r)) return {false, {}, errorOf(r, "loadTmRows failed")}; if (!ok(r)) return {false, {}, errorOf(r, "loadTmRows failed")};
return {true, dto::parseDsRows(r.data.value(QStringLiteral("list")).toArray()), {}}; return {true, dto::parseDsPage(r.data), {}};
} }
} // namespace geopro::data } // namespace geopro::data

View File

@ -14,9 +14,8 @@ public:
RepoResult<bool> switchWorkspace(const std::string& tenantId) override; RepoResult<bool> switchWorkspace(const std::string& tenantId) override;
RepoResult<std::vector<ProjectSummary>> listProjects(const std::string& lastProjectId) override; RepoResult<std::vector<ProjectSummary>> listProjects(const std::string& lastProjectId) override;
RepoResult<std::vector<StructNode>> loadStructure(const std::string& projectId) override; RepoResult<std::vector<StructNode>> loadStructure(const std::string& projectId) override;
RepoResult<std::vector<DsRow>> loadTmRows(const std::string& projectId, RepoResult<DsPage> loadTmRows(const std::string& projectId, const std::string& tmObjectId,
const std::string& tmObjectId, int classifyType, int pageNo) override;
int classifyType) override;
private: private:
net::ApiClient& api_; net::ApiClient& api_;

View File

@ -82,6 +82,7 @@ std::vector<DsRow> parseDsRows(const QJsonArray& arr) {
d.dsName = str(o, "dsName"); d.dsName = str(o, "dsName");
d.typeName = str(o, "name"); // 注意name 字段=ds类型名 d.typeName = str(o, "name"); // 注意name 字段=ds类型名
d.ddCode = str(o, "ddCode"); d.ddCode = str(o, "ddCode");
d.createTime = str(o, "createTime");
const QJsonObject f = o.value(QStringLiteral("file")).toObject(); const QJsonObject f = o.value(QStringLiteral("file")).toObject();
d.fileName = str(f, "name"); d.fileName = str(f, "name");
d.fileUrl = str(f, "url"); d.fileUrl = str(f, "url");
@ -91,6 +92,13 @@ std::vector<DsRow> parseDsRows(const QJsonArray& arr) {
return out; return out;
} }
DsPage parseDsPage(const QJsonObject& data) {
DsPage p;
p.rows = parseDsRows(data.value(QStringLiteral("list")).toArray());
p.total = data.value(QStringLiteral("total")).toInt();
return p;
}
std::vector<StructTreeNode> buildStructTree(const std::vector<StructNode>& flat) { std::vector<StructTreeNode> buildStructTree(const std::vector<StructNode>& flat) {
// 过滤 DS(type==3)DS 不进对象树(按 TM 单独拉取到数据列表)。 // 过滤 DS(type==3)DS 不进对象树(按 TM 单独拉取到数据列表)。
std::vector<StructNode> nodes; std::vector<StructNode> nodes;

View File

@ -22,6 +22,9 @@ std::vector<StructNode> parseStructNodes(const QJsonArray& arr);
// data/page / file/page 的 data["list"] 数组 → DsRow数据行无 file文件行含 file{name,size,url})。 // data/page / file/page 的 data["list"] 数组 → DsRow数据行无 file文件行含 file{name,size,url})。
std::vector<DsRow> parseDsRows(const QJsonArray& arr); std::vector<DsRow> parseDsRows(const QJsonArray& arr);
// data/page 或 file/page 的整个 data 对象 → DsPagerows + total
DsPage parseDsPage(const QJsonObject& data);
// 扁平 StructNode 按 parentId 建树。叶子(无子节点)=TM。处理项目直挂 TM、孤儿 parentId、空表。 // 扁平 StructNode 按 parentId 建树。叶子(无子节点)=TM。处理项目直挂 TM、孤儿 parentId、空表。
struct StructTreeNode { struct StructTreeNode {
StructNode node; StructNode node;

View File

@ -21,10 +21,10 @@ public:
virtual RepoResult<bool> switchWorkspace(const std::string& tenantId) = 0; virtual RepoResult<bool> switchWorkspace(const std::string& tenantId) = 0;
virtual RepoResult<std::vector<ProjectSummary>> listProjects(const std::string& lastProjectId) = 0; virtual RepoResult<std::vector<ProjectSummary>> listProjects(const std::string& lastProjectId) = 0;
virtual RepoResult<std::vector<StructNode>> loadStructure(const std::string& projectId) = 0; virtual RepoResult<std::vector<StructNode>> loadStructure(const std::string& projectId) = 0;
// 按 TM 拉数据集/文件行classifyType 3=数据(data/page) 1=文件(file/page) // 按 TM 分页拉数据/文件行classifyType 3=数据 1=文件pageNo 从 1 起pageSize 固定 100
virtual RepoResult<std::vector<DsRow>> loadTmRows(const std::string& projectId, virtual RepoResult<DsPage> loadTmRows(const std::string& projectId,
const std::string& tmObjectId, const std::string& tmObjectId, int classifyType,
int classifyType) = 0; int pageNo) = 0;
}; };
} // namespace geopro::data } // namespace geopro::data

View File

@ -6,10 +6,11 @@ struct DsNode { std::string id, name, ddType; };
// data/page 或 file/page 的一条 ds。数据行只用 dsName/typeName/ddCode文件行另含 file*。 // data/page 或 file/page 的一条 ds。数据行只用 dsName/typeName/ddCode文件行另含 file*。
struct DsRow { struct DsRow {
std::string id, dsName, typeName, ddCode; std::string id, dsName, typeName, ddCode, createTime;
std::string fileName, fileUrl; std::string fileName, fileUrl;
long long fileSize = 0; long long fileSize = 0;
}; };
struct DsPage { std::vector<DsRow> rows; int total = 0; };
struct TmNode { std::string id, name, confCode; std::vector<DsNode> dss; }; struct TmNode { std::string id, name, confCode; std::vector<DsNode> dss; };
struct GsNode { std::string id, name; std::vector<TmNode> tms; }; struct GsNode { std::string id, name; std::vector<TmNode> tms; };
struct Project { std::string id, name; std::vector<GsNode> gss; }; struct Project { std::string id, name; std::vector<GsNode> gss; };

View File

@ -147,12 +147,13 @@ TEST(NavDto, BuildStructTreeDropsDsAndTmStaysLeaf) {
TEST(NavDto, ParseDsRowsDataAndFile) { TEST(NavDto, ParseDsRowsDataAndFile) {
const auto d = dto::parseDsRows(arrOf(R"([ const auto d = dto::parseDsRows(arrOf(R"([
{"id":"d1","dsName":"ERT1-WS","name":"电阻率数据","ddCode":"dd_inversion_data"} {"id":"d1","dsName":"ERT1-WS","name":"电阻率数据","ddCode":"dd_inversion_data","createTime":"2026-03-25 16:48:57"}
])")); ])"));
ASSERT_EQ(d.size(), 1u); ASSERT_EQ(d.size(), 1u);
EXPECT_EQ(d[0].id, "d1"); EXPECT_EQ(d[0].id, "d1");
EXPECT_EQ(d[0].dsName, "ERT1-WS"); EXPECT_EQ(d[0].dsName, "ERT1-WS");
EXPECT_EQ(d[0].typeName, "电阻率数据"); EXPECT_EQ(d[0].typeName, "电阻率数据");
EXPECT_EQ(d[0].createTime, "2026-03-25 16:48:57");
EXPECT_TRUE(d[0].fileName.empty()); EXPECT_TRUE(d[0].fileName.empty());
const auto f = dto::parseDsRows(arrOf(R"([ const auto f = dto::parseDsRows(arrOf(R"([
@ -163,4 +164,12 @@ TEST(NavDto, ParseDsRowsDataAndFile) {
EXPECT_EQ(f[0].fileName, "ERT1-WS.xlsx"); EXPECT_EQ(f[0].fileName, "ERT1-WS.xlsx");
EXPECT_EQ(f[0].fileSize, 62760); EXPECT_EQ(f[0].fileSize, 62760);
EXPECT_EQ(f[0].fileUrl, "/common/file/x.xlsx"); EXPECT_EQ(f[0].fileUrl, "/common/file/x.xlsx");
const auto page = dto::parseDsPage(objOf(R"({
"total": 18,
"list": [{"id":"x","dsName":"a","name":"t","ddCode":"dd","createTime":"2026-01-01 00:00:00"}]
})"));
EXPECT_EQ(page.total, 18);
ASSERT_EQ(page.rows.size(), 1u);
EXPECT_EQ(page.rows[0].dsName, "a");
} }