Files
SimpleAccount/filterform.cpp
2026-02-27 12:29:27 +08:00

439 lines
15 KiB
C++

#include "filterform.h"
#include "recordedit.h"
#include "ui_filterform.h"
#include <QDesktopServices>
#include <QDir>
#include <QMessageBox>
#include <QRegularExpressionValidator>
#include <QShowEvent>
#include <algorithm>
#include <unordered_map>
#include <QDateTime>
FilterForm::FilterForm(QWidget* parent, std::unique_ptr<ACTSqlOpr>& sqlOpr, std::unique_ptr<ComSqlOpr>& comSqlOpr,
std::unique_ptr<RepaySqlOpr>& repaySqlOpr)
: QDialog(parent), ui(new Ui::FilterForm), sqlOpr_(sqlOpr), comSqlOpr_(comSqlOpr), repaySqlOpr_(repaySqlOpr)
{
ui->setupUi(this);
statistic_ = std::make_shared<Statistic>(repaySqlOpr_);
Init();
setWindowTitle("结果");
}
FilterForm::~FilterForm()
{
delete ui;
}
int FilterForm::exec()
{
ShowResult();
if (!over_) {
return QDialog::Rejected;
}
return QDialog::exec();
}
void FilterForm::Init()
{
auto* lay = new QVBoxLayout(this);
tw_ = new QTableWidget(this);
QStringList headers;
headers << "ID" << "类型" << "分类" << "金额" << "日期" << "内容" << "备注";
tw_->setColumnCount(headers.size());
tw_->setHorizontalHeaderLabels(headers);
tw_->setSelectionBehavior(QAbstractItemView::SelectRows);
tw_->setColumnWidth(0, 50);
tw_->setColumnWidth(1, 100);
tw_->setColumnWidth(2, 100);
tw_->setColumnWidth(3, 100);
tw_->setColumnWidth(4, 150);
tw_->setColumnWidth(5, 300);
tw_->setColumnWidth(6, 100);
lay->addWidget(tw_);
ui->widget->setLayout(lay);
ui->edCashIn->setReadOnly(true);
ui->edCashOut->setReadOnly(true);
ui->edCreditIn->setReadOnly(true);
ui->edCreditOut->setReadOnly(true);
ui->edCreditCash->setReadOnly(true);
tw_->setContextMenuPolicy(Qt::CustomContextMenu);
connect(tw_, &QTableWidget::customContextMenuRequested, this, &FilterForm::ShowContextMenu);
}
void FilterForm::ShowResult()
{
over_ = false;
AccountRecordList list;
if (!sqlOpr_->GetAccountList(list)) {
QMessageBox::warning(this, "错误", QString::fromStdString(sqlOpr_->GetLastErr()));
return;
}
result_.clear();
if (!Filter(list, result_)) {
QMessageBox::warning(this, "错误", "筛选失败");
return;
}
if (result_.empty()) {
QMessageBox::warning(this, "提示", "没有符合条件的记录");
return;
}
for (const auto& item : result_) {
tw_->insertRow(tw_->rowCount());
int row = tw_->rowCount() - 1;
auto* i1 = new QTableWidgetItem(QString::number(item.id));
i1->setFlags(i1->flags() & ~Qt::ItemIsEditable);
auto* i2 = new QTableWidgetItem(QString::fromStdString(item.payType));
i2->setFlags(i2->flags() & ~Qt::ItemIsEditable);
auto* i3 = new QTableWidgetItem(QString::fromStdString(item.classify));
i3->setFlags(i3->flags() & ~Qt::ItemIsEditable);
auto* i4 = new QTableWidgetItem(QString::number(item.money / 100.0));
i4->setFlags(i4->flags() & ~Qt::ItemIsEditable);
auto* i5 = new QTableWidgetItem(QString::fromStdString(item.dt));
i5->setFlags(i5->flags() & ~Qt::ItemIsEditable);
auto* i6 = new QTableWidgetItem(QString::fromStdString(item.thing));
i6->setFlags(i6->flags() & ~Qt::ItemIsEditable);
auto* i7 = new QTableWidgetItem(QString::fromStdString(item.remark));
i7->setFlags(i7->flags() & ~Qt::ItemIsEditable);
tw_->setItem(row, 0, i1);
tw_->setItem(row, 1, i2);
tw_->setItem(row, 2, i3);
tw_->setItem(row, 3, i4);
tw_->setItem(row, 4, i5);
tw_->setItem(row, 5, i6);
tw_->setItem(row, 6, i7);
}
CalcShow();
over_ = true;
}
void FilterForm::CalcShow()
{
statistic_->Calculate(result_);
auto* d = SharedData::instance();
ui->edCashIn->setText(QString::number(d->ttCashIn_ / 100.0));
ui->edCashOut->setText(QString::number(d->ttCashOut_ / 100.0));
ui->edCreditIn->setText(QString::number(d->ttCreditIn_ / 100.0));
ui->edCreditOut->setText(QString::number(d->ttCreditOut_ / 100.0));
ui->edCreditCash->setText(QString::number(d->ttCreditCash_ / 100.0));
ui->edCreditPay->setText(QString::number(d->ttCashPay_ / 100.0));
auto ret1 = GetClassifyCash(result_, true);
auto ret2 = GetClassifyCash(result_, false);
ui->lwCashCl->clear();
for (const auto& item : ret1) {
ui->lwCashCl->addItem(QString("%1 => %2/%3")
.arg(QString::fromStdString(item.first))
.arg(QString::number(item.second.first / 100.0))
.arg(QString::number(item.second.second / 100.0)));
}
ui->lwCreditCl->clear();
for (const auto& item : ret2) {
ui->lwCreditCl->addItem(QString("%1 => %2/%3")
.arg(QString::fromStdString(item.first))
.arg(QString::number(item.second.first / 100.0))
.arg(QString::number(item.second.second / 100.0)));
}
}
int32_t FilterForm::rePayValue(int accID)
{
RepayRecordList reuslt;
if (!repaySqlOpr_->GetRepayResult(reuslt, accID)) {
return 0;
}
int32_t sum = 0;
for (const auto& item : reuslt) {
sum += item.money;
}
return sum;
}
bool FilterForm::Filter(const AccountRecordList& list, AccountRecordList& result)
{
auto* d = SharedData::instance();
result.clear();
for (const auto& item : list) {
if (d->flType) {
if (item.payType != d->type.toStdString()) {
continue;
}
}
if (d->flKeys) {
auto qkeys = d->key.toUpper();
auto skeys = QString::fromStdString(item.thing).toUpper();
if (!skeys.contains(qkeys)) {
continue;
}
}
switch (d->filter) {
case CashFilter::FIL_BETWEEN: {
if (item.money < d->lowMoney || item.money > d->highMoney) {
continue;
}
break;
}
case CashFilter::FIL_HIGHER: {
if (item.money <= d->highMoney) {
continue;
}
break;
}
case CashFilter::FIL_LOWER: {
if (item.money >= d->lowMoney) {
continue;
}
break;
}
default:
break;
}
if (d->flClassify) {
auto qkeys = d->classify.toUpper();
auto skeys = QString::fromStdString(item.classify).toUpper();
if (!skeys.contains(qkeys)) {
continue;
}
}
if (d->flDays) {
auto days = d->days;
QDateTime datetime = QDateTime::fromString(QString::fromStdString(item.dt), "yyyy-MM-dd hh:mm:ss");
QDateTime now = QDateTime::currentDateTime();
if (datetime.daysTo(now) > days) {
continue;
}
}
result.push_back(item);
}
return true;
}
std::vector<std::pair<std::string, std::pair<uint32_t, uint32_t>>> FilterForm::GetClassifyCash(const AccountRecordList& list,
bool isCash)
{
std::vector<std::pair<std::string, std::pair<uint32_t, uint32_t>>> ret;
std::unordered_map<std::string, std::pair<uint32_t, uint32_t>> classifyMap;
for (const auto& item : list) {
if (isCash) {
if (item.payType == "现金支出") {
if (!classifyMap.count(item.classify)) {
classifyMap[item.classify] = {0, 0};
}
classifyMap[item.classify].first += item.money;
} else if (item.payType == "现金收入") {
if (!classifyMap.count(item.classify)) {
classifyMap[item.classify] = {0, 0};
}
classifyMap[item.classify].second += item.money;
}
} else {
if (item.payType == "信用支出") {
if (!classifyMap.count(item.classify)) {
classifyMap[item.classify] = {0, 0};
}
classifyMap[item.classify].first += item.money;
} else if (item.payType == "信用收入") {
if (!classifyMap.count(item.classify)) {
classifyMap[item.classify] = {0, 0};
}
classifyMap[item.classify].second += item.money;
} else if (item.payType == "信用借款") {
// 信用借款
if (!classifyMap.count(item.classify)) {
classifyMap[item.classify] = {0, 0};
}
classifyMap[item.classify].first += item.money;
}
}
}
for (const auto& item : classifyMap) {
ret.emplace_back(item.first, item.second);
}
std::sort(ret.begin(), ret.end(),
[](const std::pair<std::string, std::pair<uint32_t, uint32_t>>& a,
const std::pair<std::string, std::pair<uint32_t, uint32_t>>& b) {
auto at = a.second.first + a.second.second;
auto bt = b.second.first + b.second.second;
return at > bt;
});
return ret;
}
void FilterForm::ShowContextMenu(const QPoint& pos)
{
QList<QTableWidgetItem*> selectedItems = tw_->selectedItems();
if (selectedItems.isEmpty()) {
return;
}
QMenu menu(this);
if (selectedItems.size() != 7) {
return;
}
QAction* acFile = menu.addAction("查看附件");
int id = selectedItems[0]->text().toInt();
QString fileName;
for (const auto& item : result_) {
if (item.id == id) {
fileName = QString::fromStdString(item.additionFile);
break;
}
}
TransMenu(id, menu, selectedItems[1]);
connect(acFile, &QAction::triggered, [this, selectedItems, id, fileName]() {
if (fileName.isEmpty()) {
QMessageBox::warning(this, "警告", "附件文件名为空");
return;
}
QString mediaDir = QString::fromStdString(Util::GetMediaDir());
QString filePath = QDir(mediaDir).filePath(fileName);
if (!QFile::exists(filePath)) {
QMessageBox::critical(this, "错误", QString("文件不存在: %1").arg(filePath));
return;
}
if (!QDesktopServices::openUrl(QUrl::fromLocalFile(filePath))) {
QMessageBox::critical(nullptr, "错误", "无法打开图片文件");
}
});
QAction* acEdit = menu.addAction("编辑");
connect(acEdit, &QAction::triggered, [this, id, selectedItems]() {
RecordEdit* re = new RecordEdit(this, comSqlOpr_, repaySqlOpr_);
AccountRecord* recPtr = nullptr;
for (auto& item : result_) {
if (item.id == id) {
re->record_ = item;
recPtr = &item;
break;
}
}
re->exec();
// headers << "ID" << "类型" << "分类" << "金额" << "日期" << "内容" << "备注";
if (!re->modify_) {
return;
}
if (!sqlOpr_->UpdateAccount(re->record_)) {
QMessageBox::warning(this, "错误", QString::fromStdString(sqlOpr_->GetLastErr()));
return;
}
selectedItems[1]->setText(QString::fromStdString(re->record_.payType));
selectedItems[2]->setText(QString::fromStdString(re->record_.classify));
selectedItems[3]->setText(QString::number(re->record_.money / 100.0));
selectedItems[4]->setText(QString::fromStdString(re->record_.dt));
selectedItems[5]->setText(QString::fromStdString(re->record_.thing));
selectedItems[6]->setText(QString::fromStdString(re->record_.remark));
*recPtr = re->record_;
CalcShow();
});
QAction* acDelete = menu.addAction("删除");
connect(acDelete, &QAction::triggered, [this, id, selectedItems]() {
if (QMessageBox::question(this, "确认删除", "确定删除记录吗?") != QMessageBox::Yes) {
return;
}
if (!sqlOpr_->DeleteAccount(id)) {
QMessageBox::warning(this, "错误", QString::fromStdString(sqlOpr_->GetLastErr()));
return;
}
// 从结果列表中移除记录
for (auto it = result_.begin(); it != result_.end(); ++it) {
if (it->id == id) {
result_.erase(it);
break;
}
}
// 从表格中移除行
tw_->removeRow(selectedItems[0]->row());
CalcShow();
});
menu.exec(tw_->viewport()->mapToGlobal(pos));
}
void FilterForm::TransMenu(int id, QMenu& menu, QTableWidgetItem* item)
{
QString payType;
for (const auto& item : result_) {
if (item.id == id) {
payType = QString::fromStdString(item.payType);
break;
}
}
if (payType.isEmpty()) {
QMessageBox::warning(this, "警告", QString("未找到ID为%1的支付类型。").arg(id));
return;
}
// 根据支付类型执行不同操作
QAction* acFile = nullptr;
QString pur;
if (payType == "现金支出") {
acFile = menu.addAction("转为信用支出");
pur = "信用支出";
} else if (payType == "现金收入") {
acFile = menu.addAction("转为信用收入");
pur = "信用收入";
} else if (payType == "信用支出") {
acFile = menu.addAction("转为现金支出");
pur = "现金支出";
} else if (payType == "信用收入") {
acFile = menu.addAction("转为现金收入");
pur = "现金收入";
}
if (acFile == nullptr) {
return;
}
connect(acFile, &QAction::triggered, [this, id, pur, item]() {
// 执行支付类型转换逻辑
if (QMessageBox::question(this, "确认转换", QString("确定将ID为%1的记录转为%2吗?").arg(id).arg(pur),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
// 1. 更新数据库中的支付类型
bool done = false;
for (auto& item : result_) {
if (item.id != id) {
continue;
}
item.payType = pur.toStdString();
if (!sqlOpr_->UpdateAccount(item)) {
QMessageBox::critical(this, "错误", "更新数据库失败");
return;
}
done = true;
break;
}
if (done) {
item->setText(pur);
CalcShow();
QMessageBox::information(this, "成功", QString("ID为%1的记录已成功转为%2。").arg(id).arg(pur));
}
}
});
}