#include "ImportDatasetDialog.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FormKit.hpp" #include "Theme.hpp" #include "api/NavLoads.hpp" #include "api/NavRequest.hpp" #include "panels/DynamicFormEditor.hpp" #include "repo/IAsyncProjectRepository.hpp" namespace geopro::app { namespace { constexpr int kFormTypeScript = 6; // getDynamicForm type=6:导入脚本参数 constexpr int kStructConfTm = 2; // structParentConfType:TM=2 } // namespace ImportDatasetDialog::ImportDatasetDialog(geopro::data::IAsyncProjectRepository& repo, QString projectId, QString tmObjectId, QWidget* parent) : QDialog(parent), repo_(repo), projectId_(std::move(projectId)), tmObjectId_(std::move(tmObjectId)) { setModal(true); setWindowTitle(QStringLiteral("导入数据集")); setMinimumSize(geopro::app::scaledPx(480), geopro::app::scaledPx(520)); auto* root = formkit::dialogRoot(this); status_ = new QLabel(QStringLiteral("加载数据类型…"), this); status_->setAlignment(Qt::AlignCenter); geopro::app::applyTokenizedStyleSheet(status_, QStringLiteral("color:{{text/disabled}};padding:12px;")); root->addWidget(status_); auto* card = formkit::formCard(this); auto* cardLay = formkit::cardBody(card); auto* fl = formkit::makeEditForm(); // 空态感知下拉:数据类型/导入脚本均数据驱动(异步加载),未选显占位、无数据弹「暂无数据」。 typeCombo_ = formkit::comboBox(QStringLiteral("请选择数据类型"), card); formkit::capField(typeCombo_); fl->addRow(formkit::editLabel(QStringLiteral("数据类型")), typeCombo_); scriptCombo_ = formkit::comboBox(QStringLiteral("请选择导入脚本"), card); formkit::capField(scriptCombo_); fl->addRow(formkit::editLabel(QStringLiteral("导入脚本")), scriptCombo_); auto* fileRow = new QWidget(card); auto* fileLay = new QHBoxLayout(fileRow); fileLay->setContentsMargins(0, 0, 0, 0); fileEdit_ = new QLineEdit(fileRow); fileEdit_->setReadOnly(true); fileEdit_->setPlaceholderText(QStringLiteral("选择导入文件…")); auto* browse = new QPushButton(QStringLiteral("浏览…"), fileRow); fileLay->addWidget(fileEdit_, 1); fileLay->addWidget(browse); fl->addRow(formkit::editLabel(QStringLiteral("文件")), fileRow); cardLay->addLayout(fl); formkit::addSection(cardLay, QStringLiteral("脚本参数"), card, true); auto* scroll = new QScrollArea(card); scroll->setWidgetResizable(true); scroll->setFrameShape(QFrame::NoFrame); paramEditor_ = new DynamicFormEditor(); scroll->setWidget(paramEditor_); cardLay->addWidget(scroll, 1); root->addWidget(card, 1); auto* buttons = formkit::addDialogButtons(root, this, QStringLiteral("导入"), QStringLiteral("取消")); // Ok 不直接 accept:需先 onConfirm 校验/异步导入,成功后才 accept。断开默认 accepted→accept。 QObject::disconnect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); okBtn_ = buttons->button(QDialogButtonBox::Ok); okBtn_->setDefault(true); okBtn_->setEnabled(false); QObject::connect(browse, &QPushButton::clicked, this, &ImportDatasetDialog::chooseFile); QObject::connect(okBtn_, &QPushButton::clicked, this, &ImportDatasetDialog::onConfirm); QObject::connect(typeCombo_, qOverload(&QComboBox::currentIndexChanged), this, [this](int) { onTypeChanged(); }); QObject::connect(scriptCombo_, qOverload(&QComboBox::currentIndexChanged), this, [this](int) { onScriptChanged(); }); loadTypes(); } ImportDatasetDialog::~ImportDatasetDialog() { // 析构时取消在途请求,避免回调命中已销毁的 this。 if (typeReq_) typeReq_->abort(); if (scriptReq_) scriptReq_->abort(); if (formReq_) formReq_->abort(); if (checkReq_) checkReq_->abort(); if (importReq_) importReq_->abort(); } void ImportDatasetDialog::loadTypes() { if (typeReq_) typeReq_->abort(); typeReq_ = repo_.loadTmImportTypesAsync(tmObjectId_.toStdString()); QObject::connect(typeReq_, &geopro::data::NavRequest::done, this, [this](const QVariant& v) { types_ = qvariant_cast>(v); if (types_.empty()) { status_->setText(QStringLiteral("该方法对象下无可导入的数据类型")); return; } status_->setVisible(false); for (const auto& t : types_) typeCombo_->addItem(QString::fromStdString(t.name), QString::fromStdString(t.dsTypeId)); }); QObject::connect(typeReq_, &geopro::data::NavRequest::failed, this, [this](const QString& msg) { status_->setText(QStringLiteral("加载数据类型失败:%1").arg(msg)); }); } void ImportDatasetDialog::onTypeChanged() { scriptCombo_->clear(); scripts_.clear(); const int idx = typeCombo_->currentIndex(); if (idx < 0 || idx >= static_cast(types_.size())) return; const QString dsTypeId = typeCombo_->currentData().toString(); if (scriptReq_) scriptReq_->abort(); // tmTypeBaseConfId:暂用 TM 对象 id 占位(query/script 需方法基础配置 id)。TODO:来源待精确。 scriptReq_ = repo_.queryImportScriptsAsync(dsTypeId.toStdString(), tmObjectId_.toStdString()); QObject::connect(scriptReq_, &geopro::data::NavRequest::done, this, [this](const QVariant& v) { scripts_ = qvariant_cast>(v); for (const auto& s : scripts_) { // itemData 同时携带 scriptId/scriptCode,取值不再依赖 scripts_ 同序索引。 QVariantMap d{{QStringLiteral("scriptId"), QString::fromStdString(s.scriptId)}, {QStringLiteral("scriptCode"), QString::fromStdString(s.scriptCode)}}; scriptCombo_->addItem(QString::fromStdString(s.name), d); } if (scripts_.empty()) { paramEditor_->setForm({}); // 无脚本:清空参数区 okBtn_->setEnabled(!filePath_.isEmpty()); } }); QObject::connect(scriptReq_, &geopro::data::NavRequest::failed, this, [this](const QString&) { // 脚本可选:失败不阻塞导入(部分类型无脚本)。 okBtn_->setEnabled(!filePath_.isEmpty()); }); } void ImportDatasetDialog::onScriptChanged() { const int idx = scriptCombo_->currentIndex(); if (idx < 0) return; const QString scriptId = scriptCombo_->currentData().toMap() .value(QStringLiteral("scriptId")).toString(); if (scriptId.isEmpty()) return; if (formReq_) formReq_->abort(); // 脚本参数表单:getDynamicForm(typeId=scriptId, type=6)。 formReq_ = repo_.loadEditableFormAsync(scriptId.toStdString(), std::string(), kFormTypeScript, projectId_.toStdString()); QObject::connect(formReq_, &geopro::data::NavRequest::done, this, [this](const QVariant& v) { paramEditor_->setForm(qvariant_cast(v)); }); QObject::connect(formReq_, &geopro::data::NavRequest::failed, this, [this](const QString& msg) { // 参数表单加载失败:清空参数区,避免脏参数随导入提交。 paramEditor_->setForm({}); status_->setText(QStringLiteral("加载脚本参数失败:%1").arg(msg)); }); } void ImportDatasetDialog::chooseFile() { const QString p = QFileDialog::getOpenFileName(this, QStringLiteral("选择导入文件")); if (p.isEmpty()) return; filePath_ = p; fileEdit_->setText(p); okBtn_->setEnabled(true); } void ImportDatasetDialog::onConfirm() { if (filePath_.isEmpty()) { QMessageBox::warning(this, QStringLiteral("校验"), QStringLiteral("请选择导入文件")); return; } QString missing; if (!paramEditor_->validateRequired(&missing)) { QMessageBox::warning(this, QStringLiteral("校验"), QStringLiteral("请填写脚本参数:%1").arg(missing)); return; } const QString dsTypeId = typeCombo_->currentData().toString(); const QString scriptCode = scriptCombo_->currentData().toMap() .value(QStringLiteral("scriptCode")).toString(); // 脚本参数 → scriptParamListJsonStr({fieldCode: 值} 序列化)。 // TODO:原版 scriptParamListJsonStr 确切结构未捕获,此处用 properties 形态,失败时按服务端 msg 校正。 QJsonObject paramObj; const auto params = paramEditor_->collectValues(); for (auto it = params.constBegin(); it != params.constEnd(); ++it) paramObj.insert(it.key(), it.value()); const QString scriptParamJson = QString::fromUtf8(QJsonDocument(paramObj).toJson(QJsonDocument::Compact)); // import 参数走 query string(spec §B)。 QUrlQuery q; q.addQueryItem(QStringLiteral("dsTypeId"), dsTypeId); q.addQueryItem(QStringLiteral("projectId"), projectId_); q.addQueryItem(QStringLiteral("structParentConfType"), QString::number(kStructConfTm)); q.addQueryItem(QStringLiteral("structParentId"), tmObjectId_); if (!scriptCode.isEmpty()) q.addQueryItem(QStringLiteral("scriptCode"), scriptCode); if (!params.isEmpty()) q.addQueryItem(QStringLiteral("scriptParamListJsonStr"), scriptParamJson); const QString queryString = q.query(QUrl::FullyEncoded); // 先 checkImport 校验(坐标/轨迹文件存在性等),通过后 import。 QJsonObject checkBody{{QStringLiteral("dsTypeId"), dsTypeId}, {QStringLiteral("projectId"), projectId_}, {QStringLiteral("structParentConfType"), kStructConfTm}, {QStringLiteral("structParentId"), tmObjectId_}}; okBtn_->setEnabled(false); status_->setText(QStringLiteral("校验中…")); status_->setVisible(true); if (checkReq_) checkReq_->abort(); checkReq_ = repo_.checkImportAsync( QJsonDocument(checkBody).toJson(QJsonDocument::Compact).toStdString()); QObject::connect(checkReq_, &geopro::data::NavRequest::done, this, [this, queryString](const QVariant&) { status_->setText(QStringLiteral("导入中…")); if (importReq_) importReq_->abort(); importReq_ = repo_.importDatasetAsync(queryString.toStdString(), filePath_.toStdString()); QObject::connect(importReq_, &geopro::data::NavRequest::done, this, [this](const QVariant&) { emit imported(); accept(); }); QObject::connect(importReq_, &geopro::data::NavRequest::failed, this, [this](const QString& msg) { status_->setText(QStringLiteral("导入失败:%1").arg(msg)); okBtn_->setEnabled(true); }); }); QObject::connect(checkReq_, &geopro::data::NavRequest::failed, this, [this](const QString& msg) { status_->setText(QStringLiteral("校验失败:%1").arg(msg)); okBtn_->setEnabled(true); }); } } // namespace geopro::app