fix(ui): 容器节点紧凑卡(矮+居中+小留白)+所有段从项目根树形(自剪枝)+体/切片创建时间+筛选行去采集(日期前装置后)
This commit is contained in:
parent
6c8069b87d
commit
572fbf8d7b
|
|
@ -486,30 +486,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
const auto slices = scene3dRepo->sliceRows();
|
||||
const auto anomalies = scene3dRepo->anomalyRows();
|
||||
// 电阻率/视/瞬变段=对象树反演 ds(splitByCategory 按 dsTypeCode 分;voxel 段下方单独喂完整树)。
|
||||
// 各段据结构建「从项目根的容器层级树」(项目根/GS/TM → ds,CategorySection 自剪枝)。
|
||||
drawer->analysisTab()->setStructure(*lastStructNodes);
|
||||
drawer->analysisTab()->setBuckets(geopro::app::splitByCategory(*lastSourceRows));
|
||||
// 三维体段=从项目根的完整层级树:项目根/GS/TM 容器节点 → 体(挂生成位置) → 切片(挂体) → 异常(挂体/切片)。
|
||||
// populateDatasetList 按 parentId 自动建树(spec §7/§8)。
|
||||
// 三维体段=体 + 切片 + 异常;容器层级由 CategorySection 据 structure + 各 ds 的 structParentId 自建。
|
||||
std::vector<geopro::data::DsRow> voxelTree;
|
||||
// 剪枝:只展示有三维体挂载的容器路径(从体的生成位置向上追溯到根),不带出无关 GS/TM。
|
||||
std::map<std::string, const geopro::data::StructNode*> byId;
|
||||
for (const auto& n : *lastStructNodes) byId[n.id] = &n;
|
||||
std::set<std::string> keep;
|
||||
for (const auto& v : vols) {
|
||||
std::string p = v.parentId; // 体的生成位置(structParentId)
|
||||
while (!p.empty() && byId.count(p) && !keep.count(p)) {
|
||||
keep.insert(p);
|
||||
p = byId[p]->parentId; // 向上到祖先(GS/项目根)
|
||||
}
|
||||
}
|
||||
for (const auto& n : *lastStructNodes) { // 仅保留有体路径的容器节点(ddCode="container",无复选框)
|
||||
if (!keep.count(n.id)) continue;
|
||||
geopro::data::DsRow c;
|
||||
c.id = n.id;
|
||||
c.dsName = n.name;
|
||||
c.ddCode = "container";
|
||||
c.parentId = n.parentId;
|
||||
voxelTree.push_back(std::move(c));
|
||||
}
|
||||
for (const auto& v : vols) voxelTree.push_back(v);
|
||||
for (const auto& s : slices) voxelTree.push_back(s);
|
||||
for (const auto& a : anomalies) voxelTree.push_back(a);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,10 @@ public:
|
|||
QSize sizeHint(const QStyleOptionViewItem&, const QModelIndex& idx) const override {
|
||||
const bool special =
|
||||
idx.data(kDsLoadMoreRole).toBool() || !(idx.flags() & Qt::ItemIsSelectable);
|
||||
return QSize(0, special ? 34 : 52);
|
||||
if (special) return QSize(0, 34);
|
||||
// 无副标题(容器节点 项目/GS/TM)用紧凑矮卡,避免标题下大片留白。
|
||||
const bool hasMeta = idx.data(Qt::DisplayRole).toString().contains(QLatin1Char('\n'));
|
||||
return QSize(0, hasMeta ? 52 : 30);
|
||||
}
|
||||
|
||||
void paint(QPainter* p, const QStyleOptionViewItem& opt, const QModelIndex& idx) const override {
|
||||
|
|
@ -81,8 +84,8 @@ public:
|
|||
geopro::app::tokenColor("accent/primary"));
|
||||
}
|
||||
|
||||
// 可勾选项:左侧画复选框(用当前 style 的指示器),文本整体右移。
|
||||
int textLeftPad = 14;
|
||||
// 可勾选项:左侧画复选框(用当前 style 的指示器),文本整体右移。无复选框项(容器节点)左留白小。
|
||||
int textLeftPad = 6;
|
||||
const bool checkable = (idx.flags() & Qt::ItemIsUserCheckable);
|
||||
if (checkable) {
|
||||
const int box = 16;
|
||||
|
|
@ -105,17 +108,19 @@ public:
|
|||
meta = disp.mid(nl + 1);
|
||||
}
|
||||
|
||||
const QRect textR = r.adjusted(textLeftPad, 6, -12, -6);
|
||||
// 标题
|
||||
const QRect textR = r.adjusted(textLeftPad, 4, -12, -4);
|
||||
QFont tf = opt.font;
|
||||
tf.setPixelSize(geopro::app::scaledPx(13));
|
||||
p->setFont(tf);
|
||||
p->setPen(geopro::app::tokenColor("text/primary"));
|
||||
const QRect titleR(textR.left(), textR.top(), textR.width(), textR.height() / 2);
|
||||
p->drawText(titleR, Qt::AlignLeft | Qt::AlignVCenter,
|
||||
p->fontMetrics().elidedText(title, Qt::ElideRight, titleR.width()));
|
||||
// 元信息
|
||||
if (!meta.isEmpty()) {
|
||||
if (meta.isEmpty()) {
|
||||
// 无副标题(容器节点):标题垂直居中整卡,不留下半空白。
|
||||
p->drawText(textR, Qt::AlignLeft | Qt::AlignVCenter,
|
||||
p->fontMetrics().elidedText(title, Qt::ElideRight, textR.width()));
|
||||
} else {
|
||||
const QRect titleR(textR.left(), textR.top(), textR.width(), textR.height() / 2);
|
||||
p->drawText(titleR, Qt::AlignLeft | Qt::AlignVCenter,
|
||||
p->fontMetrics().elidedText(title, Qt::ElideRight, titleR.width()));
|
||||
QFont mf = opt.font;
|
||||
mf.setPixelSize(geopro::app::scaledPx(11));
|
||||
p->setFont(mf);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <QComboBox>
|
||||
#include <QDate>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "panels/columns/DateRangeEdit.hpp"
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
|
@ -61,8 +63,11 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset
|
|||
body->setContentsMargins(space::kMd, space::kSm, space::kMd, space::kMd);
|
||||
body->setSpacing(space::kSm);
|
||||
|
||||
// 筛选行:装置类型(仅 ERT 类)+ 采集时间段(起止;最小日期显示"不限",不再露 1752)。
|
||||
// 筛选行:采集时间范围(在前)+ 装置类型(在后,仅 ERT 类)。
|
||||
auto* filterRow = new QHBoxLayout();
|
||||
dateRange_ = new DateRangeEdit(body_);
|
||||
connect(dateRange_, &DateRangeEdit::rangeChanged, this, [this] { rebuildList(); });
|
||||
filterRow->addWidget(dateRange_, 1);
|
||||
if (spec_.hasArrayTypeFilter) {
|
||||
arrayCombo_ = new QComboBox(body_);
|
||||
arrayCombo_->addItem(QStringLiteral("全部装置"), QString());
|
||||
|
|
@ -70,16 +75,13 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset
|
|||
[this](int) { rebuildList(); });
|
||||
filterRow->addWidget(arrayCombo_);
|
||||
}
|
||||
filterRow->addWidget(new QLabel(QStringLiteral("采集"), body_));
|
||||
dateRange_ = new DateRangeEdit(body_);
|
||||
connect(dateRange_, &DateRangeEdit::rangeChanged, this, [this] { rebuildList(); });
|
||||
filterRow->addWidget(dateRange_, 1);
|
||||
body->addLayout(filterRow);
|
||||
|
||||
// 段体:可勾选数据树(勾选=渲染)。复用数据列表卡片委托与 populateDatasetList。
|
||||
list_ = new QTreeWidget(body_);
|
||||
list_->setHeaderHidden(true);
|
||||
list_->setRootIsDecorated(true);
|
||||
list_->setIndentation(14); // 紧凑父子缩进(默认 20 太宽)
|
||||
applyDatasetCardDelegate(list_);
|
||||
connect(list_, &QTreeWidget::itemChanged, this, [this](QTreeWidgetItem*, int) { emitChecked(); });
|
||||
connect(list_, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem* it, int) {
|
||||
|
|
@ -168,9 +170,39 @@ void CategorySection::rebuildList() {
|
|||
filtered.reserve(rows_.size());
|
||||
for (const auto& r : rows_)
|
||||
if (passesFilters(r)) filtered.push_back(r);
|
||||
|
||||
// 从项目根的层级树:容器节点(结构,剪枝仅留有 ds 的路径)+ ds(挂 parentId 或 structParentId)。
|
||||
std::vector<DsRow> display;
|
||||
if (!structure_.empty()) {
|
||||
std::map<std::string, const geopro::data::StructNode*> byId;
|
||||
for (const auto& n : structure_) byId[n.id] = &n;
|
||||
std::set<std::string> keep; // 收集每个 ds 的结构归属向上的祖先链
|
||||
for (const auto& d : filtered) {
|
||||
std::string p = d.structParentId;
|
||||
while (!p.empty() && byId.count(p) && !keep.count(p)) {
|
||||
keep.insert(p);
|
||||
p = byId[p]->parentId;
|
||||
}
|
||||
}
|
||||
for (const auto& n : structure_)
|
||||
if (keep.count(n.id)) {
|
||||
DsRow c;
|
||||
c.id = n.id;
|
||||
c.dsName = n.name;
|
||||
c.ddCode = "container";
|
||||
c.parentId = n.parentId;
|
||||
display.push_back(std::move(c));
|
||||
}
|
||||
}
|
||||
for (const auto& d : filtered) {
|
||||
DsRow x = d;
|
||||
if (x.parentId.empty()) x.parentId = x.structParentId; // 源 ds / 体 → 挂结构容器
|
||||
display.push_back(std::move(x));
|
||||
}
|
||||
|
||||
{
|
||||
const QSignalBlocker block(list_);
|
||||
populateDatasetList(list_, filtered, /*append=*/false);
|
||||
populateDatasetList(list_, display, /*append=*/false);
|
||||
for (QTreeWidgetItemIterator it(list_); *it; ++it) {
|
||||
// 容器节点(项目根/GS/TM)只作层级骨架——明确去掉复选框、不可勾选。
|
||||
if ((*it)->data(0, kDsDdCodeRole).toString() == QStringLiteral("container")) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "api/Api3dRepository.hpp"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QObject>
|
||||
|
|
@ -103,7 +104,12 @@ bool Api3dRepository::isVolumeDataset(const std::string& dsId) const {
|
|||
|
||||
std::string Api3dRepository::createVolume(VolumeBuildParams params, const std::string& name) {
|
||||
const std::string id = "vol-" + std::to_string(++volumeCounter_);
|
||||
volumes_[id] = StoredVolume{std::move(params), name, std::nullopt};
|
||||
StoredVolume sv;
|
||||
sv.params = std::move(params);
|
||||
sv.name = name;
|
||||
sv.createTime =
|
||||
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd HH:mm")).toStdString();
|
||||
volumes_[id] = std::move(sv);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +135,8 @@ std::vector<DsRow> Api3dRepository::volumeRows() const {
|
|||
r.dsName = sv.name;
|
||||
r.ddCode = "dd_voxel";
|
||||
r.typeName = "三维体";
|
||||
r.parentId = sv.request ? sv.request->structParentId : std::string(); // 挂生成位置(项目根/GS/TM)
|
||||
r.structParentId = sv.request ? sv.request->structParentId : std::string(); // 结构归属(生成位置)
|
||||
r.createTime = sv.createTime;
|
||||
rows.push_back(std::move(r));
|
||||
}
|
||||
return rows;
|
||||
|
|
@ -313,6 +320,7 @@ std::vector<DsRow> Api3dRepository::sliceRows() const {
|
|||
r.ddCode = "dd_slice";
|
||||
r.typeName = "切片";
|
||||
r.parentId = ss.spec.volumeDsId; // 树中挂在所属三维体下
|
||||
r.createTime = ss.createTime;
|
||||
rows.push_back(std::move(r));
|
||||
}
|
||||
return rows;
|
||||
|
|
@ -343,7 +351,9 @@ bool Api3dRepository::sliceSpec(const std::string& dsId, SliceSpec& out) const {
|
|||
void Api3dRepository::createSlice(const SliceSpec& spec, const std::string& name,
|
||||
std::function<void(std::string)> onOk, OnError /*onErr*/) {
|
||||
const std::string id = "slice-" + std::to_string(++sliceCounter_);
|
||||
slices_[id] = StoredSlice{spec, name};
|
||||
slices_[id] = StoredSlice{
|
||||
spec, name,
|
||||
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd HH:mm")).toStdString()};
|
||||
onOk(id);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ private:
|
|||
core::ColorScale cachedScale; // 与 cachedGrid 同时填(源剖面色阶)
|
||||
std::optional<std::size_t> pointCount; // 聚合散点数(finalizeVolume 时持久化,详情统计用)
|
||||
std::optional<VoxelGenerateRequest> request; // 组装的真实请求体(createVolume(req) 路径填充)
|
||||
std::string createTime; // 创建时刻(mock,列表副标题/详情用)
|
||||
};
|
||||
std::map<std::string, StoredVolume> volumes_; // dsId → 体
|
||||
int volumeCounter_ = 0;
|
||||
|
|
@ -137,6 +138,7 @@ private:
|
|||
struct StoredSlice {
|
||||
SliceSpec spec;
|
||||
std::string name;
|
||||
std::string createTime; // 创建时刻
|
||||
};
|
||||
std::map<std::string, StoredSlice> slices_; // dsId → 切片
|
||||
int sliceCounter_ = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue