放弃MySQL/PostgreSQL:如何用SQLite3+C++20打造承载千万级数据存储《软件项目管理》
·
一:【工程项目】运行结果
1:生成测试数据


2:添加软件项目


3:修改软件项目(扈三娘 更改为 vico)


剩余各项功能不在全部演示出来,大家自己去测试!!!
二:【工程项目】使用技术栈
1:项目简介
本项目是一个专为软件公司设计的项目信息管理系统,能够高效管理大量项目数据。系统采用现代化的Qt6框架开发,具备高性能分页显示、智能搜索、数据导出等核心功能,特别针对大数据量场景进行了优化。
2:使用技术栈
-
Qt 6.5.3:跨平台GUI框架
-
C++17:现代C++标准
-
SQLite 3:轻量级数据库
-
QSql:Qt数据库模块
开发工具:
-
Qt Creator:集成开发环境
-
MinGW 64-bit:编译器
CMake/qmake:构建系统
三:【工程项目】源码分析
1:mainwindow.h文件--源码注释
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QStandardItemModel>#include <QItemSelection>#include <QCalendarWidget>#include <QDialogButtonBox>#include <QLineEdit>#include <QComboBox>#include <QPushButton>#include <QLabel>#include <QProgressBar>#include <QThread>#include <QTimer>#include <QCloseEvent>QT_BEGIN_NAMESPACEclass QTableView;class QToolBar;class QAction;class QLabel;class QLineEdit;class QComboBox;class QPushButton;class QProgressBar;class QThread;class QTimer;QT_END_NAMESPACEclass DatabaseManager;class SimpleDialog;class SimplePaginationModel;class PaginationController;// 数据生成工作线程class DataGenerationWorker : public QObject{Q_OBJECTpublic:explicit DataGenerationWorker(QObject *parent = nullptr);void stopGeneration();public slots:void generateTestData(int totalRecords);signals:void progressUpdated(int current, int total, double rate);void generationCompleted(int totalRecords, int totalTime);void generationFailed(const QString &error);private:void generateBatchData(int startIndex, int batchSize, const QStringList &projectNames,const QStringList &managerNames, const QStringList &statuses,const QStringList &descriptions);void saveProgress(int currentCount, int totalCount);int loadProgress();volatile bool m_shouldStop;};class MainWindow : public QMainWindow{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void onAddProject();void onDeleteProject();void onEditProject();void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);void onSearch();void onClearSearch();void onRefreshData();void onGenerateTestData();void onDataGenerationProgress(int current, int total, double rate);void onDataGenerationCompleted(int totalRecords, int totalTime);void onDataGenerationFailed(const QString &error);void onDataLoadingFinished(); // 数据加载完成后的处理void onExportCSV(); // 导出CSV功能protected:void resizeEvent(QResizeEvent *event) override;void closeEvent(QCloseEvent *event) override;private:void setupModel();void setupViews();void createToolbar();void createSearchBar();void createStatusBar();void loadDataFromDatabase();QDate getDateFromDialog(const QString &title, const QString &label, const QDate &defaultDate);void updateStatusBar();void adjustColumnWidths();QString generateUniqueProjectId();void highlightNewRecord();QStandardItemModel *model;SimplePaginationModel *paginationModel;QTableView *tableView;PaginationController *paginationController;QLineEdit *searchLineEdit;QComboBox *searchComboBox;QPushButton *searchButton;QPushButton *clearButton;QLabel *statusLabel;QProgressBar *progressBar;DatabaseManager *dbManager;int currentSelectedRow = -1;bool isSearchMode = false;bool usePagination = true;bool shouldSelectNewRecord = false; // 标记是否需要选中新添加的记录bool shouldSelectEditedRecord = false; // 标记是否需要选中编辑后的记录int editedRecordRow = -1; // 编辑后需要选中的行号// 数据生成相关QThread *dataGenerationThread;DataGenerationWorker *dataGenerationWorker;QTimer *progressUpdateTimer;};#endif // MAINWINDOW_H
2:mainwindow.cpp文件--源码注释
#include "mainwindow.h"#include "databasemanager.h"#include "simpledialog.h"#include "simplepaginationmodel.h"#include "paginationcontroller.h"#include <QTableView>#include <QToolBar>#include <QAction>#include <QVBoxLayout>#include <QHBoxLayout>#include <QMessageBox>#include <QInputDialog>#include <QDate>#include <QDateTime>#include <QTime>#include <QRandomGenerator>#include <QMetaObject>#include <QThread>#include <QTimer>#include <QHeaderView>#include <QDialog>#include <QCalendarWidget>#include <QDialogButtonBox>#include <QLabel>#include <QLineEdit>#include <QComboBox>#include <QPushButton>#include <QProgressBar>#include <QStatusBar>#include <QApplication>#include <QTimer>#include <QResizeEvent>#include <QSqlQuery>#include <QFile>#include <QTextStream>#include <QThread>#include <QFileDialog>#include <QStandardPaths>#include <QEventLoop>#include <QStringConverter>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), model(new QStandardItemModel(this)), tableView(new QTableView(this)){// 设置窗口属性setWindowTitle("承载千万级数据存储--软件项目管理 v3.0");resize(1400, 900); // 增大窗口尺寸以更好地显示表格// 初始化数据库管理器dbManager = &DatabaseManager::getInstance();// 初始化分页模型paginationModel = new SimplePaginationModel(this);paginationController = new PaginationController(this);// 初始化数据生成工作线程dataGenerationThread = new QThread(this);dataGenerationWorker = new DataGenerationWorker(); // 不设置父对象,因为要移动到其他线程dataGenerationWorker->moveToThread(dataGenerationThread);// 连接工作线程信号connect(dataGenerationWorker, &DataGenerationWorker::progressUpdated,this, &MainWindow::onDataGenerationProgress);connect(dataGenerationWorker, &DataGenerationWorker::generationCompleted,this, &MainWindow::onDataGenerationCompleted);connect(dataGenerationWorker, &DataGenerationWorker::generationFailed,this, &MainWindow::onDataGenerationFailed);// 启动工作线程dataGenerationThread->start();// 初始化模型和视图setupModel();setupViews();createToolbar();createSearchBar();createStatusBar();// 延迟加载数据,让界面先显示出来QTimer::singleShot(100, this, &MainWindow::loadDataFromDatabase);}MainWindow::~MainWindow(){// 停止数据生成if (dataGenerationWorker) {dataGenerationWorker->stopGeneration();}// 正确清理工作线程if (dataGenerationThread && dataGenerationThread->isRunning()) {// 请求线程退出dataGenerationThread->quit();// 等待线程结束(最多等待3秒)if (!dataGenerationThread->wait(3000)) {// 如果线程没有在3秒内结束,强制终止qDebug() << "强制终止工作线程";dataGenerationThread->terminate();dataGenerationThread->wait();}}// 删除工作线程对象(先删除worker,再删除thread)if (dataGenerationWorker) {dataGenerationWorker->deleteLater();dataGenerationWorker = nullptr;}// 删除线程对象if (dataGenerationThread) {dataGenerationThread->deleteLater();dataGenerationThread = nullptr;}}void MainWindow::closeEvent(QCloseEvent *event){// 停止数据生成if (dataGenerationWorker) {dataGenerationWorker->stopGeneration();}// 等待工作线程结束if (dataGenerationThread && dataGenerationThread->isRunning()) {dataGenerationThread->quit();if (!dataGenerationThread->wait(2000)) {// 如果2秒内没有结束,强制终止dataGenerationThread->terminate();dataGenerationThread->wait();}}// 接受关闭事件event->accept();}void MainWindow::setupModel(){// 设置表头QStringList headers;headers << "项目ID" << "项目名称" << "项目经理" << "开始日期"<< "结束日期" << "预算(万元)" << "状态" << "描述";model->setHorizontalHeaderLabels(headers);}void MainWindow::setupViews(){// 设置表格视图属性if (usePagination) {tableView->setModel(paginationModel);} else {tableView->setModel(model);}tableView->setSelectionMode(QAbstractItemView::SingleSelection);tableView->setSelectionBehavior(QAbstractItemView::SelectRows);tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);// 显示行号,不隐藏左侧编号tableView->verticalHeader()->setVisible(true);tableView->verticalHeader()->setDefaultSectionSize(25);// 设置列宽 - 让表格填充满整个窗口tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // 项目ID - 固定宽度tableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); // 项目名称 - 拉伸填充tableView->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); // 项目经理 - 固定宽度tableView->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); // 开始日期 - 固定宽度tableView->horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents); // 结束日期 - 固定宽度tableView->horizontalHeader()->setSectionResizeMode(5, QHeaderView::ResizeToContents); // 预算 - 固定宽度tableView->horizontalHeader()->setSectionResizeMode(6, QHeaderView::ResizeToContents); // 状态 - 固定宽度tableView->horizontalHeader()->setSectionResizeMode(7, QHeaderView::Stretch); // 描述 - 拉伸填充// 设置表格样式tableView->setAlternatingRowColors(true); // 交替行颜色tableView->setGridStyle(Qt::SolidLine); // 网格线样式tableView->setShowGrid(true); // 显示网格线tableView->setSortingEnabled(true); // 启用排序// 连接选择变化信号connect(tableView->selectionModel(), &QItemSelectionModel::selectionChanged,this, &MainWindow::onSelectionChanged);// 连接分页控制器信号if (usePagination) {connect(paginationController, &PaginationController::pageChanged,paginationModel, &SimplePaginationModel::setCurrentPage);connect(paginationController, &PaginationController::pageSizeChanged,paginationModel, &SimplePaginationModel::setPageSize);connect(paginationController, &PaginationController::refreshRequested,paginationModel, &SimplePaginationModel::refreshData);connect(paginationModel, &SimplePaginationModel::pageChanged,paginationController, &PaginationController::setCurrentPage);connect(paginationModel, &SimplePaginationModel::totalRecordsChanged,paginationController, &PaginationController::setTotalRecords);// 连接pageChanged信号来更新总页数connect(paginationModel, &SimplePaginationModel::pageChanged,[this](int /*currentPage*/, int totalPages) {paginationController->setTotalPages(totalPages);});connect(paginationModel, &SimplePaginationModel::dataLoadingStarted,[this]() { paginationController->setLoading(true); });connect(paginationModel, &SimplePaginationModel::dataLoadingFinished,[this]() {paginationController->setLoading(false);onDataLoadingFinished(); // 数据加载完成后处理选中逻辑});}// 设置中心部件 - 让表格填充满整个窗口QWidget *centralWidget = new QWidget(this);QVBoxLayout *layout = new QVBoxLayout(centralWidget);layout->setContentsMargins(0, 0, 0, 0); // 移除边距layout->addWidget(tableView);// 添加分页控制器if (usePagination) {layout->addWidget(paginationController);}centralWidget->setLayout(layout);setCentralWidget(centralWidget);}void MainWindow::createToolbar(){QToolBar *toolBar = addToolBar("操作");QAction *addAction = new QAction(QIcon(":/new/prefix1/images/add001.ico"), "添加项目", this);QAction *deleteAction = new QAction(QIcon(":/new/prefix1/images/delete001.ico"), "删除项目", this);QAction *editAction = new QAction(QIcon(":/new/prefix1/images/edit001.ico"), "编辑项目", this);QAction *refreshAction = new QAction("刷新系统数据", this);QAction *generateTestDataAction = new QAction("生成测试数据", this);QAction *exportCSVAction = new QAction("导出数据(CSV格式)", this);// 设置工具按钮样式addAction->setShortcut(QKeySequence::New);deleteAction->setShortcut(QKeySequence::Delete);editAction->setShortcut(Qt::Key_Return);refreshAction->setShortcut(QKeySequence::Refresh);exportCSVAction->setShortcut(QKeySequence::SaveAs);toolBar->addAction(addAction);toolBar->addAction(deleteAction);toolBar->addAction(editAction);toolBar->addSeparator();toolBar->addAction(refreshAction);toolBar->addAction(generateTestDataAction);toolBar->addAction(exportCSVAction);// 连接信号槽connect(addAction, &QAction::triggered, this, &MainWindow::onAddProject);connect(deleteAction, &QAction::triggered, this, &MainWindow::onDeleteProject);connect(editAction, &QAction::triggered, this, &MainWindow::onEditProject);connect(refreshAction, &QAction::triggered, this, &MainWindow::onRefreshData);connect(generateTestDataAction, &QAction::triggered, this, &MainWindow::onGenerateTestData);connect(exportCSVAction, &QAction::triggered, this, &MainWindow::onExportCSV);}void MainWindow::createSearchBar(){QWidget *searchWidget = new QWidget();QHBoxLayout *searchLayout = new QHBoxLayout(searchWidget);searchLineEdit = new QLineEdit();searchLineEdit->setPlaceholderText("输入搜索关键词...");searchLineEdit->setMaximumWidth(200);searchComboBox = new QComboBox();searchComboBox->addItems({"全部", "项目名称", "项目经理", "状态", "项目ID"});searchComboBox->setMaximumWidth(100);searchButton = new QPushButton("搜索");clearButton = new QPushButton("清除");searchLayout->addWidget(new QLabel("搜索:"));searchLayout->addWidget(searchLineEdit);searchLayout->addWidget(searchComboBox);searchLayout->addWidget(searchButton);searchLayout->addWidget(clearButton);searchLayout->addStretch();// 添加到工具栏QToolBar *searchToolBar = addToolBar("搜索");searchToolBar->addWidget(searchWidget);// 连接信号槽connect(searchButton, &QPushButton::clicked, this, &MainWindow::onSearch);connect(clearButton, &QPushButton::clicked, this, &MainWindow::onClearSearch);connect(searchLineEdit, &QLineEdit::returnPressed, this, &MainWindow::onSearch);}void MainWindow::createStatusBar(){statusLabel = new QLabel("就绪");progressBar = new QProgressBar();progressBar->setVisible(false);progressBar->setMaximumWidth(200);statusBar()->addWidget(statusLabel);statusBar()->addPermanentWidget(progressBar);}void MainWindow::loadDataFromDatabase(){if (!dbManager->isConnected()) {QMessageBox::critical(this, "错误", "数据库连接失败: " + dbManager->getLastError());return;}// 设置进度条为确定模式,总共100步progressBar->setVisible(true);progressBar->setRange(0, 100);progressBar->setValue(0);statusLabel->setText("正在初始化高性能数据模型... (0%)");QApplication::processEvents();// 步骤1: 检查数据库连接 (10%)progressBar->setValue(10);statusLabel->setText("检查数据库连接... (10%)");QApplication::processEvents();QThread::msleep(50); // 短暂延迟让用户看到进度// 步骤2: 检查数据量 (20%)progressBar->setValue(20);statusLabel->setText("检查数据量... (20%)");QApplication::processEvents();QThread::msleep(50);int totalRecords = dbManager->getProjectCount();if (totalRecords > 10000) {usePagination = true; // 大数据量强制使用分页模式qDebug() << "检测到大量数据(" << totalRecords << "条),自动启用分页模式";}// 步骤3: 选择显示模式 (30%)progressBar->setValue(30);statusLabel->setText("选择显示模式... (30%)");QApplication::processEvents();QThread::msleep(50);if (usePagination) {// 步骤4: 初始化分页模型 (40%)progressBar->setValue(40);statusLabel->setText("初始化分页模型... (40%)");QApplication::processEvents();QThread::msleep(50);// 步骤5: 设置数据库连接 (50%)progressBar->setValue(50);statusLabel->setText("设置数据库连接... (50%)");QApplication::processEvents();QThread::msleep(50);paginationModel->setDatabase(dbManager->getDatabase());paginationModel->setPageSize(100); // 每页100条记录paginationModel->setCacheSize(20); // 设置缓存20页,提高分页响应速度// 确保分页控制器的页面大小与模型一致paginationController->setPageSize(100);// 步骤6: 计算总记录数 (60%)progressBar->setValue(60);statusLabel->setText("计算总记录数... (60%)");QApplication::processEvents();QThread::msleep(50);// 步骤7: 准备分页数据 (70%)progressBar->setValue(70);statusLabel->setText("准备分页数据... (70%)");QApplication::processEvents();QThread::msleep(50);// 步骤8: 准备加载第一页数据 (80%)progressBar->setValue(80);statusLabel->setText("准备加载第一页数据... (80%)");QApplication::processEvents();QThread::msleep(50);// 步骤9: 启动分页数据加载 (90%)progressBar->setValue(90);statusLabel->setText("启动分页数据加载... (90%)");QApplication::processEvents();QThread::msleep(50);// 步骤10: 完成初始化 (100%)progressBar->setValue(100);statusLabel->setText(QString("高性能分页模式已启用 (共%1条记录)").arg(totalRecords));QApplication::processEvents();QThread::msleep(100);// 异步启动分页数据加载,避免阻塞UIQTimer::singleShot(500, this, [this]() {paginationModel->refreshData();});} else {// 使用传统模型progressBar->setValue(40);statusLabel->setText("使用传统模式加载数据... (40%)");QApplication::processEvents();QThread::msleep(50);progressBar->setValue(60);statusLabel->setText("加载项目数据... (60%)");QApplication::processEvents();QThread::msleep(50);if (dbManager->loadProjects(model)) {progressBar->setValue(80);statusLabel->setText("更新状态栏... (80%)");QApplication::processEvents();QThread::msleep(50);updateStatusBar();progressBar->setValue(100);statusLabel->setText("数据加载完成 (100%)");QApplication::processEvents();QThread::msleep(100);// 调整列宽以填充满表格QTimer::singleShot(100, this, &MainWindow::adjustColumnWidths);} else {progressBar->setValue(100);QMessageBox::critical(this, "错误", "加载数据失败: " + dbManager->getLastError());statusLabel->setText("数据加载失败");}}// 延迟隐藏进度条,让用户看到100%完成状态QTimer::singleShot(1000, this, [this]() {progressBar->setVisible(false);});}void MainWindow::updateStatusBar(){int totalProjects = dbManager->getProjectCount();double totalBudget = dbManager->getTotalBudget();QStringList statusStats = dbManager->getProjectStatusStats();QString statusText = QString("总项目数: %1 | 总预算: %2万元 | %3").arg(totalProjects).arg(totalBudget, 0, 'f', 2).arg(statusStats.join(" | "));statusLabel->setText(statusText);}void MainWindow::adjustColumnWidths(){// 获取表格的可用宽度int tableWidth = tableView->width();int availableWidth = tableWidth - 20; // 留出一些边距// 计算固定宽度列的总宽度int fixedWidth = 0;fixedWidth += 80; // 项目IDfixedWidth += 100; // 项目经理fixedWidth += 100; // 开始日期fixedWidth += 100; // 结束日期fixedWidth += 100; // 预算fixedWidth += 80; // 状态// 计算可拉伸列的宽度int stretchWidth = (availableWidth - fixedWidth) / 2; // 项目名称和描述各占一半// 设置列宽tableView->setColumnWidth(0, 80); // 项目IDtableView->setColumnWidth(1, stretchWidth); // 项目名称tableView->setColumnWidth(2, 100); // 项目经理tableView->setColumnWidth(3, 100); // 开始日期tableView->setColumnWidth(4, 100); // 结束日期tableView->setColumnWidth(5, 100); // 预算tableView->setColumnWidth(6, 80); // 状态tableView->setColumnWidth(7, stretchWidth); // 描述}void MainWindow::onSearch(){QString keyword = searchLineEdit->text().trimmed();if (keyword.isEmpty()) {QMessageBox::information(this, "提示", "请输入搜索关键词");return;}QString filterType = "all";switch (searchComboBox->currentIndex()) {case 0: filterType = "all"; break;case 1: filterType = "name"; break;case 2: filterType = "manager"; break;case 3: filterType = "status"; break;case 4: filterType = "id"; break;}progressBar->setVisible(true);progressBar->setRange(0, 0);statusLabel->setText("正在搜索...");QApplication::processEvents();if (usePagination) {// 使用分页模型搜索paginationModel->setSearchFilter(keyword, filterType);statusLabel->setText(QString("搜索完成,关键词: %1").arg(keyword));} else {// 使用传统搜索QStandardItemModel* searchModel = dbManager->searchProjects(keyword, filterType);if (searchModel) {// 保存原始模型if (!isSearchMode) {// 这里可以保存原始模型的引用}// 设置搜索结果模型tableView->setModel(searchModel);isSearchMode = true;statusLabel->setText(QString("搜索完成,找到 %1 条结果").arg(searchModel->rowCount()));} else {QMessageBox::warning(this, "搜索失败", dbManager->getLastError());statusLabel->setText("搜索失败");}}progressBar->setVisible(false);}void MainWindow::onClearSearch(){if (usePagination) {// 使用分页模型清除搜索paginationModel->clearSearchFilter();searchLineEdit->clear();searchComboBox->setCurrentIndex(0);statusLabel->setText("已清除搜索");} else if (isSearchMode) {loadDataFromDatabase();isSearchMode = false;searchLineEdit->clear();searchComboBox->setCurrentIndex(0);statusLabel->setText("已清除搜索");}}void MainWindow::onRefreshData(){if (usePagination) {// 使用分页模型刷新paginationModel->refreshData();searchLineEdit->clear();searchComboBox->setCurrentIndex(0);statusLabel->setText("数据已刷新");} else {loadDataFromDatabase();isSearchMode = false;searchLineEdit->clear();searchComboBox->setCurrentIndex(0);}}QDate MainWindow::getDateFromDialog(const QString &title, const QString &label, const QDate &defaultDate){QDialog dialog(this);dialog.setWindowTitle(title);QCalendarWidget *calendar = new QCalendarWidget(&dialog);calendar->setSelectedDate(defaultDate);QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);QVBoxLayout *layout = new QVBoxLayout(&dialog);layout->addWidget(new QLabel(label));layout->addWidget(calendar);layout->addWidget(buttons);connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);if (dialog.exec() == QDialog::Accepted) {return calendar->selectedDate();}return QDate();}void MainWindow::onAddProject(){SimpleDialog dialog(this);dialog.setEditMode(false);// 生成唯一的项目IDQString newId = generateUniqueProjectId();// 设置项目IDQStringList initialData;initialData << newId << "" << "" << "" << "" << "" << "" << "";dialog.setProjectData(initialData);if (dialog.exec() == QDialog::Accepted) {QStringList projectData = dialog.getProjectData();// 添加到数据库if (dbManager->addProject(projectData)) {qDebug() << "项目添加成功,开始刷新数据...";// 设置标记,表示需要在数据加载完成后选中新记录shouldSelectNewRecord = true;// 刷新数据if (usePagination) {qDebug() << "使用分页模式刷新数据";// 确保跳转到第一页显示新记录paginationModel->setCurrentPage(1);paginationModel->refreshData();// 高亮显示新添加的记录(第一行)paginationModel->setHighlightedRow(0);// 3秒后清除高亮QTimer::singleShot(3000, paginationModel, &SimplePaginationModel::clearHighlight);} else {qDebug() << "使用非分页模式刷新数据";loadDataFromDatabase();highlightNewRecord();// 非分页模式立即选中新记录if (tableView->model() && tableView->model()->rowCount() > 0) {qDebug() << "非分页模式:立即选中新添加的记录,行号: 0";tableView->selectRow(0);currentSelectedRow = 0;tableView->setFocus();tableView->scrollToTop();}}qDebug() << "数据刷新完成";QMessageBox::information(this, "成功", QString("项目 %1 添加成功!").arg(newId));} else {QMessageBox::critical(this, "错误", "添加项目失败: " + dbManager->getLastError());}}}void MainWindow::onDeleteProject(){if (currentSelectedRow == -1) {QMessageBox::warning(this, "警告", "请先选择要删除的项目!");return;}QString projectId;QString projectName;// 根据模式获取项目信息if (usePagination) {// 分页模式:从分页模型获取数据QModelIndex idIndex = paginationModel->index(currentSelectedRow, 0);QModelIndex nameIndex = paginationModel->index(currentSelectedRow, 1);projectId = paginationModel->data(idIndex, Qt::DisplayRole).toString();projectName = paginationModel->data(nameIndex, Qt::DisplayRole).toString();} else {// 非分页模式:从标准模型获取数据projectId = model->item(currentSelectedRow, 0)->text();projectName = model->item(currentSelectedRow, 1)->text();}int ret = QMessageBox::question(this, "确认删除",QString("确定要删除项目:\n\n%1 - %2\n\n此操作不可撤销!").arg(projectId).arg(projectName),QMessageBox::Yes | QMessageBox::No);if (ret == QMessageBox::Yes) {// 使用项目ID删除,而不是行号if (dbManager->deleteProjectById(projectId)) {qDebug() << "项目删除成功,开始刷新数据...";// 刷新数据if (usePagination) {qDebug() << "使用分页模式刷新数据";paginationModel->refreshData();} else {qDebug() << "使用非分页模式刷新数据";loadDataFromDatabase();}currentSelectedRow = -1;qDebug() << "数据刷新完成";QMessageBox::information(this, "成功", "项目已删除");} else {QMessageBox::critical(this, "错误", "删除项目失败: " + dbManager->getLastError());}}}void MainWindow::onEditProject(){if (currentSelectedRow == -1) {QMessageBox::warning(this, "警告", "请先选择要编辑的项目!");return;}// 获取当前项目数据QStringList currentData;// 根据模式获取项目数据if (usePagination) {// 分页模式:从分页模型获取数据for (int i = 0; i < 8; ++i) {QModelIndex index = paginationModel->index(currentSelectedRow, i);QString data = paginationModel->data(index, Qt::DisplayRole).toString();currentData << data;}} else {// 非分页模式:从标准模型获取数据for (int i = 0; i < 8; ++i) {QStandardItem *item = model->item(currentSelectedRow, i);if (item) {currentData << item->text();} else {currentData << "";}}}SimpleDialog dialog(this);dialog.setEditMode(true);dialog.setProjectData(currentData);if (dialog.exec() == QDialog::Accepted) {QStringList projectData = dialog.getProjectData();// 获取项目ID用于更新QString projectId = currentData[0]; // 项目ID在第一列// 更新数据库if (dbManager->updateProjectById(projectId, projectData)) {qDebug() << "项目更新成功,开始刷新数据...";// 保存当前选中的行号int selectedRow = currentSelectedRow;// 设置标记,表示需要在数据加载完成后选中编辑后的记录shouldSelectEditedRecord = true;editedRecordRow = selectedRow;// 刷新数据if (usePagination) {qDebug() << "使用分页模式刷新数据";paginationModel->refreshData();} else {qDebug() << "使用非分页模式刷新数据";loadDataFromDatabase();// 非分页模式立即选中编辑后的记录if (tableView->model() && tableView->model()->rowCount() > 0 &&selectedRow >= 0 && selectedRow < tableView->model()->rowCount()) {qDebug() << "非分页模式:立即选中编辑后的记录,行号:" << selectedRow;tableView->selectRow(selectedRow);currentSelectedRow = selectedRow;tableView->setFocus();tableView->scrollTo(tableView->model()->index(selectedRow, 0));}}qDebug() << "数据刷新完成";QMessageBox::information(this, "成功", QString("项目 %1 更新成功!").arg(projectData[0]));} else {QMessageBox::critical(this, "错误", "更新项目失败: " + dbManager->getLastError());}}}void MainWindow::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected){Q_UNUSED(deselected);if (selected.indexes().isEmpty()) {currentSelectedRow = -1;} else {currentSelectedRow = selected.indexes().first().row();}}void MainWindow::resizeEvent(QResizeEvent *event){QMainWindow::resizeEvent(event);// 延迟调整列宽,确保表格已经完成大小调整QTimer::singleShot(50, this, &MainWindow::adjustColumnWidths);}QString MainWindow::generateUniqueProjectId(){if (!dbManager->isConnected()) {return "PRJ001";}// 查询数据库中最大的项目IDQSqlQuery query("SELECT MAX(CAST(SUBSTR(project_id, 4) AS INTEGER)) FROM projects", *dbManager->getDatabase());int maxId = 0;if (query.exec() && query.next()) {maxId = query.value(0).toInt();}// 生成新的唯一IDQString newId;int attempts = 0;do {maxId++;newId = QString("PRJ%1").arg(maxId, 3, 10, QLatin1Char('0'));// 检查ID是否已存在QSqlQuery checkQuery("SELECT COUNT(*) FROM projects WHERE project_id = ?", *dbManager->getDatabase());checkQuery.bindValue(0, newId);if (checkQuery.exec() && checkQuery.next()) {if (checkQuery.value(0).toInt() == 0) {break; // ID不存在,可以使用}}attempts++;} while (attempts < 1000); // 防止无限循环return newId;}void MainWindow::highlightNewRecord(){if (!usePagination && model->rowCount() > 0) {// 为非分页模式设置第一行高亮for (int col = 0; col < model->columnCount(); ++col) {QStandardItem *item = model->item(0, col);if (item) {item->setBackground(QColor(173, 216, 230)); // 浅蓝色背景item->setForeground(QColor(0, 0, 139)); // 深蓝色文字}}// 3秒后清除高亮QTimer::singleShot(3000, [this]() {if (model->rowCount() > 0) {for (int col = 0; col < model->columnCount(); ++col) {QStandardItem *item = model->item(0, col);if (item) {item->setBackground(QBrush()); // 恢复默认背景item->setForeground(QBrush()); // 恢复默认前景色}}}});}}void MainWindow::onGenerateTestData(){// 安全检查if (!dataGenerationWorker || !dataGenerationThread) {QMessageBox::critical(this, "错误", "数据生成工作线程未正确初始化");return;}if (!progressBar || !statusLabel) {QMessageBox::critical(this, "错误", "UI组件未正确初始化");return;}// 确认对话框QMessageBox::StandardButton reply = QMessageBox::question(this,"测试数据","确定要生成10万条测试数据吗?\n根据情况设置100万 或者 1000万等\n这可能需要几分钟时间。\n\n注意:生成过程中窗口仍可正常操作。",QMessageBox::Yes | QMessageBox::No,QMessageBox::No);if (reply != QMessageBox::Yes) {return;}// 注意:工具栏按钮通过QAction控制,这里不需要禁用按钮// 显示进度条progressBar->setVisible(true);progressBar->setRange(0, 0); // 不确定进度statusLabel->setText("正在生成测试数据,请稍候...");// 启动数据生成工作线程const int totalRecords = 100000; // 10万条记录,备注根据情况来设置此工作线程try {QMetaObject::invokeMethod(dataGenerationWorker, "generateTestData",Qt::QueuedConnection, Q_ARG(int, totalRecords));} catch (const std::exception& e) {QMessageBox::critical(this, "错误", QString("启动数据生成失败: %1").arg(e.what()));progressBar->setVisible(false);statusLabel->setText("数据生成启动失败");} catch (...) {QMessageBox::critical(this, "错误", "启动数据生成时发生未知错误");progressBar->setVisible(false);statusLabel->setText("数据生成启动失败");}}void MainWindow::onDataGenerationProgress(int current, int /*total*/, double rate){// 更新进度显示statusLabel->setText(QString("已生成 %1 条记录,速度: %2 条/秒").arg(current).arg(QString::number(rate, 'f', 0)));}void MainWindow::onDataGenerationCompleted(int totalRecords, int totalTime){// 隐藏进度条progressBar->setVisible(false);// 刷新数据if (usePagination) {paginationModel->refreshData();} else {loadDataFromDatabase();}// 显示完成信息QMessageBox::information(this, "完成",QString("测试数据生成完成!\n\n""生成记录数: %1 条\n""耗时: %2 秒\n""平均速度: %3 条/秒").arg(totalRecords).arg(totalTime / 1000.0, 0, 'f', 1).arg(QString::number((double)totalRecords / totalTime * 1000, 'f', 0)));statusLabel->setText(QString("测试数据生成完成,共 %1 条记录").arg(totalRecords));qDebug() << "测试数据生成完成,共" << totalRecords << "条记录,耗时" << totalTime << "毫秒";}void MainWindow::onDataGenerationFailed(const QString &error){// 隐藏进度条progressBar->setVisible(false);// 显示错误信息QMessageBox::critical(this, "错误", "数据生成失败: " + error);statusLabel->setText("数据生成失败");qDebug() << "数据生成失败:" << error;}// DataGenerationWorker 实现DataGenerationWorker::DataGenerationWorker(QObject *parent): QObject(parent), m_shouldStop(false){}void DataGenerationWorker::stopGeneration(){m_shouldStop = true;}
由于篇幅有限,关注UP主后进群下载工程项目源码研究学习。
更多推荐
所有评论(0)