feat(dataset-detail): dd_grid 白化数据列表 + 引擎服务端分页(vxe-pager)
⑤ dd_grid 详情:单「列表」页签,序号/x/y 三列(均居中),服务端分页。 按原版(vxe-table)实测复刻:序号列前插、按页偏移自增;total 取 data.total; 分页器对齐 vxe-pager(上一页/页码…/下一页 + 前往N页 + 每页条数 50/100/500/1000 默认50 + 共N条记录)。 引擎新增分页能力(通用,后续分页型详情复用): - TablePayload 加 pageNo/pageSize(>0 才渲染分页器;0=全量列表,measurement/trajectory 不受影响) - GridDto.parseGridTable 复用通用 parseGridHeaderTable,前插序号列 + 回填分页态 - 仓储 loadAsync 增 pageNo/pageSize 透传,新增 grid.rows 加载器(端点 dd/ert/grid/rows,默认50条/页) - 控制器新增 loadTabPaged(保留 3 参 loadTab 以维持 tabNeeded 连接) - TablePager 分页器组件 + DataTableView 按 pageSize>0 显隐并转发 pageRequested → DatasetDetailPage/Panel.tabPageNeeded → Controller.loadTabPaged 反向链路 - GridStrategy(dd_grid 单分页页签) 注册入 main 测试:test_grid_dto(序号偏移/total/分页态/空数据) + grid.rows 分派 + GridStrategy 注册 + 控制器 loadTabPaged 透传/默认页参;154/154 通过。 ABI 关键头(DetailPayloads.hpp)变更后全量重编 geopro 代码并验 obj 新鲜度。
This commit is contained in:
parent
b033dc2a2c
commit
2cf2b6aaa7
|
|
@ -34,6 +34,7 @@ add_executable(geopro_desktop WIN32
|
|||
panels/chart/RawDataChartView.cpp
|
||||
panels/chart/GridDataChartView.cpp
|
||||
panels/chart/DataTableView.cpp
|
||||
panels/chart/TablePager.cpp
|
||||
panels/chart/BarChartView.cpp
|
||||
panels/chart/LineChartView.cpp
|
||||
panels/chart/TrajectoryMapView.cpp
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@
|
|||
#include "panels/chart/MeasurementStrategy.hpp"
|
||||
#include "panels/chart/GrMeasurementStrategy.hpp"
|
||||
#include "panels/chart/TrajectoryStrategy.hpp"
|
||||
#include "panels/chart/GridStrategy.hpp"
|
||||
#include "api/ApiProjectRepository.hpp"
|
||||
#include "api/ApiDatasetRepository.hpp"
|
||||
#include "panels/ObjectTreePanel.hpp"
|
||||
|
|
@ -531,6 +532,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
// ── 页签懒加载:lazy 页签首次激活 → 控制器按 (dsId,ddCode,tabIndex) 拉载荷 → 回填 ──
|
||||
QObject::connect(detailPanel, &geopro::app::DatasetDetailPanel::tabNeeded, &detailCtrl,
|
||||
&geopro::controller::DatasetDetailController::loadTab);
|
||||
// ── 分页:分页器翻页/改每页条数 → 控制器按页加载 → 回填(同 tabReady 路径,刷新表格+分页器)──
|
||||
QObject::connect(detailPanel, &geopro::app::DatasetDetailPanel::tabPageNeeded, &detailCtrl,
|
||||
&geopro::controller::DatasetDetailController::loadTabPaged);
|
||||
// context 用 detailPanel:析构即自动断连,避免野指针。window 比 detailPanel 活得久,
|
||||
// 捕 &window 取状态栏安全。失败时清该页 lazy 遮罩(幂等)并状态栏提示。
|
||||
QObject::connect(&detailCtrl, &geopro::controller::DatasetDetailController::loadFailed, detailPanel,
|
||||
|
|
@ -924,6 +928,7 @@ int main(int argc, char* argv[])
|
|||
chartRegistry.add(std::make_unique<geopro::app::MeasurementStrategy>());
|
||||
chartRegistry.add(std::make_unique<geopro::app::GrMeasurementStrategy>());
|
||||
chartRegistry.add(std::make_unique<geopro::app::TrajectoryStrategy>());
|
||||
chartRegistry.add(std::make_unique<geopro::app::GridStrategy>());
|
||||
geopro::controller::DatasetDetailController detailCtrl(datasetRepo, chartRegistry);
|
||||
|
||||
// ── 外壳:标准 QMainWindow(原生标题栏)。buildWorkbench 直接用其
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "Glyphs.hpp"
|
||||
#include "PanelHeader.hpp"
|
||||
#include "panels/LoadingOverlay.hpp"
|
||||
#include "panels/chart/DataTableView.hpp"
|
||||
#include "panels/chart/DetailViewFactory.hpp"
|
||||
#include "panels/chart/IDetailView.hpp"
|
||||
|
||||
|
|
@ -39,6 +40,16 @@ void DatasetDetailPage::build(const QString& dsId, const QString& ddCode, const
|
|||
views_[i] = raw;
|
||||
// lazy 页签:建覆盖该视图的加载遮罩(父为视图 widget,随其尺寸覆盖图区)。
|
||||
if (spec.lazy) overlays_[static_cast<int>(i)] = new LoadingOverlay(raw->widget());
|
||||
// 分页型页签:把表格视图的分页请求冒泡为页信号(携带 dsId/ddCode/tabIndex + 页参数)。
|
||||
if (spec.paginated) {
|
||||
if (auto* table = qobject_cast<DataTableView*>(raw->widget())) {
|
||||
const int idx = static_cast<int>(i);
|
||||
connect(table, &DataTableView::pageRequested, this,
|
||||
[this, idx](int pageNo, int pageSize) {
|
||||
emit tabPageNeeded(dsId_, ddCode_, idx, pageNo, pageSize);
|
||||
});
|
||||
}
|
||||
}
|
||||
panelTabs.append({Glyph::Detail, spec.title, raw->widget(), false});
|
||||
}
|
||||
const QVector<HeaderAction> actions = {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ public:
|
|||
signals:
|
||||
// lazy 页签首次激活且未加载 → 请求懒加载。
|
||||
void tabNeeded(const QString& dsId, const QString& ddCode, int tabIndex);
|
||||
// 分页型页签(paginated)分页器翻页/改每页条数 → 请求按页加载。
|
||||
void tabPageNeeded(const QString& dsId, const QString& ddCode, int tabIndex, int pageNo,
|
||||
int pageSize);
|
||||
|
||||
private:
|
||||
QString dsId_;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ void DatasetDetailPanel::onDatasetOpened(const QString& dsId, const QString& ddC
|
|||
setTabToolTip(idx, title); // 名称过长被省略时悬停可见全名
|
||||
// 页内 lazy 页签首次激活 → 冒泡为面板信号(外部接控制器 loadTab)。
|
||||
connect(p, &DatasetDetailPage::tabNeeded, this, &DatasetDetailPanel::tabNeeded);
|
||||
// 页内分页器翻页 → 冒泡为面板信号(外部接控制器 loadTabPaged)。
|
||||
connect(p, &DatasetDetailPage::tabPageNeeded, this, &DatasetDetailPanel::tabPageNeeded);
|
||||
}
|
||||
setCurrentWidget(p);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ public:
|
|||
signals:
|
||||
void activeDatasetChanged(const QString& dsId); // 反向联动数据集列表
|
||||
void tabNeeded(const QString& dsId, const QString& ddCode, int tabIndex); // lazy 页首激活 → 懒加载
|
||||
// 分页型页签分页器翻页 → 按页加载(外部接控制器 loadTabPaged)。
|
||||
void tabPageNeeded(const QString& dsId, const QString& ddCode, int tabIndex, int pageNo,
|
||||
int pageSize);
|
||||
|
||||
private:
|
||||
DatasetDetailPage* pageFor(const QString& dsId) const;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <QTableView>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "panels/chart/TablePager.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
namespace {
|
||||
|
|
@ -126,6 +128,12 @@ DataTableView::DataTableView(QWidget* parent) : QWidget(parent) {
|
|||
table_->setItemDelegate(new ToggleSwitchDelegate(model_, table_));
|
||||
|
||||
lay->addWidget(table_);
|
||||
|
||||
// 分页器(默认隐藏;分页型载荷 pageSize>0 时显示)。转发翻页请求给壳。
|
||||
pager_ = new TablePager(this);
|
||||
pager_->hide();
|
||||
connect(pager_, &TablePager::pageRequested, this, &DataTableView::pageRequested);
|
||||
lay->addWidget(pager_);
|
||||
}
|
||||
|
||||
void DataTableView::setPayload(const QVariant& payload) {
|
||||
|
|
@ -146,6 +154,14 @@ void DataTableView::setPayload(const QVariant& payload) {
|
|||
header->setSectionResizeMode(col, QHeaderView::Stretch);
|
||||
}
|
||||
}
|
||||
|
||||
// 分页器:分页型载荷(pageSize>0,dd_grid)显示并同步状态;否则隐藏(全量列表)。
|
||||
if (t.pageSize > 0) {
|
||||
pager_->setState(t.total, t.pageNo, t.pageSize);
|
||||
pager_->show();
|
||||
} else {
|
||||
pager_->hide();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
|
|||
|
|
@ -43,7 +43,11 @@ private:
|
|||
const TablePayloadModel* model_; // 不拥有
|
||||
};
|
||||
|
||||
// 通用数据列表视图:IDetailView + QTableView。measurement/grid/trajectory 列表共用。
|
||||
class TablePager;
|
||||
|
||||
// 通用数据列表视图:IDetailView + QTableView(+ 分页型载荷时底部 TablePager 分页器)。
|
||||
// measurement/grid/trajectory 列表共用。载荷 pageSize>0(dd_grid)时显示分页器并转发翻页请求;
|
||||
// 否则隐藏分页器(全量列表)。
|
||||
class DataTableView : public QWidget, public IDetailView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
@ -52,9 +56,14 @@ public:
|
|||
QWidget* widget() override { return this; }
|
||||
void setPayload(const QVariant& payload) override; // 坏/空 variant → 保持空态不崩
|
||||
|
||||
signals:
|
||||
// 分页器请求加载某页(翻页/跳页/改每页条数)。壳据此触发控制器 loadTabPaged。
|
||||
void pageRequested(int pageNo, int pageSize);
|
||||
|
||||
private:
|
||||
QTableView* table_;
|
||||
TablePayloadModel* model_;
|
||||
TablePager* pager_; // 分页器(pageSize>0 时显示,否则隐藏)
|
||||
};
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include "IDatasetChartStrategy.hpp" // geopro::controller
|
||||
namespace geopro::app {
|
||||
|
||||
// dd_grid(白化数据)策略:单「列表」页签,服务端分页(vxe-pager)。
|
||||
// 列表 = Table(paginated;grid.rows 产 TablePayload:序号/x/y 列 + total/pageNo/pageSize;
|
||||
// 端点 dd/ert/grid/rows)。非 lazy(单页签,开页即载首页)。
|
||||
struct GridStrategy : controller::IDatasetChartStrategy {
|
||||
std::string ddCode() const override { return "dd_grid"; }
|
||||
std::vector<controller::TabSpec> tabs() const override {
|
||||
return {
|
||||
{QStringLiteral("列表"), controller::ViewKind::Table,
|
||||
QStringLiteral("grid.rows"), /*lazy*/ false, /*paginated*/ true},
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace geopro::app
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
#include "panels/chart/TablePager.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIntValidator>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QSignalBlocker>
|
||||
#include <QToolButton>
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
namespace {
|
||||
// 每页条数选项(实测原版 vxe-pager 下拉:50/100/500/1000,默认 50)。
|
||||
const int kPageSizes[] = {50, 100, 500, 1000};
|
||||
constexpr int kPagerCount = 7; // 页码窗口(>此值用 … 折叠,对齐 vxe 默认)
|
||||
constexpr int kJumpStep = 5; // 点击 … 的跳页步长
|
||||
|
||||
// 分页器样式:常态不设字色(跟随主题/全局样式表);边框用半透明灰(两主题通用);
|
||||
// hover/选中页用强调蓝 #409EFF(与 DataTableView 开关同色)。
|
||||
const char* kPagerQss = R"(
|
||||
QToolButton {
|
||||
border: 1px solid rgba(128,128,128,0.35);
|
||||
border-radius: 3px;
|
||||
padding: 1px 6px;
|
||||
min-width: 16px;
|
||||
min-height: 20px;
|
||||
background: transparent;
|
||||
}
|
||||
QToolButton:hover:enabled { color: #409EFF; border-color: #409EFF; }
|
||||
QToolButton[active="true"] { color: #409EFF; border-color: #409EFF; font-weight: bold; }
|
||||
QToolButton:disabled { color: rgba(128,128,128,0.55); }
|
||||
)";
|
||||
} // namespace
|
||||
|
||||
TablePager::TablePager(QWidget* parent) : QWidget(parent) {
|
||||
setStyleSheet(QString::fromUtf8(kPagerQss));
|
||||
|
||||
auto* lay = new QHBoxLayout(this);
|
||||
lay->setContentsMargins(8, 6, 8, 6);
|
||||
lay->setSpacing(6);
|
||||
lay->addStretch(1); // 右对齐
|
||||
|
||||
prevBtn_ = new QToolButton(this);
|
||||
prevBtn_->setText(QStringLiteral("‹"));
|
||||
prevBtn_->setCursor(Qt::PointingHandCursor);
|
||||
connect(prevBtn_, &QToolButton::clicked, this,
|
||||
[this] { emit pageRequested(pageNo_ - 1, pageSize_); });
|
||||
lay->addWidget(prevBtn_);
|
||||
|
||||
numHost_ = new QWidget(this);
|
||||
numLay_ = new QHBoxLayout(numHost_);
|
||||
numLay_->setContentsMargins(0, 0, 0, 0);
|
||||
numLay_->setSpacing(6);
|
||||
lay->addWidget(numHost_);
|
||||
|
||||
nextBtn_ = new QToolButton(this);
|
||||
nextBtn_->setText(QStringLiteral("›"));
|
||||
nextBtn_->setCursor(Qt::PointingHandCursor);
|
||||
connect(nextBtn_, &QToolButton::clicked, this,
|
||||
[this] { emit pageRequested(pageNo_ + 1, pageSize_); });
|
||||
lay->addWidget(nextBtn_);
|
||||
|
||||
auto* gotoLabel = new QLabel(QStringLiteral("前往"), this);
|
||||
lay->addWidget(gotoLabel);
|
||||
|
||||
jumpEdit_ = new QLineEdit(this);
|
||||
jumpEdit_->setFixedWidth(40);
|
||||
jumpEdit_->setAlignment(Qt::AlignCenter);
|
||||
jumpValidator_ = new QIntValidator(1, 1, jumpEdit_);
|
||||
jumpEdit_->setValidator(jumpValidator_);
|
||||
connect(jumpEdit_, &QLineEdit::returnPressed, this, [this] {
|
||||
bool ok = false;
|
||||
int p = jumpEdit_->text().toInt(&ok);
|
||||
const int pc = pageCount();
|
||||
if (!ok) {
|
||||
jumpEdit_->setText(QString::number(pageNo_));
|
||||
return;
|
||||
}
|
||||
p = std::min(std::max(1, p), std::max(1, pc));
|
||||
if (p == pageNo_) {
|
||||
jumpEdit_->setText(QString::number(pageNo_));
|
||||
return;
|
||||
}
|
||||
emit pageRequested(p, pageSize_);
|
||||
});
|
||||
lay->addWidget(jumpEdit_);
|
||||
|
||||
lay->addWidget(new QLabel(QStringLiteral("页"), this));
|
||||
|
||||
sizeCombo_ = new QComboBox(this);
|
||||
for (int s : kPageSizes)
|
||||
sizeCombo_->addItem(QStringLiteral("%1条/页").arg(s), s);
|
||||
connect(sizeCombo_, &QComboBox::activated, this, [this](int i) {
|
||||
emit pageRequested(1, sizeCombo_->itemData(i).toInt()); // 改每页条数 → 回第 1 页
|
||||
});
|
||||
lay->addWidget(sizeCombo_);
|
||||
|
||||
totalLabel_ = new QLabel(this);
|
||||
lay->addWidget(totalLabel_);
|
||||
|
||||
setState(0, 1, pageSize_);
|
||||
}
|
||||
|
||||
int TablePager::pageCount() const {
|
||||
if (pageSize_ <= 0) return 1;
|
||||
return std::max(1, (total_ + pageSize_ - 1) / pageSize_);
|
||||
}
|
||||
|
||||
void TablePager::setState(int total, int pageNo, int pageSize) {
|
||||
total_ = std::max(0, total);
|
||||
if (pageSize > 0) pageSize_ = pageSize;
|
||||
const int pc = pageCount();
|
||||
pageNo_ = std::min(std::max(1, pageNo), pc);
|
||||
|
||||
prevBtn_->setEnabled(pageNo_ > 1);
|
||||
nextBtn_->setEnabled(pageNo_ < pc);
|
||||
|
||||
jumpValidator_->setTop(pc);
|
||||
{
|
||||
QSignalBlocker b(jumpEdit_);
|
||||
jumpEdit_->setText(QString::number(pageNo_));
|
||||
}
|
||||
{
|
||||
QSignalBlocker b(sizeCombo_);
|
||||
int idx = sizeCombo_->findData(pageSize_);
|
||||
if (idx < 0) { // 非预设条数(兜底):补一项再选中
|
||||
sizeCombo_->addItem(QStringLiteral("%1条/页").arg(pageSize_), pageSize_);
|
||||
idx = sizeCombo_->findData(pageSize_);
|
||||
}
|
||||
sizeCombo_->setCurrentIndex(idx);
|
||||
}
|
||||
totalLabel_->setText(QStringLiteral("共 %1 条记录").arg(total_));
|
||||
|
||||
rebuildNumbers();
|
||||
}
|
||||
|
||||
void TablePager::rebuildNumbers() {
|
||||
// 清空旧页码按钮。
|
||||
while (QLayoutItem* it = numLay_->takeAt(0)) {
|
||||
if (QWidget* w = it->widget()) w->deleteLater();
|
||||
delete it;
|
||||
}
|
||||
|
||||
const int pc = pageCount();
|
||||
auto addNum = [this](int p, bool active) {
|
||||
auto* b = new QToolButton(numHost_);
|
||||
b->setText(QString::number(p));
|
||||
b->setCursor(Qt::PointingHandCursor);
|
||||
b->setProperty("active", active);
|
||||
b->setEnabled(!active); // 当前页不可再点
|
||||
connect(b, &QToolButton::clicked, this, [this, p] { emit pageRequested(p, pageSize_); });
|
||||
numLay_->addWidget(b);
|
||||
};
|
||||
auto addDots = [this](int target) {
|
||||
auto* b = new QToolButton(numHost_);
|
||||
b->setText(QStringLiteral("..."));
|
||||
b->setCursor(Qt::PointingHandCursor);
|
||||
connect(b, &QToolButton::clicked, this,
|
||||
[this, target] { emit pageRequested(target, pageSize_); });
|
||||
numLay_->addWidget(b);
|
||||
};
|
||||
|
||||
if (pc <= kPagerCount) {
|
||||
for (int p = 1; p <= pc; ++p) addNum(p, p == pageNo_);
|
||||
return;
|
||||
}
|
||||
// 折叠窗口:首页 [ … ] 中段(当前±2) [ … ] 末页。
|
||||
addNum(1, pageNo_ == 1);
|
||||
int left = pageNo_ - 2;
|
||||
int right = pageNo_ + 2;
|
||||
if (left < 2) {
|
||||
left = 2;
|
||||
right = 5;
|
||||
}
|
||||
if (right > pc - 1) {
|
||||
right = pc - 1;
|
||||
left = pc - 4;
|
||||
}
|
||||
if (left > 2) addDots(std::max(1, pageNo_ - kJumpStep));
|
||||
for (int p = left; p <= right; ++p) addNum(p, p == pageNo_);
|
||||
if (right < pc - 1) addDots(std::min(pc, pageNo_ + kJumpStep));
|
||||
addNum(pc, pageNo_ == pc);
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include <QWidget>
|
||||
|
||||
class QToolButton;
|
||||
class QLineEdit;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QHBoxLayout;
|
||||
class QIntValidator;
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
// 分页器(对齐原版 vxe-pager,size--mini):上一页 / 页码(多页带 … 省略跳页)/ 下一页 +
|
||||
// 「前往 [n] 页」跳页框 + 每页条数下拉 [50/100/500/1000] + 「共 N 条记录」。右对齐。
|
||||
// 数据驱动:setState(total,pageNo,pageSize) 重建页码并同步各控件。任何翻页/改每页条数都发
|
||||
// pageRequested(改每页条数时回到第 1 页,镜像原版)。仅展示与请求,不持有数据。
|
||||
// 配色:常态随主题(不显式设色,跟随全局样式表);hover/选中页用强调蓝 #409EFF(与表格开关同色)。
|
||||
class TablePager : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TablePager(QWidget* parent = nullptr);
|
||||
|
||||
// 用总数/当前页/每页条数刷新分页器(重建页码按钮、同步跳页框/下拉/总数文案)。
|
||||
void setState(int total, int pageNo, int pageSize);
|
||||
|
||||
signals:
|
||||
// 请求加载某页(翻页/跳页/改每页条数)。改每页条数时 pageNo=1。
|
||||
void pageRequested(int pageNo, int pageSize);
|
||||
|
||||
private:
|
||||
int pageCount() const; // ceil(total/pageSize),至少 1
|
||||
void rebuildNumbers(); // 按当前页重建页码按钮(含 … 跳页)
|
||||
|
||||
int total_ = 0;
|
||||
int pageNo_ = 1;
|
||||
int pageSize_ = 50;
|
||||
|
||||
QToolButton* prevBtn_ = nullptr;
|
||||
QToolButton* nextBtn_ = nullptr;
|
||||
QWidget* numHost_ = nullptr;
|
||||
QHBoxLayout* numLay_ = nullptr;
|
||||
QLineEdit* jumpEdit_ = nullptr;
|
||||
QIntValidator* jumpValidator_ = nullptr;
|
||||
QComboBox* sizeCombo_ = nullptr;
|
||||
QLabel* totalLabel_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
@ -35,6 +35,16 @@ void DatasetDetailController::openDataset(const QString& dsId, const QString& dd
|
|||
}
|
||||
|
||||
void DatasetDetailController::loadTab(const QString& dsId, const QString& ddCode, int tabIndex) {
|
||||
loadTabImpl(dsId, ddCode, tabIndex, /*pageNo*/ 1, /*pageSize*/ 0);
|
||||
}
|
||||
|
||||
void DatasetDetailController::loadTabPaged(const QString& dsId, const QString& ddCode, int tabIndex,
|
||||
int pageNo, int pageSize) {
|
||||
loadTabImpl(dsId, ddCode, tabIndex, pageNo, pageSize);
|
||||
}
|
||||
|
||||
void DatasetDetailController::loadTabImpl(const QString& dsId, const QString& ddCode, int tabIndex,
|
||||
int pageNo, int pageSize) {
|
||||
auto* s = registry_.find(ddCode.toStdString());
|
||||
if (!s) return; // 策略消失(不应发生):静默不加载
|
||||
const std::vector<controller::TabSpec> tabs = s->tabs();
|
||||
|
|
@ -45,7 +55,7 @@ void DatasetDetailController::loadTab(const QString& dsId, const QString& ddCode
|
|||
// 吞掉、遮罩永久悬挂(文档化的崩溃/挂起类)。就地兜底为 loadFailed,且不留半注册的在飞句柄。
|
||||
data::DetailLoad* load = nullptr;
|
||||
try {
|
||||
load = repo_.loadAsync(spec.loaderKey.toStdString(), dsId.toStdString());
|
||||
load = repo_.loadAsync(spec.loaderKey.toStdString(), dsId.toStdString(), pageNo, pageSize);
|
||||
} catch (const std::exception& e) {
|
||||
qWarning("[detail] loadAsync 失败 id=%s tab=%d key=%s: %s", qUtf8Printable(dsId), tabIndex,
|
||||
qUtf8Printable(spec.loaderKey), e.what());
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ public slots:
|
|||
// 打开数据集:查策略 → datasetOpened(页签集) → 对每个非 lazy 页签发起 loadTab。
|
||||
void openDataset(const QString& dsId, const QString& ddCode, const QString& dsName = QString());
|
||||
// 加载某页签(lazy 页签首次激活时由壳触发;非 lazy 由 openDataset 自动触发)。
|
||||
// 分页型页签(如 dd_grid 列表)首载用默认页(pageNo=1/pageSize=0 → 仓储解析默认每页条数)。
|
||||
void loadTab(const QString& dsId, const QString& ddCode, int tabIndex);
|
||||
// 分页加载某页签(分页器翻页/改每页条数时由壳触发)。pageSize=0 → 仓储用该类型默认值。
|
||||
void loadTabPaged(const QString& dsId, const QString& ddCode, int tabIndex, int pageNo,
|
||||
int pageSize);
|
||||
void focusDataset(const QString& dsId);
|
||||
signals:
|
||||
void datasetOpened(const QString& dsId, const QString& ddCode, const QString& dsName,
|
||||
|
|
@ -33,6 +37,10 @@ signals:
|
|||
void loadFailed(const QString& dsId, const QString& message);
|
||||
void focusRequested(const QString& dsId);
|
||||
private:
|
||||
// loadTab/loadTabPaged 共用实现:按 (dsId,ddCode,tabIndex) 查 loaderKey,带分页参数异步加载。
|
||||
void loadTabImpl(const QString& dsId, const QString& ddCode, int tabIndex, int pageNo,
|
||||
int pageSize);
|
||||
|
||||
data::IAsyncDatasetRepository& repo_;
|
||||
ChartStrategyRegistry& registry_;
|
||||
QMap<int, QPointer<data::DetailLoad>> inflight_; // 按页签槽位的在飞句柄(§5.0 身份比对)
|
||||
|
|
|
|||
|
|
@ -61,10 +61,14 @@ struct TableColumn {
|
|||
};
|
||||
|
||||
// 通用表格载荷:列定义 + 预格式化的行(每格 QString)+ 总数(分页用)。
|
||||
// 分页(dd_grid 列表,服务端分页 vxe-pager):pageSize>0 时视图渲染分页器,pageNo 为当前页(1 基);
|
||||
// pageSize=0(默认)= 不分页(measurement/trajectory 全量列表,一次性返回所有行)。
|
||||
struct TablePayload {
|
||||
std::vector<TableColumn> columns;
|
||||
std::vector<std::vector<QString>> rows;
|
||||
int total = 0;
|
||||
int pageNo = 1; // 当前页(1 基);分页用
|
||||
int pageSize = 0; // 每页条数;>0 才渲染分页器(vxe-pager),0=不分页
|
||||
};
|
||||
|
||||
// 柱状图系列:名称(图例/legend)+ 各类目的 y 值 + 填充色(hex,如 #5470c6;数据色,两主题一致)。
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ add_library(geopro_data STATIC
|
|||
dto/MeasurementDto.cpp
|
||||
dto/GrMeasurementDto.cpp
|
||||
dto/TrajectoryDto.cpp
|
||||
dto/GridDto.cpp
|
||||
api/ApiProjectRepository.cpp
|
||||
api/ApiDatasetRepository.cpp
|
||||
api/DatasetLoadHandles.cpp
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "api/DatasetLoadHandles.hpp"
|
||||
#include "dto/DatasetChartDto.hpp"
|
||||
#include "dto/GrMeasurementDto.hpp"
|
||||
#include "dto/GridDto.hpp"
|
||||
#include "dto/MeasurementDto.hpp"
|
||||
#include "dto/TrajectoryDto.hpp"
|
||||
#include "model/detail/DetailPayloads.hpp"
|
||||
|
|
@ -132,11 +133,24 @@ net::ApiBatch* trajectoryLineBatch(net::ApiClient& api, const std::string& dsId)
|
|||
return new net::ApiBatch(calls, &isFailure);
|
||||
}
|
||||
|
||||
// dd_grid 白化数据列表(服务端分页):单请求 grid/rows(GET,query:dsObjectId/pageNo/pageSize)。
|
||||
// 响应 data = {rowList[{x,y,id}], gridHeaderDisplay[x,y], total}。
|
||||
net::ApiBatch* gridRowsBatch(net::ApiClient& api, const std::string& dsId, int pageNo, int pageSize) {
|
||||
QList<net::IApiCall*> calls{
|
||||
api.getAsync(QStringLiteral("/business/dd/ert/grid/rows?dsObjectId=%1&pageNo=%2&pageSize=%3")
|
||||
.arg(enc(dsId))
|
||||
.arg(pageNo)
|
||||
.arg(pageSize)),
|
||||
};
|
||||
return new net::ApiBatch(calls, &isFailure);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ApiDatasetRepository::ApiDatasetRepository(net::ApiClient& api) : api_(api) {}
|
||||
|
||||
DetailLoad* ApiDatasetRepository::loadAsync(const std::string& loaderKey, const std::string& dsId) {
|
||||
DetailLoad* ApiDatasetRepository::loadAsync(const std::string& loaderKey, const std::string& dsId,
|
||||
int pageNo, int pageSize) {
|
||||
if (loaderKey == "inversion.scatter") return makeInversionScatter(dsId);
|
||||
if (loaderKey == "inversion.grid") return makeInversionGrid(dsId);
|
||||
if (loaderKey == "ert_measurement.scatter") return makeMeasurementScatter(dsId);
|
||||
|
|
@ -146,6 +160,7 @@ DetailLoad* ApiDatasetRepository::loadAsync(const std::string& loaderKey, const
|
|||
if (loaderKey == "traj.rows") return makeTrajectoryRows(dsId);
|
||||
if (loaderKey == "traj.elev") return makeTrajectoryElevation(dsId);
|
||||
if (loaderKey == "traj.map") return makeTrajectoryMap(dsId);
|
||||
if (loaderKey == "grid.rows") return makeGridRows(dsId, pageNo, pageSize);
|
||||
throw std::runtime_error("unknown loaderKey: " + loaderKey);
|
||||
}
|
||||
|
||||
|
|
@ -207,4 +222,15 @@ DetailLoad* ApiDatasetRepository::makeTrajectoryMap(const std::string& dsId) {
|
|||
});
|
||||
}
|
||||
|
||||
DetailLoad* ApiDatasetRepository::makeGridRows(const std::string& dsId, int pageNo, int pageSize) {
|
||||
// 解析默认值:pageNo<=0→1,pageSize<=0→50(原版默认每页 50 条),再传入 URL 与解析器
|
||||
// (保证 URL 参数与「序号」列偏移一致)。
|
||||
const int pn = pageNo > 0 ? pageNo : 1;
|
||||
const int ps = pageSize > 0 ? pageSize : 50;
|
||||
return new ApiDetailLoad(gridRowsBatch(api_, dsId, pn, ps),
|
||||
[pn, ps](const QList<net::ApiResponse>& r) {
|
||||
return QVariant::fromValue(dto::parseGridTable(r[0].data, pn, ps));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace geopro::data
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ namespace geopro::data {
|
|||
class ApiDatasetRepository : public IAsyncDatasetRepository {
|
||||
public:
|
||||
explicit ApiDatasetRepository(net::ApiClient& api);
|
||||
DetailLoad* loadAsync(const std::string& loaderKey, const std::string& dsId) override;
|
||||
DetailLoad* loadAsync(const std::string& loaderKey, const std::string& dsId,
|
||||
int pageNo = 1, int pageSize = 0) override;
|
||||
private:
|
||||
DetailLoad* makeInversionScatter(const std::string& dsId);
|
||||
DetailLoad* makeInversionGrid(const std::string& dsId);
|
||||
|
|
@ -18,6 +19,7 @@ private:
|
|||
DetailLoad* makeTrajectoryRows(const std::string& dsId);
|
||||
DetailLoad* makeTrajectoryElevation(const std::string& dsId);
|
||||
DetailLoad* makeTrajectoryMap(const std::string& dsId);
|
||||
DetailLoad* makeGridRows(const std::string& dsId, int pageNo, int pageSize);
|
||||
net::ApiClient& api_;
|
||||
};
|
||||
} // namespace geopro::data
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
#include "dto/GridDto.hpp"
|
||||
|
||||
#include <QJsonValue>
|
||||
|
||||
#include "dto/TrajectoryDto.hpp" // parseGridHeaderTable(通用 gridHeaderDisplay+rowList 解析器)复用
|
||||
|
||||
namespace geopro::data::dto {
|
||||
using namespace geopro::core;
|
||||
|
||||
TablePayload parseGridTable(const QJsonObject& data, int pageNo, int pageSize) {
|
||||
const int pn = pageNo > 0 ? pageNo : 1;
|
||||
const int ps = pageSize > 0 ? pageSize : 0;
|
||||
|
||||
TablePayload t = parseGridHeaderTable(data); // x / y 列 + 各行(按 columnSort)
|
||||
|
||||
// 前插「序号」列(vxe seq 列:居中、按页偏移自增)。
|
||||
TableColumn seq;
|
||||
seq.code = QStringLiteral("__seq");
|
||||
seq.title = QStringLiteral("序号");
|
||||
seq.sort = 0;
|
||||
t.columns.insert(t.columns.begin(), seq);
|
||||
|
||||
const int base = (pn - 1) * ps; // 本页首行的全局序号偏移(ps=0 → 偏移 0,从 1 起)
|
||||
for (size_t i = 0; i < t.rows.size(); ++i)
|
||||
t.rows[i].insert(t.rows[i].begin(), QString::number(base + static_cast<int>(i) + 1));
|
||||
|
||||
// 总数:dd_grid 用 data.total(parseGridHeaderTable 默认回退本批行数,这里以服务端总数覆盖)。
|
||||
t.total = data.value(QStringLiteral("total")).toInt(t.total);
|
||||
t.pageNo = pn;
|
||||
t.pageSize = ps;
|
||||
return t;
|
||||
}
|
||||
|
||||
} // namespace geopro::data::dto
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include <QJsonObject>
|
||||
#include "model/detail/DetailPayloads.hpp"
|
||||
|
||||
namespace geopro::data::dto {
|
||||
|
||||
// dd_grid(白化数据)列表。端点 dd/ert/grid/rows?dsObjectId=&pageNo=&pageSize=
|
||||
// (服务端分页;data 形如 {rowList[{x,y,id}], gridHeaderDisplay[x,y], total})。
|
||||
//
|
||||
// 渲染(实测 vxe-table 确认):序号 / x / y 三列,全部居中、同色灰字(无特殊着色);
|
||||
// 底部 vxe-pager(上一页/页码/下一页 + 前往 N 页 + 每页条数选择 [50,100,500,1000] + 共 N 条记录)。
|
||||
//
|
||||
// 复用通用 parseGridHeaderTable(gridHeaderDisplay→x/y 列 + rowList→行),再前插「序号」列
|
||||
// (vxe seq 列:按页偏移自增 = (pageNo-1)*pageSize + 行内序号);total 取 data.total(非 __rowTotal);
|
||||
// 回填 pageNo/pageSize 供视图渲染分页器。pageNo/pageSize 为本次请求参数(仓储已解析默认值后传入)。
|
||||
geopro::core::TablePayload parseGridTable(const QJsonObject& data, int pageNo, int pageSize);
|
||||
|
||||
} // namespace geopro::data::dto
|
||||
|
|
@ -10,7 +10,10 @@ class IAsyncDatasetRepository {
|
|||
public:
|
||||
virtual ~IAsyncDatasetRepository() = default;
|
||||
// 通用页签加载(tab 引擎):按 loaderKey 分派,载荷经 QVariant 类型擦除。
|
||||
virtual DetailLoad* loadAsync(const std::string& loaderKey, const std::string& dsId) = 0;
|
||||
// pageNo/pageSize 仅分页型 loaderKey(如 grid.rows)使用;其余 loaderKey 忽略。
|
||||
// 默认 pageNo=1/pageSize=0 → 非分页加载(分页 loaderKey 自行解析默认每页条数)。
|
||||
virtual DetailLoad* loadAsync(const std::string& loaderKey, const std::string& dsId,
|
||||
int pageNo = 1, int pageSize = 0) = 0;
|
||||
};
|
||||
|
||||
} // namespace geopro::data
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ target_sources(geopro_tests PRIVATE data/test_dataset_chart_dto.cpp)
|
|||
target_sources(geopro_tests PRIVATE data/test_measurement_dto.cpp)
|
||||
target_sources(geopro_tests PRIVATE data/test_gr_dto.cpp)
|
||||
target_sources(geopro_tests PRIVATE data/test_trajectory_dto.cpp)
|
||||
target_sources(geopro_tests PRIVATE data/test_grid_dto.cpp)
|
||||
target_sources(geopro_tests PRIVATE data/test_dataset_load_handles.cpp)
|
||||
# 通用仓储分派离线单测(loadAsync 分派 + QVariant payload round-trip)。
|
||||
target_sources(geopro_tests PRIVATE data/test_async_repo_dispatch.cpp)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "panels/chart/MeasurementStrategy.hpp"
|
||||
#include "panels/chart/GrMeasurementStrategy.hpp"
|
||||
#include "panels/chart/TrajectoryStrategy.hpp"
|
||||
#include "panels/chart/GridStrategy.hpp"
|
||||
using namespace geopro::controller;
|
||||
namespace {
|
||||
struct Fake : IDatasetChartStrategy {
|
||||
|
|
@ -93,3 +94,16 @@ TEST(TrajectoryStrategy, DrivesMapTableElevationTabs) {
|
|||
EXPECT_FALSE(tabs[2].lazy);
|
||||
EXPECT_EQ(tabs[2].loaderKey.toStdString(), "traj.elev");
|
||||
}
|
||||
|
||||
TEST(GridStrategy, DrivesSinglePaginatedListTab) {
|
||||
geopro::app::GridStrategy s;
|
||||
EXPECT_EQ(s.ddCode(), "dd_grid");
|
||||
const auto tabs = s.tabs();
|
||||
ASSERT_EQ(tabs.size(), 1u);
|
||||
// 列表:Table,非 lazy,分页(paginated)。loaderKey grid.rows → TablePayload。
|
||||
EXPECT_EQ(tabs[0].title.toStdString(), std::string("列表"));
|
||||
EXPECT_EQ(tabs[0].kind, ViewKind::Table);
|
||||
EXPECT_FALSE(tabs[0].lazy);
|
||||
EXPECT_TRUE(tabs[0].paginated);
|
||||
EXPECT_EQ(tabs[0].loaderKey.toStdString(), "grid.rows");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,11 @@ struct StubDetailLoad : data::DetailLoad {
|
|||
// 桩仓储:每个 loaderKey 都造一个新句柄,记录最近一个用于 fire。
|
||||
struct StubAsyncRepo : data::IAsyncDatasetRepository {
|
||||
StubDetailLoad* last = nullptr;
|
||||
data::DetailLoad* loadAsync(const std::string&, const std::string&) override {
|
||||
int lastPageNo = 0, lastPageSize = 0; // 记录最近一次分页参数(验证 loadTabPaged 透传)
|
||||
data::DetailLoad* loadAsync(const std::string&, const std::string&, int pageNo,
|
||||
int pageSize) override {
|
||||
lastPageNo = pageNo;
|
||||
lastPageSize = pageSize;
|
||||
last = new StubDetailLoad;
|
||||
return last;
|
||||
}
|
||||
|
|
@ -141,6 +145,26 @@ TEST(DatasetDetailController, LoadTabLazyTabStartsLoad) {
|
|||
EXPECT_EQ(ready.takeFirst().at(1).toInt(), 1);
|
||||
}
|
||||
|
||||
// loadTab(非分页)用默认页参(pageNo=1/pageSize=0);loadTabPaged 透传分页参数到仓储。
|
||||
TEST(DatasetDetailController, LoadTabUsesDefaultPageParams) {
|
||||
StubAsyncRepo repo;
|
||||
auto reg = makeInversionRegistry();
|
||||
controller::DatasetDetailController c(repo, reg);
|
||||
c.loadTab("ds1", "dd_inversion_data", 0);
|
||||
EXPECT_EQ(repo.lastPageNo, 1);
|
||||
EXPECT_EQ(repo.lastPageSize, 0);
|
||||
}
|
||||
|
||||
TEST(DatasetDetailController, LoadTabPagedThreadsPageParams) {
|
||||
StubAsyncRepo repo;
|
||||
auto reg = makeInversionRegistry();
|
||||
controller::DatasetDetailController c(repo, reg);
|
||||
c.loadTabPaged("ds1", "dd_inversion_data", 0, 3, 100);
|
||||
ASSERT_NE(repo.last, nullptr);
|
||||
EXPECT_EQ(repo.lastPageNo, 3);
|
||||
EXPECT_EQ(repo.lastPageSize, 100);
|
||||
}
|
||||
|
||||
TEST(DatasetDetailController, AbortsPreviousOnSameSlotReload) {
|
||||
StubAsyncRepo repo;
|
||||
auto reg = makeInversionRegistry();
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ TEST(AsyncRepoDispatch, KnownKeysReturnNonNullHandle) {
|
|||
DetailLoad* trajMap = repo.loadAsync("traj.map", "ds1");
|
||||
ASSERT_NE(trajMap, nullptr);
|
||||
trajMap->abort();
|
||||
|
||||
DetailLoad* gridRows = repo.loadAsync("grid.rows", "ds1", 1, 50); // 分页型:带页参
|
||||
ASSERT_NE(gridRows, nullptr);
|
||||
gridRows->abort();
|
||||
}
|
||||
|
||||
// 未知 loaderKey 抛 std::runtime_error。
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "dto/GridDto.hpp"
|
||||
|
||||
using namespace geopro::data::dto;
|
||||
using geopro::core::TableColumnKind;
|
||||
|
||||
namespace {
|
||||
|
||||
// 取自真实夹具 tests/fixtures/dd/ert-grid-rows.json 的 data(rowList 5 行,gridHeaderDisplay
|
||||
// 两列 x/y,total=62,逐字一致;端点 dd/ert/grid/rows)。内联避免引入 fixture 路径编译定义。
|
||||
const char* kGridData = R"({
|
||||
"gridHeaderDisplay": [
|
||||
{ "columnCode": "x", "columnNameChn": "x", "columnNameEng": "x", "columnWidth": 10, "columnSort": 1 },
|
||||
{ "columnCode": "y", "columnNameChn": "y", "columnNameEng": "y", "columnWidth": 10, "columnSort": 2 }
|
||||
],
|
||||
"functionList": [],
|
||||
"total": 62,
|
||||
"rowList": [
|
||||
{ "x": 2.904, "y": 25.126, "id": "1438944148742144" },
|
||||
{ "x": 4.897, "y": 25.161, "id": "1438944148742145" },
|
||||
{ "x": 6.892, "y": 25.247, "id": "1438944148742146" },
|
||||
{ "x": 8.892, "y": 25.251, "id": "1438944148742147" },
|
||||
{ "x": 10.891, "y": 25.226, "id": "1438944148742148" }
|
||||
]
|
||||
})";
|
||||
|
||||
QJsonObject gridData() { return QJsonDocument::fromJson(kGridData).object(); }
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(GridDto, FirstPagePrependsSeqColumnAndReadsTotal) {
|
||||
auto t = parseGridTable(gridData(), /*pageNo*/ 1, /*pageSize*/ 50);
|
||||
|
||||
// 三列:序号 / x / y(序号前插,x/y 来自 gridHeaderDisplay 按 columnSort)。
|
||||
ASSERT_EQ(t.columns.size(), 3u);
|
||||
EXPECT_EQ(t.columns[0].title.toStdString(), std::string("序号"));
|
||||
EXPECT_EQ(t.columns[0].code.toStdString(), "__seq");
|
||||
EXPECT_EQ(t.columns[1].title.toStdString(), "x");
|
||||
EXPECT_EQ(t.columns[2].title.toStdString(), "y");
|
||||
for (const auto& c : t.columns) EXPECT_EQ(c.kind, TableColumnKind::Text); // 无特殊列
|
||||
|
||||
// 5 行;首行 序号=1 / x=2.904 / y=25.126。
|
||||
ASSERT_EQ(t.rows.size(), 5u);
|
||||
ASSERT_EQ(t.rows[0].size(), 3u);
|
||||
EXPECT_EQ(t.rows[0][0].toStdString(), "1");
|
||||
EXPECT_EQ(t.rows[0][1].toStdString(), "2.904");
|
||||
EXPECT_EQ(t.rows[0][2].toStdString(), "25.126");
|
||||
EXPECT_EQ(t.rows[4][0].toStdString(), "5"); // 第 5 行序号=5
|
||||
|
||||
// 总数取 data.total=62(非本批 5 行);分页态回填。
|
||||
EXPECT_EQ(t.total, 62);
|
||||
EXPECT_EQ(t.pageNo, 1);
|
||||
EXPECT_EQ(t.pageSize, 50);
|
||||
}
|
||||
|
||||
TEST(GridDto, SeqColumnOffsetsByPage) {
|
||||
// 第 2 页、每页 50:本页首行全局序号 = (2-1)*50 + 1 = 51。
|
||||
auto t = parseGridTable(gridData(), /*pageNo*/ 2, /*pageSize*/ 50);
|
||||
ASSERT_EQ(t.rows.size(), 5u);
|
||||
EXPECT_EQ(t.rows[0][0].toStdString(), "51");
|
||||
EXPECT_EQ(t.rows[4][0].toStdString(), "55");
|
||||
EXPECT_EQ(t.pageNo, 2);
|
||||
EXPECT_EQ(t.pageSize, 50);
|
||||
}
|
||||
|
||||
TEST(GridDto, EmptyDataYieldsSeqOnlyColumnNoRows) {
|
||||
const QJsonObject empty;
|
||||
auto t = parseGridTable(empty, 1, 50);
|
||||
// 空数据:仅有前插的「序号」列,无行;total=0。
|
||||
ASSERT_EQ(t.columns.size(), 1u);
|
||||
EXPECT_EQ(t.columns[0].code.toStdString(), "__seq");
|
||||
EXPECT_EQ(t.rows.size(), 0u);
|
||||
EXPECT_EQ(t.total, 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"code": 200,
|
||||
"msg": "成功",
|
||||
"data": {
|
||||
"gridHeaderDisplay": [
|
||||
{ "columnCode": "x", "columnNameChn": "x", "columnNameEng": "x", "columnWidth": 10, "columnSort": 1 },
|
||||
{ "columnCode": "y", "columnNameChn": "y", "columnNameEng": "y", "columnWidth": 10, "columnSort": 2 }
|
||||
],
|
||||
"functionList": [],
|
||||
"total": 62,
|
||||
"rowList": [
|
||||
{ "x": 2.904, "y": 25.126, "id": "1438944148742144" },
|
||||
{ "x": 4.897, "y": 25.161, "id": "1438944148742145" },
|
||||
{ "x": 6.892, "y": 25.247, "id": "1438944148742146" },
|
||||
{ "x": 8.892, "y": 25.251, "id": "1438944148742147" },
|
||||
{ "x": 10.891, "y": 25.226, "id": "1438944148742148" }
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue