基于Qt6与SQLite的学生信息管理系统:从登录到CRUD的完整工程化实践

发布时间:2026/9/14 14:30:11
基于Qt6与SQLite的学生信息管理系统:从登录到CRUD的完整工程化实践 简介这是一份基于Qt6.2.4开发的学生信息管理系统完整源码与配套设计报告面向C/Qt学习者、高校学生及需要完成课程设计或毕业设计的开发者。系统实现了注册登录、学生信息增删查改、管理员信息管理等核心功能界面采用QPushButton、QLineEdit、QTableView等常用控件构建风格简约大方。数据层使用SQLite数据库并通过C结构体封装表字段便于统一读写配套设计报告涵盖课题要求、整体流程与重要代码说明能有效帮助读者理解Qt与数据库结合开发的完整思路。资源包共82个文件主要包含24个cpp源文件、20个h头文件、16个ui界面文件以及pro工程文件、数据库文件、资源文件和docx设计报告压缩包整体约617KB。包内还保留了多个版本目录可对照各版本功能演进适合边读报告边调试目前已有312人学习下载从界面布局到数据库操作均有对应模块结构清晰是练习桌面应用开发、完善课程设计的实用参考。1. 为什么这份基于 Qt 的学生信息管理系统值得拆开看多数课程设计里的学生信息管理系统代码还停留在黑窗口里 printf 菜单能跑通业务却看不出工程素养。这份项目不一样它是用 Qt6.2.4 做的完整 GUI 应用压缩包里有登录页、主窗口、增删查改对话框、SQLite 数据库文件以及四个递进版本mgr 1.0 到 4.0能清楚看到界面从裸控件到带图标、带头像的演进过程。更难得的是附了一份详细设计报告把需求分析、数据库设计、关键代码讲得很完整。适合正在做 C 课程设计的学生也适合想快速上手 Qt Widgets SQLite 开发范式、但又不想啃官方文档的入门开发者。接下来围绕数据库层、登录鉴权、CRUD 链路和版本演进四个维度展开。2. 数据库设计与 stusql 结构体映射2.1 为什么是 SQLite 而不是 MySQL项目里 data.db 是一个 SQLite 文件开发工具虽然列出了 Navicat Premium 16但它只负责可视化编辑运行时真正干活的是 Qt 自带的 QSQLITE 驱动。选 SQLite 的理由很直接零配置、单文件、随项目拷走即用不需要安装数据库服务端。对课程设计答辩演示来说这三点比 MySQL 的「专业感」更有实际价值——评委大概率当场让你换一台机器跑SQLite 不用配环境双击就能起。Qt6.2.4 里使用 SQLite 只需在 .pro 文件加一行QT sql然后在代码里通过QSqlDatabase::addDatabase(QSQLITE)建立连接。整个数据库层被封装在stusql.h/stusql.cpp里专门负责连接管理、建表、增删查改。这种把数据库操作与界面层隔离的做法后续换数据库或改表结构时只需要动这一层。2.2 两张核心表的结构定义系统数据分成两类学生业务数据和用户登录数据。学生信息表存储学号、姓名、年龄等教育属性用户表存储账号密码与权限。两个表没有外键关系登录认证和业务查询是两条独立链路。字段名类型约束说明idINTEGERPRIMARY KEY AUTOINCREMENT自增主键仅内部使用stu_idTEXTUNIQUE NOT NULL学号业务唯一标识nameTEXTNOT NULL姓名ageINTEGER年龄gradeTEXT年级stu_classTEXT班级phoneTEXT电话addressTEXT地址用户表字段相对精简核心是 role 权限标记字段名类型约束说明usernameTEXTPRIMARY KEY登录用户名passwordTEXTNOT NULL密码课程设计为明文存储roleINTEGERDEFAULT 00 普通用户1 管理员建表语句使用CREATE TABLE IF NOT EXISTS保证程序重复启动不会报错。学号加了 UNIQUE 约束防止同一学号被重复录入两次这是「增删查改」里最容易踩到的边界问题。CREATE TABLE IF NOT EXISTS student_info ( id INTEGER PRIMARY KEY AUTOINCREMENT, stu_id TEXT UNIQUE NOT NULL, name TEXT NOT NULL, age INTEGER, grade TEXT, stu_class TEXT, phone TEXT, address TEXT ); CREATE TABLE IF NOT EXISTS user_info ( username TEXT PRIMARY KEY, password TEXT NOT NULL, role INTEGER DEFAULT 0 );这里把班级字段命名为stu_class而不是class是因为在部分 SQL 方言和 ORM 映射里class容易与关键字混淆。设计数据库结构时字段名尽量带业务前缀既避免关键字冲突也让跨表引用时能一眼看出归属。2.3 表结构如何映射成 C 结构体stusql.h里定义了两个结构体这是整个项目数据流的核心约定。界面层不直接操作 QSqlQuery而是通过结构体传值好处是函数签名清晰、类型安全界面代码里不会到处散落字符串拼接的 SQL。// stusql.h —— 把数据库表映射成 C 结构体供界面层传参使用 struct StudentInfo { int id; // 自增主键新增时不需要手动赋值 QString stuId; // 学号 QString name; // 姓名 int age; // 年龄 QString grade; // 年级 QString stuClass; // 班级避开 class 关键字 QString phone; // 电话 QString address; // 地址 }; struct UserInfo { QString username; // 登录用户名 QString password; // 密码 int role; // 权限0 普通用户1 管理员 };结构体字段与数据库列一一对应顺序保持一致这样写 INSERT 语句时绑定参数不容易错位。id字段在新增时忽略交给数据库自增stuId才是业务主键删除、修改、查询都优先用学号定位记录。2.4 连接数据库与初始化建表stusql.cpp里的初始化函数完成两件事建立数据库连接、确保表存在。很多入门项目把建表 SQL 写在 main 函数里表一多就乱集中到initDb里统一管理后续加表只需要在这个函数里追加一条exec。// stusql.cpp —— 初始化数据库连接返回是否成功 bool StuSql::initDb(const QString dbPath) { // QSQLITE 是 Qt 内置驱动不需要额外配置 m_db QSqlDatabase::addDatabase(QSQLITE); m_db.setDatabaseName(dbPath); // 指定 data.db 所在路径 if (!m_db.open()) { qDebug() open database failed: m_db.lastError().text(); return false; } QSqlQuery query; // IF NOT EXISTS 保证重复初始化不会因为表已存在而报错 query.exec(CREATE TABLE IF NOT EXISTS student_info ( id INTEGER PRIMARY KEY AUTOINCREMENT, stu_id TEXT UNIQUE NOT NULL, name TEXT NOT NULL, age INTEGER, grade TEXT, stu_class TEXT, phone TEXT, address TEXT)); query.exec(CREATE TABLE IF NOT EXISTS user_info ( username TEXT PRIMARY KEY, password TEXT NOT NULL, role INTEGER DEFAULT 0)); return true; }调用时传入 data.db 的完整路径。这里有一个容易被忽略的细节如果只写setDatabaseName(data.db)程序的工作目录决定了它找到哪个文件。从 Qt Creator 直接运行和打包后双击 exe 运行工作目录往往不一样后续章节会专门讲怎么处理这个坑。值得一提的是这套「结构体 独立数据库类」分层模型即使以后把 SQLite 换成 MySQL界面代码也几乎不用改。数据库层的返回值、入参保持结构体不变底层 SQL 方言差异被隔离在stusql.cpp内部这就是它比直接堆 QSqlQuery 更工程化的原因。3. 登录模块与权限控制3.1 登录界面的控件组成page_login.ui是系统的入口界面结构不复杂两个 QLineEdit 分别接收用户名和密码一个登录按钮触发校验一个注册按钮拉起注册对话框密码框设置了EchoMode Password让输入以圆点显示。整个页面用垂直布局排列控件居中是 Qt Widgets 里最标准的登录窗体写法。界面代码和业务逻辑分离这一点在 Qt 里是天然优势。.ui文件由 Qt Designer 维护生成的ui_page_login.h负责控件初始化开发者只需要在page_login.cpp里写槽函数。即使完全不懂布局代码在设计器里拖控件也能完成 90% 的界面工作。登录按钮的点击信号在构造函数里连接到自定义槽函数// page_login.cpp —— 构造函数中连接信号与槽 PageLogin::PageLogin(QWidget *parent) : QWidget(parent) { ui-setupUi(this); // 点击登录按钮时调用 onLoginClicked 槽函数 connect(ui-btnLogin, QPushButton::clicked, this, PageLogin::onLoginClicked); // 点击注册按钮时打开注册对话框 connect(ui-btnRegister, QPushButton::clicked, this, PageLogin::onRegisterClicked); }Qt6 推荐使用函数指针形式的 connect 语法在编译期就能检查信号和槽是否存在比旧版的SIGNAL/SLOT宏更安全。按钮对象名btnLogin、btnRegister也是后续代码里引用的唯一标识所以 Designer 里给控件起名要有语义不要保留默认的pushButton_2这种名字。3.2 登录校验的槽函数实现登录的核心是查user_info表比对用户名和密码是否匹配。这里有两个容易出错的点输入框要trimmed()去掉首尾空格空值要提前拦截避免无意义的数据库查询。// page_login.cpp —— 登录按钮点击后的完整处理 void PageLogin::onLoginClicked() { QString username ui-editUser-text().trimmed(); QString password ui-editPwd-text(); // 空值直接拦截不进入数据库查询流程 if (username.isEmpty() || password.isEmpty()) { QMessageBox::warning(this, 提示, 用户名和密码不能为空); return; } UserInfo user; // 调用 stusql 层的登录校验成功时把用户信息带回来 if (m_stuSql-login(username, password, user)) { // 登录成功后进入主窗口并携带当前用户信息 MainWindow *w new MainWindow(user); w-show(); this-close(); } else { QMessageBox::critical(this, 错误, 用户名或密码错误); } }留意m_stuSql-login的参数设计用户名和密码是入参UserInfo user是出参用于把数据库里的 role 权限值带回主窗口。权限判断不放在登录页这里而是交给 MainWindow 构造函数去决定显示哪些功能入口职责更清晰。数据库层的login函数用的是参数绑定查询不是字符串拼接// stusql.cpp —— 数据库层登录校验 bool StuSql::login(const QString username, const QString password, UserInfo info) { QSqlQuery query; query.prepare(SELECT username, password, role FROM user_info WHERE username ? AND password ?); query.addBindValue(username); query.addBindValue(password); if (query.exec() query.next()) { info.username query.value(0).toString(); info.password query.value(1).toString(); info.role query.value(2).toInt(); return true; } return false; }prepare加addBindValue是 Qt 里替代字符串拼接的标准做法。传入的用户名密码无论包含什么特殊字符都会被当作普通值处理不会破坏 SQL 语句结构。这个习惯比「把用户输入拼进 SQL 再 exec」安全得多哪怕课程设计不面向上线环境也应该按正确方式写。3.3 注册流程与权限字段注册对话框dlg_adduser.ui一般包含用户名、密码、确认密码三个输入框和一个保存按钮。保存前做两次校验确认密码与密码是否一致用户名是否已在user_info表中存在。后者的 SQL 是SELECT COUNT(*) FROM user_info WHERE username ?计数大于 0 就提示更换用户名。新注册用户的 role 固定写 0即普通用户。管理员账号要么在设计报告里的初始化数据中预置要么通过后续的「用户管理」功能由已有管理员创建。这种设计避免了注册页出现「请选择权限」这种安全漏洞——如果注册时能自选管理员整个权限体系就形同虚设了。注册的核心插入语句如下// stusql.cpp —— 注册新用户 bool StuSql::registerUser(const QString username, const QString password) { QSqlQuery query; // role 固定为 0管理员只能由管理员在用户管理中创建 query.prepare(INSERT INTO user_info (username, password, role) VALUES (?, ?, 0)); query.addBindValue(username); query.addBindValue(password); return query.exec(); }3.4 登录安全上的两个取舍这个项目密码以明文存在 SQLite 里这是课程设计场景下的普遍做法但至少应该知道它的局限。正式系统通常用QCryptographicHash::hash(password, QCryptographicHash::Sha256)加盐后存摘要校验时对输入做同样哈希再比较。迁移成本也不高给 user_info 表加一个 salt 字段login 时改成查回 salt 再哈希比对即可。另一个取舍是登录成功后的状态保持。这个项目直接构造 MainWindow 并把 UserInfo 传进去没有做会话管理和超时退出。如果后续想扩展可以在 UserInfo 里加一个loginTime字段主窗口用 QTimer 定期检查登录时长超过阈值就强制返回登录页面。这两点写进设计报告的「改进方向」里答辩时反而是加分项。4. 学生信息的增删查改完整链路4.1 主窗口模块划分与信号槽连接mainwindow.ui是系统的核心界面布局分左右两区左侧是编辑表单区放置学号、姓名、年龄、年级、班级、电话、地址等一组 QLineEdit右侧是 QTableView 表格下方是查询、添加、修改、删除、重置五个按钮。这种「表单 表格」的布局是管理类系统的通用范式增删查改都在同一个页面完成不需要频繁跳转。表格通过QSqlQueryModel与数据库连接。这个模型类直接执行 SQL 并将结果集转换为表格数据不需要手动填充 QTableWidgetItem代码量大幅减少。mainwindow.cpp 里的构造函数做了两件事初始化模型绑定 tableView以及根据登录用户的 role 决定是否显示用户管理入口。// mainwindow.cpp —— 构造函数中初始化表格模型与权限 MainWindow::MainWindow(const UserInfo user, QWidget *parent) : QMainWindow(parent) { ui-setupUi(this); m_model new QSqlQueryModel(this); // 给 tableView 设置只读模型禁止在表格里直接编辑 ui-tableView-setModel(m_model); ui-tableView-setEditTriggers(QAbstractItemView::NoEditTriggers); // 权限控制普通用户隐藏用户管理按钮 if (user.role ! 1) { ui-btnManageUser-setVisible(false); } refreshTable(); // 初始加载全部学生数据 }refreshTable()是所有增删改操作完成后的统一刷新入口。它重新执行一次查询 SQL并把结果重新赋给 model表格随之更新。把刷新逻辑抽成独立函数避免每个槽函数里重复写这段代码。4.2 查询按条件过滤并刷新表格查询按钮的核心逻辑是动态拼接 WHERE 子句。学号和姓名两个条件可以同时输入也可以只填一个所以不能写死 SQL要根据输入框状态组装查询语句。// mainwindow.cpp —— 查询按钮处理 void MainWindow::onBtnQueryClicked() { QString name ui-editName-text().trimmed(); QString stuId ui-editStuId-text().trimmed(); // 基础语句WHERE 1 1 是为了方便后续追加条件 QString sql SELECT stu_id, name, age, grade, stu_class, phone, address FROM student_info WHERE 1 1; if (!name.isEmpty()) { // LIKE 模糊匹配满足「记不全名字只记得姓」的场景 sql QString( AND name LIKE %%1%).arg(name); } if (!stuId.isEmpty()) { sql QString( AND stu_id LIKE %%1%).arg(stuId); } sql ORDER BY stu_id; m_model-setQuery(sql); // 设置表格列标题默认显示数据库字段名 m_model-setHeaderData(0, Qt::Horizontal, 学号); m_model-setHeaderData(1, Qt::Horizontal, 姓名); m_model-setHeaderData(2, Qt::Horizontal, 年龄); // 其余列头设置省略写法一致 }WHERE 1 1是一个常用写法它本身没有任何过滤效果纯粹是为了让后面的AND可以直接拼接。这样写虽然简洁但要注意QString::arg不会转义单引号如果输入框里带了单引号查询 SQL 会出错。生产级代码应该把整个查询改成参数绑定这里用拼接是课程设计里常见的简化处理。查询结果直接通过setQuery反映到表格不需要额外刷新操作。列头用setHeaderData从stu_id改成中文「学号」否则表格顶部显示英文字段名用户体验会差一截。4.3 新增与修改复用同一个对话框新增和修改功能对应的对话框是dlg_addstu.ui。新增时所有输入框都是空白修改时双击表格某一行该行数据会回填到对话框。两种模式用同一个对话框类DlgAddStu通过一个构造函数参数区分是添加还是编辑这样表单校验逻辑只需要维护一份。修改时回填数据的关键代码// dlg_addstu.cpp —— 编辑模式下回填数据 void DlgAddStu::setEditData(const StudentInfo info) { ui-editStuId-setText(info.stuId); ui-editName-setText(info.name); ui-spinAge-setValue(info.age); ui-editGrade-setText(info.grade); ui-editClass-setText(info.stuClass); ui-editPhone-setText(info.phone); ui-editAddress-setText(info.address); // 编辑模式下学号不允许修改锁定输入框 ui-editStuId-setReadOnly(true); }学号在编辑模式下被锁定只读原因是stu_id是 UNIQUE 约束的主键字段修改它可能导致主键冲突也会破坏与其他表潜在的业务引用关系。这个设计在数据一致性上是对的要改学号应该删除这条记录再新增而不是直接编辑主键。新增保存时从对话框控件收集数据组装成StudentInfo结构体传给数据库层。业务校验放在对话框的accept()之前比如学号不能为空、年龄必须在合理范围内不满足就直接QMessageBox::warning并拒绝关闭对话框// dlg_addstu.cpp —— 保存按钮校验 void DlgAddStu::onBtnSaveClicked() { QString stuId ui-editStuId-text().trimmed(); if (stuId.isEmpty()) { QMessageBox::warning(this, 提示, 学号不能为空); return; } // 校验通过把数据存入成员变量并关闭对话框 m_info.stuId stuId; m_info.name ui-editName-text().trimmed(); m_info.age ui-spinAge-value(); // ... accept(); // 关闭对话框并返回 QDialog::Accepted }主窗口里通过dlg.exec()的返回值判断用户是否点了保存// mainwindow.cpp —— 添加按钮处理 void MainWindow::onBtnAddClicked() { DlgAddStu dlg(this); if (dlg.exec() QDialog::Accepted) { StudentInfo info dlg.getInfo(); // 参数绑定插入避免手动拼接 SQL QSqlQuery query; query.prepare(INSERT INTO student_info (stu_id, name, age, grade, stu_class, phone, address) VALUES (?, ?, ?, ?, ?, ?, ?)); query.addBindValue(info.stuId); query.addBindValue(info.name); query.addBindValue(info.age); query.addBindValue(info.grade); query.addBindValue(info.stuClass); query.addBindValue(info.phone); query.addBindValue(info.address); if (query.exec()) { QMessageBox::information(this, 成功, 学生信息已添加); refreshTable(); } else { QMessageBox::critical(this, 错误, query.lastError().text()); } } }INSERT 的参数顺序与prepare里的占位符一一对应写代码时保持与结构体字段顺序一致能有效降低错位概率。lastError().text()在很多情况下能直接给出 SQLite 的原始错误原因比如 UNIQUE 约束冲突调试时极有用。4.4 删除先选中行再按学号删除删除操作依赖用户先在表格里选中一行。实现时通过currentIndex()获取选中位置再从模型里取出该行的stu_id然后执行 DELETE。删除前弹确认框是为了防止误点这属于最基本的交互保护。// mainwindow.cpp —— 删除按钮处理 void MainWindow::onBtnDeleteClicked() { QModelIndex idx ui-tableView-currentIndex(); if (!idx.isValid()) { QMessageBox::warning(this, 提示, 请先选中要删除的行); return; } // 从当前行的第 0 列取学号 QString stuId m_model-record(idx.row()).value(stu_id).toString(); int ret QMessageBox::question(this, 确认删除, 确定要删除学号为 stuId 的学生吗); if (ret QMessageBox::Yes) { QSqlQuery query; query.prepare(DELETE FROM student_info WHERE stu_id ?); query.addBindValue(stuId); if (query.exec()) { refreshTable(); } } }这里以stu_id作为 WHERE 条件而不是自增 id是因为学号在业务层面是稳定的唯一标识。如果后续做了「学生成绩表」等关联表外键引用的也是学号而非内部 id。删除兜底逻辑是可选的如果不希望物理删除数据可以在 student_info 表加一个status字段把删除操作改成UPDATE student_info SET status 0 WHERE stu_id ?查询时默认过滤status 1。这种软删除方案在生产系统里更常见写进设计报告的「系统可扩展性」一节会很加分。4.5 管理员信息管理复用同一套 CRUD「管理员信息管理」即用户管理功能它复用同一套 Model-View 架构。主窗口里点击「用户管理」按钮后弹出一个新的管理对话框内部也是一个 QTableView 加一组增删改按钮。区别只是操作的表换成了user_info以及修改密码、重置权限这些字段维度上的变化。// 用户管理对话框 —— 加载用户列表 void DlgManageUser::loadUsers() { QSqlQueryModel *model new QSqlQueryModel(this); model-setQuery(SELECT username, password, role FROM user_info ORDER BY username); // role 字段转成可读文本显示 ui-tableView-setModel(model); }用户管理里有一点和普通 CRUD 不同不允许删除当前正在登录的管理员账号否则系统里可能没有任何管理员。实现上可以把当前登录用户名传进对话框删除时做个比对相等就弹窗拒绝。这个细节设计报告里如果写了会让整体方案显得更完整。5. 从 mgr 1.0 到 4.0版本演进里的工程化细节5.1 四个版本到底改了些什么压缩包内保留了四个递进版本对照文件列表能还原一条真实的开发路径。mgr 1.0 是功能骨架登录页、主窗口、增删查改、SQLite 库都齐了但没有任何资源文件界面是纯系统默认样式。mgr 2.0 开始引入res.qrc、pic1.ico和user.jpg意味着程序有了自定义图标和用户头像可执行文件在资源文件编译后变成单一 exe拷给别人不用附带图片。mgr 3.0 在此基础上继续打磨界面mgr 4.0 文件结构最完整是最终提交版。这个演进顺序对课程设计答辩很有参考价值先跑通业务流程再做视觉和工程化完善。很多学生一上来就折腾 QSS 样式结果业务逻辑没写完后面被功能问题拖垮。正确顺序应该是「功能完整 → 界面美化 → 资源整合 → 撰写报告」。资源文件相关的 .pro 配置如下# stumgr.pro —— Qt 工程文件关键配置 QT core gui sql greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET StudentManager TEMPLATE app SOURCES main.cpp \ mainwindow.cpp \ page_login.cpp \ stusql.cpp \ dlg_addstu.cpp \ dlg_adduser.cpp HEADERS mainwindow.h \ page_login.h \ stusql.h \ dlg_addstu.h \ dlg_adduser.h FORMS mainwindow.ui \ page_login.ui \ dlg_addstu.ui \ dlg_adduser.ui # 资源文件图标和头像图片都挂在 qrc 下 RESOURCES res.qrcQT sql必须显式声明否则链接阶段会报一堆 QSqlDatabase 相关的未定义错误。RESOURCES指向的 res.qrc 里用 XML 格式列出了 pic1.ico 和 user.jpgQt 会把这些资源编译进二进制文件运行时通过:/pic1.ico这样的路径访问不依赖外部文件。5.2 设计报告的核心结构建议项目自带的高级语言程序设计大作业实验报告.docx结构上可以按实验报告模板拆成五个部分。需求分析写清楚系统要解决什么问题画用例图比纯文字描述更直观。概要设计给模块划分图说明界面层、逻辑层、数据层三类模块如何交互。详细设计挑两到三个核心类重点写StuSql 的数据库操作、MainWindow 的信号槽处理、PageLogin 的登录校验配合类图和时序图。测试部分值得多花笔墨。把登录失败、重复学号录入、空列表删除这些典型场景列成测试用例表每条写明输入、操作、预期结果。这部分最能体现文档完成度也是评委喜欢翻的地方。最后加一节「系统不足与改进方向」提出压缩包内代码已知的优化空间与上述代码中提到的软删除、密码加盐、参数绑定查询防护方案相结合显示系统思维深度。5.3 data.db 路径问题的根治方案程序里用相对路径访问 data.db 会带来一个恼人的问题在 Qt Creator 里运行正常把 exe 拷到另一个目录双击数据库连不上了。原因是工作目录变动相对路径指向的位置变了。根治方案是程序启动时用应用所在目录拼绝对路径// main.cpp —— 使用应用程序路径定位数据库 #include QApplication #include QDir #include stusql.h int main(int argc, char *argv[]) { QApplication a(argc, argv); // 始终指向 exe 所在目录避免工作目录不同导致的路径问题 QString dbPath QDir(QCoreApplication::applicationDirPath()) .filePath(data.db); // 若想每次发布都重新初始化空库可在开发阶段强制删除旧文件 StuSql sql; if (!sql.initDb(dbPath)) { return -1; } // 启动登录页 PageLogin login; login.show(); return a.exec(); }applicationDirPath()返回 exe 所在目录不随系统当前目录变化。QDir::filePath负责拼接路径并自动处理斜杠方向在 Windows 和 Linux 下行为一致。配合QTextCodec处理中文编码Qt6 默认 UTF-8 环境下中文基本不出问题但如果是老工程迁移需要在 main 函数里显式指定编码策略否则从数据库读出的中文姓名字段可能出现乱码。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询