点名、成绩册统一读取“行政班在籍学生 ∪ 已选课学生”,不再错误显示 0 人。
教师选课名单改为展示全部本人教学班及完整学生名单,支持 Excel 导出。 调停课改用教师专用教学班接口,并在服务端限制只能操作本人教学班。
This commit is contained in:
@@ -101,6 +101,18 @@ async function submit() {
|
||||
} catch (e) { ElMessage.error(apiErrorMessage(e)) }
|
||||
}
|
||||
|
||||
async function loadTaskOptions() {
|
||||
if (!isTeacher.value && isManager.value) return
|
||||
try {
|
||||
tasks.value = (await http.get('/course-adjustments/task-options', {
|
||||
params: { academicTermId: termId.value || undefined },
|
||||
})).data
|
||||
} catch (error) {
|
||||
tasks.value = []
|
||||
ElMessage.error(apiErrorMessage(error))
|
||||
}
|
||||
}
|
||||
|
||||
async function doSubmit(adj: any) {
|
||||
try {
|
||||
await http.post(`/course-adjustments/${adj.id}/submit`)
|
||||
@@ -137,16 +149,13 @@ async function doReject() {
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const [termRes, taskRes] = await Promise.all([
|
||||
http.get('/base-data/terms'),
|
||||
isTeacher.value || !isManager.value
|
||||
? http.get('/teaching-tasks', { params: { page: 1, pageSize: 100 } })
|
||||
: Promise.resolve({ data: { items: [] } }),
|
||||
])
|
||||
const termRes = await http.get('/base-data/terms')
|
||||
terms.value = termRes.data
|
||||
tasks.value = taskRes.data.items
|
||||
termId.value = defaultAcademicTermId(terms.value)
|
||||
} catch (_) {}
|
||||
await loadTaskOptions()
|
||||
} catch (error) {
|
||||
ElMessage.error(apiErrorMessage(error))
|
||||
}
|
||||
await load()
|
||||
})
|
||||
|
||||
@@ -173,7 +182,7 @@ function showCancel(type: string) { return type === 'Cancel' }
|
||||
</section>
|
||||
|
||||
<section class="adj-toolbar">
|
||||
<el-select v-model="termId" clearable placeholder="全部学期" @change="load">
|
||||
<el-select v-model="termId" clearable placeholder="全部学期" @change="load(); loadTaskOptions()">
|
||||
<el-option v-for="t in terms" :key="t.id" :label="academicTermLabel(t)" :value="t.id" :class="academicTermOptionClass(t)" />
|
||||
</el-select>
|
||||
<el-segmented v-model="tab" :options="[
|
||||
|
||||
@@ -8,22 +8,22 @@ import { academicTermLabel, academicTermOptionClass, defaultAcademicTermId } fro
|
||||
const loading = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const terms = ref<any[]>([])
|
||||
const offerings = ref<any[]>([])
|
||||
const selectedOffering = ref<any>(null)
|
||||
const tasks = ref<any[]>([])
|
||||
const selectedTask = ref<any>(null)
|
||||
const roster = ref<any>(null)
|
||||
const termId = ref<string>()
|
||||
|
||||
async function loadOfferings() {
|
||||
async function loadTasks() {
|
||||
loading.value = true
|
||||
try {
|
||||
offerings.value = (await http.get('/course-selections/my-offerings', {
|
||||
tasks.value = (await http.get('/course-selections/my-teaching-tasks', {
|
||||
params: { academicTermId: termId.value || undefined },
|
||||
})).data
|
||||
const preferred = offerings.value.find((item: any) => item.id === selectedOffering.value?.id)
|
||||
?? offerings.value[0]
|
||||
if (preferred) await selectOffering(preferred)
|
||||
const preferred = tasks.value.find((item: any) => item.id === selectedTask.value?.id)
|
||||
?? tasks.value[0]
|
||||
if (preferred) await selectTask(preferred)
|
||||
else {
|
||||
selectedOffering.value = null
|
||||
selectedTask.value = null
|
||||
roster.value = null
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -33,11 +33,11 @@ async function loadOfferings() {
|
||||
}
|
||||
}
|
||||
|
||||
async function selectOffering(offering: any) {
|
||||
selectedOffering.value = offering
|
||||
async function selectTask(task: any) {
|
||||
selectedTask.value = task
|
||||
detailLoading.value = true
|
||||
try {
|
||||
roster.value = (await http.get(`/course-selections/offerings/${offering.id}/roster`)).data
|
||||
roster.value = (await http.get(`/course-selections/teaching-tasks/${task.id}/roster`)).data
|
||||
} catch (error) {
|
||||
roster.value = null
|
||||
ElMessage.error(apiErrorMessage(error))
|
||||
@@ -47,11 +47,11 @@ async function selectOffering(offering: any) {
|
||||
}
|
||||
|
||||
async function exportRoster() {
|
||||
if (!selectedOffering.value) return
|
||||
if (!selectedTask.value) return
|
||||
try {
|
||||
await downloadApiFile(
|
||||
`/course-selections/offerings/${selectedOffering.value.id}/roster/export.xlsx`,
|
||||
`选课名单-${selectedOffering.value.taskNumber}.xlsx`,
|
||||
`/course-selections/teaching-tasks/${selectedTask.value.id}/roster/export.xlsx`,
|
||||
`教学班名单-${selectedTask.value.taskNumber}.xlsx`,
|
||||
)
|
||||
} catch (error) {
|
||||
ElMessage.error(apiErrorMessage(error))
|
||||
@@ -61,7 +61,7 @@ async function exportRoster() {
|
||||
onMounted(async () => {
|
||||
terms.value = (await http.get('/base-data/terms')).data
|
||||
termId.value = defaultAcademicTermId(terms.value)
|
||||
await loadOfferings()
|
||||
await loadTasks()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -71,43 +71,43 @@ onMounted(async () => {
|
||||
<div>
|
||||
<span class="section-kicker">ENROLLMENT ROSTER</span>
|
||||
<h2>选课名单</h2>
|
||||
<p>查看本人授课班级的选课学生名单,支持 Excel 导出。</p>
|
||||
<p>查看本人授课教学班的完整学生名单,支持 Excel 导出。</p>
|
||||
</div>
|
||||
<el-button :icon="Refresh" @click="loadOfferings">刷新</el-button>
|
||||
<el-button :icon="Refresh" @click="loadTasks">刷新</el-button>
|
||||
</section>
|
||||
|
||||
<section class="roster-toolbar">
|
||||
<el-select v-model="termId" clearable placeholder="全部学期" @change="loadOfferings">
|
||||
<el-select v-model="termId" clearable placeholder="全部学期" @change="loadTasks">
|
||||
<el-option v-for="term in terms" :key="term.id" :label="academicTermLabel(term)" :value="term.id" :class="academicTermOptionClass(term)" />
|
||||
</el-select>
|
||||
<span>共 {{ offerings.length }} 个教学班</span>
|
||||
<span>共 {{ tasks.length }} 个教学班</span>
|
||||
</section>
|
||||
|
||||
<section class="roster-workspace" v-loading="loading">
|
||||
<aside class="roster-task-list">
|
||||
<button
|
||||
v-for="offering in offerings"
|
||||
:key="offering.id"
|
||||
v-for="task in tasks"
|
||||
:key="task.id"
|
||||
type="button"
|
||||
:class="{ active: selectedOffering?.id === offering.id }"
|
||||
@click="selectOffering(offering)"
|
||||
:class="{ active: selectedTask?.id === task.id }"
|
||||
@click="selectTask(task)"
|
||||
>
|
||||
<span>{{ offering.courseCode }} · {{ offering.taskNumber }}</span>
|
||||
<b>{{ offering.courseName }}</b>
|
||||
<small>{{ offering.termName }} · {{ offering.roundName }}</small>
|
||||
<i>{{ offering.enrolledCount }} / {{ offering.capacity }} 人</i>
|
||||
<span>{{ task.courseCode }} · {{ task.taskNumber }}</span>
|
||||
<b>{{ task.courseName }}</b>
|
||||
<small>{{ task.termName }}</small>
|
||||
<i>{{ task.studentCount }} / {{ task.capacity }} 人</i>
|
||||
</button>
|
||||
<el-empty v-if="!offerings.length" description="当前学期没有选课教学班" />
|
||||
<el-empty v-if="!tasks.length" description="当前学期没有授课教学班" />
|
||||
</aside>
|
||||
|
||||
<main class="roster-detail" v-loading="detailLoading">
|
||||
<el-empty v-if="!selectedOffering" description="请选择一个教学班" />
|
||||
<el-empty v-if="!selectedTask" description="请选择一个教学班" />
|
||||
<template v-else-if="roster">
|
||||
<header class="roster-head">
|
||||
<div>
|
||||
<span>{{ roster.courseCode }} · {{ roster.taskNumber }}</span>
|
||||
<h3>{{ roster.courseName }}选课名单</h3>
|
||||
<p>已选 {{ roster.enrolledCount }} 人 / 容量 {{ roster.capacity }} 人</p>
|
||||
<h3>{{ roster.courseName }}教学班名单</h3>
|
||||
<p>共 {{ roster.studentCount }} 人 / 容量 {{ roster.capacity }} 人</p>
|
||||
</div>
|
||||
<el-button :icon="Download" @click="exportRoster">导出 Excel</el-button>
|
||||
</header>
|
||||
@@ -118,8 +118,8 @@ onMounted(async () => {
|
||||
<el-table-column prop="name" label="姓名" min-width="100" />
|
||||
<el-table-column prop="className" label="班级" min-width="130" />
|
||||
<el-table-column prop="majorName" label="专业" min-width="150" />
|
||||
<el-table-column label="选课时间" width="170">
|
||||
<template #default="{ row }">{{ new Date(row.enrolledAt).toLocaleString('zh-CN') }}</template>
|
||||
<el-table-column label="名单来源" width="210">
|
||||
<template #default="{ row }">{{ row.enrolledAt ? `选课 · ${new Date(row.enrolledAt).toLocaleString('zh-CN')}` : '行政班关联' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user