1
0
forked from BRT/arc

Add bank management functionality with UI and DAO improvements

Introduce `BankSettingsWindow` for managing bank data, added `EditBankDialog` for editing bank details, and created reusable `PaddedItemDelegate` for list padding. Refactor DAO to support fetching multiple applications by device ID for improved data handling. Adjust existing components to integrate new bank-related features.
This commit is contained in:
slava 2025-05-27 14:25:33 +07:00
parent ba0b6763a2
commit dd66adf455
10 changed files with 399 additions and 19 deletions

View File

@ -260,7 +260,7 @@ void PayByPhoneScript::makePayment(
void PayByPhoneScript::doStart() { void PayByPhoneScript::doStart() {
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
const ApplicationInfo app = ApplicationDAO::getApplicationsByDeviceId(m_account.deviceId, m_account.appName); const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appName);
if (device.id.isEmpty() || app.id.isEmpty()) { if (device.id.isEmpty() || app.id.isEmpty()) {
m_error = "Device or app not found...."; m_error = "Device or app not found....";

Binary file not shown.

View File

@ -59,7 +59,20 @@ bool ApplicationDAO::updatePinCode(const QString &appId, const QString &pinCode)
return true; return true;
} }
ApplicationInfo ApplicationDAO::getApplicationsByDeviceId(const QString &deviceId, const QString &appName) { ApplicationInfo parseApplication(const QSqlQuery &query) {
ApplicationInfo app;
app.id = query.value("id").toString();
app.deviceId = query.value("device_id").toString();
app.pinCode = query.value("pin_code").toString();
app.name = query.value("name").toString();
app.package = query.value("package").toString();
app.status = query.value("status").toString();
app.comment = query.value("comment").toString();
app.image = query.value("image").toByteArray();
return app;
}
ApplicationInfo ApplicationDAO::getApplication(const QString &deviceId, const QString &appName) {
QSqlQuery query(DatabaseManager::instance().database()); QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"( query.prepare(R"(
@ -78,17 +91,30 @@ ApplicationInfo ApplicationDAO::getApplicationsByDeviceId(const QString &deviceI
} }
while (query.next()) { while (query.next()) {
ApplicationInfo app; return parseApplication(query);
app.id = query.value("id").toString();
app.deviceId = query.value("device_id").toString();
app.pinCode = query.value("pin_code").toString();
app.name = query.value("name").toString();
app.package = query.value("package").toString();
app.status = query.value("status").toString();
app.comment = query.value("comment").toString();
app.image = query.value("image").toByteArray();
return app;
} }
return {}; return {};
} }
QList<ApplicationInfo> ApplicationDAO::getApplicationsByDeviceId(const QString &deviceId) {
QSqlQuery query(DatabaseManager::instance().database());
QList<ApplicationInfo> list;
query.prepare(R"(
SELECT id, device_id, pin_code, name, package, status, comment, image
FROM applications
WHERE device_id = :device_id
)");
query.bindValue(":device_id", deviceId);
if (!query.exec()) {
qWarning() << "Ошибка при получении приложений по deviceId:" << query.lastError().text();
return {};
}
while (query.next()) {
list.append(parseApplication(query));
}
return list;
}

View File

@ -8,5 +8,7 @@ public:
[[nodiscard]] static bool updatePinCode(const QString &appId, const QString &pinCode); [[nodiscard]] static bool updatePinCode(const QString &appId, const QString &pinCode);
[[nodiscard]] static ApplicationInfo getApplicationsByDeviceId(const QString &deviceId, const QString &appName); [[nodiscard]] static ApplicationInfo getApplication(const QString &deviceId, const QString &appName);
[[nodiscard]] static QList<ApplicationInfo> getApplicationsByDeviceId(const QString &deviceId);
}; };

View File

