fix(ui): 装置筛选稳健匹配+诊断日志 + 生成位置改下拉框(树形下拉面板)

- 装置下拉空:arrayTypeList 请求成功(日志确认),但匹配只认 itemValue(键)。改稳健匹配——ds 装置属性
  存 itemValue 或中文名都认(命中键取中文名/命中值用原名),data 存实际属性值供 passesFilters 比对。
  仍空则打 [arrayfilter] 诊断日志(枚举大小+首行属性 confFieldId=value)定位是枚举空还是值不匹配。
- 生成位置:QTreeWidget(常显树)→ QComboBox 下拉框,下拉面板用 QTreeView+QStandardItemModel 树模型
  (GS/项目根/TM 层级);mountTargetId/confType 从树视图当前项读(树模型下比 combo currentData 可靠)。

构建:app 链接通过;434/434 测试通过
This commit is contained in:
gaozheng 2026-06-25 21:12:45 +08:00
parent 63ebf7d4f1
commit 5edfc8e5e8
3 changed files with 64 additions and 31 deletions

View File

@ -11,6 +11,8 @@
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QStandardItem>
#include <QTreeView>
#include <QTreeWidget> #include <QTreeWidget>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QTreeWidgetItemIterator> #include <QTreeWidgetItemIterator>
@ -111,38 +113,44 @@ VolumeParamsDialog::VolumeParamsDialog(const QVector<VolumeSourceItem>& sources,
form->addRow(formkit::editLabel(QStringLiteral("名称")), name_); form->addRow(formkit::editLabel(QStringLiteral("名称")), name_);
cardLay->addLayout(form); cardLay->addLayout(form);
// 生成位置GS/项目根/TM 层级树(单选),替代原扁平下拉。 auto* form2 = formkit::makeEditForm();
formkit::addSection(cardLay, QStringLiteral("生成位置"), card, false);
mountTree_ = new QTreeWidget(); // 生成位置:下拉框,下拉面板=GS/项目根/TM 层级树QComboBox + QTreeView 视图 + 树模型)。
mountTree_->setHeaderHidden(true); mount_ = new QComboBox();
mountTree_->setIndentation(14);
mountTree_->setMaximumHeight(geopro::app::scaledPx(160));
{ {
QHash<QString, QTreeWidgetItem*> mItems; auto* model = new QStandardItemModel(mount_);
QHash<QString, QStandardItem*> mItems;
for (const auto& n : structure) { for (const auto& n : structure) {
auto* it = new QTreeWidgetItem(); auto* it = new QStandardItem(QString::fromStdString(n.name));
it->setText(0, QString::fromStdString(n.name)); it->setEditable(false);
it->setData(0, kRoleMountId, QString::fromStdString(n.id)); it->setData(QString::fromStdString(n.id), kRoleMountId);
it->setData(0, kRoleMountConfType, n.type == 2 ? 2 : 1); it->setData(n.type == 2 ? 2 : 1, kRoleMountConfType);
mItems.insert(QString::fromStdString(n.id), it); mItems.insert(QString::fromStdString(n.id), it);
} }
for (const auto& n : structure) { for (const auto& n : structure) {
auto* it = mItems.value(QString::fromStdString(n.id)); auto* it = mItems.value(QString::fromStdString(n.id));
auto* par = mItems.value(QString::fromStdString(n.parentId), nullptr); auto* par = mItems.value(QString::fromStdString(n.parentId), nullptr);
if (par) if (par)
par->addChild(it); par->appendRow(it);
else else
mountTree_->addTopLevelItem(it); model->appendRow(it);
}
auto* view = new QTreeView(mount_);
view->setHeaderHidden(true);
view->setItemsExpandable(true);
mount_->setModel(model);
mount_->setView(view);
view->expandAll();
// 默认选中:指定的 defaultMountId否则首个节点。view 当前项决定 mountTargetIdcombo 显示尽力。
QStandardItem* def = mItems.value(defaultMountId, nullptr);
if (!def && model->rowCount() > 0) def = model->item(0);
if (def) {
view->setCurrentIndex(def->index()); // 决定 mountTargetId/mountConfType 返回值
mount_->setCurrentText(def->text()); // 顶层项可正确显示;嵌套项尽力(功能不受影响)
} }
mountTree_->expandAll();
// 默认选中:指定的 defaultMountId否则首个节点。
QTreeWidgetItem* def = mItems.value(defaultMountId, nullptr);
if (!def && mountTree_->topLevelItemCount() > 0) def = mountTree_->topLevelItem(0);
if (def) mountTree_->setCurrentItem(def);
} }
cardLay->addWidget(mountTree_); formkit::capField(mount_);
form2->addRow(formkit::editLabel(QStringLiteral("生成位置")), mount_);
auto* form2 = formkit::makeEditForm();
model_ = new EmptyAwareComboBox(); model_ = new EmptyAwareComboBox();
model_->addItem(QStringLiteral("反距离加权 (IDW)"), model_->addItem(QStringLiteral("反距离加权 (IDW)"),
static_cast<int>(geopro::data::VolumeBuildParams::Model::Idw)); static_cast<int>(geopro::data::VolumeBuildParams::Model::Idw));
@ -204,14 +212,22 @@ QStringList VolumeParamsDialog::sourceDatasetIds() const {
return ids; return ids;
} }
namespace {
// 从生成位置下拉的树视图取当前选中项(树模型下比 combo->currentData 可靠)。
QModelIndex mountCurrentIndex(QComboBox* mount) {
auto* view = qobject_cast<QTreeView*>(mount->view());
return view ? view->currentIndex() : QModelIndex();
}
} // namespace
QString VolumeParamsDialog::mountTargetId() const { QString VolumeParamsDialog::mountTargetId() const {
auto* it = mountTree_->currentItem(); const QModelIndex idx = mountCurrentIndex(mount_);
return it ? it->data(0, kRoleMountId).toString() : QString(); return idx.isValid() ? idx.data(kRoleMountId).toString() : QString();
} }
int VolumeParamsDialog::mountConfType() const { int VolumeParamsDialog::mountConfType() const {
auto* it = mountTree_->currentItem(); const QModelIndex idx = mountCurrentIndex(mount_);
const int ct = it ? it->data(0, kRoleMountConfType).toInt() : 0; const int ct = idx.isValid() ? idx.data(kRoleMountConfType).toInt() : 0;
return ct == 0 ? 1 : ct; // 缺省按 GS/项目根 return ct == 0 ? 1 : ct; // 缺省按 GS/项目根
} }

