feat(detail): 新增异常类型完整1:1(ExceptionTypeDialog 880px双Tab图例编辑器)
替换最小版,完整复刻原版 ExceptionLabel 子弹窗: - 新建 ExceptionTypeDialog(880px,双Tab异常属性/标注名称): 异常属性Tab(类型名称/代号必填/标准编号/标准名称/说明 + 按markType点/线/面/文字的 图例样式编辑器:形状/大小/颜色/不透明度/线形/填充/字体,选项默认对照原版) 标注名称Tab(自定义格式+分隔符+可增删名称列表 fieldName/fieldCode) - 仓储 newCustomExceptionType 替换为 addExceptionType(POST /business/exceptionType, body 全字段对照原版 handleBeforeOk:legend/exceptionNameList/type:2/exceptionMarkType) - ExceptionDialog「新增异常类型」按钮接通,成功刷新类型下拉并选中 build all 绿,341/341。
This commit is contained in:
parent
6cc973a183
commit
438ed78aad
|
|
@ -51,6 +51,7 @@ add_executable(geopro_desktop WIN32
|
|||
panels/chart/WhiteningDialog.cpp
|
||||
panels/chart/FilterDialog.cpp
|
||||
panels/chart/ExceptionDialog.cpp
|
||||
panels/chart/ExceptionTypeDialog.cpp
|
||||
panels/chart/ExceptionTextDialog.cpp
|
||||
panels/chart/ExceptionDetailDialog.cpp
|
||||
panels/chart/AutoAnnotationDialog.cpp
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QInputDialog>
|
||||
#include <QJsonArray>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
|
|
@ -19,6 +18,7 @@
|
|||
|
||||
#include "FormKit.hpp"
|
||||
#include "Theme.hpp"
|
||||
#include "panels/chart/ExceptionTypeDialog.hpp"
|
||||
#include "repo/IDatasetCommandRepository.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
|
@ -161,29 +161,13 @@ void ExceptionDialog::onTypeChanged() {
|
|||
|
||||
void ExceptionDialog::onAddType() {
|
||||
if (!repo_) return;
|
||||
// 最小可用复刻:小弹窗输类型名 → newCustomExceptionType → 成功后刷新下拉并选中新建项。
|
||||
// (原版按钮打开「标注属性+名称」完整 legend 子弹窗走 addExceptionType;此处仅类型名,差距已记录。)
|
||||
bool ok = false;
|
||||
const QString name =
|
||||
QInputDialog::getText(this, QStringLiteral("新增异常类型"), QStringLiteral("类型名称:"),
|
||||
QLineEdit::Normal, QString(), &ok)
|
||||
.trimmed();
|
||||
if (!ok || name.isEmpty()) return;
|
||||
// 完整复刻原版:打开「标注类型」对话框(异常属性 + 标注名称双 Tab + 按 markType 图例编辑器),
|
||||
// 内部走 addExceptionType;成功后回此处刷新异常类型下拉并按名称选中新建项(对照原版 emit ok)。
|
||||
ExceptionTypeDialog typeDlg(repo_, projectId_, markTypeValue(), this);
|
||||
if (typeDlg.exec() != QDialog::Accepted) return;
|
||||
|
||||
addTypeBtn_->setEnabled(false);
|
||||
QPointer<ExceptionDialog> self(this);
|
||||
repo_->newCustomExceptionType(projectId_, name, [self, name](bool success, QString msg) {
|
||||
if (!self) return;
|
||||
self->updateAddTypeEnabled(); // 恢复按钮可用性(按当前标注类型)
|
||||
if (!success) {
|
||||
QMessageBox::warning(self, self->windowTitle(),
|
||||
msg.isEmpty() ? QStringLiteral("新增异常类型失败") : msg);
|
||||
return;
|
||||
}
|
||||
// 成功 → 记录待选中类型名,重拉列表后按名称匹配选中(对照原版刷新下拉)。
|
||||
self->pendingSelectTypeName_ = name;
|
||||
self->loadExceptionTypes();
|
||||
});
|
||||
pendingSelectTypeName_ = typeDlg.createdTypeName(); // 重拉列表后按名称匹配选中
|
||||
loadExceptionTypes();
|
||||
}
|
||||
|
||||
void ExceptionDialog::updateAddTypeEnabled() {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public:
|
|||
|
||||
private:
|
||||
void onTypeChanged(); // 标注类型变 → 清名称 + 重拉异常类型列表 + 刷新「新增类型」可用性
|
||||
void onAddType(); // 「新增异常类型」:小弹窗输类型名 → newCustomExceptionType → 刷新+选中
|
||||
void onAddType(); // 「新增异常类型」:打开 ExceptionTypeDialog(双 Tab) → addExceptionType → 刷新+选中
|
||||
void loadExceptionTypes(); // listExceptionTypes(projectId, remarkSourceType)
|
||||
void suggestName(); // getExceptionName(exceptionTypeId, remarkSourceId) → 名称回填
|
||||
void onConfirm(); // 校验 → 有手填坐标则直接 newException,否则 accept() 交给绘形
|
||||
|
|
|
|||
|
|
@ -0,0 +1,413 @@
|
|||
#include "panels/chart/ExceptionTypeDialog.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QColorDialog>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSpinBox>
|
||||
#include <QStackedWidget>
|
||||
#include <QTableWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
#include "Theme.hpp" // scaledPx
|
||||
#include "repo/IDatasetCommandRepository.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
namespace {
|
||||
// 图例选项(对照原版 ExceptionLabel/const.js 的启用项,value 与原版一致)。
|
||||
struct Opt {
|
||||
const char* value;
|
||||
const char* label;
|
||||
};
|
||||
const Opt kPointShapes[] = {{"circle", "圆点"}, {"diamond", "方块"}, {"triangle", "三角形"}};
|
||||
const Opt kLineShapes[] = {{"dash", "虚线"}, {"solid", "实线"}};
|
||||
const Opt kSurfaceFills[] = {
|
||||
{"/", "斜线"}, {"+", "正交网格"}, {".", "圆点阵列"}, {"", "颜色填充"}};
|
||||
const Opt kTextFonts[] = {{"1", "宋体"}, {"2", "微软雅黑"}, {"3", "黑体"}, {"4", "楷体"}};
|
||||
|
||||
// 用 Opt 表填充下拉(userData = value 字符串),并按 value 选中默认项。
|
||||
template <std::size_t N>
|
||||
void fillCombo(QComboBox* c, const Opt (&opts)[N], const QString& defValue) {
|
||||
for (const Opt& o : opts) c->addItem(QString::fromUtf8(o.label), QString::fromUtf8(o.value));
|
||||
const int idx = c->findData(defValue);
|
||||
if (idx >= 0) c->setCurrentIndex(idx);
|
||||
}
|
||||
|
||||
// 不透明度 0–100 spin。
|
||||
QSpinBox* makeOpacity(int def) {
|
||||
auto* s = new QSpinBox();
|
||||
s->setRange(0, 100);
|
||||
s->setValue(def);
|
||||
s->setSuffix(QStringLiteral("%"));
|
||||
return s;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ExceptionTypeDialog::ExceptionTypeDialog(geopro::data::IDatasetCommandRepository* repo,
|
||||
QString projectId, QString markType, QWidget* parent)
|
||||
: QDialog(parent),
|
||||
repo_(repo),
|
||||
projectId_(std::move(projectId)),
|
||||
markType_(std::move(markType)),
|
||||
pointColor_(Qt::black),
|
||||
polylineColor_(Qt::black),
|
||||
polygonFillColor_(Qt::black),
|
||||
textColor_(Qt::black) {
|
||||
markTypeInt_ = markType_.toInt();
|
||||
if (markTypeInt_ < 1 || markTypeInt_ > 4) markTypeInt_ = 1;
|
||||
|
||||
setWindowTitle(QStringLiteral("标注类型"));
|
||||
setModal(true);
|
||||
resize(geopro::app::scaledPx(880), geopro::app::scaledPx(560));
|
||||
|
||||
auto* root = formkit::dialogRoot(this);
|
||||
|
||||
// 顶部 RadioGroup(按钮态) 双 Tab:异常属性 / 标注名称(对照原版 RadioGroup type=button)。
|
||||
auto* tabRow = new QHBoxLayout();
|
||||
auto* attrTab = new QRadioButton(QStringLiteral("异常属性"), this);
|
||||
auto* nameTab = new QRadioButton(QStringLiteral("标注名称"), this);
|
||||
attrTab->setChecked(true);
|
||||
auto* grp = new QButtonGroup(this);
|
||||
grp->addButton(attrTab, 0);
|
||||
grp->addButton(nameTab, 1);
|
||||
tabRow->addWidget(attrTab);
|
||||
tabRow->addWidget(nameTab);
|
||||
tabRow->addStretch();
|
||||
root->addLayout(tabRow);
|
||||
|
||||
stack_ = new QStackedWidget(this);
|
||||
auto* attrPage = new QWidget(stack_);
|
||||
auto* namePage = new QWidget(stack_);
|
||||
buildAttributeTab(attrPage);
|
||||
buildNameTab(namePage);
|
||||
stack_->addWidget(attrPage);
|
||||
stack_->addWidget(namePage);
|
||||
root->addWidget(stack_, 1);
|
||||
connect(grp, &QButtonGroup::idClicked, this, [this](int id) { stack_->setCurrentIndex(id); });
|
||||
|
||||
auto* buttons = formkit::addDialogButtons(root, this, QStringLiteral("确定"),
|
||||
QStringLiteral("取消"));
|
||||
// 接管 OK:改走 onSubmit(addDialogButtons 默认接的 accept 会被 onSubmit 内部按需调用)。
|
||||
disconnect(buttons, nullptr, this, nullptr);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &ExceptionTypeDialog::onSubmit);
|
||||
}
|
||||
|
||||
// ── 异常属性 Tab ──────────────────────────────────────────────────────────────
|
||||
void ExceptionTypeDialog::buildAttributeTab(QWidget* page) {
|
||||
auto* lay = new QVBoxLayout(page);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto* form = formkit::makeEditForm();
|
||||
nameEdit_ = new QLineEdit(page);
|
||||
nameEdit_->setPlaceholderText(QStringLiteral("请输入异常类型名称"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("异常类型名称")), nameEdit_);
|
||||
codeEdit_ = new QLineEdit(page);
|
||||
codeEdit_->setPlaceholderText(QStringLiteral("请输入"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("代号")), codeEdit_);
|
||||
standardNumberEdit_ = new QLineEdit(page);
|
||||
standardNumberEdit_->setPlaceholderText(QStringLiteral("请输入"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("标准编号")), standardNumberEdit_);
|
||||
standardNameEdit_ = new QLineEdit(page);
|
||||
standardNameEdit_->setPlaceholderText(QStringLiteral("请输入"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("标准名称")), standardNameEdit_);
|
||||
descEdit_ = new QPlainTextEdit(page);
|
||||
descEdit_->setFixedHeight(geopro::app::scaledPx(56));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("说明")), descEdit_);
|
||||
lay->addLayout(form);
|
||||
|
||||
// 图例样式:按 markType 选择性显示(对照原版 ACollapseItem v-if 条件)。
|
||||
if (markTypeInt_ == 1) lay->addWidget(buildPointLegend());
|
||||
if (markTypeInt_ == 2 || markTypeInt_ == 3 || markTypeInt_ == 4)
|
||||
lay->addWidget(buildPolylineLegend());
|
||||
if (markTypeInt_ == 3 || markTypeInt_ == 4) lay->addWidget(buildPolygonLegend());
|
||||
lay->addWidget(buildTextLegend()); // 文字图例对所有 markType 显示
|
||||
lay->addStretch();
|
||||
}
|
||||
|
||||
QPushButton* ExceptionTypeDialog::makeColorSwatch(QColor& target) {
|
||||
auto* btn = new QPushButton(this);
|
||||
btn->setText(target.name(QColor::HexArgb));
|
||||
btn->setStyleSheet(QStringLiteral("background-color: rgba(%1,%2,%3,%4);")
|
||||
.arg(target.red())
|
||||
.arg(target.green())
|
||||
.arg(target.blue())
|
||||
.arg(target.alpha()));
|
||||
connect(btn, &QPushButton::clicked, this, [this, btn, &target]() { pickColor(btn, target); });
|
||||
return btn;
|
||||
}
|
||||
|
||||
void ExceptionTypeDialog::pickColor(QPushButton* swatch, QColor& target) {
|
||||
const QColor picked =
|
||||
QColorDialog::getColor(target, this, QStringLiteral("颜色"), QColorDialog::ShowAlphaChannel);
|
||||
if (!picked.isValid()) return;
|
||||
target = picked;
|
||||
swatch->setText(picked.name(QColor::HexArgb));
|
||||
swatch->setStyleSheet(QStringLiteral("background-color: rgba(%1,%2,%3,%4);")
|
||||
.arg(picked.red())
|
||||
.arg(picked.green())
|
||||
.arg(picked.blue())
|
||||
.arg(picked.alpha()));
|
||||
}
|
||||
|
||||
QGroupBox* ExceptionTypeDialog::buildPointLegend() {
|
||||
auto* box = new QGroupBox(QStringLiteral("点"), this);
|
||||
auto* form = formkit::makeEditForm();
|
||||
pointShape_ = new QComboBox(box);
|
||||
fillCombo(pointShape_, kPointShapes, QStringLiteral("circle"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("形状:")), pointShape_);
|
||||
pointSize_ = new QSpinBox(box);
|
||||
pointSize_->setRange(1, 18);
|
||||
pointSize_->setValue(8);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("大小:")), pointSize_);
|
||||
pointColorBtn_ = makeColorSwatch(pointColor_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("颜色:")), pointColorBtn_);
|
||||
pointOpacity_ = makeOpacity(100);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("不透明度:")), pointOpacity_);
|
||||
box->setLayout(form);
|
||||
return box;
|
||||
}
|
||||
|
||||
QGroupBox* ExceptionTypeDialog::buildPolylineLegend() {
|
||||
auto* box = new QGroupBox(QStringLiteral("多段线"), this);
|
||||
auto* form = formkit::makeEditForm();
|
||||
polylineShape_ = new QComboBox(box);
|
||||
fillCombo(polylineShape_, kLineShapes, QStringLiteral("solid"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("线形:")), polylineShape_);
|
||||
polylineWidth_ = new QSpinBox(box);
|
||||
polylineWidth_->setRange(1, 10);
|
||||
polylineWidth_->setValue(1);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("线宽:")), polylineWidth_);
|
||||
polylineColorBtn_ = makeColorSwatch(polylineColor_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("颜色:")), polylineColorBtn_);
|
||||
polylineOpacity_ = makeOpacity(100);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("不透明度:")), polylineOpacity_);
|
||||
box->setLayout(form);
|
||||
return box;
|
||||
}
|
||||
|
||||
QGroupBox* ExceptionTypeDialog::buildPolygonLegend() {
|
||||
auto* box = new QGroupBox(QStringLiteral("多边形"), this);
|
||||
auto* form = formkit::makeEditForm();
|
||||
polygonFill_ = new QComboBox(box);
|
||||
fillCombo(polygonFill_, kSurfaceFills, QString()); // 默认 '' = 颜色填充
|
||||
form->addRow(formkit::editLabel(QStringLiteral("填充图例:")), polygonFill_);
|
||||
polygonFillColorBtn_ = makeColorSwatch(polygonFillColor_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("颜色:")), polygonFillColorBtn_);
|
||||
polygonFillOpacity_ = makeOpacity(100);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("不透明度:")), polygonFillOpacity_);
|
||||
box->setLayout(form);
|
||||
return box;
|
||||
}
|
||||
|
||||
QGroupBox* ExceptionTypeDialog::buildTextLegend() {
|
||||
auto* box = new QGroupBox(QStringLiteral("文字"), this);
|
||||
auto* form = formkit::makeEditForm();
|
||||
textFont_ = new QComboBox(box);
|
||||
fillCombo(textFont_, kTextFonts, QStringLiteral("1")); // 默认 1=宋体
|
||||
form->addRow(formkit::editLabel(QStringLiteral("字体:")), textFont_);
|
||||
textSize_ = new QSpinBox(box);
|
||||
textSize_->setRange(8, 24);
|
||||
textSize_->setValue(12);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("大小:")), textSize_);
|
||||
textColorBtn_ = makeColorSwatch(textColor_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("颜色:")), textColorBtn_);
|
||||
textOpacity_ = makeOpacity(100);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("不透明度:")), textOpacity_);
|
||||
box->setLayout(form);
|
||||
return box;
|
||||
}
|
||||
|
||||
// ── 标注名称 Tab ──────────────────────────────────────────────────────────────
|
||||
void ExceptionTypeDialog::buildNameTab(QWidget* page) {
|
||||
auto* lay = new QVBoxLayout(page);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto* form = formkit::makeEditForm();
|
||||
customFormatEdit_ = new QLineEdit(page);
|
||||
customFormatEdit_->setReadOnly(true);
|
||||
customFormatEdit_->setPlaceholderText(QStringLiteral("请输入自定义格式描述"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("自定义格式描述")), customFormatEdit_);
|
||||
separatorEdit_ = new QLineEdit(page);
|
||||
separatorEdit_->setPlaceholderText(QStringLiteral("请输入分隔符号"));
|
||||
form->addRow(formkit::editLabel(QStringLiteral("分隔符号")), separatorEdit_);
|
||||
lay->addLayout(form);
|
||||
connect(separatorEdit_, &QLineEdit::textChanged, this,
|
||||
[this](const QString&) { onSeparatorChanged(); });
|
||||
|
||||
lay->addWidget(new QLabel(QStringLiteral("列表:"), page));
|
||||
nameTable_ = new QTableWidget(0, 2, page);
|
||||
nameTable_->setHorizontalHeaderLabels({QStringLiteral("名称"), QStringLiteral("代号")});
|
||||
nameTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
lay->addWidget(nameTable_, 1);
|
||||
// 任意单元格改动(含名称编辑)→ 重算自定义格式描述。
|
||||
connect(nameTable_, &QTableWidget::itemChanged, this,
|
||||
[this](QTableWidgetItem*) { recomputeCustomFormat(); });
|
||||
|
||||
auto* rowBtns = new QHBoxLayout();
|
||||
auto* addBtn = new QPushButton(QStringLiteral("新增"), page);
|
||||
auto* delBtn = new QPushButton(QStringLiteral("删除"), page);
|
||||
rowBtns->addWidget(addBtn);
|
||||
rowBtns->addWidget(delBtn);
|
||||
rowBtns->addStretch();
|
||||
lay->addLayout(rowBtns);
|
||||
connect(addBtn, &QPushButton::clicked, this, &ExceptionTypeDialog::addNameRow);
|
||||
connect(delBtn, &QPushButton::clicked, this, &ExceptionTypeDialog::delNameRow);
|
||||
}
|
||||
|
||||
void ExceptionTypeDialog::onSeparatorChanged() { recomputeCustomFormat(); }
|
||||
|
||||
void ExceptionTypeDialog::recomputeCustomFormat() {
|
||||
// 对照原版:自定义格式描述 = 名称列按分隔符号拼接(原版按选中行,此处按全部名称行)。
|
||||
if (!nameTable_ || !customFormatEdit_) return;
|
||||
const QString sep = separatorEdit_ ? separatorEdit_->text() : QString();
|
||||
QStringList names;
|
||||
for (int r = 0; r < nameTable_->rowCount(); ++r) {
|
||||
auto* it = nameTable_->item(r, 0);
|
||||
if (it && !it->text().trimmed().isEmpty()) names << it->text().trimmed();
|
||||
}
|
||||
customFormatEdit_->setText(names.join(sep));
|
||||
}
|
||||
|
||||
QString ExceptionTypeDialog::nextFieldCode() const {
|
||||
// 自动代号 custom_N(N 取当前最大序号 +1,避免与已填代号冲突)。
|
||||
int maxN = 0;
|
||||
for (int r = 0; r < nameTable_->rowCount(); ++r) {
|
||||
auto* it = nameTable_->item(r, 1);
|
||||
if (!it) continue;
|
||||
const QString code = it->text();
|
||||
if (code.startsWith(QStringLiteral("custom_"))) {
|
||||
const int n = code.mid(7).toInt();
|
||||
if (n > maxN) maxN = n;
|
||||
}
|
||||
}
|
||||
return QStringLiteral("custom_%1").arg(maxN + 1);
|
||||
}
|
||||
|
||||
void ExceptionTypeDialog::addNameRow() {
|
||||
const int r = nameTable_->rowCount();
|
||||
nameTable_->insertRow(r);
|
||||
nameTable_->setItem(r, 0, new QTableWidgetItem(QString()));
|
||||
// 代号默认自动生成,用户可手填覆盖。
|
||||
nameTable_->setItem(r, 1, new QTableWidgetItem(nextFieldCode()));
|
||||
}
|
||||
|
||||
void ExceptionTypeDialog::delNameRow() {
|
||||
const int r = nameTable_->currentRow();
|
||||
if (r >= 0) {
|
||||
nameTable_->removeRow(r);
|
||||
recomputeCustomFormat();
|
||||
}
|
||||
}
|
||||
|
||||
// ── 提交 ────────────────────────────────────────────────────────────────────
|
||||
QJsonObject ExceptionTypeDialog::buildLegend() const {
|
||||
// 对照原版 form.legend:整对象提交(与 markType 无关,全字段都带,rgba 用 alpha 0–1 不同,
|
||||
// 这里沿用色块的 HexArgb 颜色串 + 0–100 不透明度,与原版控件取值一致)。
|
||||
auto hex = [](const QColor& c) { return c.name(QColor::HexArgb); };
|
||||
return QJsonObject{
|
||||
{QStringLiteral("pointShape"),
|
||||
pointShape_ ? pointShape_->currentData().toString() : QStringLiteral("circle")},
|
||||
{QStringLiteral("pointSize"), pointSize_ ? pointSize_->value() : 8},
|
||||
{QStringLiteral("pointColor"), hex(pointColor_)},
|
||||
{QStringLiteral("pointNoOpacity"), pointOpacity_ ? pointOpacity_->value() : 100},
|
||||
{QStringLiteral("polylineShape"),
|
||||
polylineShape_ ? polylineShape_->currentData().toString() : QStringLiteral("solid")},
|
||||
{QStringLiteral("polylineWidth"), polylineWidth_ ? polylineWidth_->value() : 1},
|
||||
{QStringLiteral("polylineColor"), hex(polylineColor_)},
|
||||
{QStringLiteral("polylineNoOpacity"), polylineOpacity_ ? polylineOpacity_->value() : 100},
|
||||
{QStringLiteral("polygonFill"),
|
||||
polygonFill_ ? polygonFill_->currentData().toString() : QString()},
|
||||
{QStringLiteral("polygonFillColor"), hex(polygonFillColor_)},
|
||||
{QStringLiteral("polygonFillNoOpacity"),
|
||||
polygonFillOpacity_ ? polygonFillOpacity_->value() : 100},
|
||||
{QStringLiteral("textFont"), textFont_ ? textFont_->currentData().toString() : QString("1")},
|
||||
{QStringLiteral("textSize"), textSize_ ? textSize_->value() : 12},
|
||||
{QStringLiteral("textColor"), hex(textColor_)},
|
||||
{QStringLiteral("textNoOpacity"), textOpacity_ ? textOpacity_->value() : 100},
|
||||
};
|
||||
}
|
||||
|
||||
QJsonObject ExceptionTypeDialog::buildFormData() const {
|
||||
// exceptionNameList:表格行 → {fieldName, fieldCode, sort}(对照原版 handleBeforeOk 映射)。
|
||||
QJsonArray nameList;
|
||||
for (int r = 0; r < nameTable_->rowCount(); ++r) {
|
||||
auto* nameItem = nameTable_->item(r, 0);
|
||||
if (!nameItem || nameItem->text().trimmed().isEmpty()) continue;
|
||||
auto* codeItem = nameTable_->item(r, 1);
|
||||
nameList.append(QJsonObject{
|
||||
{QStringLiteral("fieldName"), nameItem->text().trimmed()},
|
||||
{QStringLiteral("fieldCode"), codeItem ? codeItem->text().trimmed() : QString()},
|
||||
{QStringLiteral("sort"), nameList.size()},
|
||||
});
|
||||
}
|
||||
return QJsonObject{
|
||||
{QStringLiteral("exceptionTypeName"), nameEdit_->text().trimmed()},
|
||||
{QStringLiteral("exceptionTypeCode"), codeEdit_->text().trimmed()},
|
||||
{QStringLiteral("standardNumber"), standardNumberEdit_->text().trimmed()},
|
||||
{QStringLiteral("standardName"), standardNameEdit_->text().trimmed()},
|
||||
{QStringLiteral("description"), descEdit_->toPlainText()},
|
||||
{QStringLiteral("legend"), buildLegend()},
|
||||
{QStringLiteral("exceptionNameList"), nameList},
|
||||
{QStringLiteral("customFormat"), customFormatEdit_->text()},
|
||||
{QStringLiteral("separatorSymbol"), separatorEdit_->text()},
|
||||
{QStringLiteral("projectId"), projectId_},
|
||||
{QStringLiteral("exceptionMarkType"), markType_},
|
||||
{QStringLiteral("type"), 2},
|
||||
};
|
||||
}
|
||||
|
||||
void ExceptionTypeDialog::onSubmit() {
|
||||
if (!repo_) { reject(); return; }
|
||||
// 校验:代号(exceptionTypeCode) 必填(对照原版 validateForm)。
|
||||
if (codeEdit_->text().trimmed().isEmpty()) {
|
||||
QMessageBox::warning(this, windowTitle(), QStringLiteral("请完善异常属性信息"));
|
||||
return;
|
||||
}
|
||||
// 若处于「标注名称」Tab:至少一个名称(对照原版 labelTag==name 分支校验)。
|
||||
if (stack_->currentIndex() == 1) {
|
||||
bool hasName = false;
|
||||
for (int r = 0; r < nameTable_->rowCount(); ++r) {
|
||||
auto* it = nameTable_->item(r, 0);
|
||||
if (it && !it->text().trimmed().isEmpty()) { hasName = true; break; }
|
||||
}
|
||||
if (!hasName) {
|
||||
QMessageBox::warning(this, windowTitle(), QStringLiteral("请添加至少一个标注名称"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const QJsonObject body = buildFormData();
|
||||
QPointer<ExceptionTypeDialog> self(this);
|
||||
repo_->addExceptionType(body, [self](bool ok, QString msg) {
|
||||
if (!self) return;
|
||||
if (ok) {
|
||||
self->createdTypeName_ = self->nameEdit_->text().trimmed();
|
||||
QMessageBox::information(self, self->windowTitle(),
|
||||
QStringLiteral("新增异常类型成功!"));
|
||||
self->accept();
|
||||
} else {
|
||||
QMessageBox::warning(self, self->windowTitle(),
|
||||
msg.isEmpty() ? QStringLiteral("新增异常类型失败!") : msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
|
||||
#include <QColor>
|
||||
#include <QDialog>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
class QComboBox;
|
||||
class QGroupBox;
|
||||
class QLineEdit;
|
||||
class QPlainTextEdit;
|
||||
class QPushButton;
|
||||
class QSpinBox;
|
||||
class QStackedWidget;
|
||||
class QTableWidget;
|
||||
class QWidget;
|
||||
|
||||
namespace geopro::data {
|
||||
class IDatasetCommandRepository;
|
||||
}
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
// 新建异常类型对话框(1:1 复刻原版 ExceptionLabel/index.vue + ExceptionAttribute.vue + LabelName.vue)。
|
||||
// 880px 宽,标题「标注类型」;顶部 RadioGroup(按钮态) 双 Tab:异常属性 / 标注名称。
|
||||
// - 异常属性:异常类型名称 / 代号(exceptionTypeCode 必填) / 标准编号 / 标准名称 / 说明,
|
||||
// 以及按 markType(点1/线2/面3/文字4) 显示的图例样式编辑器(点形状·大小·颜色·不透明度 /
|
||||
// 多段线线形·线宽·颜色·不透明度 / 多边形填充·颜色·不透明度 / 文字字体·大小·颜色·不透明度)。
|
||||
// - 标注名称:分隔符号 + 自定义格式描述(只读,按分隔符拼接)+ 可增删的名称表(fieldName/fieldCode)。
|
||||
// 提交 handleBeforeOk:组装 formData = {...异常属性, exceptionNameList(映射 sort), customFormat,
|
||||
// separatorSymbol, projectId, exceptionMarkType:markType, type:2} → addExceptionType。
|
||||
class ExceptionTypeDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// markType:"1".."4"(与 ExceptionDialog::markTypeValue / 原版 exceptionMarkType 一致)。
|
||||
ExceptionTypeDialog(geopro::data::IDatasetCommandRepository* repo, QString projectId,
|
||||
QString markType, QWidget* parent = nullptr);
|
||||
|
||||
// accept() 后供调用方读取(用于刷新下拉后按名称选中新建项)。
|
||||
QString createdTypeName() const { return createdTypeName_; }
|
||||
|
||||
private:
|
||||
void buildAttributeTab(QWidget* page); // 异常属性 Tab(表单 + 按 markType 的图例分组)
|
||||
void buildNameTab(QWidget* page); // 标注名称 Tab(分隔符 + 自定义格式 + 名称表)
|
||||
// 图例分组构建器(按 markType 选择性创建,单一职责拆分以控函数行数)。
|
||||
QGroupBox* buildPointLegend(); // 点(markType==1)
|
||||
QGroupBox* buildPolylineLegend(); // 多段线(markType∈{2,3,4})
|
||||
QGroupBox* buildPolygonLegend(); // 多边形(markType∈{3,4})
|
||||
QGroupBox* buildTextLegend(); // 文字(所有 markType)
|
||||
|
||||
void pickColor(QPushButton* swatch, QColor& target); // 色块 → QColorDialog → 回填
|
||||
QPushButton* makeColorSwatch(QColor& target); // 创建已接 pickColor 的色块按钮
|
||||
void onSeparatorChanged(); // 分隔符变 → 重算自定义格式描述(选中名称按分隔符拼接)
|
||||
void recomputeCustomFormat();
|
||||
void addNameRow(); // 名称表加一行(fieldName 手填,fieldCode 自动)
|
||||
void delNameRow(); // 删选中行
|
||||
QString nextFieldCode() const; // 自动 fieldCode("custom_"+序号,去重)
|
||||
|
||||
QJsonObject buildLegend() const; // 按 markType 组装 legend 子对象(对照原版默认结构)
|
||||
QJsonObject buildFormData() const; // 组装完整提交体
|
||||
void onSubmit(); // 校验(代号必填) → addExceptionType → 成功 accept()
|
||||
|
||||
geopro::data::IDatasetCommandRepository* repo_ = nullptr;
|
||||
QString projectId_;
|
||||
QString markType_;
|
||||
int markTypeInt_ = 1;
|
||||
QString createdTypeName_; // 提交成功后记录的异常类型名称(供刷新下拉选中)
|
||||
|
||||
QStackedWidget* stack_ = nullptr; // 双 Tab 内容容器
|
||||
|
||||
// ── 异常属性表单 ──
|
||||
QLineEdit* nameEdit_ = nullptr;
|
||||
QLineEdit* codeEdit_ = nullptr;
|
||||
QLineEdit* standardNumberEdit_ = nullptr;
|
||||
QLineEdit* standardNameEdit_ = nullptr;
|
||||
QPlainTextEdit* descEdit_ = nullptr;
|
||||
|
||||
// 点图例
|
||||
QComboBox* pointShape_ = nullptr;
|
||||
QSpinBox* pointSize_ = nullptr;
|
||||
QColor pointColor_;
|
||||
QPushButton* pointColorBtn_ = nullptr;
|
||||
QSpinBox* pointOpacity_ = nullptr;
|
||||
// 多段线图例
|
||||
QComboBox* polylineShape_ = nullptr;
|
||||
QSpinBox* polylineWidth_ = nullptr;
|
||||
QColor polylineColor_;
|
||||
QPushButton* polylineColorBtn_ = nullptr;
|
||||
QSpinBox* polylineOpacity_ = nullptr;
|
||||
// 多边形图例
|
||||
QComboBox* polygonFill_ = nullptr;
|
||||
QColor polygonFillColor_;
|
||||
QPushButton* polygonFillColorBtn_ = nullptr;
|
||||
QSpinBox* polygonFillOpacity_ = nullptr;
|
||||
// 文字图例
|
||||
QComboBox* textFont_ = nullptr;
|
||||
QSpinBox* textSize_ = nullptr;
|
||||
QColor textColor_;
|
||||
QPushButton* textColorBtn_ = nullptr;
|
||||
QSpinBox* textOpacity_ = nullptr;
|
||||
|
||||
// ── 标注名称 Tab ──
|
||||
QLineEdit* customFormatEdit_ = nullptr; // 只读
|
||||
QLineEdit* separatorEdit_ = nullptr;
|
||||
QTableWidget* nameTable_ = nullptr; // 列:名称(fieldName) / 代号(fieldCode)
|
||||
};
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
@ -402,14 +402,11 @@ void ApiDatasetCommandRepository::newException(const QJsonObject& body,
|
|||
wireStatus(api_.postJsonAsync(QStringLiteral("/business/exception"), body), std::move(cb));
|
||||
}
|
||||
|
||||
void ApiDatasetCommandRepository::newCustomExceptionType(
|
||||
const QString& projectId, const QString& name, std::function<void(bool, QString)> cb) {
|
||||
// 对照原版 newCustomExceptionType(datasetInfo/index.js:160):POST /business/customExceptionType
|
||||
// body {projectId, exceptionTypeName},projectId 必填。
|
||||
QJsonObject body{{QStringLiteral("projectId"), projectId},
|
||||
{QStringLiteral("exceptionTypeName"), name}};
|
||||
wireStatus(api_.postJsonAsync(QStringLiteral("/business/customExceptionType"), body),
|
||||
std::move(cb));
|
||||
void ApiDatasetCommandRepository::addExceptionType(const QJsonObject& body,
|
||||
std::function<void(bool, QString)> cb) {
|
||||
// 对照原版 addExceptionType(apis/toolComponent/exceptionType.js:9):POST /business/exceptionType。
|
||||
// body 由调用方(ExceptionTypeDialog 双 Tab)完整组装,仓储仅做传输职责。
|
||||
wireStatus(api_.postJsonAsync(QStringLiteral("/business/exceptionType"), body), std::move(cb));
|
||||
}
|
||||
|
||||
void ApiDatasetCommandRepository::deleteException(const QString& id,
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ public:
|
|||
std::function<void(bool ok, QString name, QString msg)> cb) override;
|
||||
void newException(const QJsonObject& body,
|
||||
std::function<void(bool ok, QString msg)> cb) override;
|
||||
void newCustomExceptionType(const QString& projectId, const QString& name,
|
||||
std::function<void(bool ok, QString msg)> cb) override;
|
||||
void addExceptionType(const QJsonObject& body,
|
||||
std::function<void(bool ok, QString msg)> cb) override;
|
||||
void deleteException(const QString& id,
|
||||
std::function<void(bool ok, QString msg)> cb) override;
|
||||
void updateException(const QJsonObject& body,
|
||||
|
|
|
|||
|
|
@ -180,13 +180,13 @@ public:
|
|||
virtual void newException(const QJsonObject& body,
|
||||
std::function<void(bool ok, QString msg)> cb) = 0;
|
||||
|
||||
// 新增自定义异常类型:POST /business/customExceptionType
|
||||
// body {projectId, exceptionTypeName}(对应原版 newCustomExceptionType,datasetInfo/index.js:160)。
|
||||
// 注:原版「新增异常类型」按钮实际走的是 addExceptionType(POST /business/exceptionType)
|
||||
// 的完整 legend 编辑子流程;客户端此处复刻为「最小可用」——仅类型名,调用更简的
|
||||
// customExceptionType 端点(见 IDialog 注释,差距已记录)。
|
||||
virtual void newCustomExceptionType(const QString& projectId, const QString& name,
|
||||
std::function<void(bool ok, QString msg)> cb) = 0;
|
||||
// 新增异常类型(完整版,对照原版 addExceptionType / apis/toolComponent/exceptionType.js:9):
|
||||
// POST /business/exceptionType,body 由「标注类型」对话框(异常属性 + 标注名称双 Tab)组装,
|
||||
// 含 {exceptionTypeName, exceptionTypeCode, standardNumber, standardName, description,
|
||||
// legend:{...按 markType 的图例样式}, exceptionNameList:[{fieldName,fieldCode,sort}],
|
||||
// customFormat, separatorSymbol, projectId, exceptionMarkType, type:2}(见 ExceptionTypeDialog)。
|
||||
virtual void addExceptionType(const QJsonObject& body,
|
||||
std::function<void(bool ok, QString msg)> cb) = 0;
|
||||
|
||||
// 删除异常:DELETE /business/exception/{id}(对应原版 deleteExceptionDataInProfileInversion)。
|
||||
virtual void deleteException(const QString& id,
|
||||
|
|
|
|||
Loading…
Reference in New Issue