布置作业、提交与自动评分
- 作业:老师选实验、起点(界面已做好 / 空白工程)、截止时间、每项分值(自动评分项 + 老师评分项); 起点工程由设计器引擎从参考答案生成(去掉事件绑定和事件处理方法),布置时保存起点和评分规则快照 - 提交:后台排队评分(全站同时最多评 3 份、跑 6 个检查项),重启后继续评;评分中不能再交,截止后不能交 - 学生:"我的作业"列表;实验页的作业模式(作业名、截止时间、提交、得分),评分结果弹出逐项显示 - 老师:班级管理页加"作业"标签页,布置、修改、删除作业,查看全班提交情况;改分值后重新算分 - 串口列表为空时提示在 Load 或 Click 事件里填充 - 测试:作业接口(可见范围、排队评分、部分得分、改分值、截止、删除);10 个示例两种起点都能编译 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
37fc747b49
commit
4e6b0d5cb2
23 files changed
+1439
-50
No files matched your search
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
// 一个班的作业列表:布置、修改、删除作业,查看提交情况。
|
||||
import { h, ref, watch } from 'vue'
|
||||
import { NButton, NDataTable, NEmpty, useDialog, useMessage, type DataTableColumns } from 'naive-ui'
|
||||
import type { Assignment } from '@iot/shared'
|
||||
import { api } from '../api'
|
||||
import AssignmentDialog from './AssignmentDialog.vue'
|
||||
import SubmissionsDialog from './SubmissionsDialog.vue'
|
||||
|
||||
type Row = Assignment & { studentCount: number; submittedCount: number }
|
||||
|
||||
const props = defineProps<{ classId: number }>()
|
||||
const message = useMessage()
|
||||
const dialog = useDialog()
|
||||
|
||||
const rows = ref<Row[]>([])
|
||||
const editorOpen = ref(false)
|
||||
const editing = ref<Assignment | null>(null)
|
||||
const submissionsOpen = ref(false)
|
||||
const viewing = ref<Assignment | null>(null)
|
||||
|
||||
async function load() {
|
||||
rows.value = await api<Row[]>('GET', `/api/teacher/classes/${props.classId}/assignments`).catch((e: Error) => {
|
||||
message.error(e.message)
|
||||
return []
|
||||
})
|
||||
}
|
||||
watch(() => props.classId, load, { immediate: true })
|
||||
|
||||
function create() {
|
||||
editing.value = null
|
||||
editorOpen.value = true
|
||||
}
|
||||
|
||||
function edit(row: Row) {
|
||||
editing.value = row
|
||||
editorOpen.value = true
|
||||
}
|
||||
|
||||
function remove(row: Row) {
|
||||
dialog.warning({
|
||||
title: '删除作业',
|
||||
content: `将删除"${row.title}"和全班的 ${row.submittedCount} 份提交,不能恢复。确定删除吗?`,
|
||||
positiveText: '删除',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
await api('DELETE', `/api/teacher/assignments/${row.id}`).catch((e: Error) => message.error(e.message))
|
||||
await load()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function showSubmissions(row: Row) {
|
||||
viewing.value = row
|
||||
submissionsOpen.value = true
|
||||
}
|
||||
|
||||
const formatTime = (ms: number) =>
|
||||
new Date(ms).toLocaleString('zh-CN', { month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' })
|
||||
|
||||
const columns: DataTableColumns<Row> = [
|
||||
{ title: '作业', key: 'title' },
|
||||
{ title: '起点', key: 'startMode', width: 110, render: (r) => (r.startMode === 'ui' ? '界面已做好' : '空白工程') },
|
||||
{ title: '截止时间', key: 'deadline', width: 130, render: (r) => (r.deadline ? formatTime(r.deadline) : '不限') },
|
||||
{
|
||||
title: '已提交',
|
||||
key: 'submitted',
|
||||
width: 120,
|
||||
render: (r) => h(NButton, { text: true, type: 'primary', onClick: () => showSubmissions(r) }, () => `${r.submittedCount} / ${r.studentCount} 人`),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 130,
|
||||
render: (r) =>
|
||||
h('div', { class: 'row-actions' }, [
|
||||
h(NButton, { size: 'tiny', onClick: () => edit(r) }, () => '修改'),
|
||||
h(NButton, { size: 'tiny', type: 'error', quaternary: true, onClick: () => remove(r) }, () => '删除'),
|
||||
]),
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="panel">
|
||||
<div class="head">
|
||||
<NButton size="small" type="primary" @click="create">布置作业</NButton>
|
||||
</div>
|
||||
<NDataTable v-if="rows.length" :columns="columns" :data="rows" :row-key="(r: Row) => r.id" size="small" :bordered="false" />
|
||||
<NEmpty v-else description="还没有布置作业" class="empty" />
|
||||
<AssignmentDialog v-model:show="editorOpen" :class-id="classId" :editing="editing" @saved="load" />
|
||||
<SubmissionsDialog v-model:show="submissionsOpen" :assignment="viewing" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.head {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
:deep(.row-actions) {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user