255 lines
10 KiB
C++
255 lines
10 KiB
C++
#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("坐标(x,y,留空则在图上绘制):"), 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
|