#include "panels/DatasetDetailPage.hpp" #include #include #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" namespace geopro::app { DatasetDetailPage::DatasetDetailPage(QWidget* parent) : QWidget(parent) { auto* lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); lay->setSpacing(0); } void DatasetDetailPage::build(const QString& dsId, const QString& ddCode, const QString& dsName, const std::vector& tabs) { Q_ASSERT(views_.empty()); // build() 仅一次:views_ 为已 release 的裸指针,二次构建会泄漏旧视图 if (!views_.empty()) return; dsId_ = dsId; ddCode_ = ddCode; dsName_ = dsName; tabs_ = tabs; views_.assign(tabs.size(), nullptr); loaded_.assign(tabs.size(), false); requested_.assign(tabs.size(), false); overlays_.clear(); // 按 TabSpec 经工厂造视图,并组装为带表头页签的面板。 QVector panelTabs; for (size_t i = 0; i < tabs.size(); ++i) { const auto& spec = tabs[i]; auto view = makeDetailView(spec.kind, this); // 抛出由调用栈兜底(GuardedApplication) IDetailView* raw = view.release(); // QWidget 由 this/QwtPlot 父子树接管生命周期 views_[i] = raw; // lazy 页签:建覆盖该视图的加载遮罩(父为视图 widget,随其尺寸覆盖图区)。 if (spec.lazy) overlays_[static_cast(i)] = new LoadingOverlay(raw->widget()); // 分页型页签:把表格视图的分页请求冒泡为页信号(携带 dsId/ddCode/tabIndex + 页参数)。 if (spec.paginated) { if (auto* table = qobject_cast(raw->widget())) { const int idx = static_cast(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 actions = { {Glyph::Download, QStringLiteral("导出")}, }; auto tabbedPanel = buildTabbedPanel(panelTabs, actions); layout()->addWidget(tabbedPanel.container); // lazy 页签首次激活 → 发 tabNeeded 请求懒加载(数据慢,故不随开页同步拉)。 if (tabbedPanel.tabGroup) { connect(tabbedPanel.tabGroup, &QButtonGroup::idClicked, this, [this](int idx) { if (idx < 0 || idx >= static_cast(tabs_.size())) return; if (!tabs_[static_cast(idx)].lazy) return; if (requested_[static_cast(idx)] || loaded_[static_cast(idx)]) return; if (dsId_.isEmpty()) return; requested_[static_cast(idx)] = true; emit tabNeeded(dsId_, ddCode_, idx); }); } } void DatasetDetailPage::setTabPayload(int tabIndex, const QVariant& payload) { if (tabIndex < 0 || tabIndex >= static_cast(views_.size())) return; if (auto* v = views_[static_cast(tabIndex)]) v->setPayload(payload); loaded_[static_cast(tabIndex)] = true; requested_[static_cast(tabIndex)] = true; // 已加载,切回不再重复请求 setTabLoading(tabIndex, false); // 数据到达,隐藏遮罩 } void DatasetDetailPage::setTabLoading(int tabIndex, bool on) { auto it = overlays_.find(tabIndex); if (it == overlays_.end()) return; // 非 lazy 页签无遮罩 if (on) it.value()->showOver(); else it.value()->hide(); } void DatasetDetailPage::clearAllLoadingOverlays() { for (auto* overlay : overlays_) if (overlay) overlay->hide(); } } // namespace geopro::app