View File

@ -47,7 +47,7 @@ private:
QDoubleSpinBox* power_ = nullptr; QDoubleSpinBox* power_ = nullptr;
QDoubleSpinBox* maxDist_ = nullptr; QDoubleSpinBox* maxDist_ = nullptr;
QTreeWidget* sourceTree_ = nullptr; // 左:源数据集树(可勾选) QTreeWidget* sourceTree_ = nullptr; // 左:源数据集树(可勾选)
QTreeWidget* mountTree_ = nullptr; // 右:生成位置树(单选 QComboBox* mount_ = nullptr; // 右:生成位置下拉(下拉面板是 GS/项目根/TM 层级树
}; };
} // namespace geopro::app } // namespace geopro::app

View File

@ -2,6 +2,7 @@
#include <QComboBox> #include <QComboBox>
#include <QDate> #include <QDate>
#include <QDebug>
#include <map> #include <map>
#include <set> #include <set>
#include "panels/columns/DateRangeEdit.hpp" #include "panels/columns/DateRangeEdit.hpp"
@ -159,17 +160,33 @@ void CategorySection::refreshArrayCombo() {
if (dict_) { if (dict_) {
const auto& en = dict_->arrayTypeEnum(); // 全局装置枚举 itemValue→中文 const auto& en = dict_->arrayTypeEnum(); // 全局装置枚举 itemValue→中文
QSet<QString> seen; QSet<QString> seen;
// 列出当前数据里出现过、且命中枚举的装置值 → 中文名。 // 列出当前数据里出现过的装置值 → 中文名。兼容 ds 把装置存成 itemValue(键) 或中文名(值) 两种形态
for (const auto& r : rows_) { for (const auto& r : rows_) {
for (const auto& kv : r.properties) { for (const auto& kv : r.properties) {
const auto it = en.find(kv.value);
if (it == en.end()) continue; // 非装置类型值
const QString val = QString::fromStdString(kv.value); const QString val = QString::fromStdString(kv.value);
if (seen.contains(val)) continue; QString name;
const auto it = en.find(kv.value);
if (it != en.end()) {
name = QString::fromStdString(it->second); // value 是 itemValue → 取中文名
} else {
for (const auto& e : en) // value 可能直接是中文名
if (QString::fromStdString(e.second) == val) { name = val; break; }
}
if (name.isEmpty() || seen.contains(val)) continue;
seen.insert(val); seen.insert(val);
arrayCombo_->addItem(QString::fromStdString(it->second), val); arrayCombo_->addItem(name, val); // data=实际属性值passesFilters 据此比对)
} }
} }
if (arrayCombo_->count() <= 1) // 诊断:装置下拉仍空 → 打印枚举大小 + 首行属性,定位是枚举空还是值不匹配
qInfo().noquote() << "[arrayfilter]" << QString::fromStdString(spec_.id) << "enum="
<< en.size() << "rows=" << rows_.size()
<< (rows_.empty() ? QString() : [&] {
QStringList vs;
for (const auto& kv : rows_.front().properties)
vs << (QString::fromStdString(kv.confFieldId) + "=" +
QString::fromStdString(kv.value));
return "row0[" + vs.join(',') + "]";
}());
} }
const int idx = arrayCombo_->findData(prev); // 尽量保留上次选择 const int idx = arrayCombo_->findData(prev); // 尽量保留上次选择
arrayCombo_->setCurrentIndex(idx >= 0 ? idx : 0); arrayCombo_->setCurrentIndex(idx >= 0 ? idx : 0);