1
0
forked from BRT/arc

Refactor device and account data handling, enhance UI.

Introduced a new `NetworkService` for posting account and device data efficiently. Improved the transaction UI with styled tables and added lazy content loading. Changed screenshot handling to return JPEG data directly, and restructured device processing for better error handling and data integration.
This commit is contained in:
slava 2025-05-18 23:42:23 +07:00
parent c4f171259b
commit edcd931e66
12 changed files with 408 additions and 74 deletions

View File

@ -22,6 +22,7 @@ elseif (APPLE)
endif () endif ()
find_package(Qt6 REQUIRED COMPONENTS find_package(Qt6 REQUIRED COMPONENTS
Network
Widgets Widgets
Sql Sql
Xml Xml
@ -66,6 +67,7 @@ if (WIN32)
Qt6::Widgets Qt6::Widgets
Qt6::Xml Qt6::Xml
Qt6::Sql Qt6::Sql
Qt6::Network
Tesseract::libtesseract Tesseract::libtesseract
) )
@ -79,6 +81,7 @@ elseif (APPLE)
Qt6::Widgets Qt6::Widgets
Qt6::Sql Qt6::Sql
Qt6::Xml Qt6::Xml
Qt6::Network
tesseract tesseract
) )
endif () endif ()

View File

@ -1,5 +1,7 @@
#include "GetLastDaysHistoryScript.h" #include "GetLastDaysHistoryScript.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QThread> #include <QThread>
#include <utility> #include <utility>
@ -10,6 +12,7 @@
#include "dao/DeviceDAO.h" #include "dao/DeviceDAO.h"
#include "dao/TransactionDAO.h" #include "dao/TransactionDAO.h"
#include "db/DeviceInfo.h" #include "db/DeviceInfo.h"
#include "net/NetworkService.h"
#include "time/DateUtils.h" #include "time/DateUtils.h"
GetLastDaysHistoryScript::GetLastDaysHistoryScript( GetLastDaysHistoryScript::GetLastDaysHistoryScript(
@ -134,6 +137,35 @@ bool GetLastDaysHistoryScript::openAndSaveDitailInfo(TransactionInfo &transactio
return false; return false;
} }
void postAccountsData(const QList<QPair<AccountInfo, Node> > &accounts) {
if (accounts.isEmpty()) {
return;
}
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonArray list;
for (const QPair<AccountInfo, Node> &pair: accounts) {
QJsonObject obj;
AccountInfo account = pair.first;
obj["appName"] = account.appName;
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["description"] = account.description;
list.append(obj);
}
worker->setDeviceData("DT88bokcwQ", accounts.first().first.deviceId);
worker->setPayload(QJsonDocument(list).toJson());
worker->moveToThread(thread);
QObject::connect(thread, &QThread::started, worker, &NetworkService::postAccountsData);
QObject::connect(worker, &NetworkService::finished, thread, &QThread::quit);
QObject::connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void GetLastDaysHistoryScript::doStart() { void GetLastDaysHistoryScript::doStart() {
// берем все транзакции из БД // берем все транзакции из БД
QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
@ -141,8 +173,17 @@ void GetLastDaysHistoryScript::doStart() {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
if (xmlScreenParser.isHomeScreen()) { if (xmlScreenParser.isHomeScreen()) {
// парсим все транзакции
if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) {
QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();
for (auto &[account, node]: accounts) {
account.deviceId = m_account.deviceId;
account.appName = m_account.appName;
if (!AccountDAO::upsertAccount(account)) {
qDebug() << "Not updated: " << account.lastNumbers;
}
}
postAccountsData(accounts);
findAndTapOnAccount(device.id, m_account.lastNumbers); findAndTapOnAccount(device.id, m_account.lastNumbers);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
@ -339,7 +380,6 @@ void GetLastDaysHistoryScript::doStart() {
// Если по карте, мы знаем ток дату + сумма // Если по карте, мы знаем ток дату + сумма
} }
} }
} else { } else {
qDebug() << "--- home screen not found"; qDebug() << "--- home screen not found";

Binary file not shown.

View File

@ -75,7 +75,7 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
const QString phoneNumber = "79641815146"; const QString phoneNumber = "79641815146";
const QString bankName = "Альфа-Банк"; const QString bankName = "Альфа-Банк";
// const QString bankName = "Сбербанк"; // const QString bankName = "Сбербанк";
const double amount = 440; const double amount = 520;
const AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber); const AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber);
if (account.id != -1) { if (account.id != -1) {
@ -136,31 +136,10 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
QApplication app(argc, argv); QApplication app(argc, argv);
// QList<Card> cards = AccountInfoScreener::parseCardData(QByteArray()); setupWorkerAndThreadHistory(app);
// for (const Card &card: cards) { setupWorkerAndThread(app);
// AccountInfo info;
// info.deviceId = "6edc4a47";
// info.appName = "bank";
// info.cardName = card.name;
// info.amount = card.amount;
// info.description = card.description;
// info.status = "active";
// info.updateTime = QDateTime::currentDateTime();
// qDebug() << "Account info: " << AccountDAO::upsertAccount(info);
// }
setupScriptAutoPaymentAndThread(app); MainWindow window;
window.show();
// setupWorkerAndThread(app); return QApplication::exec();
//
// const QDateTime current = QDateTime::currentDateTimeUtc();
// qDebug() << "Обновление lastScan:" << CommonDataDAO::updateLastScan(current);
//
// const QDateTime savedTime = CommonDataDAO::getLastScan();
// qDebug() << "Сохранившееся время:" << savedTime.toString();
//
//
// MainWindow window;
// window.show();
return app.exec();
} }

View File

@ -4,8 +4,14 @@
#include <QProcess> #include <QProcess>
#include <QFile> #include <QFile>
#include <QBuffer> #include <QBuffer>
#include <qeventloop.h>
#include <QThread> #include <QThread>
#include <QImage> #include <QImage>
#include <QHttpMultiPart>
#include <QJsonObject>
#include <QJsonArray>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <dao/DeviceDAO.h> #include <dao/DeviceDAO.h>
#include <db/DeviceInfo.h> #include <db/DeviceInfo.h>
@ -14,44 +20,119 @@ DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
DeviceScreener::~DeviceScreener() = default; DeviceScreener::~DeviceScreener() = default;
void DeviceScreener::start() { void postDevicesData(const QList<DeviceInfo> &devices) {
qDebug() << "Worker started in thread:" << QThread::currentThread(); auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
while (m_running) {
QList<QPair<QString, QString>> devices = readAdbDevices();
QStringList onlineIds;
for (const auto&[id, status] : devices) {
qDebug() << "Id:" << id << ", Status:" << status;
QHttpPart jsonPart;
jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="metadata")");
QJsonArray list;
for (const DeviceInfo &device: devices) {
QJsonObject obj;
obj["uniqueName"] = device.id;
obj["status"] = device.status;
obj["name"] = device.name;
obj["screenWidth"] = device.screenWidth;
obj["screenHeight"] = device.screenHeight;
obj["battery"] = device.battery;
list.append(obj);
}
jsonPart.setBody(QJsonDocument(list).toJson());
multiPart->append(jsonPart);
for (const DeviceInfo &device: devices) {
QHttpPart imagePart;
QString imageHeader = QStringLiteral("form-data; name=files; filename=%1.jpg").arg(device.id);
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/jpeg");
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, imageHeader.toUtf8());
imagePart.setBody(device.image);
multiPart->append(imagePart);
}
QHttpPart uniquePart;
uniquePart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="uniqueName")");
uniquePart.setBody("DT88bokcwQ");
multiPart->append(uniquePart);
QNetworkAccessManager manager; // создаём внутри потока
QUrl url("http://localhost:9999/api/v1/app/update");
QNetworkRequest request(url);
// делаем запрос
QNetworkReply *reply = manager.post(request, multiPart);
multiPart->setParent(reply); // multiPart удалится, когда придёт ответ
// локальный цикл — ждём, пока придёт finished()
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
QObject::connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
loop.quit();
});
loop.exec(); // <- здесь и запускается event-loop потока
// обрабатываем результат
if (reply->error() == QNetworkReply::NoError) {
QByteArray raw = reply->readAll();
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
if (err.error == QJsonParseError::NoError && doc.isObject()) {
QJsonObject root = doc.object();
qDebug() << "Успешный ответ: " << root.value("success").toBool();
}
} else {
qWarning() << "Ошибка:" << reply->errorString();
}
reply->deleteLater();
}
void DeviceScreener::start() {
while (m_running) {
QList<QPair<QString, QString> > devices = readAdbDevices();
QStringList onlineIds;
QList<DeviceInfo> deviceInfos;
for (const auto &[id, status]: devices) {
DeviceInfo device; DeviceInfo device;
device.id = id; device.id = id;
if (status == "device") { if (status == "device") {
onlineIds << device.id; onlineIds << device.id;
device = readDeviceInfo(id); device = readDeviceInfo(id);
takeScreenshot(id); device.image = takeScreenshot(id);
if (!DeviceDAO::updateImage(id, device.image)) {
qDebug() << "Cant update screenshot";
}
} }
device.status = status; device.status = status;
device.updateTime = QDateTime::currentDateTime(); device.updateTime = QDateTime::currentDateTime();
qDebug() << "Update device: " << DeviceDAO::upsertDevice(device); deviceInfos.append(device);
if (!DeviceDAO::upsertDevice(device)) {
qDebug() << "Cant update devive";
}
}
postDevicesData(deviceInfos);
if (!DeviceDAO::markDevicesOfflineExcept(onlineIds)) {
qDebug() << "Cant mark devices offline";
} }
qDebug() << "All devices offline: " << DeviceDAO::markDevicesOfflineExcept(onlineIds);
QThread::sleep(3); QThread::sleep(3);
} }
emit finished(); emit finished();
} }
QByteArray convertPngToJpeg(const QByteArray& pngData, const int quality = 90) { QByteArray convertPngToJpeg(const QByteArray &pngData, const int quality = 90) {
QImage image; QImage image;
if (!image.loadFromData(pngData, "PNG")) { if (!image.loadFromData(pngData, "PNG")) {
qWarning() << "Не удалось загрузить PNG из байт"; qWarning() << "Не удалось загрузить PNG из байт";
return {}; return {};
} }
// if (!image.save("test.jpg", "JPG", quality)) {
// qWarning() << "Не удалось сохранить как JPG:" << "test.jpg";
// return {};
// }
QByteArray jpgData; QByteArray jpgData;
QBuffer buffer(&jpgData); QBuffer buffer(&jpgData);
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
@ -59,7 +140,7 @@ QByteArray convertPngToJpeg(const QByteArray& pngData, const int quality = 90) {
return jpgData; return jpgData;
} }
bool DeviceScreener::takeScreenshot(const QString& deviceId) { QByteArray DeviceScreener::takeScreenshot(const QString &deviceId) {
QProcess process; QProcess process;
QStringList arguments; QStringList arguments;
@ -68,34 +149,26 @@ bool DeviceScreener::takeScreenshot(const QString& deviceId) {
if (!process.waitForFinished(5000)) { if (!process.waitForFinished(5000)) {
qWarning() << "ADB скриншот не завершился для" << deviceId; qWarning() << "ADB скриншот не завершился для" << deviceId;
return false; return {};
} }
const QByteArray imageData = process.readAllStandardOutput(); const QByteArray imageData = process.readAllStandardOutput();
if (imageData.isEmpty()) { if (imageData.isEmpty()) {
qWarning() << "Скриншот пустой для " << deviceId; qWarning() << "Скриншот пустой для " << deviceId;
return false; return {};
} }
// Обновляем изображение в БД return convertPngToJpeg(imageData, 90);
if (!DeviceDAO::updateImage(deviceId, convertPngToJpeg(imageData, 90))) {
qWarning() << "Не удалось записать изображение в БД для" << deviceId;
return false;
}
qDebug() << "Screenshot saved, device: " << deviceId;
return true;
} }
QList<QPair<QString, QString>> DeviceScreener::readAdbDevices() { QList<QPair<QString, QString> > DeviceScreener::readAdbDevices() {
QProcess process; QProcess process;
process.start("adb", QStringList() << "devices"); process.start("adb", QStringList() << "devices");
process.waitForFinished(); process.waitForFinished();
const QString output = process.readAllStandardOutput(); const QString output = process.readAllStandardOutput();
// qDebug() << "[adb devices output]:\n" << output; QList<QPair<QString, QString> > devices;
QList<QPair<QString, QString>> devices;
const QStringList lines = output.split('\n'); const QStringList lines = output.split('\n');
for (int i = 1; i < lines.size(); ++i) { for (int i = 1; i < lines.size(); ++i) {
QString line = lines.at(i).trimmed(); QString line = lines.at(i).trimmed();
@ -111,8 +184,8 @@ QList<QPair<QString, QString>> DeviceScreener::readAdbDevices() {
*/ */
// Разделяем строку на id и type по табуляции // Разделяем строку на id и type по табуляции
if (QStringList parts = line.split('\t'); parts.size() == 2) { if (QStringList parts = line.split('\t'); parts.size() == 2) {
const QString& id = parts.at(0); const QString &id = parts.at(0);
const QString& status = parts.at(1); const QString &status = parts.at(1);
devices.append(qMakePair(id, status)); devices.append(qMakePair(id, status));
} }
} }
@ -122,14 +195,14 @@ QList<QPair<QString, QString>> DeviceScreener::readAdbDevices() {
static const QRegularExpression screenRegex(R"(Physical size: (\d+)x(\d+))"); static const QRegularExpression screenRegex(R"(Physical size: (\d+)x(\d+))");
static const QRegularExpression batteryRegex(R"(level:\s*(\d+))"); static const QRegularExpression batteryRegex(R"(level:\s*(\d+))");
DeviceInfo DeviceScreener::readDeviceInfo(const QString& deviceId) { DeviceInfo DeviceScreener::readDeviceInfo(const QString &deviceId) {
DeviceInfo info; DeviceInfo info;
info.id = deviceId; info.id = deviceId;
info.screenWidth = 0; info.screenWidth = 0;
info.screenHeight = 0; info.screenHeight = 0;
info.battery = 0; info.battery = 0;
auto runAdbCommand = [&](const QStringList& args) -> QString { auto runAdbCommand = [&](const QStringList &args) -> QString {
QProcess process; QProcess process;
process.start("adb", QStringList{"-s", deviceId} + args); process.start("adb", QStringList{"-s", deviceId} + args);
process.waitForFinished(); process.waitForFinished();

View File

@ -24,7 +24,7 @@ private:
static QList<QPair<QString, QString> > readAdbDevices(); static QList<QPair<QString, QString> > readAdbDevices();
static bool takeScreenshot(const QString &deviceId); static QByteArray takeScreenshot(const QString &deviceId);
static DeviceInfo readDeviceInfo(const QString &deviceId); static DeviceInfo readDeviceInfo(const QString &deviceId);
}; };

View File

@ -0,0 +1,69 @@
#include "NetworkService.h"
#include <QEventLoop>
#include <QHttpPart>
#include <QNetworkReply>
#include <QJsonObject>
NetworkService::NetworkService(QObject *parent) : QObject(parent) {
}
NetworkService::~NetworkService() = default;
void NetworkService::postAccountsData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart jsonPart;
jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="metadata")");
jsonPart.setBody(m_json);
multiPart->append(jsonPart);
QHttpPart desktopUniqueName;
desktopUniqueName.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="desktopUniqueName")");
desktopUniqueName.setBody(m_desktopId.toUtf8());
multiPart->append(desktopUniqueName);
QHttpPart androidUniqueName;
androidUniqueName.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="androidUniqueName")");
androidUniqueName.setBody(m_deviceId.toUtf8());
multiPart->append(androidUniqueName);
QNetworkAccessManager manager; // создаём внутри потока
const QUrl url("http://localhost:9999/api/v1/account/update");
const QNetworkRequest request(url);
QNetworkReply *reply = manager.post(request, multiPart);
multiPart->setParent(reply);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
loop.quit();
});
loop.exec(); // <- здесь и запускается event-loop потока
// обрабатываем результат
if (reply->error() == QNetworkReply::NoError) {
const QByteArray raw = reply->readAll();
QJsonParseError err;
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
if (err.error == QJsonParseError::NoError && doc.isObject()) {
const QJsonObject root = doc.object();
qDebug() << "Успешный ответ: " << root.value("success").toBool();
}
} else {
qWarning() << "Ошибка:" << reply->errorString();
}
reply->deleteLater();
emit finished();
}
void NetworkService::stop() {
m_running = false;
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <QObject>
class NetworkService final : public QObject {
Q_OBJECT
public:
explicit NetworkService(QObject *parent = nullptr);
~NetworkService() override;
void setPayload(const QByteArray &json) { m_json = json; }
void setDeviceData(const QString &desktopId, const QString &deviceId) {
m_desktopId = desktopId;
m_deviceId = deviceId;
}
public slots:
void postAccountsData();
void stop();
signals:
void finished();
private:
QByteArray m_json;
QString m_deviceId;
QString m_desktopId;
bool m_running = true;
};

View File

@ -30,7 +30,7 @@ AccountWindow::AccountWindow(QWidget *parent): QWidget(parent) {
QList<DeviceInfo> devices = DeviceDAO::getAllDevices(false); QList<DeviceInfo> devices = DeviceDAO::getAllDevices(false);
for (int i = 0; i < devices.size(); ++i) { for (int i = 0; i < devices.size(); ++i) {
DeviceInfo device = devices[i]; DeviceInfo device = devices[i];
QList<AccountInfo> accounts = AccountDAO::getAccounts(device.id, "bank"); QList<AccountInfo> accounts = AccountDAO::getAccounts(device.id, "rshb");
// Текст перед первой таблицей // Текст перед первой таблицей
QString title = QString("%1 [%2] | %3").arg(device.name, device.id, device.status); QString title = QString("%1 [%2] | %3").arg(device.name, device.id, device.status);
QLabel *label = new QLabel(title, this); QLabel *label = new QLabel(title, this);

View File

@ -13,7 +13,7 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p
img.loadFromData(device.image, "JPG"); img.loadFromData(device.image, "JPG");
auto *imageLabel = new QLabel(this); auto *imageLabel = new QLabel(this);
qDebug() << "imageLabel" << img.width() ; // qDebug() << "imageLabel" << img.width() ;
imageLabel->setPixmap(QPixmap::fromImage(img).scaled(200, 350, Qt::KeepAspectRatio, Qt::SmoothTransformation)); imageLabel->setPixmap(QPixmap::fromImage(img).scaled(200, 350, Qt::KeepAspectRatio, Qt::SmoothTransformation));
imageLabel->setAlignment(Qt::AlignCenter); imageLabel->setAlignment(Qt::AlignCenter);
layout->addWidget(imageLabel); layout->addWidget(imageLabel);

View File

@ -21,12 +21,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
createDevicePage(); createDevicePage();
stackedWidget->addWidget(devicePage); stackedWidget->addWidget(devicePage);
// stackedWidget->setCurrentWidget(devicePage); FIXME stackedWidget->setCurrentWidget(devicePage);
// FIXME переделать на ленивое создание // FIXME переделать на ленивое создание
accountPage = new AccountWindow(this); accountPage = new AccountWindow(this);
stackedWidget->addWidget(accountPage); stackedWidget->addWidget(accountPage);
stackedWidget->setCurrentWidget(accountPage); // stackedWidget->setCurrentWidget(accountPage);
resize(800, 600); resize(800, 600);

View File

@ -1,21 +1,159 @@
#include "TransactionWindow.h" #include "TransactionWindow.h"
#include <qabstractitemview.h>
#include <QHeaderView>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QLabel> #include <QLabel>
#include <QScrollArea>
#include <QTableWidget>
#include <QStyledItemDelegate>
#include "dao/AccountDAO.h" #include "dao/AccountDAO.h"
#include "dao/TransactionDAO.h"
class AmountDelegate : public QStyledItemDelegate {
public:
explicit AmountDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
// Получаем значение суммы
QString text = index.data().toString();
bool isNegative = text.toDouble() < 0;
// Настраиваем цвета
if (isNegative) {
opt.backgroundBrush = QColor(255, 230, 230); // Светло-красный фон
opt.palette.setColor(QPalette::Text, QColor(255, 0, 0)); // Красный текст
} else {
opt.backgroundBrush = QColor(230, 255, 230); // Светло-зеленый фон
opt.palette.setColor(QPalette::Text, QColor(0, 128, 0)); // Зеленый текст
}
// Рисуем ячейку
QStyledItemDelegate::paint(painter, opt, index);
}
};
QTableWidgetItem *createTableWidget(const QString &text) {
QTableWidgetItem *item = new QTableWidgetItem(text);
item->setTextAlignment(Qt::AlignLeft | Qt::AlignTop);
return item;
}
TransactionWindow::TransactionWindow(const int accountId, QWidget *parent): QDialog(parent) { TransactionWindow::TransactionWindow(const int accountId, QWidget *parent): QDialog(parent) {
setWindowTitle("Транзакции"); setWindowTitle("Транзакции");
setMinimumSize(300, 200); // минимальный размер setMinimumSize(1000, 600); // минимальный размер
setMaximumSize(800, 400); // максимальный размер setMaximumSize(1000, 600); // максимальный размер
auto *layout = new QVBoxLayout(this); auto *mainLayout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignCenter);
// Скролл-область
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);
AccountInfo account = AccountDAO::getAccountById(accountId); AccountInfo account = AccountDAO::getAccountById(accountId);
QList<TransactionInfo> transactions = TransactionDAO::getTransactions(accountId);
QLabel *label = new QLabel(QString("*%1 | %2").arg(account.lastNumbers, account.description), this); QTableWidget *tableWidget = new QTableWidget(this);
layout->addWidget(label); 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: 4px;"
" border: none;"
" text-align: left top;"
"}"
"QTableWidget::item:selected {"
" background-color: #cce5ff;"
" color: #003366;"
"}"
"QTableCornerButton::section {"
" background-color: #e0e0e0;"
" border: 1px solid grey;"
"}"
);
tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
tableWidget->setRowCount(transactions.length());
tableWidget->setColumnCount(6);
tableWidget->setHorizontalHeaderLabels({
"Дата и время",
"Сумма",
"Банк",
"Телефон",
"Статус",
"Описание"
});
tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
for (int i = 0; i < 5; ++i) {
tableWidget->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
}
tableWidget->horizontalHeader()->setSectionResizeMode(5, QHeaderView::Stretch);
// tableWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
tableWidget->setItemDelegateForColumn(1, new AmountDelegate(tableWidget));
for (int k = 0; k < transactions.size(); ++k) {
TransactionInfo tr = transactions[k];
tableWidget->setItem(k, 0, createTableWidget(tr.timestamp.toString("dd.MM.yyyy HH:mm:ss")));
tableWidget->setItem(k, 1, createTableWidget(QString::number(tr.amount)));
tableWidget->setItem(k, 2, createTableWidget(tr.bankName));
tableWidget->setItem(k, 3, createTableWidget(tr.phone));
tableWidget->setItem(k, 4, createTableWidget(transactionStatusToString(tr.status)));
QTableWidgetItem *item = new QTableWidgetItem(tr.description);
item->setTextAlignment(Qt::AlignLeft | Qt::AlignTop);
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
item->setSizeHint(QSize(100, tr.description.length() / 2)); // Минимальная высота ячейки для переноса
tableWidget->setItem(k, 5, item);
}
layout->addWidget(tableWidget);
tableWidget->resizeRowsToContents();
// for (int row = 0; row < tableWidget->rowCount(); ++row) {
// for (int col = 0; col < tableWidget->columnCount(); ++col) {
// QTableWidgetItem *item = tableWidget->item(row, col);
// if (item) {
// item->setTextAlignment(Qt::AlignLeft | Qt::AlignTop);
// }
// }
// }
// Добавим вертикальный 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("Транзакции");
setModal(true); setModal(true);
} }