下面是一个基于 **Flask(后端) + Vue3(前端) + MySQL(数据库) + Element Plus(UI组件库)** 的完整学生信息管理系统的实现方案。我们将从项目结构、环境搭建、代码实现到前后端交互进行详细说明。
---
## ✅ 系统功能概述
- 学生信息的增删改查(CRUD)
- 分页显示
- 模糊搜索(按姓名或学号)
- 响应式界面
---
## 🧱 项目结构
```
student-system/
│
├── backend/ # Flask 后端
│ ├── app.py
│ ├── config.py
│ ├── models.py
│ ├── routes.py
│ └── requirements.txt
│
├── frontend/ # Vue3 + Element Plus 前端
│ ├── public/
│ ├── src/
│ │ ├── assets/
│ │ ├── components/
│ │ ├── views/StudentList.vue
│ │ ├── App.vue
│ │ └── main.js
│ ├── package.json
│ └── vite.config.js
│
└── database.sql # 数据库建表语句
```
---
## 🔧 技术栈
| 模块 | 技术 |
|------------|------------------------|
| 后端框架 | Flask |
| 数据库 | MySQL (使用 PyMySQL) |
| ORM | SQLAlchemy |
| 接口 | RESTful API |
| 前端框架 | Vue3 + Vite |
| UI 库 | Element Plus |
| 通信 | Axios |
---
## 🛠️ 第一步:创建数据库和表
### `database.sql`
```sql
CREATE DATABASE IF NOT EXISTS student_db CHARACTER SET utf8mb4;
USE student_db;
CREATE TABLE students (
id INT AUTO_INCREMENT PRIMARY KEY,
student_id VARCHAR(20) NOT NULL UNIQUE,
name VARCHAR(50) NOT NULL,
gender ENUM('男', '女') NOT NULL,
age INT NOT NULL,
phone VARCHAR(15),
email VARCHAR(100),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
```
导入数据库:
```bash
mysql -u root -p < database.sql
```
---
## 🐍 第二步:Flask 后端实现
### `backend/config.py`
```python
import os
class Config:
SECRET_KEY = 'your-secret-key'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:your_password@localhost/student_db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
```
> ⚠️ 修改 `your_password` 为你的 MySQL 密码
---
### `backend/models.py`
```python
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.String(20), unique=True, nullable=False)
name = db.Column(db.String(50), nullable=False)
gender = db.Column(db.Enum('男', '女'), nullable=False)
age = db.Column(db.Integer, nullable=False)
phone = db.Column(db.String(15))
email = db.Column(db.String(100))
def to_dict(self):
return {
'id': self.id,
'student_id': self.student_id,
'name': self.name,
'gender': self.gender,
'age': self.age,
'phone': self.phone,
'email': self.email
}
```
---
### `backend/routes.py`
```python
from flask import Blueprint, request, jsonify
from .models import db, Student
api = Blueprint('api', __name__)
# 获取所有学生(支持分页和搜索)
@api.route('/students', methods=['GET'])
def get_students():
page = request.args.get('page', 1, type=int)
limit = request.args.get('limit', 10, type=int)
keyword = request.args.get('keyword', '')
query = Student.query
if keyword:
query = query.filter(
db.or_(
Student.name.contains(keyword),
Student.student_id.contains(keyword)
)
)
pagination = query.paginate(page=page, per_page=limit, error_out=False)
students = [s.to_dict() for s in pagination.items]
return jsonify({
'success': True,
'data': students,
'total': pagination.total
})
# 添加学生
@api.route('/students', methods=['POST'])
def add_student():
data = request.json
try:
student = Student(
student_id=data['student_id'],
name=data['name'],
gender=data['gender'],
age=data['age'],
phone=data.get('phone'),
email=data.get('email')
)
db.session.add(student)
db.session.commit()
return jsonify({'success': True, 'message': '添加成功'})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': str(e)}), 400
# 更新学生
@api.route('/students/<int:id>', methods=['PUT'])
def update_student(id):
student = Student.query.get_or_404(id)
data = request.json
try:
student.name = data['name']
student.gender = data['gender']
student.age = data['age']
student.phone = data.get('phone')
student.email = data.get('email')
db.session.commit()
return jsonify({'success': True, 'message': '更新成功'})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': str(e)}), 400
# 删除学生
@api.route('/students/<int:id>', methods=['DELETE'])
def delete_student(id):
student = Student.query.get_or_404(id)
try:
db.session.delete(student)
db.session.commit()
return jsonify({'success': True, 'message': '删除成功'})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': str(e)}), 400
```
---
### `backend/app.py`
```python
from flask import Flask
from .config import Config
from .models import db
from .routes import api
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
app.register_blueprint(api, url_prefix='/api')
@app.route('/')
def index():
return '<h1>Student Management System Backend</h1>'
return app
if __name__ == '__main__':
app = create_app()
with app.app_context():
db.create_all() # 创建表
app.run(debug=True)
```
---
### 安装依赖(`backend/requirements.txt`)
```txt
Flask==2.3.3
Flask-SQLAlchemy==3.0.5
PyMySQL==1.1.0
```
安装命令:
```bash
pip install -r requirements.txt
```
启动后端:
```bash
python app.py
```
访问:`http://localhost:5000/api/students` 应返回空列表。
---
## 🌐 第三步:Vue3 + Element Plus 前端实现
### 创建 Vue 项目(使用 Vite)
```bash
cd frontend
npm create vue@latest
# 选择 Vue Router, Pinia 可选,CSS 预处理器等
npm install
```
然后安装 Element Plus 和 Axios:
```bash
npm install element-plus axios
npm install --save-dev @element-plus/icons-vue
```
---
### `frontend/src/main.js`
```js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import '@element-plus/icons-vue'
const app = createApp(App)
app.use(router)
app.use(ElementPlus)
app.mount('#app')
```
---
### `frontend/src/views/StudentList.vue`
```vue
<template>
<div class="p-6">
<h2 class="text-2xl font-bold mb-4">学生信息管理</h2>
<!-- 搜索与新增 -->
<div class="flex justify-between mb-4">
<el-input v-model="keyword" placeholder="请输入姓名或学号" style="width: 300px;" @input="search" />
<el-button type="primary" @click="openAddDialog">新增学生</el-button>
</div>
<!-- 表格 -->
<el-table :data="tableData" stripe style="width: 100%" v-loading="loading">
<el-table-column prop="student_id" label="学号" width="120" />
<el-table-column prop="name" label="姓名" width="100" />
<el-table-column prop="gender" label="性别" width="80" />
<el-table-column prop="age" label="年龄" width="80" />
<el-table-column prop="phone" label="电话" width="120" />
<el-table-column prop="email" label="邮箱" />
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="editStudent(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="deleteStudent(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="mt-4 flex justify-end">
<el-pagination
@current-change="handlePageChange"
:current-page="currentPage"
:page-size="pageSize"
:total="total"
layout="total, prev, pager, next"
/>
</div>
<!-- 弹窗表单 -->
<el-dialog :title="dialogTitle" v-model="dialogVisible">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item label="学号" prop="student_id">
<el-input v-model="form.student_id" :disabled="!!form.id" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="form.gender">
<el-radio label="男">男</el-radio>
<el-radio label="女">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input-number v-model="form.age" :min="1" :max="150" />
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import axios from 'axios'
const tableData = ref([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(10)
const keyword = ref('')
const loading = ref(false)
// 表单相关
const dialogVisible = ref(false)
const formRef = ref(null)
const isEdit = ref(false)
const form = reactive({
id: null,
student_id: '',
name: '',
gender: '男',
age: 18,
phone: '',
email: ''
})
const rules = {
student_id: [{ required: true, message: '请输入学号', trigger: 'blur' }],
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
age: [{ required: true, message: '请输入年龄', trigger: 'blur' }]
}
// 获取数据
const fetchData = () => {
loading.value = true
axios.get('/api/students', {
params: {
page: currentPage.value,
limit: pageSize.value,
keyword: keyword.value
}
}).then(res => {
const data = res.data
if (data.success) {
tableData.value = data.data
total.value = data.total
}
}).finally(() => {
loading.value = false
})
}
// 分页变化
const handlePageChange = (page) => {
currentPage.value = page
fetchData()
}
// 搜索
const search = () => {
currentPage.value = 1
fetchData()
}
// 打开新增弹窗
const openAddDialog = () => {
isEdit.value = false
dialogVisible.value = true
nextTick(() => {
formRef.value?.resetFields()
Object.assign(form, {
id: null,
student_id: '',
name: '',
gender: '男',
age: 18,
phone: '',
email: ''
})
})
}
// 编辑
const editStudent = (row) => {
isEdit.value = true
dialogVisible.value = true
nextTick(() => {
formRef.value?.resetFields()
Object.assign(form, row)
})
}
// 提交表单
const submitForm = () => {
formRef.value.validate(valid => {
if (!valid) return
const method = isEdit.value ? 'put' : 'post'
const url = isEdit.value ? `/api/students/${form.id}` : '/api/students'
axios[method](url, form).then(res => {
if (res.data.success) {
ElMessage.success(res.data.message)
dialogVisible.value = false
fetchData()
} else {
ElMessage.error(res.data.message)
}
})
})
}
// 删除
const deleteStudent = (id) => {
ElMessageBox.confirm('确定删除该学生?', '警告', {
type: 'warning'
}).then(() => {
axios.delete(`/api/students/${id}`).then(res => {
if (res.data.success) {
ElMessage.success('删除成功')
fetchData()
} else {
ElMessage.error(res.data.message)
}
})
})
}
onMounted(() => {
fetchData()
})
</script>
<style scoped>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>
```
---
### 配置代理(解决跨域)
修改 `vite.config.js`
```js
export default defineConfig({
plugins: [...],
server: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true
}
}
}
})
```
---
## ▶️ 启动方式
1. 启动后端(Flask):
```bash
cd backend
python app.py
```
2. 启动前端(Vue):
```bash
cd frontend
npm run dev
```
3. 访问:`http://localhost:5173`
---
## ✅ 效果展示
- 显示学生列表(带分页)
- 支持模糊搜索
- 可新增、编辑、删除
- 使用 Element Plus 提供现代化 UI
---
## 🔐 安全建议(进阶)
- 添加 JWT 身份验证
- 参数校验增强
- SQL 注入防护(已由 SQLAlchemy 天然防止)
- 前端输入 XSS 过滤
- 日志记录操作行为
---
## 📦 打包部署建议
- 后端:使用 Gunicorn + Nginx 部署 Flask
- 前端:`npm run build` 生成静态文件,由 Nginx 托管
- 数据库:生产环境使用连接池(如 SQLAlchemy + gevent)
---