diff --git a/src/app/main.cpp b/src/app/main.cpp index e6abf93..feb0358 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -473,13 +473,10 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re const auto vols = scene3dRepo->volumeRows(); const auto slices = scene3dRepo->sliceRows(); const auto anomalies = scene3dRepo->anomalyRows(); - // 电阻率/视/瞬变段=对象树反演 ds;voxel/slice 段先由 splitByCategory 按 ddCode 填体/切片。 - std::vector forCat = *lastSourceRows; - for (const auto& v : vols) forCat.push_back(v); - for (const auto& s : slices) forCat.push_back(s); - drawer->analysisTab()->setBuckets(geopro::app::splitByCategory(forCat)); - // 三维体段重置为「体→切片/异常」三级树:体(根)+切片(parentId=体)+异常(parentId=体/切片), - // populateDatasetList 按 parentId 自动建三级树(spec §8)。 + // 电阻率/视/瞬变段=对象树反演 ds(splitByCategory 按 dsTypeCode 分;voxel 段下方单独喂三级树)。 + drawer->analysisTab()->setBuckets(geopro::app::splitByCategory(*lastSourceRows)); + // 三维体段=「体→切片/异常」三级树:体(根)+切片(parentId=体)+异常(parentId=体/切片); + // 切片不单列段,只在此树(spec §8 修订)。populateDatasetList 按 parentId 自动建树。 std::vector voxelTree = vols; for (const auto& s : slices) voxelTree.push_back(s); for (const auto& a : anomalies) voxelTree.push_back(a); diff --git a/src/app/panels/columns/CategorySection.cpp b/src/app/panels/columns/CategorySection.cpp index e4f9a9d..97decc8 100644 --- a/src/app/panels/columns/CategorySection.cpp +++ b/src/app/panels/columns/CategorySection.cpp @@ -29,23 +29,37 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset root->setContentsMargins(0, 0, 0, 0); root->setSpacing(0); - // 折叠头:标题 + 箭头(点击展开/收起段体)。 - header_ = new QToolButton(this); + // 数据类型标题行(spec §7):折叠箭头+标题(左) | 「+新增三维体」(右,仅反演类,在标题行而非筛选行)。 + auto* headerRow = new QWidget(this); + auto* hl = new QHBoxLayout(headerRow); + hl->setContentsMargins(space::kSm, 0, space::kSm, 0); + hl->setSpacing(space::kSm); + header_ = new QToolButton(headerRow); header_->setText(QString::fromStdString(spec_.title)); header_->setCheckable(true); header_->setChecked(true); header_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); header_->setArrowType(Qt::DownArrow); header_->setAutoRaise(true); - header_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - root->addWidget(header_); + hl->addWidget(header_); + hl->addStretch(1); + if (spec_.canGenerateVolume) { + auto* gen = new QToolButton(headerRow); + gen->setText(QStringLiteral("+ 新增三维体")); + gen->setAutoRaise(true); + connect(gen, &QToolButton::clicked, this, [this] { + emit generateVolumeRequested(QString::fromStdString(spec_.dsTypeCode), checkedDsIds()); + }); + hl->addWidget(gen); + } + root->addWidget(headerRow); body_ = new QWidget(this); auto* body = new QVBoxLayout(body_); body->setContentsMargins(space::kMd, space::kSm, space::kMd, space::kMd); body->setSpacing(space::kSm); - // 段头筛选行:装置类型(仅 ERT 类)+ 采集时间下限 +「+新增三维体」(仅反演类)。 + // 筛选行:装置类型(仅 ERT 类)+ 采集时间段(起止;最小日期显示"不限",不再露 1752)。 auto* filterRow = new QHBoxLayout(); if (spec_.hasArrayTypeFilter) { arrayCombo_ = new QComboBox(body_); @@ -54,21 +68,21 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset [this](int) { rebuildList(); }); filterRow->addWidget(arrayCombo_); } - fromDate_ = new QDateEdit(body_); - fromDate_->setCalendarPopup(true); - fromDate_->setDisplayFormat(QStringLiteral("yyyy-MM-dd")); - fromDate_->setDate(fromDate_->minimumDate()); // 最小日期=不限(哨兵) - fromDate_->setToolTip(QStringLiteral("采集时间下限(设为最小日期=不限)")); - connect(fromDate_, &QDateEdit::dateChanged, this, [this](const QDate&) { rebuildList(); }); - filterRow->addWidget(new QLabel(QStringLiteral("起始日期"), body_)); + auto makeDate = [this]() { + auto* d = new QDateEdit(body_); + d->setCalendarPopup(true); + d->setDisplayFormat(QStringLiteral("yyyy-MM-dd")); + d->setSpecialValueText(QStringLiteral("不限")); // 最小日期→显示"不限" + d->setDate(d->minimumDate()); + connect(d, &QDateEdit::dateChanged, this, [this](const QDate&) { rebuildList(); }); + return d; + }; + fromDate_ = makeDate(); + toDate_ = makeDate(); + filterRow->addWidget(new QLabel(QStringLiteral("采集"), body_)); filterRow->addWidget(fromDate_, 1); - if (spec_.canGenerateVolume) { - auto* gen = new QPushButton(QStringLiteral("+ 新增三维体"), body_); - connect(gen, &QPushButton::clicked, this, [this] { - emit generateVolumeRequested(QString::fromStdString(spec_.dsTypeCode), checkedDsIds()); - }); - filterRow->addWidget(gen); - } + filterRow->addWidget(new QLabel(QStringLiteral("至"), body_)); + filterRow->addWidget(toDate_, 1); body->addLayout(filterRow); // 段体:可勾选数据树(勾选=渲染)。复用数据列表卡片委托与 populateDatasetList。 @@ -138,13 +152,18 @@ bool CategorySection::passesFilters(const DsRow& row) const { if (!hit) return false; } } - // 采集时间下限(collectTime 经 dict 取;缺则回退 createTime;最小日期=不限)。 - if (fromDate_ && fromDate_->date() > fromDate_->minimumDate()) { + // 采集时间段(collectTime 经 dict 取;缺则回退 createTime;最小日期="不限"不约束)。 + const bool hasFrom = fromDate_ && fromDate_->date() > fromDate_->minimumDate(); + const bool hasTo = toDate_ && toDate_->date() > toDate_->minimumDate(); + if (hasFrom || hasTo) { const DsTypeFields* f = dict_ ? dict_->fields(spec_.dsTypeCode) : nullptr; std::string ts = f ? collectTimeOf(row, *f) : std::string(); if (ts.empty()) ts = row.createTime; const QDate d = QDate::fromString(QString::fromStdString(ts).left(10), QStringLiteral("yyyy-MM-dd")); - if (d.isValid() && d < fromDate_->date()) return false; + if (d.isValid()) { + if (hasFrom && d < fromDate_->date()) return false; + if (hasTo && d > toDate_->date()) return false; + } } return true; } diff --git a/src/app/panels/columns/CategorySection.hpp b/src/app/panels/columns/CategorySection.hpp index bf2dbaf..e85ca08 100644 --- a/src/app/panels/columns/CategorySection.hpp +++ b/src/app/panels/columns/CategorySection.hpp @@ -51,7 +51,8 @@ private: QToolButton* header_ = nullptr; // 折叠头(标题 + 箭头) QWidget* body_ = nullptr; // 段体容器(折叠时隐藏) QComboBox* arrayCombo_ = nullptr; // 装置类型筛选(仅 hasArrayTypeFilter) - QDateEdit* fromDate_ = nullptr; // 采集时间下限 + QDateEdit* fromDate_ = nullptr; // 采集时间段·起 + QDateEdit* toDate_ = nullptr; // 采集时间段·止 QTreeWidget* list_ = nullptr; }; diff --git a/src/data/repo/CategoryConfig.hpp b/src/data/repo/CategoryConfig.hpp index dea58df..f6c4b0f 100644 --- a/src/data/repo/CategoryConfig.hpp +++ b/src/data/repo/CategoryConfig.hpp @@ -21,7 +21,7 @@ inline const std::vector& categoryConfigs() { {"apparent", "视电阻率数据", "visual resistivity data", "", true, true}, {"transient", "瞬变电磁数据", "DD TRANSIENT ELECTROMAGNETIC INVERSION", "", true, false}, {"voxel", "三维体", "", "dd_voxel", false, false}, - {"slice", "切片", "", "dd_slice", false, false}, + // 切片不单列段——挂在三维体段「体→切片/异常」三级树下(spec §8 修订)。 }; return kCfg; } diff --git a/tests/app/test_dataset_category.cpp b/tests/app/test_dataset_category.cpp index 8e4e84f..82876f2 100644 --- a/tests/app/test_dataset_category.cpp +++ b/tests/app/test_dataset_category.cpp @@ -28,11 +28,10 @@ TEST(SplitByCategory, RoutesByDsTypeCodeAndDdCode) { EXPECT_EQ(b.segments[1].size(), 1u); EXPECT_EQ(b.segments[1][0].id, "b"); EXPECT_EQ(b.segments[2].size(), 1u); EXPECT_EQ(b.segments[2][0].id, "c"); EXPECT_EQ(b.segments[3].size(), 1u); EXPECT_EQ(b.segments[3][0].id, "v"); - EXPECT_EQ(b.segments[4].size(), 1u); EXPECT_EQ(b.segments[4][0].id, "s"); - // 接地电阻不进任何段。 + // 切片(dd_slice)不单列段——挂三维体树,不进任何段;接地电阻同样丢弃。 std::size_t total = 0; for (auto& s : b.segments) total += s.size(); - EXPECT_EQ(total, 5u); + EXPECT_EQ(total, 4u); } TEST(SplitByCategory, PreservesOrderWithinSegment) {