diff --git a/android/rshb/PayByPhoneScript.cpp b/android/rshb/PayByPhoneScript.cpp index 9a6b23e..7c8f55e 100644 --- a/android/rshb/PayByPhoneScript.cpp +++ b/android/rshb/PayByPhoneScript.cpp @@ -260,7 +260,7 @@ void PayByPhoneScript::makePayment( void PayByPhoneScript::doStart() { 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()) { m_error = "Device or app not found...."; diff --git a/database/app_data.db b/database/app_data.db index 6adda89..41dbd73 100644 Binary files a/database/app_data.db and b/database/app_data.db differ diff --git a/database/dao/ApplicationDAO.cpp b/database/dao/ApplicationDAO.cpp index d81f73e..1f29cb7 100644 --- a/database/dao/ApplicationDAO.cpp +++ b/database/dao/ApplicationDAO.cpp @@ -59,7 +59,20 @@ bool ApplicationDAO::updatePinCode(const QString &appId, const QString &pinCode) 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()); query.prepare(R"( @@ -78,17 +91,30 @@ ApplicationInfo ApplicationDAO::getApplicationsByDeviceId(const QString &deviceI } while (query.next()) { - 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; + return parseApplication(query); } - return {}; } + +QList ApplicationDAO::getApplicationsByDeviceId(const QString &deviceId) { + QSqlQuery query(DatabaseManager::instance().database()); + QList 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; +} diff --git a/database/dao/ApplicationDAO.h b/database/dao/ApplicationDAO.h index e4e6997..6d3ba5c 100644 --- a/database/dao/ApplicationDAO.h +++ b/database/dao/ApplicationDAO.h @@ -8,5 +8,7 @@ public: [[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 getApplicationsByDeviceId(const QString &deviceId); }; diff --git a/views/bank/BankSettingsWindow.cpp b/views/bank/BankSettingsWindow.cpp new file mode 100644 index 0000000..d3cd064 --- /dev/null +++ b/views/bank/BankSettingsWindow.cpp @@ -0,0 +1,221 @@ +#include "BankSettingsWindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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); +} diff --git a/views/bank/BankSettingsWindow.h b/views/bank/BankSettingsWindow.h new file mode 100644 index 0000000..1600520 --- /dev/null +++ b/views/bank/BankSettingsWindow.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include + +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); +}; diff --git a/views/common/PaddedItemDelegate.h b/views/common/PaddedItemDelegate.h new file mode 100644 index 0000000..7ff99bc --- /dev/null +++ b/views/common/PaddedItemDelegate.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +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; +}; diff --git a/views/device/DeviceWidget.cpp b/views/device/DeviceWidget.cpp index 11ec977..1a6380c 100644 --- a/views/device/DeviceWidget.cpp +++ b/views/device/DeviceWidget.cpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include "bank/BankSettingsWindow.h" + DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(parent) { auto *layout = new QGridLayout(this); @@ -89,14 +92,13 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p "border-radius: 6px;" ); - // 3) две кнопки auto *btn1 = new QPushButton(overlay); QIcon btnIcon(QCoreApplication::applicationDirPath() + "/images/turn_on.png"); btn1->setIcon(btnIcon); btn1->setIconSize(QSize(16, 16)); btn1->setFlat(true); btn1->setStyleSheet( - "margin: 0px;" + "margin: 0px;" "background-color: transparent;" "border: none;" ); @@ -107,12 +109,11 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p btn2->setIconSize(QSize(16, 16)); btn2->setFlat(true); btn2->setStyleSheet( - "margin: 0px;" + "margin: 0px;" "background-color: transparent;" "border: none;" ); - // собираем hbox->addWidget(icon); hbox->addWidget(label); @@ -125,7 +126,37 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p } vbox->addStretch(); + // кладём overlay в ту же ячейку, что и картинку, // но выравниваем его по левому-нижнему углу 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; + + // Если окна нет (либо ещё не было создано, либо уже удалилось) — создаём заново + 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); } diff --git a/views/device/DeviceWidget.h b/views/device/DeviceWidget.h index e2d983f..e37fc03 100644 --- a/views/device/DeviceWidget.h +++ b/views/device/DeviceWidget.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include "db/DeviceInfo.h" @@ -6,5 +7,5 @@ class DeviceWidget final : public QWidget { Q_OBJECT public: - explicit DeviceWidget(const DeviceInfo& device, QWidget* parent = nullptr); + explicit DeviceWidget(const DeviceInfo &device, QWidget *parent = nullptr); }; diff --git a/views/device/EditBankDialog.h b/views/device/EditBankDialog.h new file mode 100644 index 0000000..e19e641 --- /dev/null +++ b/views/device/EditBankDialog.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include +#include +#include +#include + +// 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; +};