bill-manager/ui/mainwindow.cpp

290 lines
10 KiB
C++

//
// Created by skyone on 2023/4/24.
//
#include <QDoubleValidator>
#include <QMessageBox>
#include <QSqlQuery>
#include <QDate>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include "mainwindow.h"
#include "ui_MainWindow.h"
int calc_page_count(int size) {
return size > 0 ? (size - 1) / 10 + 1 : 0;
}
int calc_current_size(int size, int current) {
int r = size - current * 10;
return r > 0 ? (r > 10 ? 10 : r) : 0;
}
QString date_format(long timestamp) {
auto date = QDateTime::fromSecsSinceEpoch(timestamp).date();
return std::move(QString::asprintf("%d/%d/%d", date.year(), date.month(), date.day()));
}
QString price_format(double price) {
return std::move(QString::asprintf("%.02lf", price));
}
MainWindow::MainWindow(QWidget *parent) :
QWidget(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
setFixedSize(790, 660);
ui->input_query->setValidator(new QIntValidator(0, 99999999, ui->input_query));
ui->input_add_price->setValidator(new QDoubleValidator(0, 9999, 2, ui->input_add_price));
auto currentDate = QDate::currentDate();
auto current = QString::asprintf("%04d%02d%02d", currentDate.year(), currentDate.month(), currentDate.day());
ui->input_query->setText(current);
connect(ui->btn_query, &QPushButton::clicked, this, &MainWindow::handleQuery);
connect(ui->btn_search, &QPushButton::clicked, this, &MainWindow::handleSearch);
connect(ui->btn_add_submit, &QPushButton::clicked, this, &MainWindow::handleAddSubmit);
connect(ui->btn_page_previous, &QPushButton::clicked, this, &MainWindow::handlePreviousPage);
connect(ui->btn_page_next, &QPushButton::clicked, this, &MainWindow::handleNextPage);
// Prepare Database
db = QSqlDatabase::addDatabase("QSQLITE");
QString appdata = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir appdataPath {appdata};
if (appdata.isEmpty() || !appdataPath.mkpath(appdataPath.absolutePath())) {
QMessageBox::critical(this, "致命错误", "无法获取数据目录!");
exit(-1);
}
db.setDatabaseName(appdata + "/data.sqlite");
if (!db.open()) {
QMessageBox::critical(this, "致命错误", "无法创建数据库!");
exit(-1);
}
QSqlQuery sql;
sql.prepare(R"(
CREATE TABLE IF NOT EXISTS account
(
id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
object text NOT NULL,
price real NOT NULL,
date integer NOT NULL,
comment text NOT NULL
);
)");
if (!sql.exec()) {
QMessageBox::critical(this, "致命错误", "无法创建数据表!");
exit(-1);
}
applyQuery(current);
renderData();
}
MainWindow::~MainWindow() {
db.close();
delete ui;
}
void MainWindow::handleAddSubmit() {
QString object = ui->input_add_object->text();
double price = ui->input_add_price->text().toDouble();
QString comment = ui->input_add_comment->text();
if (object.isEmpty() || price == 0) {
return;
}
if (comment.isEmpty()) {
comment = QString("");
}
if (!MainWindow::insertRecord(object, price, comment)) {
QMessageBox::warning(this, "警告", "插入数据失败!");
return;
}
ui->input_add_object->setText("");
ui->input_add_price->setText("");
ui->input_add_comment->setText("");
if (query_is_current) {
applyQuery(query);
renderData();
}
}
void MainWindow::handleQuery() {
QString date = ui->input_query->text();
applyQuery(date);
renderData();
}
void MainWindow::handleSearch() {
QString search = ui->input_search->text();
applySearch(search);
renderData();
}
void MainWindow::handlePreviousPage() {
int page_count = calc_page_count(all_size);
if (page_current > 0) {
page_current--;
}
if (page_current >= page_count) {
page_current = page_count - 1;
}
renderData();
}
void MainWindow::handleNextPage() {
int page_count = calc_page_count(all_size);
if (page_current < page_count - 1) {
page_current++;
}
if (page_current < 0) {
page_current = 0;
}
renderData();
}
void MainWindow::renderData() {
int page_count = calc_page_count(all_size);
if (page_current >= page_count) {
page_current = page_count - 1;
}
if (page_current < 0) {
page_current = 0;
}
int current_size = calc_current_size(all_size, page_current);
// Setup page count
ui->label_page_current->setText(QString("第 %1 页").arg(page_current + 1));
ui->label_page_all->setText(QString("共 %1 页").arg(page_count));
// Setup records
for (int i = 0; i < current_size; i++) {
int index = page_current * 10 + i;
auto record = data[index];
findChild<QLabel *>("label_table_id_" + QString::number(i))->setText(QString::number(index + 1));
findChild<QLabel *>("label_table_object_" + QString::number(i))->setText(record.object);
findChild<QLabel *>("label_table_price_" + QString::number(i))->setText(price_format(record.price));
findChild<QLabel *>("label_table_date_" + QString::number(i))->setText(date_format(record.date));
findChild<QLabel *>("label_table_comment_" + QString::number(i))->setText(record.comment);
}
for (int i = 9; i >= current_size; i--) {
findChild<QLabel *>("label_table_id_" + QString::number(i))->setText("");
findChild<QLabel *>("label_table_object_" + QString::number(i))->setText("");
findChild<QLabel *>("label_table_price_" + QString::number(i))->setText("");
findChild<QLabel *>("label_table_date_" + QString::number(i))->setText("");
findChild<QLabel *>("label_table_comment_" + QString::number(i))->setText("");
}
// Others
ui->label_statistics_sum->setText(QString::asprintf("共 %.2f 元", sum));
ui->label_statistics_days->setText(QString::asprintf("共 %d 天", days));
ui->label_statistics_average->setText(QString::asprintf("平均 %.2f 元/天", average));
}
bool MainWindow::insertRecord(const QString &object, double price, const QString &comment) {
QSqlQuery sql;
sql.prepare("INSERT INTO account(object, price, date, comment) VALUES (:object, :price, :date, :comment)");
sql.bindValue(":object", object);
sql.bindValue(":price", price);
sql.bindValue(":date", QDateTime::currentDateTimeUtc().toTime_t());
sql.bindValue(":comment", comment);
return sql.exec();
}
void MainWindow::applyQuery(const QString &date) {
query = date;
QDateTime current = QDateTime::currentDateTime();
QString where;
int day;
if (date.length() == 4) {
QDate currentDate = current.date();
int year = date.toInt();
if (year == currentDate.year()) {
// Is current
query_is_current = true;
day = currentDate.dayOfYear();
} else {
query_is_current = false;
day = QDate(year, 1, 1).daysInYear();
}
QDateTime start = QDateTime(QDate(year, 1, 1), QTime(0, 0, 0, 0));
QDateTime end = start.addDays(start.date().daysInYear());
where = QString::asprintf("WHERE date >= %d AND date < %d", start.toTime_t(), end.toTime_t());
} else if (date.length() == 6) {
QDate currentDate = current.date();
int num = date.toInt();
int year = num / 100;
int month = num % 100;
if (year == currentDate.year() && month == currentDate.month()) {
// Is current
query_is_current = true;
day = currentDate.day();
} else {
query_is_current = false;
day = QDate(year, month, 1).daysInMonth();
}
QDateTime start = QDateTime(QDate(year, month, 1), QTime(0, 0, 0, 0));
QDateTime end = start.addDays(start.date().daysInMonth());
where = QString::asprintf("WHERE date >= %d AND date < %d", start.toTime_t(), end.toTime_t());
} else if (date.length() == 8) {
day = 1;
QDate currentDate = current.date();
int num = date.toInt();
int _year = num / 10000;
int _month = num % 10000 / 100;
int _day = num % 100;
if (currentDate.year() == _year && currentDate.month() == _month && currentDate.day() == _day) {
// Is current
query_is_current = true;
} else {
query_is_current = false;
}
QDateTime start = QDateTime(QDate(_year, _month, _day), QTime(0, 0, 0, 0));
QDateTime end = start.addDays(1);
where = QString::asprintf("WHERE date >= %d AND date < %d", start.toTime_t(), end.toTime_t());
} else {
query = "";
query_is_current = true;
day = 1;
QDateTime start = QDateTime(current.date(), QTime(0, 0, 0, 0));
QDateTime end = start.addDays(1);
where = QString::asprintf("WHERE date >= %d AND date < %d", start.toTime_t(), end.toTime_t());
}
QSqlQuery sql;
sql.prepare("SELECT id, object, price, date, comment FROM account " + where + " ORDER BY date");
applySQL(sql);
average = sum / ((double) day);
days = day;
}
void MainWindow::applySearch(const QString &search) {
query_is_current = false;
QSqlQuery sql;
sql.prepare("SELECT id, object, price, date, comment FROM account WHERE object LIKE :search OR comment LIKE :search");
sql.bindValue(":search", "%" + search + "%");
applySQL(sql);
average = 0;
days = 0;
}
void MainWindow::applySQL(QSqlQuery &sql) {
if (!sql.exec()) {
QMessageBox::warning(this, "警告", "获取数据失败!");
}
double s = 0;
data.clear();
while (sql.next()) {
auto id = sql.value(0).toInt();
auto object = sql.value(1).toString();
auto price = sql.value(2).toDouble();
auto _date = sql.value(3).toLongLong();
auto comment = sql.value(4).toString();
s += price;
Record record(id, std::move(object), price, (long) _date, std::move(comment));
data.push_back(std::move(record));
}
sum = s;
all_size = (int) data.size();
}