实验排课
This commit is contained in:
@@ -18,6 +18,7 @@ const termId = ref('')
|
||||
const projects = ref<any[]>([])
|
||||
const options = reactive({
|
||||
tasks: [] as any[],
|
||||
scheduleEntries: [] as any[],
|
||||
classrooms: [] as any[],
|
||||
periods: [] as any[],
|
||||
})
|
||||
@@ -28,6 +29,7 @@ const projectDialog = ref(false)
|
||||
const editingProjectId = ref('')
|
||||
const projectForm = reactive({
|
||||
teachingTaskIds: [] as string[],
|
||||
scheduleEntryId: '',
|
||||
code: '',
|
||||
name: '',
|
||||
arrangementMode: 'Centralized',
|
||||
@@ -59,8 +61,12 @@ const participantsLoading = ref(false)
|
||||
const selectedTask = computed(() =>
|
||||
options.tasks.find((task) => task.id === projectForm.teachingTaskIds[0]),
|
||||
)
|
||||
const selectedScheduleEntry = computed(() =>
|
||||
options.scheduleEntries.find((entry) => entry.id === projectForm.scheduleEntryId),
|
||||
)
|
||||
const batchProjectOptions = computed(() =>
|
||||
projects.value.filter((project) => project.status !== 'Closed'),
|
||||
projects.value.filter((project) =>
|
||||
project.status !== 'Closed' && project.arrangementMode === 'SelfScheduled'),
|
||||
)
|
||||
const activePeriods = computed(() =>
|
||||
options.periods.filter((period) =>
|
||||
@@ -90,6 +96,7 @@ function resetProjectForm() {
|
||||
editingProjectId.value = ''
|
||||
Object.assign(projectForm, {
|
||||
teachingTaskIds: [],
|
||||
scheduleEntryId: '',
|
||||
code: '',
|
||||
name: '',
|
||||
arrangementMode: 'Centralized',
|
||||
@@ -105,6 +112,13 @@ function onTaskChange() {
|
||||
projectForm.dates = [task.termStartDate, task.termEndDate]
|
||||
}
|
||||
|
||||
function onScheduleEntryChange() {
|
||||
const entry = selectedScheduleEntry.value
|
||||
if (!entry) return
|
||||
projectForm.teachingTaskIds = [entry.teachingTaskId]
|
||||
onTaskChange()
|
||||
}
|
||||
|
||||
function openCreateProject() {
|
||||
resetProjectForm()
|
||||
projectDialog.value = true
|
||||
@@ -114,6 +128,7 @@ function openEditProject(project: any) {
|
||||
editingProjectId.value = project.id
|
||||
Object.assign(projectForm, {
|
||||
teachingTaskIds: [project.teachingTaskId],
|
||||
scheduleEntryId: project.scheduleEntryId ?? '',
|
||||
code: project.code,
|
||||
name: project.name,
|
||||
arrangementMode: project.arrangementMode,
|
||||
@@ -125,13 +140,18 @@ function openEditProject(project: any) {
|
||||
}
|
||||
|
||||
async function saveProject() {
|
||||
if (!projectForm.teachingTaskIds.length || !projectForm.code.trim()
|
||||
if (!projectForm.teachingTaskIds.length ||
|
||||
(projectForm.arrangementMode === 'Centralized' && !projectForm.scheduleEntryId) ||
|
||||
!projectForm.code.trim()
|
||||
|| !projectForm.name.trim() || projectForm.dates.length !== 2) {
|
||||
ElMessage.warning('请填写教学任务、项目编码、名称和开放日期')
|
||||
ElMessage.warning(projectForm.arrangementMode === 'Centralized'
|
||||
? '请选择课表实验课,并填写项目编码、名称和开放日期'
|
||||
: '请填写教学任务、项目编码、名称和开放日期')
|
||||
return
|
||||
}
|
||||
const payload = {
|
||||
teachingTaskId: projectForm.teachingTaskIds[0],
|
||||
scheduleEntryId: projectForm.scheduleEntryId || null,
|
||||
code: projectForm.code,
|
||||
name: projectForm.name,
|
||||
arrangementMode: projectForm.arrangementMode,
|
||||
@@ -144,6 +164,9 @@ async function saveProject() {
|
||||
if (editingProjectId.value) {
|
||||
await http.put(`/experiments/${editingProjectId.value}`, payload)
|
||||
ElMessage.success('实验项目已更新')
|
||||
} else if (projectForm.arrangementMode === 'Centralized') {
|
||||
await http.post('/experiments', payload)
|
||||
ElMessage.success('实验项目已绑定课表实验课')
|
||||
} else {
|
||||
await http.post('/experiments/batch', {
|
||||
...payload,
|
||||
@@ -404,6 +427,7 @@ async function loadOptions() {
|
||||
params: { academicTermId: termId.value || undefined },
|
||||
})
|
||||
options.tasks = data.tasks
|
||||
options.scheduleEntries = data.scheduleEntries
|
||||
options.classrooms = data.classrooms
|
||||
options.periods = data.periods
|
||||
} catch (error) {
|
||||
@@ -462,11 +486,11 @@ onMounted(async () => {
|
||||
<span class="section-kicker">LABORATORY OPERATIONS</span>
|
||||
<h2>{{ isStudent ? '我的实验' : '实验管理' }}</h2>
|
||||
<p v-if="isStudent">查看统一安排的实验课次,或为规定实验项目选择适合自己的开放时段。</p>
|
||||
<p v-else>把实验项目分成两条运行轨道:集中排入固定课次,或开放场次供学生自主预约。</p>
|
||||
<p v-else>集中实验直接绑定已发布课表;仅自主预约实验需要在此开放场次。</p>
|
||||
</div>
|
||||
<div class="intro-actions">
|
||||
<el-button v-if="!isStudent" :icon="Calendar" @click="openBatchSession">
|
||||
批量排课
|
||||
批量开放场次
|
||||
</el-button>
|
||||
<el-button v-if="!isStudent" type="primary" :icon="Plus" @click="openCreateProject">
|
||||
批量设置实验任务
|
||||
@@ -563,7 +587,7 @@ onMounted(async () => {
|
||||
<b>{{ modeMeta[project.arrangementMode].note }}</b>
|
||||
</div>
|
||||
<el-button
|
||||
v-if="!isStudent && project.status !== 'Closed'"
|
||||
v-if="!isStudent && project.status !== 'Closed' && project.arrangementMode === 'SelfScheduled'"
|
||||
size="small"
|
||||
:icon="Calendar"
|
||||
@click="openSession(project)"
|
||||
@@ -572,7 +596,17 @@ onMounted(async () => {
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="project.sessions.length" class="session-grid">
|
||||
<div v-if="project.arrangementMode === 'Centralized' && project.scheduleEntry" class="session-grid">
|
||||
<article class="session-ticket">
|
||||
<div class="ticket-date"><strong>课表</strong><span>固定</span></div>
|
||||
<div class="ticket-body">
|
||||
<b>星期 {{ project.scheduleEntry.dayOfWeek }} · 第 {{ project.scheduleEntry.startPeriod }}–{{ project.scheduleEntry.startPeriod + project.scheduleEntry.periodCount - 1 }} 节</b>
|
||||
<span>第 {{ project.scheduleEntry.startWeek }}–{{ project.scheduleEntry.endWeek }} 周 · {{ project.scheduleEntry.campusName }} · {{ project.scheduleEntry.buildingName }} {{ project.scheduleEntry.classroomName }}</span>
|
||||
</div>
|
||||
<div class="ticket-action"><el-tag type="primary" size="small" effect="plain">课表已安排</el-tag></div>
|
||||
</article>
|
||||
</div>
|
||||
<div v-else-if="project.sessions.length" class="session-grid">
|
||||
<article
|
||||
v-for="session in project.sessions"
|
||||
:key="session.id"
|
||||
@@ -653,7 +687,7 @@ onMounted(async () => {
|
||||
<el-empty
|
||||
v-else
|
||||
:image-size="58"
|
||||
:description="project.status === 'Draft' ? '尚未安排场次,安排后才能发布' : '暂无有效实验场次'"
|
||||
:description="project.arrangementMode === 'Centralized' ? '尚未绑定已发布课表中的实验课' : (project.status === 'Draft' ? '尚未开放场次,安排后才能发布' : '暂无有效实验场次')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -664,7 +698,9 @@ onMounted(async () => {
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:disabled="!project.sessions.some((item: any) => item.status !== 'Cancelled')"
|
||||
:disabled="project.arrangementMode === 'Centralized'
|
||||
? !project.scheduleEntry
|
||||
: !project.sessions.some((item: any) => item.status !== 'Cancelled')"
|
||||
@click="publishProject(project)"
|
||||
>
|
||||
发布给学生
|
||||
@@ -720,11 +756,22 @@ onMounted(async () => {
|
||||
<el-form label-position="top" class="experiment-form">
|
||||
<div class="form-section">
|
||||
<header><span>PROJECT</span><b>规定实验项目</b></header>
|
||||
<el-form-item :label="editingProjectId ? '所属教学任务' : '适用教学任务(可多选)'" required>
|
||||
<el-form-item v-if="projectForm.arrangementMode === 'Centralized'" label="已发布课表中的实验课" required>
|
||||
<el-select v-model="projectForm.scheduleEntryId" filterable :disabled="!!editingProjectId" placeholder="选择已安排实验室的实验课" @change="onScheduleEntryChange">
|
||||
<el-option
|
||||
v-for="entry in options.scheduleEntries"
|
||||
:key="entry.id"
|
||||
:label="`${entry.courseCode} · ${entry.courseName} · ${entry.taskNumber} · 星期 ${entry.dayOfWeek} 第 ${entry.startPeriod}–${entry.startPeriod + entry.periodCount - 1} 节 · ${entry.campusName} ${entry.buildingName} ${entry.classroomName}`"
|
||||
:value="entry.id"
|
||||
/>
|
||||
</el-select>
|
||||
<small class="form-help">集中实验复用课表的时间和实验室,不会再生成独立实验场次。</small>
|
||||
</el-form-item>
|
||||
<el-form-item v-else :label="editingProjectId ? '所属教学任务' : '适用教学任务(可多选)'" required>
|
||||
<el-select
|
||||
v-model="projectForm.teachingTaskIds"
|
||||
filterable
|
||||
multiple
|
||||
:multiple="!editingProjectId"
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
:disabled="!!editingProjectId"
|
||||
@@ -757,9 +804,9 @@ onMounted(async () => {
|
||||
|
||||
<div class="form-section">
|
||||
<header><span>ROUTE</span><b>选择运行轨道</b></header>
|
||||
<el-radio-group v-model="projectForm.arrangementMode" class="mode-choice">
|
||||
<el-radio-group v-model="projectForm.arrangementMode" class="mode-choice" :disabled="!!editingProjectId">
|
||||
<el-radio-button value="Centralized">
|
||||
<b>集中安排</b><small>统一时间,像上课一样到场</small>
|
||||
<b>集中安排</b><small>复用已发布课表的实验课</small>
|
||||
</el-radio-button>
|
||||
<el-radio-button value="SelfScheduled">
|
||||
<b>自行安排</b><small>开放多个场次,学生自主预约</small>
|
||||
@@ -858,7 +905,7 @@ onMounted(async () => {
|
||||
<el-form-item label="实验室" required>
|
||||
<el-select v-model="sessionForm.classroomId" filterable placeholder="选择实验室或教学场所">
|
||||
<el-option
|
||||
v-for="room in options.classrooms"
|
||||
v-for="room in options.classrooms.filter((item: any) => (Number(item.teachingVenueNature) & (2 | 4 | 8 | 16)) !== 0)"
|
||||
:key="room.id"
|
||||
:label="`${room.campusName} · ${room.buildingName} ${room.name} · ${room.capacity} 人`"
|
||||
:value="room.id"
|
||||
@@ -940,7 +987,7 @@ onMounted(async () => {
|
||||
<el-input-number v-model="row.periodCount" :min="1" :max="12" controls-position="right" />
|
||||
<el-select v-model="row.classroomId" filterable placeholder="实验室">
|
||||
<el-option
|
||||
v-for="room in options.classrooms"
|
||||
v-for="room in options.classrooms.filter((item: any) => (Number(item.teachingVenueNature) & (2 | 4 | 8 | 16)) !== 0)"
|
||||
:key="room.id"
|
||||
:label="`${room.campusName} · ${room.buildingName} ${room.name} · ${room.capacity} 人`"
|
||||
:value="room.id"
|
||||
|
||||
Reference in New Issue
Block a user