项目背景
"知分"系统是一款基于SpringBoot和Vue架构开发的B/S模式在线成绩管理平台,旨在解决传统成绩通知方式(如纸质成绩单、家长群通知)存在的效率低下、易出错、查询不便且不易保存等问题。
该系统作为连接家长与教师的数字化桥梁,具有以下核心价值:
- 对于家长:通过微信小程序即可随时随地、安全便捷地查询子女的最新考试成绩与排名,促进家校沟通
- 对于教师/学校:提供高效的Excel一键式成绩录入工具,自动完成分数统计与排名分析,通过可视化图表直观展示教学成果,极大减轻成绩管理工作负担
该系统实现了成绩管理的数字化、自动化与智能化,显著提升了家校互动的体验与效率。
本文将重点讲解成绩录入功能中Excel模板下载的实现细节。
功能展示

点击"录入成绩"按钮后会出现弹窗:

该功能支持上传xls和xlsx文件,并提供模板下载功能。模板会自动加载对应班级学生和考试名称,教师只需填写各科分数即可。
前端实现详解
1. 按钮触发逻辑
前端页面使用Element UI组件,点击"录入成绩"按钮触发相应事件:
html
<div class="card-actions">
<el-button
type="primary"
size="small"
@click="handleImportScore(classItem.id)"
class="action-btn"
>
<i class="el-icon-upload"></i>
录入成绩
</el-button>
<el-button
type="default"
size="small"
@click="handleViewDetails(classItem.id)"
class="action-btn"
>
<i class="el-icon-view"></i>
查看详情
</el-button>
</div>
点击"录入成绩"按钮后,会触发handleImportScore
方法并将班级ID作为参数传递:
javascript
// 处理导入成绩
handleImportScore(classId) {
this.currentClassId = classId
this.scoreDialogVisible = true
this.examName = ''
this.fileList = []
this.selectedFile = null
}
此方法主要完成以下工作:
- 设置当前班级ID
- 打开成绩录入弹窗
- 重置相关表单数据
2. 模板下载功能
弹窗中的模板下载区域代码如下:
html
<div class="upload-tips">
<el-button
type="primary"
size="small"
@click="downloadExcelTemplate"
class="download-template-btn"
>
<i class="el-icon-download"></i>
点击下载样本
</el-button>
<p style="margin-top: 10px; color: #909399; font-size: 12px;">
下载后填写分数,再上传即可
</p>
</div>
点击下载按钮后,触发downloadExcelTemplate
方法:
javascript
// 下载Excel模板
downloadExcelTemplate() {
// 条件校验
if (!this.currentClassId) {
this.$message.error('未选择班级')
return
}
if (!this.selectedExamId) {
this.$message.warning('请先选择考试')
return
}
try {
// 显示加载提示
this.$message({
message: '正在生成模板,请稍候',
type: 'info',
duration: 0
})
// 获取班级和考试信息用于构建文件名
const exam = this.homeData.exams.find(e => e.id === this.selectedExamId)
const className = this.homeData.classes.find(c => c.id === this.currentClassId)?.name || '未知班级'
const examName = exam?.name || '未知考试'
// 设置token到请求头,防止被权限拦截
const token = this.$store.state.user.token
if (!token) {
this.$message.warning('登录状态失效,请重新登录')
setTimeout(() => {
this.$router.push('/login')
}, 1000)
return
}
// 使用XHR方式下载文件,确保携带token
const xhr = new XMLHttpRequest()
const requestUrl = `/teacher/home/downloadTemplate/${this.currentClassId}/${this.selectedExamId}`
xhr.open('GET', requestUrl, true)
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
xhr.responseType = 'blob'
// 传递className和examName到回调函数中
const downloadContext = {
className: className,
examName: examName,
component: this
}
xhr.onload = function() {
// 处理返回结果
if (xhr.status === 200) {
// 关闭所有消息提示
downloadContext.component.$message.closeAll()
downloadContext.component.$message.success('模板下载成功')
try {
// 设置文件名
let finalFileName = `${downloadContext.className}成绩导入表${downloadContext.examName}.xlsx`
const contentDisposition = xhr.getResponseHeader('Content-Disposition')
// 根据响应类型设置正确的MIME类型
let mimeType = 'application/octet-stream'
const contentType = xhr.getResponseHeader('Content-Type')
if (contentType) {
mimeType = contentType
}
// 创建下载链接
const blob = new Blob([xhr.response], { type: mimeType })
const url = URL.createObjectURL(blob)
// 创建a标签并模拟点击下载
const downloadLink = document.createElement('a')
downloadLink.href = url
downloadLink.setAttribute('download', finalFileName)
downloadLink.style.display = 'none'
document.body.appendChild(downloadLink)
downloadLink.click()
// 延迟移除,确保点击事件完成
setTimeout(() => {
document.body.removeChild(downloadLink)
URL.revokeObjectURL(url)
}, 100)
} catch (downloadError) {
console.error('下载文件处理失败:', downloadError)
downloadContext.component.$message.error('文件下载处理失败')
}
} else {
console.error('请求失败,状态码:', xhr.status)
downloadContext.component.$message.closeAll()
downloadContext.component.$message.error('生成模板失败:请稍后重试')
}
}
xhr.onerror = function(e) {
console.error('网络错误:', e)
this.$message.closeAll()
this.$message.error('生成模板失败:网络错误')
}
xhr.send()
} catch (error) {
console.error('生成模板失败:', error)
this.$message.closeAll()
this.$message.error('生成模板失败:' + (error.message || '请稍后重试'))
}
}
该方法的关键实现点包括:
- 进行前置条件校验(班级和考试选择)
- 获取token并设置到请求头中
- 使用XMLHttpRequest发起请求,设置responseType为blob
- 处理响应并创建下载链接
- 添加异常处理机制,确保用户体验
后端实现详解
1. Controller层实现
后端使用SpringBoot框架,Controller层代码如下:
java
package com.eduscore.controller;
import com.eduscore.domain.Student;
import com.eduscore.service.ITeacherHomeService;
import com.eduscore.common.core.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/teacher/home")
public class TeacherHomeController extends BaseController {
@Autowired
private ITeacherHomeService teacherHomeService;
/**
* 下载Excel成绩导入模板
*/
@GetMapping("/downloadTemplate/{classId}/{examId}")
public void downloadExcelTemplate(@PathVariable Integer classId,
@PathVariable Integer examId,
HttpServletResponse response) {
try {
// 设置响应头
response.setContentType("text/csv;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" +
URLEncoder.encode("成绩导入模板.csv", "UTF-8"));
// 获取班级学生列表
List<Student> students = teacherHomeService.getClassStudents(classId);
// 获取班级考试信息
Map<String, Object> examInfo = teacherHomeService.getClassExamInfo(classId, examId);
// 获取考试科目
List<String> subjectNames = (List<String>) examInfo.get("subjectNames");
// 生成CSV内容
StringBuilder csvContent = new StringBuilder();
// 添加标题行
csvContent.append("学生姓名,班级");
for (String subject : subjectNames) {
csvContent.append(",").append(subject);
}
csvContent.append(",").append("考试名称");
csvContent.append("\n");
// 添加学生数据行
String className = (String) examInfo.get("className");
String examName = (String) examInfo.get("examName");
for (Student student : students) {
csvContent.append(student.getName()).append(",").append(className);
// 为每个科目添加空成绩列
for (int i = 0; i < subjectNames.size(); i++) {
csvContent.append(",");
}
csvContent.append(",").append(examName);
csvContent.append("\n");
}
// 输出CSV文件
try (OutputStream out = response.getOutputStream()) {
// 添加BOM头,确保Excel能正确识别UTF-8编码
out.write(0xEF);
out.write(0xBB);
out.write(0xBF);
out.write(csvContent.toString().getBytes(StandardCharsets.UTF_8));
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. Service层实现
Service层负责业务逻辑处理:
java
package com.eduscore.service.impl;
import com.eduscore.domain.*;
import com.eduscore.mapper.*;
import com.eduscore.service.ITeacherHomeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.HashMap;
@Service
public class TeacherHomeServiceImpl implements ITeacherHomeService {
@Autowired
private TeacherMapper teacherMapper;
@Autowired
private SchoolMapper schoolMapper;
@Autowired
private StudentMapper studentMapper;
@Autowired
private ClassMapper classMapper;
@Autowired
private ExamMapper examMapper;
@Autowired
private ScoreMapper scoreMapper;
@Autowired
private SubjectMapper subjectMapper;
@Override
public Map<String, Object> getClassExamInfo(Integer classId, Integer examId) {
// 获取班级信息
Class clazz = classMapper.selectClassById(classId);
// 获取考试信息
Exam exam = examMapper.selectExamById(examId);
// 获取该班级该考试的所有科目
List<Subject> subjects = subjectMapper.selectSubjectList(new Subject());
Map<String, Object> result = new HashMap<>();
result.put("className", clazz != null ? clazz.getName() : "");
result.put("examName", exam != null ? exam.getName() : "");
List<String> subjectNames = subjects.stream()
.map(Subject::getName)
.collect(Collectors.toList());
result.put("subjectNames", subjectNames);
return result;
}
}
实现原理总结
- 前端触发:用户点击下载模板按钮,前端进行条件校验并携带认证信息发起请求
- 后端处理:Controller接收请求,调用Service层获取班级、考试和科目信息
- 模板生成:后端动态生成CSV格式的模板文件,包含学生列表和科目信息
- 文件返回:设置正确的响应头和信息,将文件流返回给前端
- 前端下载:前端接收文件流,创建下载链接并触发浏览器下载
生成的模板示例

通过以上实现,教师可以下载预先填充了班级学生信息和考试科目的模板,只需填写分数即可完成成绩录入,极大提高了工作效率。
下一篇文章将详细讲解成绩录入功能的实现细节,包括文件上传、数据解析和批量导入等关键技术点。