#include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupUI(); setWindowTitle("学生信息管理系统"); } MainWindow::~MainWindow() {} void MainWindow::setupUI() { QWidget *central = new QWidget(this); setCentralWidget(central); // Action 定义 QAction *actImport = new QAction("导入", this); QAction *actSave = new QAction("保存", this); QAction *actAddCls = new QAction("新增班级", this); QAction *actDelCls = new QAction("删除班级", this); QAction *actRenCls = new QAction("重命名班级", this); // 工具栏(唯一一行) QToolBar *toolBar = addToolBar("主工具栏"); toolBar->setMovable(false); toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); // 默认显示“文件”相关 toolBar->addAction(actImport); toolBar->addAction(actSave); // 伪菜单栏按钮 QToolButton *fileBtn = new QToolButton(this); fileBtn->setText("文件"); fileBtn->setToolButtonStyle(Qt::ToolButtonTextOnly); fileBtn->setAutoRaise(true); // 看起来像菜单 QToolButton *editBtn = new QToolButton(this); editBtn->setText("编辑"); editBtn->setToolButtonStyle(Qt::ToolButtonTextOnly); editBtn->setAutoRaise(true); // 放到菜单栏位置 QWidget *menuWidget = new QWidget(this); QHBoxLayout *menuLayout = new QHBoxLayout(menuWidget); menuLayout->setContentsMargins(6, 0, 0, 0); menuLayout->setSpacing(10); menuLayout->addWidget(fileBtn); menuLayout->addWidget(editBtn); menuLayout->addStretch(); menuBar()->setCornerWidget(menuWidget, Qt::TopLeftCorner); // 按钮点击 → 切换工具栏 connect(fileBtn, &QToolButton::clicked, this, [=]() { toolBar->clear(); toolBar->addAction(actImport); toolBar->addAction(actSave); }); connect(editBtn, &QToolButton::clicked, this, [=]() { toolBar->clear(); toolBar->addAction(actAddCls); toolBar->addAction(actDelCls); toolBar->addAction(actRenCls); }); // 核心控件初始化 tree = new QTreeWidget; tree->setHeaderLabel("班级列表"); table = new QTableWidget(0, 5); table->setHorizontalHeaderLabels({"姓名", "性别", "年龄", "学号", "电话"}); table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); table->setSelectionBehavior(QAbstractItemView::SelectRows); // 右侧编辑面板 nameEdit = new QLineEdit; genderEdit = new QLineEdit; ageEdit = new QLineEdit; idEdit = new QLineEdit; phoneEdit = new QLineEdit; addBtn = new QPushButton("新增"); delBtn = new QPushButton("删除"); updateBtn = new QPushButton("修改"); searchBtn = new QPushButton("查询"); refreshBtn = new QPushButton("刷新/清空查询"); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addWidget(new QLabel("姓名:")); rightLayout->addWidget(nameEdit); rightLayout->addWidget(new QLabel("性别:")); rightLayout->addWidget(genderEdit); rightLayout->addWidget(new QLabel("年龄:")); rightLayout->addWidget(ageEdit); rightLayout->addWidget(new QLabel("学号:")); rightLayout->addWidget(idEdit); rightLayout->addWidget(new QLabel("电话:")); rightLayout->addWidget(phoneEdit); rightLayout->addWidget(addBtn); rightLayout->addWidget(updateBtn); rightLayout->addWidget(delBtn); rightLayout->addWidget(searchBtn); rightLayout->addWidget(refreshBtn); rightLayout->addStretch(); // 分页控件 btnPrev = new QPushButton("上一页"); btnNext = new QPushButton("下一页"); pageLabel = new QLabel("第 1/1 页"); QHBoxLayout *pageLayout = new QHBoxLayout; pageLayout->addStretch(); pageLayout->addWidget(btnPrev); pageLayout->addWidget(pageLabel); pageLayout->addWidget(btnNext); pageLayout->addStretch(); // 布局组合 QVBoxLayout *centerLayout = new QVBoxLayout; centerLayout->addWidget(table); centerLayout->addLayout(pageLayout); QHBoxLayout *mainLayout = new QHBoxLayout(central); mainLayout->addWidget(tree, 1); mainLayout->addLayout(centerLayout, 3); mainLayout->addLayout(rightLayout, 1); // 信号槽连接 connect(actAddCls, &QAction::triggered, this, &MainWindow::addClass); connect(actDelCls, &QAction::triggered, this, &MainWindow::deleteClass); connect(actRenCls, &QAction::triggered, this, &MainWindow::renameClass); connect(actImport, &QAction::triggered, this, &MainWindow::importData); connect(actSave, &QAction::triggered, this, &MainWindow::saveData); connect(tree, &QTreeWidget::itemClicked, this, &MainWindow::onClassClicked); connect(addBtn, &QPushButton::clicked, this, &MainWindow::addStudent); connect(delBtn, &QPushButton::clicked, this, &MainWindow::deleteStudent); connect(updateBtn, &QPushButton::clicked, this, &MainWindow::updateStudent); connect(searchBtn, &QPushButton::clicked, this, &MainWindow::searchStudent); connect(refreshBtn, &QPushButton::clicked, this, &MainWindow::refreshAll); connect(table, &QTableWidget::cellClicked, this, &MainWindow::tableItemClicked); connect(btnPrev, &QPushButton::clicked, this, &MainWindow::prevPage); connect(btnNext, &QPushButton::clicked, this, &MainWindow::nextPage); resize(1000, 600); } // 业务逻辑实现 QString MainWindow::currentClass() const { auto item = tree->currentItem(); return item ? item->text(0) : ""; } void MainWindow::refreshTable() { table->setRowCount(0); int total = displayList.size(); int maxPage = qMax(1, (total + pageSize - 1) / pageSize); if (currentPage > maxPage) currentPage = maxPage; pageLabel->setText(QString("第 %1/%2 页").arg(currentPage).arg(maxPage)); int start = (currentPage - 1) * pageSize; int end = qMin(start + pageSize, total); for (int i = start; i < end; ++i) { int r = table->rowCount(); table->insertRow(r); table->setItem(r, 0, new QTableWidgetItem(displayList[i].name)); table->setItem(r, 1, new QTableWidgetItem(displayList[i].gender)); table->setItem(r, 2, new QTableWidgetItem(QString::number(displayList[i].age))); table->setItem(r, 3, new QTableWidgetItem(displayList[i].id)); table->setItem(r, 4, new QTableWidgetItem(displayList[i].phone)); } } void MainWindow::onClassClicked(QTreeWidgetItem *item) { displayList = classData[item->text(0)]; currentPage = 1; refreshTable(); } void MainWindow::addStudent() { QString cls = currentClass(); if (cls.isEmpty()) return; Student s{nameEdit->text(), genderEdit->text(), ageEdit->text().toInt(), idEdit->text(), phoneEdit->text()}; if (s.name.isEmpty() || s.id.isEmpty()) { QMessageBox::warning(this, "错误", "姓名和学号不能为空"); // return; } // 查重 for (const auto &st : classData[cls]) { if (st.id == s.id) { QMessageBox::warning(this, "错误", "学号重复"); return; } } classData[cls].append(s); saveClassToFile(cls); // 自动保存 // 刷新输入框内容 nameEdit->clear(); genderEdit->clear(); ageEdit->clear(); idEdit->clear(); phoneEdit->clear(); refreshAll(); } void MainWindow::updateStudent() { QString cls = currentClass(); int row = table->currentRow(); if (cls.isEmpty() || row < 0) return; int realIdx = (currentPage - 1) * pageSize + row; Student &s = classData[cls][realIdx]; s.name = nameEdit->text(); s.gender = genderEdit->text(); s.age = ageEdit->text().toInt(); s.phone = phoneEdit->text(); saveClassToFile(cls); refreshAll(); } void MainWindow::deleteStudent() { QString cls = currentClass(); int row = table->currentRow(); if (row < 0 || QMessageBox::question(this, "确认", "确定删除?") != QMessageBox::Yes) return; int realIdx = (currentPage - 1) * pageSize + row; classData[cls].removeAt(realIdx); saveClassToFile(cls); refreshAll(); } void MainWindow::searchStudent() { QString cls = currentClass(); if (cls.isEmpty()) { QMessageBox::warning(this, "提示", "请先在左侧选择一个班级再进行查询"); return; } // 1. 获取所有输入框的关键字,并去掉首尾空格 QString nameKw = nameEdit->text().trimmed(); QString genderKw = genderEdit->text().trimmed(); QString ageKw = ageEdit->text().trimmed(); QString idKw = idEdit->text().trimmed(); QString phoneKw = phoneEdit->text().trimmed(); displayList.clear(); // 2. 遍历当前班级的所有学生 for (const auto &s : classData[cls]) { // 初始假设该学生符合条件 bool isMatch = true; // 3. 逐个字段进行条件校验(逻辑:如果输入框不为空,则必须匹配该条件) // 校验姓名 if (!nameKw.isEmpty() && !s.name.contains(nameKw, Qt::CaseInsensitive)) { isMatch = false; } // 校验性别 if (isMatch && !genderKw.isEmpty() && !s.gender.contains(genderKw, Qt::CaseInsensitive)) { isMatch = false; } // 校验年龄 if (isMatch && !ageKw.isEmpty() && !QString::number(s.age).contains(ageKw)) { isMatch = false; } // 校验学号 if (isMatch && !idKw.isEmpty() && !s.id.contains(idKw, Qt::CaseInsensitive)) { isMatch = false; } // 校验电话 if (isMatch && !phoneKw.isEmpty() && !s.phone.contains(phoneKw, Qt::CaseInsensitive)) { isMatch = false; } // 4. 如果所有非空条件都通过,则加入显示列表 if (isMatch) { displayList.append(s); } } currentPage = 1; // 重置到第一页 refreshTable(); // 刷新表格显示 } void MainWindow::refreshAll() { QString cls = currentClass(); if (!cls.isEmpty()) displayList = classData[cls]; // 恢复初始状态 currentPage = 1; // 刷新输入框内容 nameEdit->clear(); genderEdit->clear(); ageEdit->clear(); idEdit->clear(); phoneEdit->clear(); refreshTable(); } // --- 文件与分页 --- void MainWindow::saveClassToFile(const QString &className) { QFile file(className + ".txt"); // 按班级名保存 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&file); for (const auto &s : classData[className]) { out << s.name << "," << s.gender << "," << s.age << "," << s.id << "," << s.phone << "\n"; } } } void MainWindow::saveData() { for (auto it = classData.begin(); it != classData.end(); ++it) saveClassToFile(it.key()); QMessageBox::information(this, "提示", "所有班级已保存"); } void MainWindow::importData() { // 1. 直接弹出文件选择框,不需要预先选中班级 QString path = QFileDialog::getOpenFileName(this, "导入班级文件", "", "文本文件 (*.txt);;所有文件 (*.*)"); if (path.isEmpty()) return; // 2. 提取文件名作为班级名 QFileInfo fileInfo(path); QString className = fileInfo.baseName(); // 例如 "高三二班" // 3. 检查班级是否已存在,不存在则新建 if (!classData.contains(className)) { classData[className] = QList(); QTreeWidgetItem *newItem = new QTreeWidgetItem({className}); tree->addTopLevelItem(newItem); tree->setCurrentItem(newItem); // 自动选中这个新创建的班级 } else { // 如果班级已存在,询问是追加还是清空 QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "班级已存在", QString("班级 '%1' 已存在,是否追加数据?\n(选“否”将清空原数据并覆盖)").arg(className), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (reply == QMessageBox::Cancel) return; if (reply == QMessageBox::No) { classData[className].clear(); // 清空原数据实现“覆盖” } // 选中已有的班级节点 QList items = tree->findItems(className, Qt::MatchExactly); if (!items.isEmpty()) tree->setCurrentItem(items[0]); } // 4. 读取文件内容 QFile file(path); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox::critical(this, "错误", "无法打开文件"); return; } QTextStream in(&file); int count = 0; while (!in.atEnd()) { QString line = in.readLine(); QStringList fields = line.split(","); if (fields.size() >= 5) { Student s; s.name = fields[0].trimmed(); s.gender = fields[1].trimmed(); s.age = fields[2].trimmed().toInt(); s.id = fields[3].trimmed(); s.phone = fields[4].trimmed(); classData[className].append(s); count++; } } file.close(); // 5. 更新显示 displayList = classData[className]; currentPage = 1; refreshTable(); QMessageBox::information(this, "导入成功", QString("已成功创建/更新班级 '%1',并导入 %2 条学生记录。").arg(className).arg(count)); } void MainWindow::prevPage() { if (currentPage > 1) { currentPage--; refreshTable(); } } void MainWindow::nextPage() { if (currentPage * pageSize < displayList.size()) { currentPage++; refreshTable(); } } void MainWindow::tableItemClicked(int row, int) { int realIdx = (currentPage - 1) * pageSize + row; if (realIdx < displayList.size()) { const auto &s = displayList[realIdx]; nameEdit->setText(s.name); genderEdit->setText(s.gender); ageEdit->setText(QString::number(s.age)); idEdit->setText(s.id); phoneEdit->setText(s.phone); } } // 班级管理槽函数(逻辑同前,移至菜单触发) void MainWindow::addClass() { while (true) { bool ok; // 弹出输入框 QString name = QInputDialog::getText(this, "新建班级", "请输入班级名称:", QLineEdit::Normal, "", &ok); // 如果用户点击了“取消”或直接关闭窗口,则退出函数 if (!ok) return; name = name.trimmed(); // 去除首尾空格 // 逻辑判断 1:不能为空 if (name.isEmpty()) { QMessageBox::warning(this, "错误", "班级名称不能为空,请重新输入!"); continue; // 跳回循环开始,重新弹出输入框 } // 逻辑判断 2:检查是否已存在 if (classData.contains(name)) { QMessageBox::warning(this, "错误", QString("班级“%1”已存在,请换一个名称!").arg(name)); continue; // 跳回循环开始,重新弹出输入框 } // 校验通过:保存数据并更新界面 classData[name] = QList(); QTreeWidgetItem *newItem = new QTreeWidgetItem({name}); tree->addTopLevelItem(newItem); tree->setCurrentItem(newItem); // 自动选中新创建的班级 break; // 成功创建后跳出循环 } } void MainWindow::deleteClass() { auto item = tree->currentItem(); if (!item) { QMessageBox::warning(this, "提示", "请先选择要删除的班级"); return; } if (item && QMessageBox::question(this, "确认", "删除班级及其信息?") == QMessageBox::Yes) { classData.remove(item->text(0)); delete item; displayList.clear(); refreshTable(); } } void MainWindow::renameClass() { // 1. 获取当前选中的班级节点 auto item = tree->currentItem(); if (!item) { QMessageBox::warning(this, "提示", "请先选择要重命名的班级"); return; } QString oldName = item->text(0); // 2. 使用循环处理重复或为空的情况 while (true) { bool ok; // 弹出输入框,默认显示旧名称 QString newName = QInputDialog::getText(this, "重命名班级", "请输入新的班级名称:", QLineEdit::Normal, oldName, &ok); // 如果用户点击了“取消”,则直接退出 if (!ok) return; newName = newName.trimmed(); // 去除多余空格 // 情况 A:名称没变,直接退出 if (newName == oldName) return; // 情况 B:名称为空 if (newName.isEmpty()) { QMessageBox::warning(this, "错误", "班级名称不能为空,请重新输入!"); continue; // 跳回循环开始,再次弹出对话框 } // 情况 C:名称与现有其他班级重复 if (classData.contains(newName)) { QMessageBox::warning(this, "错误", QString("班级“%1”已存在,请换一个名称!").arg(newName)); continue; // 跳回循环开始 } // 3. 校验通过,更新底层数据结构 QMap // 使用 take() 移除旧键并返回对应的数据列表,再赋值给新键 classData[newName] = classData.take(oldName); // 4. 更新界面显示的名称 item->setText(0, newName); break; // 完成重命名,跳出循环 } }