[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-public-OSx3Mogl":3,"public-project-articles-OSx3Mogl":17},{"id":4,"uuid":5,"project_id":6,"title":7,"content":8,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},1149,"OSx3Mogl",55,"12. 开发学生成绩模块","## SQL\n\n```sql\nCREATE TABLE `grade` (\n  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `student_id` int DEFAULT NULL COMMENT '学生ID',\n  `course_id` int DEFAULT NULL COMMENT '课程ID',\n  `score` int DEFAULT NULL COMMENT '成绩',\n  `ispass` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '是否及格',\n  `time` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='成绩信息';\n```\n\n## 学生选课的查询接口\n\n```python\n# 查询所有数据\n@router.get('\u002FselectAll')\nasync def select_all(studentId :int = 0, status: str = \"\"):\n    query = StudentCourse.all().prefetch_related(\"course\")\n    if studentId > 0:\n        query = query.filter(student__id=studentId)\n    if status != \"\":\n        query = query.filter(status=status)\n    student_course_list = await query\n    student_course_dict_list = [\n        {\n            **StudentCoursePydantic.model_validate(student_course).model_dump(),\n            \"courseId\": student_course.course.id if student_course.course else None,\n            \"courseName\": student_course.course.name if student_course.course else None\n        }\n        for student_course in student_course_list\n    ]\n    return Result.success(student_course_dict_list)\n\n\n```\n\n## 给学生加上学分的字段\n\n```sql\nCREATE TABLE `student` (\n  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '账号',\n  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',\n  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',\n  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像',\n  `role` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色',\n  `clazz_id` int DEFAULT NULL COMMENT '班级',\n  `score` int DEFAULT NULL COMMENT '学分',\n  PRIMARY KEY (`id`) USING BTREE,\n  UNIQUE KEY `username` (`username`) USING BTREE\n) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='学生信息';\n```\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2025\u002Fpng\u002F751015\u002F1766654561333-1d3de9e6-dab0-45f1-8c93-fe4cd99bfe87.png)\n\n\n\n注意登录的返回模型 加上 score 字段\n\n```python\nclass Account(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n\n    id: int | None = None\n    username: str | None = None\n    password: str | None = None\n    newPassword: str | None = None\n    role: str | None = None\n    name: str | None = None\n    avatar: str | None = None\n    clazzId: int | None = None\n    majorId: int | None = None\n    score: int | None = None\n\n```\n\n## 退课 更新状态\n\n```python\n# 更新\n@router.put(\"\u002Fupdate\")\nasync def add(student_course_create_pydantic: StudentCourseCreatePydantic):\n    if student_course_create_pydantic.id is None:\n        raise CustomException(\"缺少参数ID\")\n    if student_course_create_pydantic.status == '已退':\n        grade = await (Grade.filter(student_id=student_course_create_pydantic.student_id)\n                       .filter(course_id=student_course_create_pydantic.course_id).first())\n        if grade is not None:\n            raise CustomException('当前课程已打分，无法退课')\n    if student_course_create_pydantic.checkStatus == '通过':\n        student_course_create_pydantic.status = '已选'\n    elif student_course_create_pydantic.checkStatus == '拒绝':\n        student_course_create_pydantic.status = '未选中'\n    # 将参数转换成 字典数据\n    update_data = student_course_create_pydantic.model_dump(exclude_unset=True, exclude={\"id\"})\n    await StudentCourse.filter(id=student_course_create_pydantic.id).update(\n        **update_data)  # no=xxx,name=xxx,college=xxx where id = xxx\n    return Result.success()\n```\n\n## 学生成绩后台接口\n\n```python\nfrom datetime import datetime\nfrom typing import Optional\n\nfrom fastapi import APIRouter\nfrom pydantic import create_model, BaseModel, Field\nfrom tortoise.contrib.pydantic import pydantic_model_creator\nfrom tortoise.expressions import F\n\nfrom common.exception_handler import CustomException\nfrom common.result import Result, PageInfo\nfrom models import Grade, Student, Course\n\nrouter = APIRouter(prefix=\"\u002Fgrade\")\n\nGradePydantic = pydantic_model_creator(Grade)\n\nGradeCreatePydantic = create_model(\n    \"GradeCreatePydantic\",\n    **{\n        name: (Optional[field.annotation], None)\n        for name, field in GradePydantic.model_fields.items()\n    },\n    student_id=(Optional[int], Field(None, alias=\"studentId\")),\n    course_id=(Optional[int], Field(None, alias=\"courseId\"))\n)\n\n\n# 新增\n@router.post(\"\u002Fadd\")\nasync def add(grade_create_pydantic: GradeCreatePydantic):\n    # 当前的这个学生是否有已选的课程\n    db_grade = await (Grade.filter(student_id=grade_create_pydantic.student_id)\n                      .filter(course_id=grade_create_pydantic.course_id)\n                      .first())\n    if db_grade is not None:\n        raise CustomException(\"该学生课程成绩已被录入\")\n    grade_create_pydantic.time = datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n    # 将参数转换成 字典数据\n    create_data = grade_create_pydantic.model_dump(exclude_unset=True, exclude={\"id\"})\n    await Grade.create(**create_data)  # no=xxx,name=xxx,college=xxx\n    if grade_create_pydantic.ispass == '是':\n        # 查询课程的学分\n        course = await Course.filter(id=grade_create_pydantic.course_id).first()\n        add_score = course.score\n        # 设置学分\n        await Student.filter(id=grade_create_pydantic.student_id).update(score=F('score') + add_score)\n    return Result.success()\n\n\n# 更新\n@router.put(\"\u002Fupdate\")\nasync def add(grade_create_pydantic: GradeCreatePydantic):\n    if grade_create_pydantic.id is None:\n        raise CustomException(\"缺少参数ID\")\n    # 将参数转换成 字典数据\n    update_data = grade_create_pydantic.model_dump(exclude_unset=True, exclude={\"id\"})\n    await Grade.filter(id=grade_create_pydantic.id).update(**update_data)  # no=xxx,name=xxx,college=xxx where id = xxx\n    return Result.success()\n\n\n# 删除\n@router.delete('\u002Fdelete\u002F{grade_id}')\nasync def delete(grade_id: int):\n    await Grade.filter(id=grade_id).delete()\n    return Result.success()\n\n\n# 单个查询\n@router.get('\u002FselectById\u002F{grade_id}')\nasync def select_by_id(grade_id: int):\n    grade = await Grade.get_or_none(id=grade_id)\n    return Result.success(grade)\n\n\n# 查询所有数据\n@router.get('\u002FselectAll')\nasync def select_all(name: str = \"\"):\n    query = Grade.all()\n    grade_list = await query.filter(name__contains=name)\n    return Result.success(grade_list)\n\n\n# 分页查询数据\n@router.get('\u002FselectPage')\nasync def select_page(studentId: int = 0, studentName: str = \"\", courseName: str = \"\", pageNum: int = 1, pageSize: int = 10):\n    # name__contains表示根据name进行模糊查询  prefetch_related 关联查询到 major模块的数据\n    query = Grade.all().prefetch_related(\"student\", \"course\")\n    if studentId > 0:\n        query = query.filter(student__id=studentId)\n    if studentName != \"\":\n        query = query.filter(student__name__contains=studentName)\n    if courseName != \"\":\n        query = query.filter(course__name__contains=courseName)\n    grade_list = await query.order_by(\"-id\").offset((pageNum - 1) * pageSize).limit(pageSize)\n    total = await query.count()\n    grade_dict_list = [\n        {\n            **GradePydantic.model_validate(grade).model_dump(),  # id=xxx,no=xxx,name=xxx\n            \"studentName\": grade.student.name if grade.student else None,\n            \"courseName\": grade.course.name if grade.course else None\n        }\n        for grade in grade_list\n    ]\n    page_info = PageInfo(list=grade_dict_list, total=total)\n    return Result.success(page_info)\n\n```\n\n## 学生成绩页面\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n\n    \u003Cdiv class=\"card\" style=\"margin-bottom: 5px;\">\n      \u003Cel-input v-model=\"data.courseName\" style=\"width: 300px; margin-right: 10px\" placeholder=\"请输入课程名称查询\">\u003C\u002Fel-input>\n      \u003Cel-input v-if=\"data.user.role === '管理员'\" v-model=\"data.studentName\" style=\"width: 300px; margin-right: 10px\" placeholder=\"请输入学生名称查询\">\u003C\u002Fel-input>\n      \u003Cel-button type=\"primary\" @click=\"load\">查询\u003C\u002Fel-button>\n      \u003Cel-button type=\"info\" style=\"margin: 0 10px\" @click=\"reset\">重置\u003C\u002Fel-button>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"card\" style=\"margin-bottom: 5px\">\n      \u003Cdiv style=\"margin-bottom: 10px\" v-if=\"data.user.role === '管理员'\">\n        \u003Cel-button type=\"primary\" @click=\"handleAdd\" >新增\u003C\u002Fel-button>\n      \u003C\u002Fdiv>\n      \u003Cel-table :data=\"data.tableData\" stripe>\n        \u003Cel-table-column label=\"课程名称\" prop=\"courseName\">\u003C\u002Fel-table-column>\n        \u003Cel-table-column label=\"学生名称\" prop=\"studentName\">\u003C\u002Fel-table-column>\n        \u003Cel-table-column label=\"分数\" prop=\"score\">\u003C\u002Fel-table-column>\n        \u003Cel-table-column label=\"是否及格\" prop=\"ispass\">\n          \u003Ctemplate #default=\"scope\">\n            \u003Cb style=\"color: #1abc00\" v-if=\"scope.row.ispass === '是'\">是\u003C\u002Fb>\n            \u003Cb style=\"color: red\" v-if=\"scope.row.ispass === '否'\">否\u003C\u002Fb>\n          \u003C\u002Ftemplate>\n        \u003C\u002Fel-table-column>\n        \u003Cel-table-column label=\"创建时间\" prop=\"time\">\u003C\u002Fel-table-column>\n        \u003Cel-table-column label=\"操作\" align=\"center\" width=\"160\" v-if=\"data.user.role === '管理员'\">\n          \u003Ctemplate #default=\"scope\">\n            \u003Cel-button type=\"primary\" @click=\"handleEdit(scope.row)\">编辑\u003C\u002Fel-button>\n            \u003Cel-button type=\"danger\" @click=\"handleDelete(scope.row.id)\">删除\u003C\u002Fel-button>\n          \u003C\u002Ftemplate>\n        \u003C\u002Fel-table-column>\n      \u003C\u002Fel-table>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"card\">\n      \u003Cel-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\"\u002F>\n    \u003C\u002Fdiv>\n\n    \u003Cel-dialog title=\"成绩信息\" width=\"40%\" v-model=\"data.formVisible\" :close-on-click-modal=\"false\" destroy-on-close>\n      \u003Cel-form ref=\"formRef\" :model=\"data.form\" :rules=\"data.rules\" label-width=\"100px\" style=\"padding-right: 50px\">\n        \u003Cel-form-item label=\"学生\" prop=\"studentId\" v-if=\"!data.form.id\">\n          \u003Cel-select placeholder=\"请选择学生\" v-model=\"data.form.studentId\" @change=\"selectCourse\">\n            \u003Cel-option v-for=\"item in data.studentList\" :key=\"item.id\" :label=\"item.name\" :value=\"item.id\">\u003C\u002Fel-option>\n          \u003C\u002Fel-select>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"课程\" prop=\"courseId\" v-if=\"!data.form.id\">\n          \u003Cel-select :disabled=\"!data.form.studentId\" placeholder=\"请选择课程\" v-model=\"data.form.courseId\">\n            \u003Cel-option v-for=\"item in data.studentCourseList\" :key=\"item.id\" :label=\"item.courseName\" :value=\"item.courseId\">\u003C\u002Fel-option>\n          \u003C\u002Fel-select>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"成绩\" prop=\"score\">\n          \u003Cel-input-number style=\"width: 200px\" :min=\"1\" placeholder=\"请输入成绩\" v-model=\"data.form.score\" autocomplete=\"off\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"是否合格\" prop=\"ispass\">\n          \u003Cel-radio-group v-model=\"data.form.ispass\">\n            \u003Cel-radio-button label=\"是\" value=\"是\">\u003C\u002Fel-radio-button>\n            \u003Cel-radio-button label=\"否\" value=\"否\">\u003C\u002Fel-radio-button>\n          \u003C\u002Fel-radio-group>\n        \u003C\u002Fel-form-item>\n\n      \u003C\u002Fel-form>\n      \u003Ctemplate #footer>\n      \u003Cspan class=\"dialog-footer\">\n        \u003Cel-button @click=\"data.formVisible = false\">取 消\u003C\u002Fel-button>\n        \u003Cel-button type=\"primary\" @click=\"save\">保 存\u003C\u002Fel-button>\n      \u003C\u002Fspan>\n      \u003C\u002Ftemplate>\n    \u003C\u002Fel-dialog>\n\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport request from \"@\u002Futils\u002Frequest\";\nimport {reactive, ref} from \"vue\";\nimport {ElMessageBox, ElMessage} from \"element-plus\";\n\nconst formRef = ref()\nconst data = reactive({\n  user: JSON.parse(localStorage.getItem('system-user') || '{}'),\n  pageNum: 1,\n  pageSize: 10,\n  total: 0,\n  formVisible: false,\n  form: {},\n  tableData: [],\n  studentList: [],\n  studentCourseList: [],\n  courseName: null,\n  studentName: null,\n  rules: {\n    studentId: [\n      { required: true, message: '请选择学生', trigger: 'change' }\n    ],\n    courseId: [\n      { required: true, message: '请选择课程', trigger: 'change' }\n    ],\n    score: [\n      { required: true, message: '请输入成绩', trigger: 'blur' }\n    ],\n    ispass: [\n      { required: true, message: '请选择是否合格', trigger: 'change' }\n    ],\n  }\n})\n\n\u002F\u002F 查询学生的信息list\nrequest.get('\u002Fstudent\u002FselectAll').then(res => {\n  data.studentList = res.data\n})\n\n\u002F\u002F 查询选课的接口\nconst selectCourse = () => {\n  data.form.courseId = null  \u002F\u002F 先清空课程\n  request.get('\u002FstudentCourse\u002FselectAll', {\n    params: {\n      studentId: data.form.studentId,\n      status: '已选'\n    }\n  }).then(res => {\n  data.studentCourseList = res.data\n})\n}\n\n\u002F\u002F 分页查询\nconst load = () => {\n  request.get('\u002Fgrade\u002FselectPage', {\n    params: {\n      pageNum: data.pageNum,\n      pageSize: data.pageSize,\n      courseName: data.courseName,\n      studentName: data.studentName,\n      studentId: data.user.role === '管理员' ? null : data.user.id,\n    }\n  }).then(res => {\n    if (res.code === '200') {\n      data.tableData = res.data?.list\n      data.total = res.data?.total\n    } else {\n      ElMessage.error(res.msg)\n    }\n  })\n}\nload()\n\n\u002F\u002F 新增\nconst handleAdd = () => {\n  data.form = {}\n  data.formVisible = true\n}\n\n\u002F\u002F 编辑\nconst handleEdit = (row) => {\n  data.form = JSON.parse(JSON.stringify(row))\n  data.formVisible = true\n}\n\n\u002F\u002F 新增保存\nconst add = () => {\n  request.post('\u002Fgrade\u002Fadd', data.form).then(res => {\n    if (res.code === '200') {\n      load()\n      ElMessage.success('操作成功')\n      data.formVisible = false\n    } else {\n      ElMessage.error(res.msg)\n    }\n  })\n}\n\n\u002F\u002F 编辑保存\nconst update = () => {\n  request.put('\u002Fgrade\u002Fupdate', data.form).then(res => {\n    if (res.code === '200') {\n      load()\n      ElMessage.success('操作成功')\n      data.formVisible = false\n    } else {\n      ElMessage.error(res.msg)\n    }\n  })\n}\n\n\u002F\u002F 弹窗保存\nconst save = () => {\n  formRef.value.validate(valid => {\n    if (valid) {\n      \u002F\u002F data.form有id就是更新，没有就是新增\n      data.form.id ? update() : add()\n    }\n  })\n}\n\n\u002F\u002F 删除\nconst handleDelete = (id) => {\n  ElMessageBox.confirm('删除后数据无法恢复，您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {\n    request.delete('\u002Fgrade\u002Fdelete\u002F' + id).then(res => {\n      if (res.code === '200') {\n        load()\n        ElMessage.success('操作成功')\n      } else {\n        ElMessage.error(res.msg)\n      }\n    })\n  }).catch(err => {})\n}\n\n\u002F\u002F 重置\nconst reset = () => {\n  data.courseName = null\n  data.studentName = null\n  load()\n}\n\u003C\u002Fscript>\n```\n\n","coding",1,163,2246,"2026-01-08 18:02:51","2026-05-03 22:49:02","基于FastAPI+Vue3的学生信息管理系统","student-information",{"project":18,"items":19},{"id":6,"title":15,"slug":16},[20,28,35,42,49,56,63,70,77,84,91,98,104,105],{"id":21,"uuid":22,"project_id":6,"title":23,"type":9,"status":10,"public_enabled":10,"views":24,"sort":25,"created_at":26,"updated_at":27,"project_title":15,"project_slug":16},1118,"ziKCD5pq","00. 资料和脚手架获取方式",1029,2182,"2026-01-08 17:19:23","2026-05-03 22:50:49",{"id":29,"uuid":30,"project_id":6,"title":31,"type":9,"status":10,"public_enabled":10,"views":32,"sort":33,"created_at":34,"updated_at":14,"project_title":15,"project_slug":16},1119,"bxgr45Kb","01. 学生信息管理系统介绍",461,2187,"2025-12-18 18:35:51",{"id":36,"uuid":37,"project_id":6,"title":38,"type":9,"status":10,"public_enabled":10,"views":39,"sort":40,"created_at":41,"updated_at":14,"project_title":15,"project_slug":16},1120,"jlnSrc9J","02. 导入并运行项目脚手架",509,2188,"2025-12-18 18:36:05",{"id":43,"uuid":44,"project_id":6,"title":45,"type":9,"status":10,"public_enabled":10,"views":46,"sort":47,"created_at":48,"updated_at":14,"project_title":15,"project_slug":16},1124,"OIwLCsr8","03. 开发专业信息增删改查功能（一）",366,2198,"2025-12-19 17:28:47",{"id":50,"uuid":51,"project_id":6,"title":52,"type":9,"status":10,"public_enabled":10,"views":53,"sort":54,"created_at":55,"updated_at":14,"project_title":15,"project_slug":16},1128,"anqokUWU","04. 开发专业信息增删改查功能（二）",241,2211,"2025-12-22 17:24:47",{"id":57,"uuid":58,"project_id":6,"title":59,"type":9,"status":10,"public_enabled":10,"views":60,"sort":61,"created_at":62,"updated_at":14,"project_title":15,"project_slug":16},1129,"8MbeMJwP","05. 开发前端页面管理专业信息",239,2212,"2025-12-22 17:25:07",{"id":64,"uuid":65,"project_id":6,"title":66,"type":9,"status":10,"public_enabled":10,"views":67,"sort":68,"created_at":69,"updated_at":14,"project_title":15,"project_slug":16},1136,"wLyZvGac","06. 开发班级信息管理",207,2225,"2026-01-08 18:03:28",{"id":71,"uuid":72,"project_id":6,"title":73,"type":9,"status":10,"public_enabled":10,"views":74,"sort":75,"created_at":76,"updated_at":14,"project_title":15,"project_slug":16},1137,"PIryZqq8","07. 开发学生信息管理功能",191,2226,"2025-12-23 18:00:42",{"id":78,"uuid":79,"project_id":6,"title":80,"type":9,"status":10,"public_enabled":10,"views":81,"sort":82,"created_at":83,"updated_at":14,"project_title":15,"project_slug":16},1141,"PeUzzy9n","08. 优化学生模块代码",167,2233,"2025-12-24 16:54:01",{"id":85,"uuid":86,"project_id":6,"title":87,"type":9,"status":10,"public_enabled":10,"views":88,"sort":89,"created_at":90,"updated_at":14,"project_title":15,"project_slug":16},1142,"CTQ2w9Yp","09. 开发学生登录、注册、个人信息、修改密码功能",164,2234,"2025-12-24 16:54:18",{"id":92,"uuid":93,"project_id":6,"title":94,"type":9,"status":10,"public_enabled":10,"views":95,"sort":96,"created_at":97,"updated_at":14,"project_title":15,"project_slug":16},1143,"6GmNfUzm","10. 开发课程信息管理模块",161,2235,"2025-12-24 16:54:34",{"id":99,"uuid":100,"project_id":6,"title":101,"type":9,"status":10,"public_enabled":10,"views":81,"sort":102,"created_at":103,"updated_at":14,"project_title":15,"project_slug":16},1148,"u4OeLaw2","11. 开发学生选课功能",2245,"2025-12-25 17:51:39",{"id":4,"uuid":5,"project_id":6,"title":7,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},{"id":106,"uuid":107,"project_id":6,"title":108,"type":9,"status":10,"public_enabled":10,"views":109,"sort":110,"created_at":111,"updated_at":14,"project_title":15,"project_slug":16},1155,"mnCD9CqK","13. 开发系统公告功能",157,2258,"2025-12-29 15:53:35"]