1
0
forked from BRT/arc

Implement AccountWindow and TransactionWindow for account management

Added AccountWindow to display account tables linked to devices and TransactionWindow for viewing account details. Enhanced DeviceDAO and AccountDAO to support additional filtering and data retrieval functionalities.
This commit is contained in:
slava 2025-04-22 12:22:56 +07:00
parent c1b8d0c23c
commit c1b34ce7d7
10 changed files with 281 additions and 47 deletions

View File

@ -74,3 +74,35 @@ QList<AccountInfo> AccountDAO::getAccounts(const QString &deviceId, const QStrin
return accounts; return accounts;
} }
AccountInfo AccountDAO::getAccountById(const int accountId) {
AccountInfo acc;
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
SELECT id, device_id, app_name, card_name, amount, update_time, status, description
FROM accounts
WHERE id = :id
LIMIT 1
)");
query.bindValue(":id", accountId);
if (!query.exec()) {
qWarning() << "Ошибка при получении account по ID:" << query.lastError().text();
return acc;
}
if (query.next()) {
acc.id = query.value("id").toInt();
acc.deviceId = query.value("device_id").toString();
acc.appName = query.value("app_name").toString();
acc.cardName = query.value("card_name").toString();
acc.amount = query.value("amount").toDouble();
acc.updateTime = QDateTime::fromString(query.value("update_time").toString(), Qt::ISODate);
acc.status = query.value("status").toString();
acc.description = query.value("description").toString();
}
return acc;
}

View File

@ -8,4 +8,6 @@ public:
[[nodiscard]] static bool deleteAccount(int id); [[nodiscard]] static bool deleteAccount(int id);
[[nodiscard]] static QList<AccountInfo> getAccounts(const QString &deviceId, const QString &appName); [[nodiscard]] static QList<AccountInfo> getAccounts(const QString &deviceId, const QString &appName);
[[nodiscard]] static AccountInfo getAccountById(int accountId);
}; };

View File

