feat(ui): 统一对话框外壳+全局控件高度,数据集属性改只读
对话框外壳(formkit)统一,编辑态与只读态同款卡片/分组/边距/按钮栏: - 新增 dialogRoot/formCard/cardBody/addDialogButtons;buildDetailDialog 复用 formCard - 生成三维体/导入/导出/异常保存/对象新建/等值线/色阶/设置等对话框迁到统一外壳 - FormKit.hpp 直接 include QLabel/QFrame(editLabel/formCard 返回类型在调用点需完整) 全局控件高度/外观一次补齐(QSS,覆盖所有对话框): - QSpinBox/QDoubleSpinBox 经 QAbstractSpinBox 统一 box,与输入框/下拉框同高;上下按钮扁平 chevron - QPlainTextEdit/QTextEdit 补输入框同款边框/圆角/底色 - 新增 chevron-up.svg(数字框上箭头) 数据集属性改为纯只读:移除"修改描述"功能(QTextEdit+保存+saved 信号+repo 依赖), DatasetAttrPanel 构造简化、main.cpp 相应接线清理
This commit is contained in:
parent
959f030c98
commit
08b8ebbf01
|
|
@ -1,13 +1,14 @@
|
|||
#include "AnomalySaveDialog.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPixmap>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
#include "Theme.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
|
|
@ -28,37 +29,42 @@ AnomalySaveDialog::AnomalySaveDialog(const QString& screenshotPath, int shotW, i
|
|||
setWindowTitle(QStringLiteral("保存异常"));
|
||||
setModal(true);
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
auto* root = formkit::dialogRoot(this);
|
||||
|
||||
auto* form = new QFormLayout();
|
||||
auto* card = formkit::formCard(this);
|
||||
auto* cardLay = formkit::cardBody(card);
|
||||
|
||||
auto* form = formkit::makeEditForm();
|
||||
name_ = new QLineEdit(QStringLiteral("异常"));
|
||||
form->addRow(QStringLiteral("名称"), name_);
|
||||
formkit::capField(name_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("名称")), name_);
|
||||
|
||||
type_ = new QComboBox();
|
||||
for (const auto& t : kMockTypes)
|
||||
type_->addItem(QString::fromUtf8(t.label), QString::fromUtf8(t.id));
|
||||
form->addRow(QStringLiteral("异常类型"), type_);
|
||||
formkit::capField(type_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("异常类型")), type_);
|
||||
|
||||
remark_ = new QPlainTextEdit();
|
||||
remark_->setFixedHeight(60);
|
||||
form->addRow(QStringLiteral("备注"), remark_);
|
||||
root->addLayout(form);
|
||||
remark_->setFixedHeight(geopro::app::scaledPx(60));
|
||||
formkit::capField(remark_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("备注")), remark_);
|
||||
cardLay->addLayout(form);
|
||||
|
||||
// 截图预览 + 大小(R50「确定截图大小」)。
|
||||
if (!screenshotPath.isEmpty()) {
|
||||
root->addWidget(new QLabel(QStringLiteral("截图(%1 × %2)").arg(shotW).arg(shotH)));
|
||||
cardLay->addWidget(new QLabel(QStringLiteral("截图(%1 × %2)").arg(shotW).arg(shotH)));
|
||||
QPixmap pm(screenshotPath);
|
||||
if (!pm.isNull()) {
|
||||
auto* img = new QLabel();
|
||||
img->setPixmap(pm.scaledToWidth(320, Qt::SmoothTransformation));
|
||||
root->addWidget(img);
|
||||
img->setPixmap(pm.scaledToWidth(geopro::app::scaledPx(320), Qt::SmoothTransformation));
|
||||
cardLay->addWidget(img);
|
||||
}
|
||||
}
|
||||
|
||||
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
root->addWidget(buttons);
|
||||
root->addWidget(card);
|
||||
|
||||
formkit::addDialogButtons(root, this);
|
||||
}
|
||||
|
||||
QString AnomalySaveDialog::anomalyName() const {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
#include <QVBoxLayout>
|
||||
|
||||
#include "ColorScaleIO.hpp"
|
||||
#include "FormKit.hpp"
|
||||
#include "Theme.hpp"
|
||||
#include "repo/IColorTemplateRepository.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
|
@ -78,12 +80,6 @@ QPixmap previewPixmap(const std::vector<Stop>& stops, int w = 100, int h = 16) {
|
|||
return pm;
|
||||
}
|
||||
|
||||
QLabel* rightLabel(const QString& text) {
|
||||
auto* l = new QLabel(text);
|
||||
l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
l->setMinimumWidth(72);
|
||||
return l;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double minValue,
|
||||
|
|
@ -104,8 +100,8 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
|
||||
// ── 顶部两列 grid(grid-template-columns: 50% 50%) ──────────────────────
|
||||
auto* grid = new QGridLayout();
|
||||
grid->setHorizontalSpacing(12);
|
||||
grid->setVerticalSpacing(8);
|
||||
grid->setHorizontalSpacing(geopro::app::space::kLg); // formkit 标准列距
|
||||
grid->setVerticalSpacing(geopro::app::space::kMd); // formkit 标准行距
|
||||
int rowIdx = 0;
|
||||
|
||||
// 配色方案(下拉带预览色条)。
|
||||
|
|
@ -113,7 +109,7 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
schemeCombo_->setIconSize(QSize(100, 16));
|
||||
{
|
||||
auto* cell = new QHBoxLayout();
|
||||
cell->addWidget(rightLabel(QStringLiteral("配色方案:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("配色方案:")));
|
||||
cell->addWidget(schemeCombo_, 1);
|
||||
grid->addLayout(cell, rowIdx, 0);
|
||||
}
|
||||
|
|
@ -121,7 +117,7 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
// 分布方式(disabled, 默认线性)+ 反向。
|
||||
{
|
||||
auto* cell = new QHBoxLayout();
|
||||
cell->addWidget(rightLabel(QStringLiteral("分布方式:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("分布方式:")));
|
||||
auto* distCombo = new QComboBox(this);
|
||||
distCombo->addItem(QStringLiteral("线性"), QStringLiteral("linear"));
|
||||
distCombo->addItem(QStringLiteral("对数"), QStringLiteral("log"));
|
||||
|
|
@ -142,7 +138,7 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
.arg(QString::number(originMax_, 'f', 6)));
|
||||
{
|
||||
auto* cell = new QHBoxLayout();
|
||||
cell->addWidget(rightLabel(QStringLiteral("数值范围:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("数值范围:")));
|
||||
cell->addWidget(rangeEdit_, 1);
|
||||
grid->addLayout(cell, rowIdx, 0);
|
||||
}
|
||||
|
|
@ -150,13 +146,13 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
// 最小值/最大值(可编辑)。
|
||||
{
|
||||
auto* cell = new QHBoxLayout();
|
||||
cell->addWidget(rightLabel(QStringLiteral("最小值:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("最小值:")));
|
||||
minSpin_ = new QDoubleSpinBox(this);
|
||||
minSpin_->setDecimals(6);
|
||||
minSpin_->setRange(-1e12, 1e12);
|
||||
minSpin_->setValue(minValue);
|
||||
cell->addWidget(minSpin_, 1);
|
||||
cell->addWidget(rightLabel(QStringLiteral("最大值:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("最大值:")));
|
||||
maxSpin_ = new QDoubleSpinBox(this);
|
||||
maxSpin_->setDecimals(6);
|
||||
maxSpin_->setRange(-1e12, 1e12);
|
||||
|
|
@ -169,7 +165,7 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
curDataLabel_ = new QLabel(QStringLiteral("-"), this);
|
||||
{
|
||||
auto* cell = new QHBoxLayout();
|
||||
cell->addWidget(rightLabel(QStringLiteral("当前数据值:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("当前数据值:")));
|
||||
cell->addWidget(curDataLabel_, 1);
|
||||
grid->addLayout(cell, rowIdx, 0);
|
||||
}
|
||||
|
|
@ -178,7 +174,7 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
curPosLabel_ = new QLabel(QStringLiteral("-"), this);
|
||||
{
|
||||
auto* cell = new QHBoxLayout();
|
||||
cell->addWidget(rightLabel(QStringLiteral("当前数据位置:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("当前数据位置:")));
|
||||
cell->addWidget(curPosLabel_, 1);
|
||||
grid->addLayout(cell, rowIdx++, 1);
|
||||
}
|
||||
|
|
@ -189,7 +185,7 @@ ColorGradientDialog::ColorGradientDialog(const std::vector<Stop>& init, double m
|
|||
curColorBtn_->setEnabled(false);
|
||||
{
|
||||
auto* cell = new QHBoxLayout();
|
||||
cell->addWidget(rightLabel(QStringLiteral("当前颜色:")));
|
||||
cell->addWidget(formkit::editLabel(QStringLiteral("当前颜色:")));
|
||||
cell->addWidget(curColorBtn_);
|
||||
cell->addStretch(1);
|
||||
grid->addLayout(cell, rowIdx++, 0);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
namespace {
|
||||
|
|
@ -27,12 +29,12 @@ ContourLevelDialog::ContourLevelDialog(const ContourLevelParams& init, double or
|
|||
setWindowTitle(QStringLiteral("等值线层级"));
|
||||
setModal(true);
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
auto* form = new QFormLayout();
|
||||
auto* root = formkit::dialogRoot(this);
|
||||
auto* form = formkit::makeEditForm();
|
||||
root->addLayout(form);
|
||||
|
||||
// 数据范围(原始,只读展示)。
|
||||
form->addRow(QStringLiteral("数据范围"),
|
||||
form->addRow(formkit::editLabel(QStringLiteral("数据范围")),
|
||||
new QLabel(QStringLiteral("%1 ~ %2").arg(originMin_).arg(originMax_)));
|
||||
|
||||
// 分层方式。
|
||||
|
|
@ -41,7 +43,8 @@ ContourLevelDialog::ContourLevelDialog(const ContourLevelParams& init, double or
|
|||
methodCombo_->addItem(QStringLiteral("对数"), 1);
|
||||
methodCombo_->addItem(QStringLiteral("等积"), 2);
|
||||
methodCombo_->setCurrentIndex(static_cast<int>(init.method));
|
||||
form->addRow(QStringLiteral("分层方式"), methodCombo_);
|
||||
formkit::capField(methodCombo_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("分层方式")), methodCombo_);
|
||||
|
||||
auto* validator = new QDoubleValidator(this);
|
||||
validator->setNotation(QDoubleValidator::StandardNotation);
|
||||
|
|
@ -52,40 +55,50 @@ ContourLevelDialog::ContourLevelDialog(const ContourLevelParams& init, double or
|
|||
maxEdit_ = new QLineEdit(QString::number(init.maxValue), this);
|
||||
minEdit_->setValidator(validator);
|
||||
maxEdit_->setValidator(validator);
|
||||
formkit::capField(minEdit_);
|
||||
formkit::capField(maxEdit_);
|
||||
rangeRow_ = new QWidget(this);
|
||||
auto* rangeForm = new QFormLayout(rangeRow_);
|
||||
auto* rangeForm = formkit::makeEditForm();
|
||||
rangeForm->setContentsMargins(0, 0, 0, 0);
|
||||
rangeForm->addRow(QStringLiteral("最大等值线"), maxEdit_);
|
||||
rangeForm->addRow(QStringLiteral("最小等值线"), minEdit_);
|
||||
rangeForm->addRow(formkit::editLabel(QStringLiteral("最大等值线")), maxEdit_);
|
||||
rangeForm->addRow(formkit::editLabel(QStringLiteral("最小等值线")), minEdit_);
|
||||
rangeRow_->setLayout(rangeForm);
|
||||
root->addWidget(rangeRow_);
|
||||
|
||||
// normal:间隔数 + 层数(双向联动)。
|
||||
intervalEdit_ = new QLineEdit(QString::number(init.interval), this);
|
||||
layerCountEdit_ = new QLineEdit(QString::number(init.layerCount), this);
|
||||
intervalEdit_->setValidator(validator);
|
||||
formkit::capField(intervalEdit_);
|
||||
formkit::capField(layerCountEdit_);
|
||||
normalRow_ = new QWidget(this);
|
||||
auto* normalForm = new QFormLayout(normalRow_);
|
||||
auto* normalForm = formkit::makeEditForm();
|
||||
normalForm->setContentsMargins(0, 0, 0, 0);
|
||||
normalForm->addRow(QStringLiteral("数值间隔"), intervalEdit_);
|
||||
normalForm->addRow(QStringLiteral("层数"), layerCountEdit_);
|
||||
normalForm->addRow(formkit::editLabel(QStringLiteral("数值间隔")), intervalEdit_);
|
||||
normalForm->addRow(formkit::editLabel(QStringLiteral("层数")), layerCountEdit_);
|
||||
normalRow_->setLayout(normalForm);
|
||||
root->addWidget(normalRow_);
|
||||
|
||||
// logarithmic:每数量级次要等值线数。
|
||||
logLinesEdit_ = new QLineEdit(QString::number(init.logLinesCount), this);
|
||||
formkit::capField(logLinesEdit_);
|
||||
logRow_ = new QWidget(this);
|
||||
auto* logForm = new QFormLayout(logRow_);
|
||||
auto* logForm = formkit::makeEditForm();
|
||||
logForm->setContentsMargins(0, 0, 0, 0);
|
||||
logForm->addRow(QStringLiteral("每数量级次要等值线数"), logLinesEdit_);
|
||||
logForm->addRow(formkit::editLabel(QStringLiteral("每数量级次要等值线数")), logLinesEdit_);
|
||||
logRow_->setLayout(logForm);
|
||||
root->addWidget(logRow_);
|
||||
|
||||
// equalArea:等积分层层数 + 区间面积(只读,自动算)。
|
||||
equalAreaCountEdit_ = new QLineEdit(QString::number(init.equalAreaLayerCount), this);
|
||||
formkit::capField(equalAreaCountEdit_);
|
||||
intervalAreaLabel_ = new QLabel(this);
|
||||
equalAreaRow_ = new QWidget(this);
|
||||
auto* eaForm = new QFormLayout(equalAreaRow_);
|
||||
auto* eaForm = formkit::makeEditForm();
|
||||
eaForm->setContentsMargins(0, 0, 0, 0);
|
||||
eaForm->addRow(QStringLiteral("层数"), equalAreaCountEdit_);
|
||||
eaForm->addRow(QStringLiteral("区间面积"), intervalAreaLabel_);
|
||||
eaForm->addRow(formkit::editLabel(QStringLiteral("层数")), equalAreaCountEdit_);
|
||||
eaForm->addRow(formkit::editLabel(QStringLiteral("区间面积")), intervalAreaLabel_);
|
||||
equalAreaRow_->setLayout(eaForm);
|
||||
root->addWidget(equalAreaRow_);
|
||||
|
||||
auto* reset = new QPushButton(QStringLiteral("恢复默认值"), this);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
namespace {
|
||||
|
|
@ -26,41 +28,45 @@ ContourLineDialog::ContourLineDialog(const ContourLineConfig& init, QWidget* par
|
|||
setWindowTitle(QStringLiteral("等值线修改"));
|
||||
setModal(true);
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
auto* form = new QFormLayout();
|
||||
root->addLayout(form);
|
||||
auto* root = formkit::dialogRoot(this);
|
||||
auto* card = formkit::formCard(this);
|
||||
auto* cardLay = formkit::cardBody(card);
|
||||
auto* form = formkit::makeEditForm();
|
||||
|
||||
// 复刻 contourLine.vue:选项顺序「虚线」在前、「实线」在后。
|
||||
lineTypeCombo_ = new QComboBox(this);
|
||||
lineTypeCombo_->addItem(QStringLiteral("- - - - - - - - -"), true); // dashed
|
||||
lineTypeCombo_->addItem(QStringLiteral("——————"), false); // solid
|
||||
lineTypeCombo_->setCurrentIndex(cfg_.dashed ? 0 : 1);
|
||||
form->addRow(QStringLiteral("线形:"), lineTypeCombo_);
|
||||
formkit::capField(lineTypeCombo_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("线形:")), lineTypeCombo_);
|
||||
|
||||
lineShowChk_ = new QCheckBox(this);
|
||||
lineShowChk_->setChecked(cfg_.lineShow);
|
||||
form->addRow(QStringLiteral("显示线段:"), lineShowChk_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("显示线段:")), lineShowChk_);
|
||||
|
||||
lineColorBtn_ = new QPushButton(this);
|
||||
paintSwatch(lineColorBtn_, cfg_.lineColor);
|
||||
paintSwatch(lineColorBtn_, cfg_.lineColor); // 专用色块控件:样式表保留
|
||||
connect(lineColorBtn_, &QPushButton::clicked, this, &ContourLineDialog::pickLineColor);
|
||||
form->addRow(QStringLiteral("线段颜色:"), lineColorBtn_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("线段颜色:")), lineColorBtn_);
|
||||
|
||||
labelShowChk_ = new QCheckBox(this);
|
||||
labelShowChk_->setChecked(cfg_.labelShow);
|
||||
form->addRow(QStringLiteral("显示标注:"), labelShowChk_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("显示标注:")), labelShowChk_);
|
||||
|
||||
labelColorBtn_ = new QPushButton(this);
|
||||
paintSwatch(labelColorBtn_, cfg_.labelColor);
|
||||
paintSwatch(labelColorBtn_, cfg_.labelColor); // 专用色块控件:样式表保留
|
||||
connect(labelColorBtn_, &QPushButton::clicked, this, &ContourLineDialog::pickLabelColor);
|
||||
form->addRow(QStringLiteral("标注颜色:"), labelColorBtn_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("标注颜色:")), labelColorBtn_);
|
||||
|
||||
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttons->button(QDialogButtonBox::Ok)->setText(QStringLiteral("应用"));
|
||||
buttons->button(QDialogButtonBox::Cancel)->setText(QStringLiteral("取消"));
|
||||
cardLay->addLayout(form);
|
||||
root->addWidget(card);
|
||||
|
||||
auto* buttons = formkit::addDialogButtons(root, this, QStringLiteral("应用"),
|
||||
QStringLiteral("取消"));
|
||||
// addDialogButtons 默认 accepted→accept();本对话框须先在 onAccept 写回 cfg_ 再 accept。
|
||||
disconnect(buttons, &QDialogButtonBox::accepted, this, nullptr);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &ContourLineDialog::onAccept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
root->addWidget(buttons);
|
||||
}
|
||||
|
||||
void ContourLineDialog::paintSwatch(QPushButton* btn, const geopro::core::Rgba& c) {
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
#include <utility>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
#include "Theme.hpp"
|
||||
#include "api/NavLoads.hpp"
|
||||
#include "api/NavRequest.hpp"
|
||||
|
|
@ -29,31 +29,30 @@ ExportDatasetDialog::ExportDatasetDialog(geopro::data::IAsyncProjectRepository&
|
|||
setWindowTitle(QStringLiteral("导出数据集"));
|
||||
setMinimumWidth(geopro::app::scaledPx(400));
|
||||
|
||||
auto* lay = new QVBoxLayout(this);
|
||||
lay->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kMd,
|
||||
geopro::app::space::kLg, geopro::app::space::kMd);
|
||||
lay->setSpacing(geopro::app::space::kMd);
|
||||
auto* root = formkit::dialogRoot(this);
|
||||
|
||||
auto* fl = new QFormLayout();
|
||||
auto* card = formkit::formCard(this);
|
||||
auto* cardLay = formkit::cardBody(card);
|
||||
|
||||
auto* fl = formkit::makeEditForm();
|
||||
templateCombo_ = new QComboBox(this);
|
||||
fl->addRow(QStringLiteral("导出模板"), templateCombo_);
|
||||
lay->addLayout(fl);
|
||||
formkit::capField(templateCombo_);
|
||||
fl->addRow(formkit::editLabel(QStringLiteral("导出模板")), templateCombo_);
|
||||
cardLay->addLayout(fl);
|
||||
|
||||
status_ = new QLabel(QStringLiteral("加载模板…"), this);
|
||||
geopro::app::applyTokenizedStyleSheet(status_, QStringLiteral("color:{{text/disabled}};"));
|
||||
lay->addWidget(status_);
|
||||
cardLay->addWidget(status_);
|
||||
|
||||
auto* btnRow = new QHBoxLayout();
|
||||
btnRow->addStretch();
|
||||
auto* cancel = new QPushButton(QStringLiteral("取消"), this);
|
||||
okBtn_ = new QPushButton(QStringLiteral("导出"), this);
|
||||
root->addWidget(card);
|
||||
|
||||
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);
|
||||
btnRow->addWidget(cancel);
|
||||
btnRow->addWidget(okBtn_);
|
||||
lay->addLayout(btnRow);
|
||||
|
||||
QObject::connect(cancel, &QPushButton::clicked, this, &QDialog::reject);
|
||||
QObject::connect(okBtn_, &QPushButton::clicked, this, &ExportDatasetDialog::onConfirm);
|
||||
|
||||
loadTemplates();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <QFormLayout>
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <utility>
|
||||
|
|
@ -31,24 +32,49 @@ DetailForm& DetailForm::row(const QString& key, const QString& value) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
void buildDetailDialog(QDialog* dlg, const geopro::data::DynamicForm& form,
|
||||
const QList<QWidget*>& extras) {
|
||||
dlg->setModal(true);
|
||||
QVBoxLayout* dialogRoot(QDialog* dlg) {
|
||||
auto* root = new QVBoxLayout(dlg);
|
||||
root->setContentsMargins(space::kLg, space::kLg, space::kLg, space::kLg);
|
||||
root->setSpacing(space::kMd);
|
||||
return root;
|
||||
}
|
||||
|
||||
// 卡片:与属性面板同款(bg/panel-subtle + 1px 边框 + 中圆角),内嵌唯一只读渲染器
|
||||
// KeyValueView——对话框随内容自适应大小,无嵌套滚动/留空。
|
||||
auto* card = new QFrame(dlg);
|
||||
card->setObjectName(QStringLiteral("detailCard"));
|
||||
QFrame* formCard(QWidget* parent) {
|
||||
auto* card = new QFrame(parent);
|
||||
card->setObjectName(QStringLiteral("formCard"));
|
||||
applyTokenizedStyleSheet(
|
||||
card, QStringLiteral("#detailCard { background:{{bg/panel-subtle}};"
|
||||
card, QStringLiteral("#formCard { background:{{bg/panel-subtle}};"
|
||||
"border:1px solid {{border/default}}; border-radius:%1px; }")
|
||||
.arg(radius::kMd));
|
||||
auto* cardLay = new QVBoxLayout(card);
|
||||
cardLay->setContentsMargins(space::kLg, space::kLg, space::kLg, space::kLg);
|
||||
cardLay->setSpacing(space::kMd);
|
||||
auto* lay = new QVBoxLayout(card);
|
||||
lay->setContentsMargins(space::kLg, space::kLg, space::kLg, space::kLg);
|
||||
lay->setSpacing(space::kMd);
|
||||
return card;
|
||||
}
|
||||
|
||||
QVBoxLayout* cardBody(QFrame* card) {
|
||||
return qobject_cast<QVBoxLayout*>(card->layout());
|
||||
}
|
||||
|
||||
QDialogButtonBox* addDialogButtons(QVBoxLayout* root, QDialog* dlg, const QString& okText,
|
||||
const QString& cancelText) {
|
||||
auto* box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dlg);
|
||||
if (!okText.isEmpty()) box->button(QDialogButtonBox::Ok)->setText(okText);
|
||||
if (!cancelText.isEmpty()) box->button(QDialogButtonBox::Cancel)->setText(cancelText);
|
||||
QObject::connect(box, &QDialogButtonBox::accepted, dlg, &QDialog::accept);
|
||||
QObject::connect(box, &QDialogButtonBox::rejected, dlg, &QDialog::reject);
|
||||
root->addWidget(box);
|
||||
return box;
|
||||
}
|
||||
|
||||
void buildDetailDialog(QDialog* dlg, const geopro::data::DynamicForm& form,
|
||||
const QList<QWidget*>& extras) {
|
||||
dlg->setModal(true);
|
||||
auto* root = dialogRoot(dlg);
|
||||
|
||||
// 卡片:与属性面板/编辑对话框同款,内嵌唯一只读渲染器 KeyValueView——随内容自适应大小。
|
||||
auto* card = formCard(dlg);
|
||||
auto* cardLay = cardBody(card);
|
||||
|
||||
auto* kv = new KeyValueView(card);
|
||||
kv->setForm(form);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
// 本模块把「只读键值详情」与「可编辑表单」的视觉度量收敛到唯一实现,所有表单必须经此产出,
|
||||
// 一致性由「代码复用」强制,而非「人工遵守文档」。
|
||||
|
||||
#include <QFrame> // editLabel/formCard 返回 QLabel*/QFrame*;调用方常把结果直接传给
|
||||
#include <QLabel> // addRow/addWidget(QWidget*),需在调用点见到完整类型(QLabel/QFrame 派生自 QWidget)。
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
|
|
@ -14,8 +16,9 @@
|
|||
|
||||
class QBoxLayout;
|
||||
class QDialog;
|
||||
class QDialogButtonBox;
|
||||
class QFormLayout;
|
||||
class QLabel;
|
||||
class QVBoxLayout;
|
||||
class QWidget;
|
||||
|
||||
namespace geopro::app::formkit {
|
||||
|
|
@ -38,6 +41,17 @@ private:
|
|||
void buildDetailDialog(QDialog* dlg, const geopro::data::DynamicForm& form,
|
||||
const QList<QWidget*>& extras = {});
|
||||
|
||||
// ── 对话框外壳:统一所有对话框的边距/卡片/按钮栏(编辑态与只读态共用 → 外观一致)──────
|
||||
// 标准根布局:统一外边距 space/lg + 行距 space/md。
|
||||
QVBoxLayout* dialogRoot(QDialog* dlg);
|
||||
// 标准表单卡片:bg/panel-subtle + 1px 边框 + 中圆角 + 统一内距。把表单/分组/键值包成
|
||||
// 与「数据详情 / 属性面板」同款的卡片面。返回 QFrame;其 layout() 即 QVBoxLayout,向内 addSection/addLayout。
|
||||
QFrame* formCard(QWidget* parent);
|
||||
QVBoxLayout* cardBody(QFrame* card); // 取 formCard 的内层 QVBoxLayout(便捷器)
|
||||
// 标准底部按钮栏:QDialogButtonBox(Ok|Cancel),已接 accept/reject;okText/cancelText 可定制文案。
|
||||
QDialogButtonBox* addDialogButtons(QVBoxLayout* root, QDialog* dlg, const QString& okText = QString(),
|
||||
const QString& cancelText = QString());
|
||||
|
||||
// ── 可编辑表单:§7.0 统一度量(DynamicFormEditor 与各参数对话框共用,单一真相)──────
|
||||
QFormLayout* makeEditForm(); // 右对齐标签 + 标准行距/列距
|
||||
QLabel* editLabel(const QString& text, QWidget* parent = nullptr,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
|
|
@ -15,9 +16,9 @@
|
|||
#include <QScrollArea>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
#include "Theme.hpp"
|
||||
#include "api/NavLoads.hpp"
|
||||
#include "api/NavRequest.hpp"
|
||||
|
|
@ -39,29 +40,27 @@ ImportDatasetDialog::ImportDatasetDialog(geopro::data::IAsyncProjectRepository&
|
|||
setWindowTitle(QStringLiteral("导入数据集"));
|
||||
setMinimumSize(geopro::app::scaledPx(480), geopro::app::scaledPx(520));
|
||||
|
||||
auto* lay = new QVBoxLayout(this);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->setSpacing(0);
|
||||
auto* root = formkit::dialogRoot(this);
|
||||
|
||||
status_ = new QLabel(QStringLiteral("加载数据类型…"), this);
|
||||
status_->setAlignment(Qt::AlignCenter);
|
||||
geopro::app::applyTokenizedStyleSheet(status_,
|
||||
QStringLiteral("color:{{text/disabled}};padding:12px;"));
|
||||
lay->addWidget(status_);
|
||||
root->addWidget(status_);
|
||||
|
||||
auto* form = new QWidget(this);
|
||||
auto* fl = new QFormLayout(form);
|
||||
fl->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kMd,
|
||||
geopro::app::space::kLg, 0);
|
||||
fl->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
fl->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
auto* card = formkit::formCard(this);
|
||||
auto* cardLay = formkit::cardBody(card);
|
||||
|
||||
typeCombo_ = new QComboBox(form);
|
||||
fl->addRow(QStringLiteral("数据类型"), typeCombo_);
|
||||
scriptCombo_ = new QComboBox(form);
|
||||
fl->addRow(QStringLiteral("导入脚本"), scriptCombo_);
|
||||
auto* fl = formkit::makeEditForm();
|
||||
|
||||
auto* fileRow = new QWidget(form);
|
||||
typeCombo_ = new QComboBox(card);
|
||||
formkit::capField(typeCombo_);
|
||||
fl->addRow(formkit::editLabel(QStringLiteral("数据类型")), typeCombo_);
|
||||
scriptCombo_ = new QComboBox(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);
|
||||
|
|
@ -70,36 +69,28 @@ ImportDatasetDialog::ImportDatasetDialog(geopro::data::IAsyncProjectRepository&
|
|||
auto* browse = new QPushButton(QStringLiteral("浏览…"), fileRow);
|
||||
fileLay->addWidget(fileEdit_, 1);
|
||||
fileLay->addWidget(browse);
|
||||
fl->addRow(QStringLiteral("文件"), fileRow);
|
||||
lay->addWidget(form);
|
||||
fl->addRow(formkit::editLabel(QStringLiteral("文件")), fileRow);
|
||||
cardLay->addLayout(fl);
|
||||
|
||||
auto* paramLabel = new QLabel(QStringLiteral("脚本参数"), this);
|
||||
geopro::app::applyTokenizedStyleSheet(
|
||||
paramLabel, QStringLiteral("color:{{text/secondary}};padding:%1px %2px 0;")
|
||||
.arg(geopro::app::space::kSm)
|
||||
.arg(geopro::app::space::kLg));
|
||||
lay->addWidget(paramLabel);
|
||||
formkit::addSection(cardLay, QStringLiteral("脚本参数"), card, true);
|
||||
|
||||
auto* scroll = new QScrollArea(this);
|
||||
auto* scroll = new QScrollArea(card);
|
||||
scroll->setWidgetResizable(true);
|
||||
scroll->setFrameShape(QFrame::NoFrame);
|
||||
paramEditor_ = new DynamicFormEditor();
|
||||
scroll->setWidget(paramEditor_);
|
||||
lay->addWidget(scroll, 1);
|
||||
cardLay->addWidget(scroll, 1);
|
||||
|
||||
auto* btnRow = new QHBoxLayout();
|
||||
btnRow->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kSm,
|
||||
geopro::app::space::kLg, geopro::app::space::kMd);
|
||||
btnRow->addStretch();
|
||||
auto* cancel = new QPushButton(QStringLiteral("取消"), this);
|
||||
okBtn_ = new QPushButton(QStringLiteral("导入"), this);
|
||||
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);
|
||||
btnRow->addWidget(cancel);
|
||||
btnRow->addWidget(okBtn_);
|
||||
lay->addLayout(btnRow);
|
||||
|
||||
QObject::connect(cancel, &QPushButton::clicked, this, &QDialog::reject);
|
||||
QObject::connect(browse, &QPushButton::clicked, this, &ImportDatasetDialog::chooseFile);
|
||||
QObject::connect(okBtn_, &QPushButton::clicked, this, &ImportDatasetDialog::onConfirm);
|
||||
QObject::connect(typeCombo_, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
#include "Theme.hpp"
|
||||
#include "api/NavLoads.hpp" // Q_DECLARE_METATYPE(EditableForm) / GsTypeOption
|
||||
#include "api/NavRequest.hpp"
|
||||
|
|
@ -121,22 +122,17 @@ void ObjectFormDialog::buildTopFields() {
|
|||
delete old;
|
||||
}
|
||||
|
||||
auto* fl = new QFormLayout(topBox_);
|
||||
// body 内距:对话框用 space/xl(24) 环绕表单(规范 §7.0.6)。
|
||||
fl->setContentsMargins(geopro::app::space::kXxl, geopro::app::space::kXxl,
|
||||
geopro::app::space::kXxl, 0);
|
||||
fl->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
fl->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
fl->setHorizontalSpacing(geopro::app::space::kLg); // 标签↔字段 space/md(12)=项目 kLg
|
||||
fl->setVerticalSpacing(geopro::app::space::kMd); // 行距 ≈8px(与动态表单一致)
|
||||
auto* fl = formkit::makeEditForm();
|
||||
// body 内距:对话框用 space/lg 环绕顶部固定字段区(与统一外壳一致)。
|
||||
fl->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
|
||||
geopro::app::space::kLg, 0);
|
||||
|
||||
// 等宽右标签列 + 字段最大宽上限(规范 §7.0.2),与动态表单对齐。
|
||||
// 等宽右标签列(formkit::editLabel)+ 字段最大宽上限(formkit::capField),与动态表单对齐。
|
||||
auto addRow = [&](const QString& text, QWidget* field) {
|
||||
auto* lbl = new QLabel(text, topBox_);
|
||||
lbl->setFixedWidth(geopro::app::scaledPx(geopro::app::space::kFormLabelCol));
|
||||
field->setMaximumWidth(geopro::app::scaledPx(geopro::app::space::kFormFieldMax));
|
||||
fl->addRow(lbl, field);
|
||||
formkit::capField(field);
|
||||
fl->addRow(formkit::editLabel(text, topBox_), field);
|
||||
};
|
||||
topBox_->setLayout(fl);
|
||||
|
||||
const bool isCreate = objectId_.isEmpty();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QFrame>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
|
|
@ -14,6 +13,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "FormKit.hpp"
|
||||
#include "Glyphs.hpp"
|
||||
#include "Theme.hpp"
|
||||
|
||||
|
|
@ -47,42 +47,20 @@ QWidget* makeRow(const QString& label, QWidget* control, const QString& caption
|
|||
.arg(geopro::app::scaledPx(geopro::app::type::kCaption)));
|
||||
lv->addWidget(cap);
|
||||
}
|
||||
labelCol->setMinimumWidth(160);
|
||||
labelCol->setMinimumWidth(geopro::app::scaledPx(160));
|
||||
|
||||
lay->addWidget(labelCol);
|
||||
lay->addWidget(control, 1, Qt::AlignTop);
|
||||
return row;
|
||||
}
|
||||
|
||||
// 分组标题(§7.10):text/heading 字号 + text/secondary 色,下方 1px divider。
|
||||
QWidget* sectionTitle(const QString& text, QWidget* parent) {
|
||||
auto* box = new QWidget(parent);
|
||||
auto* v = new QVBoxLayout(box);
|
||||
v->setContentsMargins(0, 0, 0, 0);
|
||||
v->setSpacing(geopro::app::space::kSm);
|
||||
|
||||
auto* t = new QLabel(text, box);
|
||||
geopro::app::applyTokenizedStyleSheet(
|
||||
t, QStringLiteral("color:{{text/secondary}}; font-size:%1px; font-weight:%2;")
|
||||
.arg(geopro::app::scaledPx(geopro::app::type::kHeading))
|
||||
.arg(geopro::app::type::kWeightSemibold));
|
||||
v->addWidget(t);
|
||||
|
||||
auto* divider = new QFrame(box);
|
||||
divider->setFrameShape(QFrame::HLine);
|
||||
divider->setFixedHeight(1);
|
||||
geopro::app::applyTokenizedStyleSheet(
|
||||
divider, QStringLiteral("background:{{divider}}; border:none;"));
|
||||
v->addWidget(divider);
|
||||
return box;
|
||||
}
|
||||
|
||||
QWidget* buildAppearancePage() {
|
||||
auto* page = new QWidget();
|
||||
auto* v = new QVBoxLayout(page);
|
||||
v->setContentsMargins(24, 20, 24, 20);
|
||||
v->setSpacing(16);
|
||||
v->addWidget(sectionTitle(QStringLiteral("外观"), page));
|
||||
v->setContentsMargins(geopro::app::space::kXxl, geopro::app::scaledPx(20),
|
||||
geopro::app::space::kXxl, geopro::app::scaledPx(20));
|
||||
v->setSpacing(geopro::app::space::kXl);
|
||||
geopro::app::formkit::addSection(v, QStringLiteral("外观"), page, false);
|
||||
|
||||
// 主题:跟随系统 / 浅色 / 深色(热切)。
|
||||
auto* themeCombo = new QComboBox(page);
|
||||
|
|
@ -111,8 +89,9 @@ QWidget* buildAppearancePage() {
|
|||
// 字号改动:持久化 + 提示重启(提供立即重启)。
|
||||
auto* restartRow = new QWidget(page);
|
||||
auto* rlay = new QHBoxLayout(restartRow);
|
||||
rlay->setContentsMargins(160 + geopro::app::space::kLg, 0, 0, 0); // 与控件列对齐
|
||||
rlay->setSpacing(10);
|
||||
rlay->setContentsMargins(geopro::app::scaledPx(160) + geopro::app::space::kLg, 0, 0,
|
||||
0); // 与控件列对齐
|
||||
rlay->setSpacing(geopro::app::space::kMl);
|
||||
auto* hint = new QLabel(QStringLiteral("界面字号将在重启后生效"), restartRow);
|
||||
geopro::app::applyTokenizedStyleSheet(
|
||||
hint, QStringLiteral("color:{{text/secondary}}; font-size:%1px;")
|
||||
|
|
@ -141,9 +120,10 @@ QWidget* buildAppearancePage() {
|
|||
QWidget* buildAboutPage() {
|
||||
auto* page = new QWidget();
|
||||
auto* v = new QVBoxLayout(page);
|
||||
v->setContentsMargins(24, 20, 24, 20);
|
||||
v->setSpacing(12);
|
||||
v->addWidget(sectionTitle(QStringLiteral("关于"), page));
|
||||
v->setContentsMargins(geopro::app::space::kXxl, geopro::app::scaledPx(20),
|
||||
geopro::app::space::kXxl, geopro::app::scaledPx(20));
|
||||
v->setSpacing(geopro::app::space::kLg);
|
||||
geopro::app::formkit::addSection(v, QStringLiteral("关于"), page, false);
|
||||
|
||||
auto* ver = new QLabel(QStringLiteral("Geopro 3.0 — 项目分析视图 (M1)"), page);
|
||||
ver->setStyleSheet(QStringLiteral("font-size:%1px; font-weight:600;")
|
||||
|
|
@ -169,7 +149,7 @@ QWidget* buildAboutPage() {
|
|||
|
||||
SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) {
|
||||
setWindowTitle(QStringLiteral("设置"));
|
||||
resize(720, 480);
|
||||
resize(geopro::app::scaledPx(720), geopro::app::scaledPx(480));
|
||||
|
||||
auto* root = new QHBoxLayout(this);
|
||||
root->setContentsMargins(0, 0, 0, 0);
|
||||
|
|
@ -179,7 +159,7 @@ SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) {
|
|||
// 左竖条用「选中项 2px 左边框 + accent/primary」实现,非选中项留同宽透明左边框防文字跳动。
|
||||
auto* sidebar = new QListWidget(this);
|
||||
sidebar->setObjectName(QStringLiteral("settingsSidebar"));
|
||||
sidebar->setFixedWidth(160);
|
||||
sidebar->setFixedWidth(geopro::app::scaledPx(160));
|
||||
sidebar->setIconSize(QSize(geopro::app::scaledPx(16), geopro::app::scaledPx(16)));
|
||||
geopro::app::applyTokenizedStyleSheet(
|
||||
sidebar,
|
||||
|
|
|
|||
|
|
@ -263,6 +263,28 @@ QLineEdit:disabled {
|
|||
color: {{text/disabled}};
|
||||
}
|
||||
|
||||
/* 多行文本(备注/描述):与输入框同款 box(边框/底色/圆角/内距),仅不设 min-height
|
||||
(高度由控件自定)。避免多行框沿用 Fusion 默认边框、与单行输入观感不一。 */
|
||||
QPlainTextEdit, QTextEdit {
|
||||
background: {{bg/panel}};
|
||||
color: {{text/primary}};
|
||||
border: 1px solid {{border/default}};
|
||||
border-radius: 4px; /* radius/sm */
|
||||
padding: 4px 8px;
|
||||
selection-background-color: {{accent/primary}};
|
||||
selection-color: {{text/on-primary}};
|
||||
}
|
||||
QPlainTextEdit:hover, QTextEdit:hover {
|
||||
border-color: {{border/strong}};
|
||||
}
|
||||
QPlainTextEdit:focus, QTextEdit:focus {
|
||||
border: 1px solid {{border/focus}};
|
||||
}
|
||||
QPlainTextEdit:disabled, QTextEdit:disabled {
|
||||
background: {{bg/app}};
|
||||
color: {{text/disabled}};
|
||||
}
|
||||
|
||||
/* ── 滚动条:纤细现代(无需图片资源)───────────────────────── */
|
||||
QScrollBar:vertical {
|
||||
background: transparent;
|
||||
|
|
@ -388,22 +410,24 @@ QComboBox:disabled {
|
|||
background: {{bg/app}};
|
||||
color: {{text/disabled}};
|
||||
}
|
||||
/* 日期/时间编辑器:与输入框/下拉框同款外观(box 一致,避免「布设日期」与下拉框不一致)。 */
|
||||
QDateEdit, QTimeEdit, QDateTimeEdit {
|
||||
/* 数字框/日期/时间编辑器:与输入框/下拉框同款外观。QSpinBox/QDoubleSpinBox/QDateEdit/
|
||||
QTimeEdit/QDateTimeEdit 均派生自 QAbstractSpinBox,一处统一其 box(高度/边框/圆角/内距),
|
||||
避免数字框比下拉框/输入框矮(历史坑:只给了 QLineEdit/QComboBox/Date 没给 SpinBox)。 */
|
||||
QAbstractSpinBox {
|
||||
background: {{bg/panel}};
|
||||
color: {{text/primary}};
|
||||
border: 1px solid {{border/default}};
|
||||
border-radius: 4px; /* radius/sm */
|
||||
padding: 6px 8px;
|
||||
min-height: 16px;
|
||||
min-height: 16px; /* 与 QLineEdit/QComboBox 完全一致 → 同高 */
|
||||
}
|
||||
QDateEdit:hover, QTimeEdit:hover, QDateTimeEdit:hover {
|
||||
QAbstractSpinBox:hover {
|
||||
border-color: {{border/strong}};
|
||||
}
|
||||
QDateEdit:focus, QTimeEdit:focus, QDateTimeEdit:focus {
|
||||
QAbstractSpinBox:focus {
|
||||
border-color: {{border/focus}};
|
||||
}
|
||||
QDateEdit:disabled, QTimeEdit:disabled, QDateTimeEdit:disabled {
|
||||
QAbstractSpinBox:disabled {
|
||||
background: {{bg/app}};
|
||||
color: {{text/disabled}};
|
||||
}
|
||||
|
|
@ -421,6 +445,38 @@ QComboBox::down-arrow, QDateEdit::down-arrow, QTimeEdit::down-arrow, QDateTimeEd
|
|||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
/* 数字框上下按钮:平面化 + 扁平 chevron(与下拉箭头同族)。覆写 up/down-button 须同时给
|
||||
up/down-arrow 图,否则 Fusion 原生箭头消失。日期/时间用日历下拉(::drop-down),不在此列。 */
|
||||
QSpinBox::up-button, QDoubleSpinBox::up-button {
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: top right;
|
||||
width: 18px;
|
||||
border: none;
|
||||
border-left: 1px solid {{border/default}};
|
||||
background: transparent;
|
||||
}
|
||||
QSpinBox::down-button, QDoubleSpinBox::down-button {
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: bottom right;
|
||||
width: 18px;
|
||||
border: none;
|
||||
border-left: 1px solid {{border/default}};
|
||||
background: transparent;
|
||||
}
|
||||
QSpinBox::up-button:hover, QDoubleSpinBox::up-button:hover,
|
||||
QSpinBox::down-button:hover, QDoubleSpinBox::down-button:hover {
|
||||
background: {{bg/hover}};
|
||||
}
|
||||
QSpinBox::up-arrow, QDoubleSpinBox::up-arrow {
|
||||
image: url(:/icons/chevron-up.svg);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
QSpinBox::down-arrow, QDoubleSpinBox::down-arrow {
|
||||
image: url(:/icons/chevron-down.svg);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
QComboBox QAbstractItemView {
|
||||
background: {{bg/panel}};
|
||||
border: 1px solid {{border/default}};
|
||||
|
|
|
|||
|
|
@ -26,15 +26,17 @@ VolumeParamsDialog::VolumeParamsDialog(int sourceCount, QWidget* parent) : QDial
|
|||
setWindowTitle(QStringLiteral("生成三维体"));
|
||||
setModal(true);
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
root->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
|
||||
geopro::app::space::kLg, geopro::app::space::kLg);
|
||||
root->setSpacing(geopro::app::space::kMd);
|
||||
auto* root = formkit::dialogRoot(this);
|
||||
|
||||
auto* intro = new QLabel(QStringLiteral("由 %1 个源数据集插值生成三维体").arg(sourceCount));
|
||||
geopro::app::applyTokenizedStyleSheet(intro, QStringLiteral("color:{{text/secondary}};"));
|
||||
root->addWidget(intro);
|
||||
|
||||
// 统一外壳:表单卡片 + 分组标题(与「数据详情 / 属性面板」同款),编辑态/只读态一致。
|
||||
auto* card = formkit::formCard(this);
|
||||
auto* cardLay = formkit::cardBody(card);
|
||||
formkit::addSection(cardLay, QStringLiteral("参数"), card, false);
|
||||
|
||||
auto* form = formkit::makeEditForm();
|
||||
|
||||
name_ = new QLineEdit(QStringLiteral("三维体"));
|
||||
|
|
@ -72,12 +74,10 @@ VolumeParamsDialog::VolumeParamsDialog(int sourceCount, QWidget* parent) : QDial
|
|||
form->addRow(formkit::editLabel(QStringLiteral("IDW 幂次")), power_);
|
||||
form->addRow(formkit::editLabel(QStringLiteral("最大影响距离 (米)")), maxDist_);
|
||||
|
||||
root->addLayout(form);
|
||||
cardLay->addLayout(form);
|
||||
root->addWidget(card);
|
||||
|
||||
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
root->addWidget(buttons);
|
||||
formkit::addDialogButtons(root, this, QStringLiteral("生成"), QStringLiteral("取消"));
|
||||
}
|
||||
|
||||
QString VolumeParamsDialog::volumeName() const {
|
||||
|
|
|
|||
|
|
@ -1015,8 +1015,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
rightDock->setWidget(anomalyPanel.container);
|
||||
auto* rightArea = dockManager->addDockWidget(ads::RightDockWidgetArea, rightDock);
|
||||
|
||||
// 右下 dock:属性(数据集属性,上半只读元字段 + 下半可编辑描述)。
|
||||
auto* propView = new geopro::app::DatasetAttrPanel(projectRepo);
|
||||
// 右下 dock:属性(数据集属性,只读元字段;不可编辑)。
|
||||
auto* propView = new geopro::app::DatasetAttrPanel();
|
||||
auto* propDock = new ads::CDockWidget(QStringLiteral("数据集属性"));
|
||||
propDock->setWidget(
|
||||
wrapWithHeader(geopro::app::Glyph::Property, QStringLiteral("数据集属性"), propView));
|
||||
|
|
@ -1040,7 +1040,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
|
||||
// ── 单击左下数据列表的采集批次(DS) → 属性表单 + 聚焦详情已开页 ──
|
||||
QObject::connect(datasetList, &QTreeWidget::itemClicked, datasetList,
|
||||
[&nav, &detailCtrl, propView](QTreeWidgetItem* item, int) {
|
||||
[&nav, &detailCtrl](QTreeWidgetItem* item, int) {
|
||||
if (item->data(0, geopro::app::kDsLoadMoreRole).toBool()) {
|
||||
nav.loadMoreData();
|
||||
return;
|
||||
|
|
@ -1048,7 +1048,6 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
const QString dsId = item->data(0, geopro::app::kDsIdRole).toString();
|
||||
if (dsId.isEmpty()) return;
|
||||
nav.selectDataset(dsId); // 只读元字段表单(datasetDetailLoaded)
|
||||
propView->selectDataset(dsId); // 可编辑描述:回填 + 启用保存
|
||||
detailCtrl.focusDataset(dsId); // 单击=聚焦已开页
|
||||
});
|
||||
|
||||
|
|
@ -1370,9 +1369,6 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
toast(QStringLiteral("保存成功"));
|
||||
nav.switchProject(nav.currentProjectId());
|
||||
});
|
||||
// 数据集属性面板描述保存成功 → toast。
|
||||
QObject::connect(propView, &geopro::app::DatasetAttrPanel::saved, &window,
|
||||
[toast]() { toast(QStringLiteral("描述已保存")); });
|
||||
|
||||
// 增删改结果 → 状态栏反馈(成功后控制器已自行刷新)。
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::mutationSucceeded, &window,
|
||||
|
|
@ -1473,9 +1469,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
[&detailCtrl, dsId, ddCode, dsName]() {
|
||||
detailCtrl.openDataset(dsId, ddCode, dsName);
|
||||
});
|
||||
menu.addAction(QStringLiteral("属性"), datasetList, [&nav, propView, dsId]() {
|
||||
menu.addAction(QStringLiteral("属性"), datasetList, [&nav, dsId]() {
|
||||
nav.selectDataset(dsId); // 只读元字段
|
||||
propView->selectDataset(dsId); // 可编辑描述
|
||||
});
|
||||
menu.addSeparator();
|
||||
QMenu* plugins = menu.addMenu(QStringLiteral("插件"));
|
||||
|
|
|
|||
|
|
@ -1,61 +1,20 @@
|
|||
#include "panels/DatasetAttrPanel.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Theme.hpp"
|
||||
#include "api/NavRequest.hpp"
|
||||
#include "panels/DynamicFormView.hpp"
|
||||
#include "repo/IAsyncProjectRepository.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
DatasetAttrPanel::DatasetAttrPanel(geopro::data::IAsyncProjectRepository& repo, QWidget* parent)
|
||||
: QWidget(parent), repo_(repo) {
|
||||
DatasetAttrPanel::DatasetAttrPanel(QWidget* parent) : QWidget(parent) {
|
||||
auto* lay = new QVBoxLayout(this);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->setSpacing(0);
|
||||
|
||||
// 上半:只读元字段(复用 DynamicFormView)。
|
||||
// 只读元字段(复用 DynamicFormView)。数据集属性不可编辑。
|
||||
metaView_ = new DynamicFormView(this);
|
||||
metaView_->showMessage(QStringLiteral("(单击数据集查看属性)"));
|
||||
lay->addWidget(metaView_, 1);
|
||||
|
||||
// 下半:可编辑描述区。
|
||||
auto* descBox = new QWidget(this);
|
||||
auto* descLay = new QVBoxLayout(descBox);
|
||||
descLay->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kSm,
|
||||
geopro::app::space::kLg, geopro::app::space::kMd);
|
||||
descLay->setSpacing(geopro::app::space::kMd); // 标签↔输入↔保存:统一 ≈8px 节奏(规范 §7.0)
|
||||
|
||||
auto* hint = new QLabel(QStringLiteral("描述(备注)"), descBox);
|
||||
geopro::app::applyTokenizedStyleSheet(hint, QStringLiteral("color:{{text/secondary}};"));
|
||||
descLay->addWidget(hint);
|
||||
|
||||
descEdit_ = new QTextEdit(descBox);
|
||||
descEdit_->setAcceptRichText(false); // 纯文本(与服务端 delta 纯文本往返一致)
|
||||
descEdit_->setEnabled(false);
|
||||
descEdit_->setMaximumHeight(geopro::app::scaledPx(120));
|
||||
descLay->addWidget(descEdit_);
|
||||
|
||||
auto* btnRow = new QHBoxLayout();
|
||||
status_ = new QLabel(QString(), descBox);
|
||||
geopro::app::applyTokenizedStyleSheet(status_, QStringLiteral("color:{{text/disabled}};"));
|
||||
btnRow->addWidget(status_, 1);
|
||||
saveBtn_ = new QPushButton(QStringLiteral("保存"), descBox);
|
||||
saveBtn_->setEnabled(false);
|
||||
btnRow->addWidget(saveBtn_);
|
||||
descLay->addLayout(btnRow);
|
||||
|
||||
lay->addWidget(descBox);
|
||||
|
||||
QObject::connect(saveBtn_, &QPushButton::clicked, this, &DatasetAttrPanel::onSave);
|
||||
}
|
||||
|
||||
void DatasetAttrPanel::setForm(const geopro::data::DynamicForm& form) {
|
||||
|
|
@ -63,66 +22,7 @@ void DatasetAttrPanel::setForm(const geopro::data::DynamicForm& form) {
|
|||
}
|
||||
|
||||
void DatasetAttrPanel::showMessage(const QString& message) {
|
||||
dsObjectId_.clear();
|
||||
metaView_->showMessage(message);
|
||||
descEdit_->clear();
|
||||
descEdit_->setEnabled(false);
|
||||
saveBtn_->setEnabled(false);
|
||||
status_->clear();
|
||||
}
|
||||
|
||||
void DatasetAttrPanel::selectDataset(const QString& dsObjectId) {
|
||||
// 切换数据集:中止在途保存,避免旧 save 回调串台触发 saved()/启用按钮。
|
||||
if (saveReq_) saveReq_->abort();
|
||||
dsObjectId_ = dsObjectId;
|
||||
descEdit_->setEnabled(false);
|
||||
saveBtn_->setEnabled(false);
|
||||
status_->setText(QStringLiteral("加载描述…"));
|
||||
|
||||
if (loadReq_) loadReq_->abort();
|
||||
loadReq_ = repo_.loadDatasetDetailAsync(dsObjectId.toStdString());
|
||||
QObject::connect(loadReq_, &geopro::data::NavRequest::done, this, [this](const QVariant& v) {
|
||||
descEdit_->setPlainText(v.toString()); // payload=QString(现有描述纯文本)
|
||||
descEdit_->setEnabled(true);
|
||||
saveBtn_->setEnabled(true);
|
||||
status_->clear();
|
||||
});
|
||||
QObject::connect(loadReq_, &geopro::data::NavRequest::failed, this, [this](const QString& msg) {
|
||||
// 加载失败仍允许编辑/保存(视为新建描述),仅提示。
|
||||
descEdit_->setEnabled(true);
|
||||
saveBtn_->setEnabled(true);
|
||||
status_->setText(QStringLiteral("加载描述失败:%1(可直接编辑保存)").arg(msg));
|
||||
});
|
||||
}
|
||||
|
||||
void DatasetAttrPanel::onSave() {
|
||||
if (dsObjectId_.isEmpty()) return;
|
||||
const QString text = descEdit_->toPlainText();
|
||||
// 最小 Quill delta:[{ insert: <文本 + "\n"> }],承载纯文本与服务端往返。
|
||||
const QJsonArray deltaOps{QJsonObject{{QStringLiteral("insert"), text + QStringLiteral("\n")}}};
|
||||
const QJsonObject body{
|
||||
{QStringLiteral("dsObjectId"), dsObjectId_},
|
||||
{QStringLiteral("description"), text},
|
||||
{QStringLiteral("attachedParameters"),
|
||||
QJsonObject{{QStringLiteral("deltaContent"), deltaOps}}}};
|
||||
|
||||
saveBtn_->setEnabled(false);
|
||||
descEdit_->setEnabled(false);
|
||||
status_->setText(QStringLiteral("保存中…"));
|
||||
if (saveReq_) saveReq_->abort();
|
||||
saveReq_ =
|
||||
repo_.updateDatasetAsync(QJsonDocument(body).toJson(QJsonDocument::Compact).toStdString());
|
||||
QObject::connect(saveReq_, &geopro::data::NavRequest::done, this, [this](const QVariant&) {
|
||||
descEdit_->setEnabled(true);
|
||||
saveBtn_->setEnabled(true);
|
||||
status_->clear();
|
||||
emit saved();
|
||||
});
|
||||
QObject::connect(saveReq_, &geopro::data::NavRequest::failed, this, [this](const QString& msg) {
|
||||
status_->setText(QStringLiteral("保存失败:%1").arg(msg));
|
||||
descEdit_->setEnabled(true);
|
||||
saveBtn_->setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
|
|||
|
|
@ -1,52 +1,25 @@
|
|||
#pragma once
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "repo/RepoTypes.hpp"
|
||||
|
||||
namespace geopro::data {
|
||||
class IAsyncProjectRepository;
|
||||
class NavRequest;
|
||||
} // namespace geopro::data
|
||||
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QTextEdit;
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
class DynamicFormView;
|
||||
|
||||
// 数据集属性面板(右下「数据集属性」):上半只读元字段(DynamicForm)+ 下半可编辑描述 + 保存。
|
||||
// 元字段无写接口,仅展示(datasetDetailLoaded 推送的 DynamicForm)。
|
||||
// 描述可写:PUT dsObject/updateDsObject/ body
|
||||
// { dsObjectId, description:<纯文本>, attachedParameters:{ deltaContent:[{ insert:<文本+"\n"> }] } }
|
||||
// 描述文本经 loadDatasetDetailAsync(GET getDetail) 回填(payload=QString)。
|
||||
// 数据集属性面板(右下「数据集属性」):**只读**元字段展示(datasetDetailLoaded 推送的 DynamicForm)。
|
||||
// 数据集属性不可编辑——仅展示,无写接口(描述编辑功能已移除)。
|
||||
class DatasetAttrPanel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DatasetAttrPanel(geopro::data::IAsyncProjectRepository& repo, QWidget* parent = nullptr);
|
||||
explicit DatasetAttrPanel(QWidget* parent = nullptr);
|
||||
|
||||
void setForm(const geopro::data::DynamicForm& form); // 元字段只读展示
|
||||
void showMessage(const QString& message); // 空/占位
|
||||
void selectDataset(const QString& dsObjectId); // 选中 → 回填描述、启用保存
|
||||
|
||||
signals:
|
||||
void saved(); // 描述保存成功
|
||||
|
||||
private:
|
||||
void onSave();
|
||||
|
||||
geopro::data::IAsyncProjectRepository& repo_;
|
||||
QString dsObjectId_;
|
||||
|
||||
DynamicFormView* metaView_ = nullptr;
|
||||
QTextEdit* descEdit_ = nullptr;
|
||||
QLabel* status_ = nullptr;
|
||||
QPushButton* saveBtn_ = nullptr;
|
||||
QPointer<geopro::data::NavRequest> loadReq_;
|
||||
QPointer<geopro::data::NavRequest> saveReq_;
|
||||
};
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@
|
|||
跨明暗主题用中性灰,避免覆写 ::drop-down 后 Fusion 原生斜角箭头的复古观感。 -->
|
||||
<qresource prefix="/icons">
|
||||
<file alias="chevron-down.svg">icons/chevron-down.svg</file>
|
||||
<file alias="chevron-up.svg">icons/chevron-up.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
||||
<path d="M3 7.5 L6 4.5 L9 7.5" fill="none" stroke="#6B7785" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 229 B |
Loading…
Reference in New Issue