feat(exam): 实现按学生批量生成并下载试题功能

- 增加学生多选功能和生成试题按钮,支持批量操作
- 新增ExamGenerateDialog组件,提供选择年级和难度界面
- 设计后端接口支持多个学生ID,生成对应的试题文档
- 在后端实现批量生成Word文档并压缩打包下载
- 新增StudentDetail业务对象,完善学生信息展示
- 优化了Mapper接口及XML,支持批量查询学生和班级数据
- 提供前端API封装用于调用试题生成和下载服务
- 实现下载失败时的错误处理与提示机制
This commit is contained in:
lbw
2025-12-15 14:26:24 +08:00
parent 89df9af8c6
commit e3b993dd27
16 changed files with 406 additions and 47 deletions

View File

@@ -35,8 +35,19 @@
selectedGradeId }})</el-tag>
<el-button type="primary" @click="fetchStudents">查询</el-button>
<el-button @click="resetStudentFilters">重置</el-button>
<el-button type="success" :disabled="selectedStudentIds.length === 0" @click="showGenerateDialog = true">
生成试题
</el-button>
</div>
<el-table :data="students" border class="w-full" v-loading="studentLoading">
<el-table
ref="studentTableRef"
:data="students"
border
class="w-full"
v-loading="studentLoading"
@selection-change="onStudentSelectionChange"
>
<el-table-column type="selection" width="48" />
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="姓名" min-width="120" />
<el-table-column prop="classId" label="班级ID" width="100" />
@@ -47,6 +58,11 @@
:total="studentTotalCount" :page-size="studentPageSize" :current-page="studentPageNo"
@current-change="handleStudentPageChange" @size-change="handleStudentSizeChange" />
</div>
<ExamGenerateDialog
v-model="showGenerateDialog"
:student-ids="selectedStudentIds"
:default-grade-id="selectedGradeId"
/>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6" v-loading="gradeLoading">
@@ -76,6 +92,7 @@ import { ref, onMounted } from 'vue'
import { getClassList } from '@/api/class'
import { getGradeList } from '@/api/grade'
import { getStudentList } from '@/api/student'
import ExamGenerateDialog from '@/layouts/components/ExamGenerateDialog.vue'
const classes = ref([])
const pageNo = ref(1)
@@ -85,6 +102,7 @@ const loading = ref(false)
const classTableRef = ref(null)
const selectedClassId = ref(null)
const selectedClassTitle = ref('')
const grades = ref([])
const gradePageNo = ref(1)
const gradePageSize = ref(10)
@@ -93,12 +111,16 @@ const gradeLoading = ref(false)
const gradeTableRef = ref(null)
const selectedGradeId = ref(null)
const selectedGradeTitle = ref('')
const students = ref([])
const studentPageNo = ref(1)
const studentPageSize = ref(10)
const studentTotalCount = ref(0)
const studentLoading = ref(false)
const studentName = ref('')
const studentTableRef = ref(null)
const selectedStudentIds = ref([])
const showGenerateDialog = ref(false)
async function fetchClasses() {
loading.value = true
@@ -178,6 +200,9 @@ function handleStudentSizeChange(s) {
studentPageNo.value = 1
fetchStudents()
}
function onStudentSelectionChange(rows) {
selectedStudentIds.value = rows.map(r => r.id)
}
function onClassRowClick(row) {
selectedClassId.value = row.id
selectedClassTitle.value = row.title