fix(data): 装置筛选空-parseDsRows 兼容对象形态 properties+顶层装置字段 + CLAUDE规则

诊断(自读日志[arrayfilter]):enum=15(枚举正常)但 row0[](行 properties 空)→装置值没进 row.properties。
DsObjectDataVO.properties 是泛型 JSON(可能对象而非数组),旧 parseDsRows 只 toArray()→对象形态丢空。
修:properties 数组/对象两种形态都解析;顶层 arrayType/arrayTypeName 也收为属性兜底。
另加 [dsrow-raw] 一次性打印首行原始 JSON,若仍空可据此精准定位装置字段实际位置。

生成位置下拉:combo 自身按内容加宽(对话框随之变宽)→ popup 宽=combo 宽,不再外溢出对话框。

CLAUDE.md:新增「能自己做的绝不让用户做」绑定规则——日志/数据/构建/诊断都自己来,只在 LNK1104
需关 app、或真正产品决策时才找用户。
This commit is contained in:
gaozheng 2026-06-25 21:37:42 +08:00
parent 98bbc9f947
commit 1742b7508b
3 changed files with 48 additions and 11 deletions

View File

@ -49,6 +49,15 @@ as a reason to leave it. Surface it, then handle it. (User directive, 2026-06-25
This overrides the "don't fix what isn't broken" bias above *for genuine defects* — it does
not license cosmetic refactors or unrequested rewrites.
**Do it yourself — never offload work you can do (User directive, 2026-06-25, binding):**
If you have the tools to do something, DO IT — never tell the user to do it for you.
Read the logs yourself (`%LOCALAPPDATA%/Geomative/Geopro3/logs/geopro_*.log` via Bash/grep),
inspect data/fixtures yourself, build and link yourself (`build.bat app` via PowerShell),
diagnose by adding logging and then reading that log yourself. The ONLY things to ask the
user for are: (a) closing a running app so the exe can relink (LNK1104 — a lock only they can
release), and (b) genuine product decisions. Do not ask the user to read logs, inspect data,
run diagnostics, or interpret output — that is your job.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**

View File

@ -143,13 +143,16 @@ VolumeParamsDialog::VolumeParamsDialog(const QVector<VolumeSourceItem>& sources,
mount_->setModel(model);
mount_->setView(view);
view->expandAll();
// 下拉面板加宽:默认只取 combo 宽度+缩进 → 长名被截。按最长节点名的文本宽 + 缩进/箭头/滚动条余量
// 定最小宽,保证 GS/TM 全名可见(用户反馈被遮住)。
// 关键:让 combo **自身**按内容加宽(对话框随布局一起变宽)→ popup 宽 = combo 宽,落在对话框内
// 不外溢;只加宽 popup 而不加宽 combo 会让浮窗比对话框还宽、超出右缘(用户反馈)。
// 宽度 = 最长节点名文本宽 + 余量(树缩进/展开箭头/滚动条/combo 下拉箭头/内距)。
int maxTextW = 0;
const QFontMetrics fm = view->fontMetrics();
for (const auto& n : structure)
maxTextW = std::max(maxTextW, fm.horizontalAdvance(QString::fromStdString(n.name)));
view->setMinimumWidth(std::max(geopro::app::scaledPx(260), maxTextW + geopro::app::scaledPx(120)));
const int contentW = std::max(geopro::app::scaledPx(200), maxTextW + geopro::app::scaledPx(80));
mount_->setMinimumWidth(contentW); // combo 加宽 → 对话框变宽
view->setMinimumWidth(contentW); // popup 与 combo 同宽 → 不外溢
// 默认选中:指定的 defaultMountId否则首个节点。view 当前项决定 mountTargetIdcombo 显示尽力。
QStandardItem* def = mItems.value(defaultMountId, nullptr);
if (!def && model->rowCount() > 0) def = model->item(0);

View File

@ -1,5 +1,7 @@
#include "dto/NavDto.hpp"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonValue>
#include <QString>
#include <QStringList>
@ -116,6 +118,13 @@ std::vector<StructNode> parseStructNodes(const QJsonArray& arr) {
std::vector<DsRow> parseDsRows(const QJsonArray& arr) {
std::vector<DsRow> out;
out.reserve(static_cast<size_t>(arr.size()));
// 诊断(一次):打印首行原始 JSON定位装置/采集时间等字段实际所在properties 形态/attachedParameters
static bool s_loggedRaw = false;
if (!s_loggedRaw && !arr.isEmpty()) {
s_loggedRaw = true;
qInfo().noquote() << "[dsrow-raw]"
<< QJsonDocument(arr.first().toObject()).toJson(QJsonDocument::Compact);
}
for (const QJsonValue& v : arr) {
const QJsonObject o = v.toObject();
DsRow d;
@ -130,15 +139,31 @@ std::vector<DsRow> parseDsRows(const QJsonArray& arr) {
d.dsTypeCode = str(o, "dsTypeCode");
d.structParentId = str(o, "structParentId");
d.structParentConfType = o.value(QStringLiteral("structParentConfType")).toInt();
// properties[] = [{confFieldId,value}]value 用 toVariant().toString() 兼容字符串/数值/时间,
// 与 parseDynamicForm 同口径);非数组形态(文件型 ds安全返回空。
const QJsonArray props = o.value(QStringLiteral("properties")).toArray();
d.properties.reserve(static_cast<size_t>(props.size()));
for (const QJsonValue& pv : props) {
const QJsonObject po = pv.toObject();
d.properties.push_back(
{str(po, "confFieldId"), po.value(QStringLiteral("value")).toVariant().toString().toStdString()});
// properties 后端是泛型 JSON可能是数组 [{confFieldId,value}] 或对象 {confFieldId:value}。两种都解析
// (历史只 toArray()→对象形态时丢空,导致装置/采集时间取不到,装置下拉空,用户实测)。
const QJsonValue propsV = o.value(QStringLiteral("properties"));
if (propsV.isArray()) {
const QJsonArray props = propsV.toArray();
d.properties.reserve(static_cast<size_t>(props.size()));
for (const QJsonValue& pv : props) {
const QJsonObject po = pv.toObject();
d.properties.push_back({str(po, "confFieldId"),
po.value(QStringLiteral("value")).toVariant().toString().toStdString()});
}
} else if (propsV.isObject()) {
const QJsonObject po = propsV.toObject();
for (auto it = po.begin(); it != po.end(); ++it)
d.properties.push_back(
{it.key().toStdString(), it.value().toVariant().toString().toStdString()});
}
// 装置类型也可能是顶层字段arrayType 代码 / arrayTypeName 中文名)→ 同时收为属性,兜底供筛选匹配。
if (o.contains(QStringLiteral("arrayType")))
d.properties.push_back(
{"arrayType", o.value(QStringLiteral("arrayType")).toVariant().toString().toStdString()});
if (o.contains(QStringLiteral("arrayTypeName")))
d.properties.push_back(
{"arrayTypeName",
o.value(QStringLiteral("arrayTypeName")).toVariant().toString().toStdString()});
const QJsonObject f = o.value(QStringLiteral("file")).toObject();
d.fileName = str(f, "name");
d.fileUrl = str(f, "url");