@ -0,0 +1,221 @@
#include "BankSettingsWindow.h"
#include <QVBoxLayout>
#include <QDir>
#include <QFile>
#include <QHeaderView>
#include <QScrollArea>
#include <QCoreApplication>
#include <QPushButton>
#include <QTableWidget>
#include <utility>
#include <QMessageBox>
#include <QLabel>
#include <QDialog>
#include "dao/ApplicationDAO.h"
#include "db/ApplicationInfo.h"
#include "common/PaddedItemDelegate.h"
#include "device/EditBankDialog.h"
#include "widget/common/CollapsibleSection.h"
QTableWidgetItem *BankSettingsWindow::createTableWidget(const QString &text) {
QTableWidgetItem *item = new QTableWidgetItem(text);
item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
return item;
}
BankSettingsWindow::BankSettingsWindow(QWidget *parent, QString deviceId, QString deviceName)
: QWidget(parent,
Qt::Window
| Qt::WindowTitleHint
| Qt::WindowSystemMenuHint
| Qt::WindowCloseButtonHint
| Qt::WindowMinMaxButtonsHint),
m_deviceId(std::move(deviceId)),
m_deviceName(std::move(deviceName)
) {
setWindowTitle("Настройки банка [" + m_deviceName + "]");
setMinimumSize(820, 400);
setMaximumSize(820, 400);
resize(820, 400);
auto *mainLayout = new QVBoxLayout(this);
// Скролл-область
auto *scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
// Виджет-контейнер, в котором будет layout с таблицами и лейблами
auto *contentWidget = new QWidget;
auto *layout = new QVBoxLayout(contentWidget);
// Убираем отступы и рамку
contentWidget->setContentsMargins(0, 0, 0, 0); // Убираем отступы
layout->setContentsMargins(0, 0, 0, 0); // Убираем отступы у layout
scrollArea->setStyleSheet("border: none;"); // Убираем рамку
layout->setAlignment(Qt::AlignTop);
QList<ApplicationInfo> applications = ApplicationDAO::getApplicationsByDeviceId(m_deviceId);
QTableWidget *tableWidget = new QTableWidget(this);
tableWidget->setSelectionMode(QAbstractItemView::NoSelection);
tableWidget->setFocusPolicy(Qt::NoFocus);
tableWidget->setStyleSheet(
"QTableWidget {"
" border: 1px solid grey;"
// " gridline-color: lightgrey;"
// " background-color: #f9f9f9;"
" font-size: 14px;"
"}"
"QHeaderView::section {"
// " background-color: #e0e0e0;"
// " border: 1px solid grey;"
" padding: 4px;"
" font-weight: bold;"
" text-align: left center;"
"}"
"QTableWidget::item {"
" padding: 2px;"
" border: none;"
" text-align: left;"
" vertical-align: middle; "
"}"
"QTableWidget::item:selected {"
// " background-color: #cce5ff;"
// " color: #003366;"
"}"
"QTableCornerButton::section {"
// " background-color: #e0e0e0;"
// " border: 1px solid grey;"
"}"
"QPushButton {"
"border-radius: 4px;"
"height: 26px;"
"}"
"QPushButton[btnClass='delete'] {"
"background-color: red;"
"}"
"QPushButton[btnClass='edit'] {"
"background-color: green;"
"}"
"QPushButton[btnClass='delete']:pressed {"
"background-color: blue;"
"}"
"QPushButton[btnClass='edit']:pressed {"
"background-color: blue;"
"}"
);
tableWidget->setRowCount(applications.length());
tableWidget->setColumnCount(7);
tableWidget->setHorizontalHeaderLabels({
"",
"Имя",
"Пин-код",
"Статус",
"Комментарий",
"",
""
});
tableWidget->setWordWrap(true);
tableWidget->setTextElideMode(Qt::ElideNone);
QMargins cellPadding{8, 4, 8, 4};
auto *delegate = new PaddedItemDelegate(cellPadding, tableWidget);
tableWidget->setItemDelegateForColumn(1, delegate);
tableWidget->setItemDelegateForColumn(2, delegate);
tableWidget->setItemDelegateForColumn(3, delegate);
tableWidget->setItemDelegateForColumn(4, delegate);
tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
for (int i = 0; i < 8; ++i) {
tableWidget->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
}
tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
tableWidget->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch);
for (int k = 0; k < applications.size(); ++k) {
const ApplicationInfo &app = applications[k];
auto *icon = new QLabel(tableWidget);
QPixmap pixmap(QCoreApplication::applicationDirPath() + "/images/alfa_icon.png");
icon->setPixmap(pixmap);
icon->setPixmap(pixmap.scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation));
icon->setStyleSheet(
"background-color: transparent;"
"padding:0 4px 0 0;"
);
icon->setMinimumSize(24, 20);
tableWidget->setCellWidget(k, 0, icon);
tableWidget->setItem(k, 1, createTableWidget(app.name == "rshb" ? "Россельхозбанк" : app.name));
tableWidget->setItem(k, 2, createTableWidget(app.pinCode));
tableWidget->setItem(k, 3, createTableWidget(app.status));
QTableWidgetItem *item = new QTableWidgetItem(app.comment);
item->setTextAlignment(Qt::AlignLeft | Qt::AlignTop);
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
item->setSizeHint(QSize(100, app.comment.length() / 2)); // Минимальная высота ячейки для переноса
tableWidget->setItem(k, 4, item);
// auto *deleteBtnItem = new QTableWidgetItem;
// tableWidget->setItem(k, 4, deleteBtnItem);
auto *deleteBtn = new QPushButton(" Удалить ", tableWidget);
deleteBtn->setProperty("btnClass", "delete");
// deleteBtn->setStyleSheet("padding: 4px 8px;");
// deleteBtn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
tableWidget->setCellWidget(k, 5, deleteBtn);
connect(deleteBtn, &QPushButton::clicked, this, [this, app]() {
QMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Подтверждение"));
msgBox.setText("Точно удалить все данные банка: " + app.name + "?");
// добавляем свои кнопки с нужными надписями
QPushButton *deleteBtn = msgBox.addButton(tr("Удалить"), QMessageBox::AcceptRole);
QPushButton *closeBtn = msgBox.addButton(tr("Отмена"), QMessageBox::RejectRole);
// по умолчанию фокус на «Закрыть»
msgBox.setDefaultButton(closeBtn);
// делаем окно модальным и блокируем всё остальное
msgBox.setModal(true);
msgBox.exec(); // <-- блокирует до закрытия сообщения
if (msgBox.clickedButton() == deleteBtn) {
// тут выполняем удаление
} else {
// просто закрыли — ничего не делаем
}
});
// auto *editBtn = new QTableWidgetItem;
// tableWidget->setItem(k, 5, editBtn);
auto *btn = new QPushButton(" Редактировать ", tableWidget);
btn->setProperty("btnClass", "edit");
// btn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
tableWidget->setCellWidget(k, 6, btn);
connect(btn, &QPushButton::clicked, this, [this]() {
BankEditDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
// Пользователь нажал «OK»
QString v1 = dlg.firstValue();
QString v2 = dlg.secondValue();
bool on = dlg.toggled();
// … обработка значений …
} else {
// Пользователь нажал «Cancel» или закрыл окно
}
});
}
layout->addWidget(tableWidget);
tableWidget->resizeRowsToContents();
// Добавим вертикальный Spacer в конец, чтобы прокручиваемая область не растягивала контент
// QSpacerItem *spacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
// layout->addItem(spacer);
contentWidget->setLayout(layout);
scrollArea->setWidget(contentWidget);
mainLayout->addWidget(scrollArea);
setLayout(mainLayout);
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <qtablewidget.h>
#include <QWidget>
class BankSettingsWindow final : public QWidget {
Q_OBJECT
public:
BankSettingsWindow(QWidget *parent, QString deviceId, QString deviceName);
private:
QString m_deviceId;
QString m_deviceName;
QTableWidgetItem *createTableWidget(const QString &text);
};

