geopro/src/app/panels/chart/ExceptionDialog.cpp

255 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "panels/chart/ExceptionDialog.hpp"
#include <utility>
#include <QComboBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QJsonArray>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPointer>
#include <QPushButton>
#include <QTableWidget>
#include <QVBoxLayout>
#include "FormKit.hpp"
#include "Theme.hpp"
#include "repo/IDatasetCommandRepository.hpp"
namespace geopro::app {
namespace {
// 标注类型 → 最少坐标点数(点/文字=1线≥2面≥3
int minPoints(const QString& markType) {
if (markType == QStringLiteral("2")) return 2;
if (markType == QStringLiteral("3")) return 3;
return 1; // 点("1")/文字("4")
}
} // namespace
ExceptionDialog::ExceptionDialog(geopro::data::IDatasetCommandRepository* repo, QString projectId,
QString remarkSourceId, QWidget* parent)
: QDialog(parent),
repo_(repo),
projectId_(std::move(projectId)),
remarkSourceId_(std::move(remarkSourceId)) {
setWindowTitle(QStringLiteral("新建异常"));
setModal(true);
resize(440, 480);
auto* root = formkit::dialogRoot(this);
auto* card = formkit::formCard(this);
auto* cardLay = formkit::cardBody(card);
auto* form = formkit::makeEditForm();
// 标注类型remarkSourceType "1".."4",与原版 annotationType 一致)。
markTypeCombo_ = new QComboBox(this);
markTypeCombo_->addItem(QStringLiteral(""), QStringLiteral("1"));
markTypeCombo_->addItem(QStringLiteral("线"), QStringLiteral("2"));
markTypeCombo_->addItem(QStringLiteral(""), QStringLiteral("3"));
markTypeCombo_->addItem(QStringLiteral("文字"), QStringLiteral("4"));
formkit::capField(markTypeCombo_);
form->addRow(formkit::editLabel(QStringLiteral("标注类型")), markTypeCombo_);
// 异常类型行:下拉 + 「新增异常类型」按钮(对照原版 exceptionDialog 同行布局)。
exceptionTypeCombo_ = new QComboBox(this);
formkit::capField(exceptionTypeCombo_);
addTypeBtn_ = new QPushButton(QStringLiteral("新增异常类型"), this);
auto* typeRow = new QWidget(this);
auto* typeRowLay = new QHBoxLayout(typeRow);
typeRowLay->setContentsMargins(0, 0, 0, 0);
typeRowLay->addWidget(exceptionTypeCombo_, 1);
typeRowLay->addWidget(addTypeBtn_);
form->addRow(formkit::editLabel(QStringLiteral("异常类型")), typeRow);
connect(addTypeBtn_, &QPushButton::clicked, this, [this]() {
// 原版点开「标注类型」新增弹窗(异常属性+名称,含完整 legend提交 addExceptionType。
// 该子流程含整套 legend 编辑,后端 addExceptionType 端点客户端尚未接入;此处先提示,
// 不静默吞操作。用户可在 web 端新增类型后回到客户端选用。
QMessageBox::information(
this, windowTitle(),
QStringLiteral("新增异常类型请在 Web 端完成,新增后此处下拉会同步可选。"));
});
nameEdit_ = new QLineEdit(this);
nameEdit_->setPlaceholderText(QStringLiteral("数据名称+异常类型代号+序号"));
formkit::capField(nameEdit_);
form->addRow(formkit::editLabel(QStringLiteral("名称")), nameEdit_);
remarkEdit_ = new QPlainTextEdit(this);
remarkEdit_->setFixedHeight(geopro::app::scaledPx(60));
formkit::capField(remarkEdit_);
form->addRow(formkit::editLabel(QStringLiteral("备注")), remarkEdit_);
cardLay->addLayout(form);
root->addWidget(card);
// 坐标兜底表x/y 多行):留空 → 确定后在图上绘形采集(主路径);手填 → 直接提交。
root->addWidget(new QLabel(QStringLiteral("坐标xy留空则在图上绘制"), this));
coordTable_ = new QTableWidget(0, 2, this);
coordTable_->setHorizontalHeaderLabels({QStringLiteral("x"), QStringLiteral("y")});
coordTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
root->addWidget(coordTable_, 1);
auto* rowBtns = new QHBoxLayout();
auto* addRow = new QPushButton(QStringLiteral("加一行"), this);
auto* delRow = new QPushButton(QStringLiteral("删一行"), this);
rowBtns->addWidget(addRow);
rowBtns->addWidget(delRow);
rowBtns->addStretch();
root->addLayout(rowBtns);
connect(addRow, &QPushButton::clicked, this,
[this]() { coordTable_->insertRow(coordTable_->rowCount()); });
connect(delRow, &QPushButton::clicked, this, [this]() {
if (coordTable_->rowCount() > 0) coordTable_->removeRow(coordTable_->rowCount() - 1);
});
auto* btnLay = new QHBoxLayout();
btnLay->addStretch();
auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this);
okBtn_ = new QPushButton(QStringLiteral("确定"), this);
okBtn_->setDefault(true);
btnLay->addWidget(cancelBtn);
btnLay->addWidget(okBtn_);
root->addLayout(btnLay);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
connect(okBtn_, &QPushButton::clicked, this, &ExceptionDialog::onConfirm);
connect(markTypeCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[this](int) { onTypeChanged(); });
connect(exceptionTypeCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[this](int) { suggestName(); });
onTypeChanged(); // 初始:拉首个类型的异常类型列表 + 铺最少坐标行
}
QString ExceptionDialog::markTypeValue() const {
return markTypeCombo_->currentData().toString();
}
QString ExceptionDialog::exceptionTypeId() const {
return exceptionTypeCombo_->currentData().toString();
}
QString ExceptionDialog::exceptionName() const {
return nameEdit_->text().trimmed();
}
QString ExceptionDialog::exceptionRemark() const {
return remarkEdit_->toPlainText();
}
QJsonArray ExceptionDialog::manualCoordinates() const {
QJsonArray coords;
for (int r = 0; r < coordTable_->rowCount(); ++r) {
auto* ix = coordTable_->item(r, 0);
auto* iy = coordTable_->item(r, 1);
if (!ix || !iy || ix->text().trimmed().isEmpty() || iy->text().trimmed().isEmpty()) continue;
bool okx = false, oky = false;
const double x = ix->text().toDouble(&okx);
const double y = iy->text().toDouble(&oky);
if (!okx || !oky) continue;
coords.append(QJsonObject{{QStringLiteral("x"), x}, {QStringLiteral("y"), y}});
}
return coords;
}
void ExceptionDialog::onTypeChanged() {
// 对照原版 handleAnnotationTypeChange标注类型变 → 清空名称(待重选类型后回填)+
// 重拉对应几何形态的异常类型列表 + 刷新「新增类型」按钮可用性。
nameEdit_->clear();
updateAddTypeEnabled();
loadExceptionTypes();
}
void ExceptionDialog::updateAddTypeEnabled() {
if (!addTypeBtn_) return;
// 原版:文字类型(4) 或 未选标注类型时禁用「新增异常类型」。
const QString mt = markTypeValue();
addTypeBtn_->setEnabled(!mt.isEmpty() && mt != QStringLiteral("4"));
}
void ExceptionDialog::loadExceptionTypes() {
if (!repo_) return;
QPointer<ExceptionDialog> self(this);
repo_->listExceptionTypes(
projectId_, markTypeValue(), [self](bool ok, QJsonArray list, QString) {
if (!self || !ok) return;
self->exceptionTypeCombo_->clear();
for (const QJsonValue& v : list) {
const QJsonObject o = v.toObject();
// 兼容 {label,value} 与 {exceptionTypeName,id} 两种返回形态。
QString label = o.value(QStringLiteral("label")).toString();
if (label.isEmpty()) label = o.value(QStringLiteral("exceptionTypeName")).toString();
QString id = o.value(QStringLiteral("value")).toString();
if (id.isEmpty()) id = o.value(QStringLiteral("id")).toString();
if (!id.isEmpty()) self->exceptionTypeCombo_->addItem(label, id);
}
if (self->exceptionTypeCombo_->count() > 0) self->suggestName();
});
}
void ExceptionDialog::suggestName() {
if (!repo_) return;
const QString typeId = exceptionTypeCombo_->currentData().toString();
if (typeId.isEmpty()) return;
QPointer<ExceptionDialog> self(this);
// 对照原版 handleExceptionTypeChange每次选/换异常类型都回填名称res.data 为纯字符串)。
repo_->getExceptionName(typeId, remarkSourceId_, [self](bool ok, QString name, QString) {
if (!self || !ok) return;
self->nameEdit_->setText(name);
});
}
void ExceptionDialog::onConfirm() {
if (!repo_) { reject(); return; }
const QString name = nameEdit_->text().trimmed();
const QString typeId = exceptionTypeCombo_->currentData().toString();
if (name.isEmpty()) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("请输入名称"));
return;
}
if (typeId.isEmpty()) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("请选择异常类型"));
return;
}
// 主路径:坐标表留空 → accept(),由调用方在图上绘形采集坐标后 newException。
const QJsonArray coords = manualCoordinates();
if (coords.isEmpty()) { accept(); return; }
// 兜底路径:用户手填了坐标 → 校验点数后直接弹窗内提交。
if (coords.size() < minPoints(markTypeValue())) {
QMessageBox::warning(this, windowTitle(),
QStringLiteral("坐标点数不足(点/文字≥1线≥2面≥3"));
return;
}
QJsonObject body{
{QStringLiteral("exceptionName"), name},
{QStringLiteral("exceptionTypeId"), typeId},
{QStringLiteral("remark"), remarkEdit_->toPlainText()},
{QStringLiteral("remarkSourceType"), markTypeValue()}, // 几何形态字符串
{QStringLiteral("remarkSourceId"), remarkSourceId_}, // = dsObjectId
{QStringLiteral("projectId"), projectId_},
{QStringLiteral("location"), QJsonObject{{QStringLiteral("coordinate"), coords}}},
};
okBtn_->setEnabled(false);
QPointer<ExceptionDialog> self(this);
repo_->newException(body, [self](bool ok, QString msg) {
if (!self) return;
self->okBtn_->setEnabled(true);
if (ok) {
self->accept();
} else {
QMessageBox::warning(self, self->windowTitle(),
msg.isEmpty() ? QStringLiteral("创建失败") : msg);
}
});
}
} // namespace geopro::app