QT qml写了一个学生管理系统

QT qml写了一个学生管理系统

此系统只是简单的实现学生信息的维护

一.数据库端设计:使用的是mysql数据库

CREATE TABLE `student` ( `id` int NOT NULL, `name` varchar(45) COLLATE utf8mb3_bin DEFAULT NULL, `age` int DEFAULT NULL, `classnum` int DEFAULT NULL, `gender` varchar(1) COLLATE utf8mb3_bin DEFAULT NULL, `phone` varchar(11) COLLATE utf8mb3_bin DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin

表里面存了大概100条数据

二.model层设计

建立了学生实体,属性和数据库表属性保持一致

学生实体头文件代码:

#ifndef STUDENT1_H #define STUDENT1_H #include<string> #include<QString> #include<QObject> class Student1: public QObject { Q_OBJECT Q_PROPERTY(int id READ id1 CONSTANT) Q_PROPERTY(QString name READ name1 CONSTANT) Q_PROPERTY(int age READ age1 CONSTANT) Q_PROPERTY(int classNum READ classNum1 CONSTANT) Q_PROPERTY(QString gender READ gender1 CONSTANT) Q_PROPERTY(QString phone READ phone1 CONSTANT) /*Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged) Q_PROPERTY(QString gender READ gender WRITE setGender NOTIFY genderChanged) Q_PROPERTY(int classNum READ classNum WRITE setClassNum NOTIFY classNumChanged) signals: void idChanged(const QString &id); void nameChanged(const QString &name); void ageChanged(int age); void genderChanged(const QString &gender); void classNumChanged(const QString &classNum);*/ public: int id; QString name; int age; QString gender; int classNum; QString phone; Student1(); Student1(int id,QString name,int age,int classnum,QString gender,QString phone); int id1() const { return this->id; } QString name1() const { return this->name; } int age1() const { return this->age; } int classNum1() const { return this->classNum; } QString gender1() const { return this->gender; } QString phone1() const { return this->phone; } QString getPhone() const; void setPhone(const QString &newPhone); int getAge() const; void setAge(int newAge); int getId() const; void setId(int newId); QString getGender() const; void setGender(const QString &newGender); QString getName() const; void setName(const QString &newName); int getClassnum() const; void setClassnum(int newClassnum); }; Q_DECLARE_METATYPE(Student1*) #endif // STUDENT1_H

Q_PROPERTY一定要加上,因为在qml中我们能够调用student的属性

Q_DECLARE_METATYPE(Student1*)注册学生了类,我们也需要注册学生,方便qml访问学生实体。

三.业务层DBManager

DBManager主要就是对数据库表的增加、删除、修改、查询

代码如下:

#ifndef DBMANAGER_H #define DBMANAGER_H #include <QtSql/QSqlDatabase> #include <QObject> #include "Database/database.h" #include "Model/student1.h" #include <QtSql/QSqlQuery> class Dbmanager: public QObject { Q_OBJECT private: QSqlDatabase db; public: explicit Dbmanager(QObject *parent = nullptr); //对数据库的增删改查 Q_INVOKABLE bool deleteStudent(int id); Q_INVOKABLE bool updateStudent(const int &id, const QString &name, int age, const int &classNum, const QString &gender, const QString &phone); Q_INVOKABLE QVariantList QueryStudent(); Q_INVOKABLE bool addStudent(const int &id, const QString &name,const int age, const int &classNum, const QString &gender, const QString &phone,QString *errorMsg = nullptr); QSqlQuery queryDatabase(const QString &query); }; #endif // DBMANAGER_H

函数一定要加上Q_INVOKABLE

这样在qml能够直接调用

四.工具层,因为qml要用到treeModel,所以我们实现这个左边菜单栏的工具

代码如下:

#ifndef TREEMODEL_H #define TREEMODEL_H #include <QAbstractItemModel> #include <QVariant> #include <QVector> #include <QIcon> // 1. 定义树节点结构 struct TreeItem { QString name; // 显示名称 QString url; // 页面路径 (自定义数据) QIcon icon; // 图标 (可选) TreeItem *parent; // 父节点 QVector<TreeItem*> children; // 子节点列表 explicit TreeItem(const QString &name, const QString &url = "", TreeItem *parentItem = nullptr) : name(name), url(url), parent(parentItem) {} ~TreeItem() { qDeleteAll(children); } void appendChild(TreeItem *child) { children.append(child); } TreeItem *child(int row) { if (row < 0 || row >= children.size()) return nullptr; return children.at(row); } int childCount() const { return children.count(); } int row() const { if (parent) return parent->children.indexOf(const_cast<TreeItem*>(this)); return 0; } }; // 2. 定义模型类 class TreeModel : public QAbstractItemModel { Q_OBJECT // 注册自定义角色,以便在 QML 中通过 model.url 访问 Q_ENUMS(Roles) public: enum Roles { NameRole = Qt::UserRole + 1, UrlRole, IconRole }; explicit TreeModel(QObject *parent = nullptr); ~TreeModel(); // --- 必须实现的核心虚函数 --- QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; // --- 辅助函数:构建测试数据 --- void setupModelData(); protected: // 映射 Role 到 QML 属性名 QHash<int, QByteArray> roleNames() const override; private: TreeItem *rootItem; // 根节点(不可见) }; #endif // TREEMODEL_H

实现代码,可以用ai生成。

五.前端页面实现层

Main.qml代码,主要实现通过子页面进行实现:

import QtQuick import QtQuick.Layouts import QtQuick.Controls ApplicationWindow { id: root width: 640 height: 480 minimumWidth: 200 minimumHeight: 250 visible: true title: qsTr("Hello World") color: "lightblue" /*Component.onCompleted: { // 直接输出数组长度 console.log("学生数组长度:", students.length) console.log("第一个属性:", students[0]) }*/ /*property var students1: [ { "id": 1, "name": "张三", "age": 20, "major": "计算机科学", "email": "zhangsan@example.com" }, { "id": 2, "name": "李四", "age": 21, "major": "软件工程", "email": "lisi@example.com" }, { "id": 3, "name": "王五", "age": 19, "major": "人工智能", "email": "wangwu@example.com" } ]*/ ColumnLayout { anchors.fill: parent // 让布局填满整个窗口 spacing: 10 // Text 和 StackView 之间的间距 Text { text: "学生信息管理系统" font.pixelSize: 32 font.bold: true color: "#2c3e50" // 在 ColumnLayout 中,Layout.alignment 控制水平对齐 Layout.alignment: Qt.AlignHCenter // 如果希望 Text 高度自适应,不需要设置固定高度 } StackView { id: stackView5 // 使用 Layout 属性来控制大小,而不是固定的 width/height Layout.fillWidth: true // 宽度填满父容器 Layout.fillHeight: true // 高度占据剩余所有空间 Layout.preferredHeight: 400 // 可选:设置一个首选高度 background: Rectangle { color: "lightblue" } initialItem: "qml1/first.qml" } Text { text: "系统注册号4353465465654" font.pixelSize: 32 font.bold: true color: "#2c3e50" // 在 ColumnLayout 中,Layout.alignment 控制水平对齐 Layout.alignment: Qt.AlignHCenter // 如果希望 Text 高度自适应,不需要设置固定高度 } } }

实现效果图:

这是主页面,

点击编辑跳转到编辑界面

点击更新就可成功保存到数据库。

点击删除,提示后即可从数据库删除成功

点击菜单栏增加学生信息

成功后会提示增加成功。

主页面的分页菜单是通过ai生成的,listStudent.qml代码如下:

import QtQuick 2.14 import QtQuick.Controls 2.14 import QtQuick.Layouts 1.14 Rectangle { id: root1 color: "lightblue" property var students: dbManager.QueryStudent() // ========== 分页相关属性 ========== property int currentPage: 1 // 当前页码(从1开始) property int pageSize: 10 // 每页显示条数 property int totalCount: students.length // 总条数 property int totalPages: Math.ceil(totalCount / pageSize) // 总页数 // 计算当前页的数据 property var pagedStudents: { var start = (currentPage - 1) * pageSize var end = Math.min(start + pageSize, totalCount) return students.slice(start, end) } // 过滤后的数据(搜索 + 分页) property var filteredStudents: pagedStudents.filter(function(s) { return s.name.indexOf(searchField.text) !== -1 }) ColumnLayout { anchors.fill: parent spacing: 0 // ==================== 工具栏 ==================== Rectangle { Layout.fillWidth: true Layout.preferredHeight: 50 color: "white" border.color: "#e0e0e0" RowLayout { anchors.fill: parent anchors.leftMargin: 20 anchors.rightMargin: 20 TextField { id: searchField placeholderText: "搜索学生姓名..." Layout.preferredWidth: 200 background: Rectangle { color: "#f9f9f9" radius: 4 border.color: "#ddd" } // 搜索时重置到第一页 onTextChanged: root1.currentPage = 1 } Item { Layout.fillWidth: true } Button { text: "+ 新增学生" background: Rectangle { color: parent.pressed ? "#45a049" : "#4caf50" radius: 4 } contentItem: Text { text: parent.text color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } onClicked: { stackView.push("EditStudentPage.qml", { student: null, isEdit: false }) } } } } // ==================== 表头 ==================== Rectangle { Layout.preferredWidth: parent.width * 0.8 Layout.preferredHeight: 50 Layout.alignment: Qt.AlignHCenter radius: 8 clip: true border.color: "#e0e0e0" color: "#e0e0e0" RowLayout { anchors.fill: parent anchors.leftMargin: 20 anchors.rightMargin: 20 spacing: 0 Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "id" font.bold: true } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "name" font.bold: true } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "age" font.bold: true } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "classNum" font.bold: true } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "gender" font.bold: true } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "phone" font.bold: true } Text { Layout.fillWidth: false Layout.preferredWidth: 160 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "操作" font.bold: true } } } // ==================== 学生列表 ==================== ListView { id: studentListView Layout.preferredWidth: parent.width * 0.8 Layout.fillHeight: true Layout.alignment: Qt.AlignHCenter clip: true spacing: 2 model: filteredStudents delegate: Rectangle { width: studentListView.width height: 60 color: "white" radius: 4 border.color: "#e0e0e0" RowLayout { anchors.fill: parent anchors.leftMargin: 20 anchors.rightMargin: 20 spacing: 0 Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: modelData.id font.pixelSize: 14 color: "#333" } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: modelData.name font.pixelSize: 14 color: "#333" } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: modelData.age font.pixelSize: 14 color: "#333" } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: modelData.classNum font.pixelSize: 14 color: "#333" } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: modelData.gender font.pixelSize: 14 color: "#333" } Text { Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 100 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: modelData.phone font.pixelSize: 14 color: "#333" } RowLayout { Layout.fillWidth: false Layout.preferredWidth: 160 spacing: 5 Button { Layout.preferredWidth: 75 Layout.preferredHeight: 30 text: "编辑" padding: 0 background: Rectangle { color: parent.pressed ? "#1565c0" : "#2196f3" radius: 4 } contentItem: Text { text: parent.text color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: 12 } onClicked: { stackView1.pop() stackView1.push("EditStudentPage.qml", { student: modelData, isEdit: true }, StackView.Immediate) } } Button { Layout.preferredWidth: 75 Layout.preferredHeight: 30 text: "删除" padding: 0 background: Rectangle { color: parent.pressed ? "#c62828" : "#f44336" radius: 4 } contentItem: Text { text: parent.text color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: 12 } onClicked: { console.log("删除学生 id:", modelData.id) var result = dbManager.deleteStudent(modelData.id) if (result) { console.log("删除成功") // 重新加载数据 root1.students = dbManager.QueryStudent() root1.totalCount = root1.students.length root1.totalPages = Math.ceil(root1.totalCount / root1.pageSize) // 如果当前页超出范围,回到最后一页 if (root1.currentPage > root1.totalPages && root1.totalPages > 0) { root1.currentPage = root1.totalPages } } else { console.log("删除失败") } } } } } } } // ==================== 分页控件 ==================== Rectangle { Layout.fillWidth: true Layout.preferredHeight: 50 color: "white" border.color: "#e0e0e0" RowLayout { anchors.centerIn: parent spacing: 5 // 每页条数选择 Text { text: "每页" color: "#666" font.pixelSize: 13 } ComboBox { id: pageSizeCombo Layout.preferredWidth: 90 Layout.preferredHeight: 30 model: [5, 10, 20, 50] currentIndex: 1 // 默认 10 onCurrentTextChanged: { root1.pageSize = parseInt(currentText) root1.currentPage = 1 root1.totalPages = Math.ceil(root1.totalCount / root1.pageSize) } } Text { text: "条" color: "#666" font.pixelSize: 13 } Item { Layout.preferredWidth: 20 } // 上一页 Button { Layout.preferredWidth: 70 Layout.preferredHeight: 30 text: "上一页" enabled: root1.currentPage > 1 padding: 0 background: Rectangle { color: parent.enabled ? (parent.pressed ? "#1976d2" : "#2196f3") : "#ccc" radius: 4 } contentItem: Text { text: parent.text color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: 12 } onClicked: { if (root1.currentPage > 1) { root1.currentPage-- } } } // 页码显示 Text { text: root1.currentPage + " / " + root1.totalPages + " 页" color: "#333" font.pixelSize: 13 Layout.alignment: Qt.AlignVCenter } // 页码输入跳转 TextField { id: jumpPageField Layout.preferredWidth: 50 Layout.preferredHeight: 30 placeholderText: "" text: root1.currentPage horizontalAlignment: Text.AlignHCenter validator: IntValidator { bottom: 1; top: root1.totalPages } background: Rectangle { color: "#f9f9f9" radius: 4 border.color: "#ddd" } onEditingFinished: { var page = parseInt(text) if (page >= 1 && page <= root1.totalPages) { root1.currentPage = page } else { text = root1.currentPage } } } Button { Layout.preferredWidth: 50 Layout.preferredHeight: 30 text: "跳转" padding: 0 background: Rectangle { color: parent.pressed ? "#1976d2" : "#2196f3" radius: 4 } contentItem: Text { text: parent.text color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: 12 } onClicked: { var page = parseInt(jumpPageField.text) if (page >= 1 && page <= root1.totalPages) { root1.currentPage = page } } } // 下一页 Button { Layout.preferredWidth: 70 Layout.preferredHeight: 30 text: "下一页" enabled: root1.currentPage < root1.totalPages padding: 0 background: Rectangle { color: parent.enabled ? (parent.pressed ? "#1976d2" : "#2196f3") : "#ccc" radius: 4 } contentItem: Text { text: parent.text color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: 12 } onClicked: { if (root1.currentPage < root1.totalPages) { root1.currentPage++ } } } Item { Layout.preferredWidth: 20 } // 总条数显示 Text { text: "共 " + root1.totalCount + " 条" color: "#666" font.pixelSize: 13 Layout.alignment: Qt.AlignVCenter } } } } // 数据变化时更新总页数 onStudentsChanged: { totalCount = students.length totalPages = Math.ceil(totalCount / pageSize) if (currentPage > totalPages && totalPages > 0) { currentPage = totalPages } } }

资源下载链接:

创作中心-CSDN