@ -4,12 +4,19 @@
#include "DatabaseManager.h" #include "DatabaseManager.h"
#include "db/DeviceInfo.h" #include "db/DeviceInfo.h"
QList<DeviceInfo> DeviceDAO::getAllDevices() { QList<DeviceInfo> DeviceDAO::getAllDevices(const bool online) {
QList<DeviceInfo> devices; QList<DeviceInfo> devices;
QSqlQuery query(DatabaseManager::instance().database()); QSqlQuery query(DatabaseManager::instance().database());
query.prepare( QString sql = R"(
"SELECT id, status, name, android, screen_width, screen_height, battery, update_time, image FROM devices WHERE image IS NOT NULL AND status = 'device'"); SELECT id, status, name, android, screen_width, screen_height, battery, update_time, image
FROM devices
WHERE image IS NOT NULL
)";
if (online) {
sql += " AND status = 'device'";
}
query.prepare(sql);
if (!query.exec()) { if (!query.exec()) {
qWarning() << "Ошибка при получении списка устройств:" << query.lastError().text(); qWarning() << "Ошибка при получении списка устройств:" << query.lastError().text();

View File

@ -10,5 +10,5 @@ public:
[[nodiscard]] static bool updateImage(const QString &deviceId, const QByteArray &imageData); [[nodiscard]] static bool updateImage(const QString &deviceId, const QByteArray &imageData);
[[nodiscard]] static QList<DeviceInfo> getAllDevices(); [[nodiscard]] static QList<DeviceInfo> getAllDevices(bool online);
}; };

100
views/AccountWindow.cpp Normal file
View File

@ -0,0 +1,100 @@
#include "AccountWindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QTableWidget>
#include <QHeaderView>
#include <QScrollArea>
#include "TransactionWindow.h"
#include "dao/AccountDAO.h"
#include "dao/DeviceDAO.h"
AccountWindow::AccountWindow(QWidget *parent): QWidget(parent) {
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<DeviceInfo> devices = DeviceDAO::getAllDevices(false);
for (int i = 0; i < devices.size(); ++i) {
DeviceInfo device = devices[i];
QList<AccountInfo> accounts = AccountDAO::getAccounts(device.id, "bank");
// Текст перед первой таблицей
QString title = QString("%1 [%2] | %3").arg(device.name, device.id, device.status);
QLabel *label = new QLabel(title, this);
layout->addWidget(label);
if (accounts.length() > 0) {
QTableWidget *tableWidget = new QTableWidget(this);
tableWidget->setStyleSheet("QTableWidget {"
"border: 1px solid grey;"
"}");
tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
connect(tableWidget, &QTableWidget::itemDoubleClicked, this, &AccountWindow::onItemDoubleClicked);
tableWidget->setRowCount(accounts.length()); // Устанавливаем количество строк
tableWidget->setColumnCount(5); // Устанавливаем количество столбцов
tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableWidget->setHorizontalHeaderLabels({"Карта", "Сумма", "Время обновления", "Описание", "Статус"});
tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
int totalHeight = tableWidget->horizontalHeader()->height();
for (int k = 0; k < accounts.size(); ++k) {
AccountInfo account = accounts[k];
QTableWidgetItem *item = new QTableWidgetItem("*" + account.cardName);
item->setData(Qt::UserRole, account.id);
tableWidget->setItem(k, 0, item);
tableWidget->setItem(k, 1, new QTableWidgetItem(QString::number(account.amount)));
tableWidget->setItem(k, 2, new QTableWidgetItem(account.updateTime.toString("dd.MM.yyyy HH:mm:ss")));
tableWidget->setItem(k, 3, new QTableWidgetItem(account.description));
tableWidget->setItem(k, 4, new QTableWidgetItem(account.status));
totalHeight += tableWidget->rowHeight(k);
}
tableWidget->setFixedHeight(totalHeight + 2);
layout->addWidget(tableWidget);
}
if (i < devices.size() - 1) {
QFrame *separator = new QFrame(this);
separator->setFrameShape(QFrame::VLine);
separator->setStyleSheet("background-color: black;"); // Цвет разделителя
separator->setFixedHeight(1);
separator->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
layout->addWidget(separator);
}
}
// Добавим вертикальный 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);
setWindowTitle("Таблица устройств");
}
void AccountWindow::onItemDoubleClicked(QTableWidgetItem *item) {
int column = item->column();
if (column == 0) {
int accountId = item->data(Qt::UserRole).toInt();
qDebug() << accountId;
TransactionWindow *detailWindow = new TransactionWindow(accountId, this);
detailWindow->exec(); // Ожидаем закрытия окна
}
}

14
views/AccountWindow.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <qtablewidget.h>
#include <QWidget>
class AccountWindow final : public QWidget {
Q_OBJECT
public:
explicit AccountWindow(QWidget *parent = nullptr);
private:
void onItemDoubleClicked(QTableWidgetItem *item);
};

View File

@ -1,8 +1,10 @@
#include "MainWindow.h" #include "MainWindow.h"
#include "AccountWindow.h"
#include <QLabel> #include <QLabel>
#include <QScrollArea> #include <QScrollArea>
#include <QMenuBar> #include <QMenuBar>
#include <QStackedWidget>
#include "db/DeviceInfo.h" #include "db/DeviceInfo.h"
#include "DeviceWidget.h" #include "DeviceWidget.h"
@ -10,64 +12,102 @@
#include "widget/FlowLayout.h" #include "widget/FlowLayout.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
// QPushButton* btn = new QPushButton("Hello Qt6!", this); // Инициализируем таймер обновления
// setCentralWidget(btn); timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MainWindow::loadDevices);
auto* scrollArea = new QScrollArea(this); stackedWidget = new QStackedWidget(this);
auto* contentWidget = new QWidget; setCentralWidget(stackedWidget);
flowLayout = new FlowLayout(contentWidget);
createDevicePage();
stackedWidget->addWidget(devicePage);
// stackedWidget->setCurrentWidget(devicePage); FIXME
// FIXME переделать на ленивое создание
contentWidget->setContentsMargins(0, 0, 0, 0); // Убираем отступы у контейнера accountPage = new AccountWindow(this);
flowLayout->setContentsMargins(0, 0, 0, 0); // Убираем отступы у layout stackedWidget->addWidget(accountPage);
flowLayout->setSpacing(0); stackedWidget->setCurrentWidget(accountPage);
QVector<DeviceInfo> devices = DeviceDAO::getAllDevices();
for (const auto& device : devices) {
auto* deviceWidget = new DeviceWidget(device); // Создаем виджет, используя первый элемент из списка devices
deviceWidget->setFixedWidth(220);
deviceWidget->setFixedHeight(400);
deviceWidget->setContentsMargins(0, 0, 0, 0);
flowLayout->addWidget(deviceWidget);
}
resize(800, 600); resize(800, 600);
// Работа с меню
QMenu *menu = menuBar()->addMenu("Меню");
auto *devicesPageAction = new QAction("Активные устройства", this);
auto *accountPageAction = new QAction("Аккаунты", this);
menu->addAction(devicesPageAction);
menu->addAction(accountPageAction);
connect(devicesPageAction, &QAction::triggered, this, [=]() {
if (!devicePage) {
createDevicePage();
}
stackedWidget->setCurrentWidget(devicePage);
});
connect(accountPageAction, &QAction::triggered, this, [=]() {
stackedWidget->setCurrentWidget(accountPage);
deleteDevicePage(); // освобождаем ресурсы
});
}
void MainWindow::createDevicePage() {
if (devicePage) {
return;
}
auto *scrollArea = new QScrollArea(this);
auto *contentWidget = new QWidget;
flowLayout = new FlowLayout(contentWidget);
flowLayout->setContentsMargins(0, 0, 0, 0);
flowLayout->setSpacing(0);
QVector<DeviceInfo> devices = DeviceDAO::getAllDevices(true);
for (const auto &device: devices) {
auto *deviceWidget = new DeviceWidget(device);
deviceWidget->setFixedSize(220, 400);
flowLayout->addWidget(deviceWidget);
}
contentWidget->setLayout(flowLayout); contentWidget->setLayout(flowLayout);
scrollArea->setWidget(contentWidget); scrollArea->setWidget(contentWidget);
scrollArea->setWidgetResizable(true); scrollArea->setWidgetResizable(true);
setCentralWidget(scrollArea);
// Меню devicePage = scrollArea;
QMenu* menu = menuBar()->addMenu("Навигация"); stackedWidget->addWidget(devicePage);
QAction* actionPage1 = new QAction("Окно 1", this); timer->start(1000);
QAction* actionPage2 = new QAction("Окно 2", this);
menu->addAction(actionPage1);
menu->addAction(actionPage2);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MainWindow::loadDevices);
timer->start(1000); // каждые 1 сек
} }
void MainWindow::deleteDevicePage() {
void MainWindow::loadDevices() const { if (!devicePage) {
const auto devices = DeviceDAO::getAllDevices(); return;
// Очистить старые виджеты
QLayoutItem* item;
while ((item = flowLayout->takeAt(0)) != nullptr) {
delete item->widget(); // Удаляем виджет
delete item; // Удаляем сам элемент из layout
} }
timer->stop();
stackedWidget->removeWidget(devicePage);
devicePage->deleteLater();
devicePage = nullptr;
flowLayout = nullptr;
}
/**
* Однако, в нем вы вызываете delete для элементов, что изменяет память.
* Для того, чтобы не нарушать константность, этот метод должен быть неконстантным (без const в сигнатуре).
*/
void MainWindow::loadDevices() {
const auto devices = DeviceDAO::getAllDevices(true);
// Очистить старые виджеты
QLayoutItem *item;
while ((item = flowLayout->takeAt(0)) != nullptr) {
if (item->widget()) {
delete item->widget(); // Удаляем виджет
}
delete item; // Удаляем сам элемент из layout
}
// Добавляем новые виджеты для каждого устройства // Добавляем новые виджеты для каждого устройства
for (const auto& device : devices) { for (const auto &device: devices) {
flowLayout->addWidget(new DeviceWidget(device)); flowLayout->addWidget(new DeviceWidget(device));
} }
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <QMainWindow> #include <QMainWindow>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QStackedWidget>
#include <QTimer> #include <QTimer>
#include "db/DeviceInfo.h" #include "db/DeviceInfo.h"
#include "widget/FlowLayout.h" #include "widget/FlowLayout.h"
@ -9,10 +10,17 @@ class MainWindow final : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget *parent = nullptr); explicit MainWindow(QWidget *parent = nullptr);
void loadDevices() const; void loadDevices();
private: private:
QWidget* central; QWidget* central;
FlowLayout* flowLayout; FlowLayout* flowLayout;
QTimer* timer; QTimer* timer;
QStackedWidget* stackedWidget;
QWidget* devicePage = nullptr;
QWidget* accountPage = nullptr;
void createDevicePage();
void deleteDevicePage();
}; };

View File

@ -0,0 +1,21 @@
#include "TransactionWindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include "dao/AccountDAO.h"
TransactionWindow::TransactionWindow(const int accountId, QWidget *parent): QDialog(parent) {
setWindowTitle("Транзакции");
setMinimumSize(300, 200); // минимальный размер
setMaximumSize(800, 400); // максимальный размер
auto *layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignCenter);
AccountInfo account = AccountDAO::getAccountById(accountId);
QLabel *label = new QLabel(QString("*%1 | %2").arg(account.cardName, account.description), this);
layout->addWidget(label);
setModal(true);
}

10
views/TransactionWindow.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <QDialog>
class TransactionWindow final : public QDialog {
Q_OBJECT
public:
explicit TransactionWindow(int accountId, QWidget *parent = nullptr);
};