10. 开发课程信息管理模块

SQL
CREATE TABLE `course` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`no` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '编号',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
`teacher` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '老师',
`score` int NOT NULL COMMENT '学分',
`major_id` int DEFAULT NULL COMMENT '所属专业',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='课程信息';
插入的数据:
-- 计算机专业课程(major_id = 1)
INSERT INTO `course` (`no`, `name`, `teacher`, `score`, `major_id`) VALUES
('CS101', '计算机导论', '张伟', 2, 1),
('CS102', 'C语言程序设计', '李明', 3, 1),
('CS103', '数据结构', '王芳', 4, 1),
('CS104', '操作系统原理', '赵刚', 3, 1),
('CS105', '数据库系统', '刘洋', 3, 1),
('CS106', '计算机网络', '陈静', 3, 1),
('CS107', '软件工程', '孙磊', 3, 1),
('CS108', '人工智能基础', '周慧', 2, 1),
('CS109', 'Web前端开发', '吴昊', 3, 1),
('CS110', 'Python数据分析', '郑涛', 3, 1),
('CS201', 'Java高级编程', '钱勇', 4, 1),
('CS202', '算法设计与分析', '朱琳', 4, 1),
('CS203', '计算机组成原理', '何强', 3, 1),
('CS204', 'Linux系统管理', '马超', 2, 1),
('CS205', '移动应用开发', '宋佳', 3, 1);
-- 电子工程专业课程(major_id = 2)
INSERT INTO `course` (`no`, `name`, `teacher`, `score`, `major_id`) VALUES
('EE101', '电路分析基础', '黄建国', 3, 2),
('EE102', '模拟电子技术', '徐敏', 4, 2),
('EE103', '数字电子技术', '高伟', 3, 2),
('EE104', '信号与系统', '林芳', 4, 2),
('EE105', '电磁场理论', '谢军', 3, 2),
('EE106', '通信原理', '罗斌', 3, 2),
('EE107', '微机原理与接口', '唐娜', 3, 2),
('EE108', '数字信号处理', '董华', 3, 2),
('EE109', '电力电子技术', '韩梅', 3, 2),
('EE110', '嵌入式系统设计', '曹阳', 4, 2),
('EE201', '自动控制原理', '彭丽', 3, 2),
('EE202', '传感器技术', '方明', 2, 2),
('EE203', 'VLSI设计基础', '苏婷', 3, 2),
('EE204', '射频电路设计', '姜涛', 3, 2),
('EE205', '物联网技术', '程琳', 3, 2);
后端接口 course.py
from typing import Optional
from fastapi import APIRouter
from pydantic import create_model, BaseModel, Field
from tortoise.contrib.pydantic import pydantic_model_creator
from common.exception_handler import CustomException
from common.result import Result, PageInfo
from models import Course
router = APIRouter(prefix="/course")
CoursePydantic = pydantic_model_creator(Course)
CourseCreatePydantic = create_model(
"CourseCreatePydantic",
**{
name: (Optional[field.annotation], None)
for name, field in CoursePydantic.model_fields.items()
},
major_id=(Optional[int], Field(None, alias="majorId"))
)
# 新增
@router.post("/add")
async def add(course_create_pydantic: CourseCreatePydantic):
db_course = await Course.get_or_none(no=course_create_pydantic.no)
if db_course is not None:
raise CustomException("课程编号重复")
# 将参数转换成 字典数据
create_data = course_create_pydantic.model_dump(exclude_unset=True, exclude={"id"})
await Course.create(**create_data) # no=xxx,name=xxx,college=xxx
return Result.success()
# 更新
@router.put("/update")
async def add(course_create_pydantic: CourseCreatePydantic):
if course_create_pydantic.id is None:
raise CustomException("缺少参数ID")
# 将参数转换成 字典数据
update_data = course_create_pydantic.model_dump(exclude_unset=True, exclude={"id"})
await Course.filter(id=course_create_pydantic.id).update(**update_data) # no=xxx,name=xxx,college=xxx where id = xxx
return Result.success()
# 删除
@router.delete('/delete/{course_id}')
async def delete(course_id: int):
await Course.filter(id=course_id).delete()
return Result.success()
# 单个查询
@router.get('/selectById/{course_id}')
async def select_by_id(course_id: int):
course = await Course.get_or_none(id=course_id)
return Result.success(course)
# 查询所有数据
@router.get('/selectAll')
async def select_all(name: str = ""):
course_list = await Course.filter(name__contains=name) # name__contains表示根据name进行模糊查询
return Result.success(course_list)
# 分页查询数据
@router.get('/selectPage')
async def select_page(name: str = "", no: str = "", teacher: str = "", majorId: int = 0, pageNum: int = 1, pageSize: int = 10):
# name__contains表示根据name进行模糊查询 prefetch_related 关联查询到 major模块的数据
query = (Course.filter(name__contains=name).filter(no__contains=no).filter(teacher__contains=teacher)
.prefetch_related("major"))
if majorId and majorId > 0:
query = query.filter(major__id=majorId)
course_list = await query.offset((pageNum - 1) * pageSize).limit(pageSize)
total = await query.count()
course_dict_list = [
{
**CoursePydantic.model_validate(course).model_dump(), # id=xxx,no=xxx,name=xxx
"majorName": course.major.name if course.major else None
}
for course in course_list
]
page_info = PageInfo(list=course_dict_list, total=total)
return Result.success(page_info)
前端页面 Course.vue
<template>
<div>
<div class="card" style="margin-bottom: 5px;">
<el-select style="width: 300px; margin-right: 10px" placeholder="请选择专业" v-model="data.majorId">
<el-option v-for="item in data.majorList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-input v-model="data.no" style="width: 300px; margin-right: 10px" placeholder="请输入课程编号查询"></el-input>
<el-input v-model="data.name" style="width: 300px; margin-right: 10px" placeholder="请输入课程名称查询"></el-input>
<el-input v-model="data.teacher" style="width: 300px; margin-right: 10px" placeholder="请输入老师名称查询"></el-input>
<el-button type="primary" @click="load">查询</el-button>
<el-button type="info" style="margin: 0 10px" @click="reset">重置</el-button>
</div>
<div class="card" style="margin-bottom: 5px">
<div style="margin-bottom: 10px" v-if="data.user.role === '管理员'">
<el-button type="primary" @click="handleAdd" >新增</el-button>
</div>
<el-table :data="data.tableData" stripe>
<el-table-column label="课程编号" prop="no"></el-table-column>
<el-table-column label="课程名称" prop="name"></el-table-column>
<el-table-column label="学分" prop="score"></el-table-column>
<el-table-column label="所属专业" prop="majorName"></el-table-column>
<el-table-column label="教课老师" prop="teacher"></el-table-column>
<el-table-column label="操作" align="center" width="160" v-if="data.user.role === '管理员'">
<template #default="scope">
<el-button type="primary" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="card">
<el-pagination @current-change="load" background layout="total, prev, pager, next" v-model:page-size="data.pageSize" v-model:current-page="data.pageNum" :total="data.total"/>
</div>
<el-dialog title="课程信息" width="40%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<el-form ref="formRef" :model="data.form" :rules="data.rules" label-width="100px" style="padding-right: 50px">
<el-form-item label="课程编号" prop="no">
<el-input placeholder="请输入课程编号" v-model="data.form.no" autocomplete="off" />
</el-form-item>
<el-form-item label="课程名称" prop="name">
<el-input placeholder="请输入课程名称" v-model="data.form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="课程学分" prop="score">
<el-input-number style="width: 200px" :min="1" placeholder="请输入课程学分" v-model="data.form.score" autocomplete="off" />
</el-form-item>
<el-form-item label="教课老师" prop="teacher">
<el-input placeholder="请输入教课老师" v-model="data.form.teacher" autocomplete="off" />
</el-form-item>
<el-form-item label="所属专业" prop="majorId">
<el-select placeholder="请选择专业" v-model="data.form.majorId">
<el-option v-for="item in data.majorList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import request from "@/utils/request";
import {reactive, ref} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";
const formRef = ref()
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}'),
pageNum: 1,
pageSize: 10,
total: 0,
formVisible: false,
form: {},
tableData: [],
majorList: [],
majorId: null,
no: null,
name: null,
teacher: null,
rules: {
no: [
{ required: true, message: '请输入课程编号', trigger: 'blur' }
],
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
majorId: [
{ required: true, message: '请选择专业', trigger: 'change' }
],
teacher: [
{ required: true, message: '请输入教课老师', trigger: 'blur' }
],
score: [
{ required: true, message: '请输入学分', trigger: 'blur' }
],
}
})
// 查询专业的信息list
request.get('/major/selectAll').then(res => {
data.majorList = res.data
})
// 分页查询
const load = () => {
request.get('/course/selectPage', {
params: {
pageNum: data.pageNum,
pageSize: data.pageSize,
majorId: data.majorId,
no: data.no,
teacher: data.teacher,
name: data.name
}
}).then(res => {
if (res.code === '200') {
data.tableData = res.data?.list
data.total = res.data?.total
} else {
ElMessage.error(res.msg)
}
})
}
load()
// 新增
const handleAdd = () => {
data.form = {}
data.formVisible = true
}
// 编辑
const handleEdit = (row) => {
data.form = JSON.parse(JSON.stringify(row))
data.formVisible = true
}
// 新增保存
const add = () => {
request.post('/course/add', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 编辑保存
const update = () => {
request.put('/course/update', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 弹窗保存
const save = () => {
formRef.value.validate(valid => {
if (valid) {
// data.form有id就是更新,没有就是新增
data.form.id ? update() : add()
}
})
}
// 删除
const handleDelete = (id) => {
ElMessageBox.confirm('删除后数据无法恢复,您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {
request.delete('/course/delete/' + id).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
} else {
ElMessage.error(res.msg)
}
})
}).catch(err => {})
}
// 重置
const reset = () => {
data.name = null
data.majorId = null
data.no = null
data.teacher = null
load()
}
</script>