View File

@ -0,0 +1,35 @@
#pragma once
#include <QStyledItemDelegate>
#include <QMargins>
class PaddedItemDelegate final : public QStyledItemDelegate {
Q_OBJECT
public:
explicit PaddedItemDelegate(const QMargins &margins, QObject *parent = nullptr)
: QStyledItemDelegate(parent), m_margins(margins) {
}
void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override {
QStyledItemDelegate::initStyleOption(option, index);
// «вырезаем» рамки со всех сторон
option->rect = option->rect.adjusted(
m_margins.left(),
m_margins.top(),
-m_margins.right(),
-m_margins.bottom()
);
}
// расширяем sizeHint — чтобы ResizeToContents «увидел» отступы
[[nodiscard]] QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override {
const QSize baseSize = QStyledItemDelegate::sizeHint(option, index);
return QSize(
baseSize.width() + m_margins.left() + m_margins.right(),
baseSize.height() + m_margins.top() + m_margins.bottom()
);
}
private:
QMargins m_margins;
};

View File

@ -5,6 +5,9 @@
#include <QImage> #include <QImage>
#include <QPushButton> #include <QPushButton>
#include <QCoreApplication> #include <QCoreApplication>
#include <QPointer>
#include "bank/BankSettingsWindow.h"
DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(parent) { DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(parent) {
auto *layout = new QGridLayout(this); auto *layout = new QGridLayout(this);
@ -89,14 +92,13 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p
"border-radius: 6px;" "border-radius: 6px;"
); );
// 3) две кнопки
auto *btn1 = new QPushButton(overlay); auto *btn1 = new QPushButton(overlay);
QIcon btnIcon(QCoreApplication::applicationDirPath() + "/images/turn_on.png"); QIcon btnIcon(QCoreApplication::applicationDirPath() + "/images/turn_on.png");
btn1->setIcon(btnIcon); btn1->setIcon(btnIcon);
btn1->setIconSize(QSize(16, 16)); btn1->setIconSize(QSize(16, 16));
btn1->setFlat(true); btn1->setFlat(true);
btn1->setStyleSheet( btn1->setStyleSheet(
"margin: 0px;" "margin: 0px;"
"background-color: transparent;" "background-color: transparent;"
"border: none;" "border: none;"
); );
@ -107,12 +109,11 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p
btn2->setIconSize(QSize(16, 16)); btn2->setIconSize(QSize(16, 16));
btn2->setFlat(true); btn2->setFlat(true);
btn2->setStyleSheet( btn2->setStyleSheet(
"margin: 0px;" "margin: 0px;"
"background-color: transparent;" "background-color: transparent;"
"border: none;" "border: none;"
); );
// собираем // собираем
hbox->addWidget(icon); hbox->addWidget(icon);
hbox->addWidget(label); hbox->addWidget(label);
@ -125,7 +126,37 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p
} }
vbox->addStretch(); vbox->addStretch();
// кладём overlay в ту же ячейку, что и картинку, // кладём overlay в ту же ячейку, что и картинку,
// но выравниваем его по левому-нижнему углу // но выравниваем его по левому-нижнему углу
layout->addWidget(overlay, 0, 0, Qt::AlignLeft | Qt::AlignBottom); layout->addWidget(overlay, 0, 0, Qt::AlignLeft | Qt::AlignBottom);
auto *addBankBtn = new QPushButton(this);
addBankBtn->setMaximumWidth(300);
addBankBtn->setText("Добавить банк +");
addBankBtn->setStyleSheet(
"background-color: #fff;"
"margin-top: 2px;"
"padding: 4px;"
"color: #000;"
);
QString deviceId = device.id;
QString deviceName = device.name;
connect(addBankBtn, &QPushButton::clicked, this, [deviceId, deviceName]() {
static QPointer<BankSettingsWindow> bankSettingsWindow;
// Если окна нет (либо ещё не было создано, либо уже удалилось) — создаём заново
if (!bankSettingsWindow) {
bankSettingsWindow = new BankSettingsWindow(nullptr, deviceId, deviceName);
bankSettingsWindow->setWindowFlags(bankSettingsWindow->windowFlags() | Qt::Window);
bankSettingsWindow->setAttribute(Qt::WA_DeleteOnClose);
}
// Показать/поднять/активировать
bankSettingsWindow->show();
bankSettingsWindow->raise();
bankSettingsWindow->activateWindow();
});
layout->addWidget(addBankBtn, 1, 0);
} }

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <qtablewidget.h>
#include <QWidget> #include <QWidget>
#include "db/DeviceInfo.h" #include "db/DeviceInfo.h"
@ -6,5 +7,5 @@ class DeviceWidget final : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit DeviceWidget(const DeviceInfo& device, QWidget* parent = nullptr); explicit DeviceWidget(const DeviceInfo &device, QWidget *parent = nullptr);
}; };

View File

@ -0,0 +1,48 @@
#pragma once
#include <QDialog>
#include <QFormLayout>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
// 1) Определяем диалог
class BankEditDialog : public QDialog {
Q_OBJECT
public:
explicit BankEditDialog(QWidget *parent = nullptr) : QDialog(parent) {
setWindowTitle(tr("Введите данные"));
setModal(true); // делаем его модальным
// 2) Форма с полями
auto *form = new QFormLayout(this);
firstEdit = new QLineEdit(this);
secondEdit = new QLineEdit(this);
toggle = new QCheckBox(tr("Включено"), this);
form->addRow(tr("Первое поле:"), firstEdit);
form->addRow(tr("Второе поле:"), secondEdit);
form->addRow(QString(), toggle);
// 3) Кнопки OK / Cancel
auto *buttons = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal,
this
);
form->addRow(buttons);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
QString firstValue() const { return firstEdit->text(); }
QString secondValue() const { return secondEdit->text(); }
bool toggled() const { return toggle->isChecked(); }
private:
QLineEdit *firstEdit;
QLineEdit *secondEdit;
QCheckBox *toggle;
};