diff --git a/src/app/ColorScaleConfigDialog.cpp b/src/app/ColorScaleConfigDialog.cpp index 0b29a3c..8954879 100644 --- a/src/app/ColorScaleConfigDialog.cpp +++ b/src/app/ColorScaleConfigDialog.cpp @@ -97,14 +97,16 @@ ColorScaleConfigDialog::ColorScaleConfigDialog(const geopro::core::ColorScale& i double vmax, std::vector samples, const ContourLineConfig& lineInit, geopro::data::IColorTemplateRepository* tplRepo, - QString projectId, QWidget* parent) + QString projectId, QString lvlTemplateId, + QWidget* parent) : QDialog(parent), vmin_(vmin), vmax_(vmax), samples_(std::move(samples)), lineCfg_(lineInit), tplRepo_(tplRepo), - projectId_(std::move(projectId)) { + projectId_(std::move(projectId)), + lvlTemplateId_(std::move(lvlTemplateId)) { setWindowTitle(QStringLiteral("色阶配置")); setModal(true); resize(560, 420); @@ -433,11 +435,31 @@ void ColorScaleConfigDialog::loadColorBar( void ColorScaleConfigDialog::onSaveOther() { if (tplRepo_ == nullptr || projectId_.isEmpty()) return; - bool ok = false; - const QString name = QInputDialog::getText(this, QStringLiteral("另存模板配置"), - QStringLiteral("模板名称:"), QLineEdit::Normal, - QStringLiteral("等值线配置.lvl"), &ok); - if (!ok || name.trimmed().isEmpty()) return; + + // 自定义另存为弹窗(复刻 handleSaveOther):名称输入 + 覆盖复选框。 + // 「覆盖」仅当有来源模板 id(lvlTemplateId_ 非空)时可勾选,对照原版 props.data.lvlTemplateId。 + QDialog askDlg(this); + askDlg.setWindowTitle(QStringLiteral("另存模板配置")); + askDlg.setModal(true); + auto* askRoot = new QVBoxLayout(&askDlg); + auto* nameRow = new QHBoxLayout(); + nameRow->addWidget(new QLabel(QStringLiteral("模板名称:"), &askDlg)); + auto* nameEdit = new QLineEdit(QStringLiteral("等值线配置.lvl"), &askDlg); + nameRow->addWidget(nameEdit, 1); + askRoot->addLayout(nameRow); + auto* overwriteCheck = new QCheckBox(QStringLiteral("覆盖原模板"), &askDlg); + overwriteCheck->setEnabled(!lvlTemplateId_.isEmpty()); // 无来源模板 → 禁用覆盖 + askRoot->addWidget(overwriteCheck); + auto* askBtns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &askDlg); + askBtns->button(QDialogButtonBox::Ok)->setText(QStringLiteral("应用")); + askBtns->button(QDialogButtonBox::Cancel)->setText(QStringLiteral("取消")); + connect(askBtns, &QDialogButtonBox::accepted, &askDlg, &QDialog::accept); + connect(askBtns, &QDialogButtonBox::rejected, &askDlg, &QDialog::reject); + askRoot->addWidget(askBtns); + if (askDlg.exec() != QDialog::Accepted) return; + const QString name = nameEdit->text().trimmed(); + if (name.isEmpty()) return; + const bool overwrite = overwriteCheck->isChecked() && !lvlTemplateId_.isEmpty(); // 组装 properties(复刻 handleSaveOther)。 QJsonArray colorBar; @@ -457,18 +479,22 @@ void ColorScaleConfigDialog::onSaveOther() { {QStringLiteral("colorBar"), colorBar}}; // 走仓储传输;回调里用 QPointer 守卫 this(模态对话框可能已关)。 + // 勾选覆盖 → PUT 更新来源模板(updateLvlTemplate);否则 → POST 新建(saveLvlTemplate)。 QPointer self(this); - tplRepo_->saveLvlTemplate(projectId_, name.trimmed(), properties, - [self](bool ok, QString msg) { - if (!self) return; - if (ok) - QMessageBox::information(self, QStringLiteral("另存"), - QStringLiteral("另存成功。")); - else - QMessageBox::warning( - self, QStringLiteral("另存"), - QStringLiteral("另存失败:%1").arg(msg)); - }); + auto onDone = [self, overwrite](bool ok, QString msg) { + if (!self) return; + if (ok) + QMessageBox::information( + self, QStringLiteral("另存"), + overwrite ? QStringLiteral("更新成功。") : QStringLiteral("另存成功。")); + else + QMessageBox::warning(self, QStringLiteral("另存"), + QStringLiteral("另存失败:%1").arg(msg)); + }; + if (overwrite) + tplRepo_->updateLvlTemplate(lvlTemplateId_, name, properties, std::move(onDone)); + else + tplRepo_->saveLvlTemplate(projectId_, name, properties, std::move(onDone)); } void ColorScaleConfigDialog::onOpen() { diff --git a/src/app/ColorScaleConfigDialog.hpp b/src/app/ColorScaleConfigDialog.hpp index d170fe6..f8b6aba 100644 --- a/src/app/ColorScaleConfigDialog.hpp +++ b/src/app/ColorScaleConfigDialog.hpp @@ -29,12 +29,15 @@ public: // init:当前色阶(升序断点填表);vmin/vmax:数据原始范围(层级/颜色子对话框 + 新增外推用); // samples:数据原始标量(等积分层 + 颜色编辑器直方图用,空则等积退化为线性); // lineInit:线形/标注初值(2D 传当前态,3D 用默认); - // tplRepo/projectId:lvl 模板库仓储句柄(可空 → 另存为/打开 禁用)。 + // tplRepo/projectId:lvl 模板库仓储句柄(可空 → 另存为/打开 禁用); + // lvlTemplateId:当前色阶来源模板 id(可空,对照原版 props.data.lvlTemplateId)。 + // 非空时「另存为」弹窗的「覆盖」复选框可勾选 → 走 PUT 更新该模板;3D/无模板场景不传即可。 ColorScaleConfigDialog(const geopro::core::ColorScale& init, double vmin, double vmax, std::vector samples = {}, const ContourLineConfig& lineInit = {}, geopro::data::IColorTemplateRepository* tplRepo = nullptr, - QString projectId = {}, QWidget* parent = nullptr); + QString projectId = {}, QString lvlTemplateId = {}, + QWidget* parent = nullptr); // 由表格当前断点装配的新色阶(按层级升序 addStop)。 geopro::core::ColorScale colorScale() const; @@ -74,6 +77,7 @@ private: geopro::data::IColorTemplateRepository* tplRepo_ = nullptr; // lvl 模板库仓储(可空) QString projectId_; + QString lvlTemplateId_; // 当前色阶来源模板 id(可空 → 另存为弹窗禁用「覆盖」) // 随子对话框更新、写入另存为 properties(复刻原版透传字段)。 QString lvlSchemeType_ = QStringLiteral("normal"); int logLinesCount_ = 8; diff --git a/src/app/main.cpp b/src/app/main.cpp index a7b0c30..9d7c8c2 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -807,10 +807,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re } // 3D 无等值线,线形/标注配置忽略(用默认);仅取色阶应用。 // 「另存为/打开」与「新建色阶/配色方案」走色阶模板仓储,projectId 取当前项目。 + // 3D 体无来源 lvl 模板 → lvlTemplateId 传空(覆盖复选框禁用,行为不变)。 geopro::app::ColorScaleConfigDialog dlg( sceneView->currentColorScale(), sceneView->currentVmin(), sceneView->currentVmax(), std::move(samples), {}, &colorTplRepo, - nav.currentProjectId(), &window); + nav.currentProjectId(), QString(), &window); if (dlg.exec() == QDialog::Accepted) sceneCtrl->setVolumeColorScale(dsId, dlg.colorScale()); }); diff --git a/src/app/panels/chart/DetailViewFactory.cpp b/src/app/panels/chart/DetailViewFactory.cpp index f110754..bf1d3df 100644 --- a/src/app/panels/chart/DetailViewFactory.cpp +++ b/src/app/panels/chart/DetailViewFactory.cpp @@ -23,6 +23,8 @@ std::unique_ptr makeDetailView(controller::ViewKind kind, QWidget* auto* raw = new RawDataChartView(parent); // 注入反演命令仓储 + dsId/projectId 取值回调(measurement 反演运算/生成视电阻率)。 raw->setCommandRepo(cmdRepo, dsIdGetter, projectIdGetter); + // 注入色阶模板仓储(散点「色阶配置」编辑器另存为/打开/覆盖用;projectId 复用上面的 getter)。 + raw->setColorTemplateRepo(colorTplRepo); return std::unique_ptr(raw); } case controller::ViewKind::FilledContour: { diff --git a/src/app/panels/chart/GridDataChartView.cpp b/src/app/panels/chart/GridDataChartView.cpp index eb226a1..25bd664 100644 --- a/src/app/panels/chart/GridDataChartView.cpp +++ b/src/app/panels/chart/GridDataChartView.cpp @@ -318,8 +318,9 @@ void GridDataChartView::openColorScaleEditor() { // projectId 在打开时取一次(随项目切换生效);无 getter 退化为空 → 后端按钮禁用。 const QString projectId = projectIdGetter_ ? projectIdGetter_() : QString(); + // 网格剖面载荷(ContourPayload)无 templateId 字段 → lvlTemplateId 传空(覆盖复选框禁用)。 ColorScaleConfigDialog dlg(gridScale_, grid_.vmin, grid_.vmax, std::move(samples), lineCfg_, - tplRepo_, projectId, this); + tplRepo_, projectId, QString(), this); if (dlg.exec() != QDialog::Accepted) return; gridScale_ = dlg.colorScale(); diff --git a/src/app/panels/chart/RawDataChartView.cpp b/src/app/panels/chart/RawDataChartView.cpp index 76fade6..62f092f 100644 --- a/src/app/panels/chart/RawDataChartView.cpp +++ b/src/app/panels/chart/RawDataChartView.cpp @@ -352,6 +352,10 @@ void RawDataChartView::setCommandRepo(geopro::data::IDatasetCommandRepository* r projectIdGetter_ = std::move(projectIdGetter); } +void RawDataChartView::setColorTemplateRepo(geopro::data::IColorTemplateRepository* repo) { + colorTplRepo_ = repo; +} + void RawDataChartView::openInversionDialog(bool apparentResistivity, QWidget* anchor) { // 无仓储/无 dsId 取值回调 → 退化占位(与未注入时一致)。 const QString dsId = dsIdGetter_ ? dsIdGetter_() : QString(); @@ -388,7 +392,10 @@ void RawDataChartView::openInversionColorScale(QWidget* anchor) { } if (vMin > vMax) { vMin = 0.0; vMax = 1.0; } std::vector samples = data_.scatter.v; - ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, nullptr, {}, this); + // 接通色阶模板库:注入仓储 + 当前 projectId + 载荷 templateId(另存为/打开/覆盖 可用)。 + ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, colorTplRepo_, + projectIdGetter_ ? projectIdGetter_() : QString(), data_.templateId, + this); if (dlg.exec() != QDialog::Accepted) return; // 本地重建上色重绘。 @@ -565,8 +572,10 @@ void RawDataChartView::openScatterColorScale(QWidget* anchor) { if (vMin > vMax) { vMin = 0.0; vMax = 1.0; } std::vector samples = data_.scatter.v; // 直方图/等积分层用原始标量 - // 散点无独立 lvl 模板仓储(视图只持命令仓储)→ tplRepo 传空(另存为/打开 禁用)。 - ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, nullptr, {}, this); + // 接通色阶模板库:注入仓储 + 当前 projectId + 载荷 templateId(另存为/打开/覆盖 可用)。 + ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, colorTplRepo_, + projectIdGetter_ ? projectIdGetter_() : QString(), data_.templateId, + this); if (dlg.exec() != QDialog::Accepted) return; // 本地重建 colorSvc_ 重绘散点(M8 即时生效)。 diff --git a/src/app/panels/chart/RawDataChartView.hpp b/src/app/panels/chart/RawDataChartView.hpp index 7e43aa5..f354f93 100644 --- a/src/app/panels/chart/RawDataChartView.hpp +++ b/src/app/panels/chart/RawDataChartView.hpp @@ -15,6 +15,7 @@ class QwtPlotRescaler; namespace geopro::data { class IDatasetCommandRepository; +class IColorTemplateRepository; } namespace geopro::app { @@ -47,6 +48,10 @@ public: std::function dsIdGetter, std::function projectIdGetter); + // 注入色阶模板仓储(散点「色阶配置」编辑器「另存为/打开/覆盖」用;projectId 复用 + // setCommandRepo 注入的 projectIdGetter_)。可传空 → 编辑器后端按钮禁用。 + void setColorTemplateRepo(geopro::data::IColorTemplateRepository* repo); + protected: // 信息模式(M13)下捕获画布点击:找最近散点显示属性。其余事件不消费。 bool eventFilter(QObject* obj, QEvent* ev) override; @@ -123,6 +128,8 @@ private: geopro::data::IDatasetCommandRepository* cmdRepo_ = nullptr; std::function dsIdGetter_; std::function projectIdGetter_; + // 色阶模板仓储(注入;空则编辑器「另存为/打开」禁用)。 + geopro::data::IColorTemplateRepository* colorTplRepo_ = nullptr; }; } // namespace geopro::app diff --git a/src/data/api/ApiColorTemplateRepository.cpp b/src/data/api/ApiColorTemplateRepository.cpp index 1819c5f..c08e192 100644 --- a/src/data/api/ApiColorTemplateRepository.cpp +++ b/src/data/api/ApiColorTemplateRepository.cpp @@ -35,6 +35,24 @@ void ApiColorTemplateRepository::saveLvlTemplate(const QString& projectId, }); } +void ApiColorTemplateRepository::updateLvlTemplate(const QString& id, + const QString& templateName, + const QJsonObject& properties, + std::function cb) { + QJsonObject body{{QStringLiteral("id"), id}, + {QStringLiteral("templateName"), templateName}, + {QStringLiteral("properties"), properties}}; + auto* call = api_.putJsonAsync(QStringLiteral("/business/lvlTemplate"), body); + if (call == nullptr) { + if (cb) cb(false, QStringLiteral("请求创建失败")); + return; + } + QObject::connect(call, &net::IApiCall::finished, call, + [cb = std::move(cb)](const net::ApiResponse& resp) { + if (cb) cb(isOk(resp), resp.msg); + }); +} + void ApiColorTemplateRepository::listLvlTemplates( const QString& projectId, std::function cb) { QJsonObject body{{QStringLiteral("projectId"), projectId}, diff --git a/src/data/api/ApiColorTemplateRepository.hpp b/src/data/api/ApiColorTemplateRepository.hpp index 3aa5e55..9f047c6 100644 --- a/src/data/api/ApiColorTemplateRepository.hpp +++ b/src/data/api/ApiColorTemplateRepository.hpp @@ -15,6 +15,9 @@ public: void saveLvlTemplate(const QString& projectId, const QString& templateName, const QJsonObject& properties, std::function cb) override; + void updateLvlTemplate(const QString& id, const QString& templateName, + const QJsonObject& properties, + std::function cb) override; void listLvlTemplates(const QString& projectId, std::function cb) override; void newClrScheme(const QString& projectId, const QJsonObject& properties, diff --git a/src/data/repo/IColorTemplateRepository.hpp b/src/data/repo/IColorTemplateRepository.hpp index 7f31ec4..31f560a 100644 --- a/src/data/repo/IColorTemplateRepository.hpp +++ b/src/data/repo/IColorTemplateRepository.hpp @@ -19,6 +19,12 @@ public: const QJsonObject& properties, std::function cb) = 0; + // 覆盖更新 lvl 模板:PUT /business/lvlTemplate(body {id, templateName, properties})。 + // 复刻原版 updateLvlTemplate(另存为弹窗勾选「覆盖」时调用,id=当前模板 lvlTemplateId)。 + virtual void updateLvlTemplate(const QString& id, const QString& templateName, + const QJsonObject& properties, + std::function cb) = 0; + // 列 lvl 模板:POST /business/lvlTemplate/page(pageNo=1,pageSize=1000)。 // 回调 list = 响应 data.list 数组(每项含 templateName/properties)。 virtual void listLvlTemplates(const QString& projectId,