290 lines
10 KiB
C++
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();
|
|
}
|