实验视图

This commit is contained in:
2026-08-10 19:04:29 +08:00 Unverified
parent a942fe34ca
commit 04c693a981
3 changed files with 120 additions and 2 deletions
@@ -94,13 +94,18 @@ public sealed class ExperimentGradesController(
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
var projects = await source var projects = await source
.Where(x => taskIds.Contains(x.TeachingTaskId)) .Where(x => taskIds.Contains(x.TeachingTaskId))
.OrderBy(x => x.Code) .OrderBy(x => x.ScheduleWeek ?? int.MaxValue)
.ThenBy(x => x.ScheduleEntry == null ? int.MaxValue : x.ScheduleEntry.DayOfWeek)
.ThenBy(x => x.ScheduleEntry == null ? int.MaxValue : x.ScheduleEntry.StartPeriod)
.ThenBy(x => x.StartDate)
.ThenBy(x => x.Code)
.Select(x => new .Select(x => new
{ {
x.Id, x.Id,
x.Code, x.Code,
x.Name, x.Name,
x.ArrangementMode, x.ArrangementMode,
x.ScheduleWeek,
ProjectStatus = x.Status, ProjectStatus = x.Status,
x.TeachingTaskId, x.TeachingTaskId,
x.TeachingTask!.TaskNumber, x.TeachingTask!.TaskNumber,
@@ -113,6 +118,14 @@ public sealed class ExperimentGradesController(
TeacherNames = x.TeachingTask.Teachers TeacherNames = x.TeachingTask.Teachers
.OrderByDescending(item => item.IsPrimary) .OrderByDescending(item => item.IsPrimary)
.Select(item => item.Teacher!.Name), .Select(item => item.Teacher!.Name),
ScheduleEntry = x.ScheduleEntry == null
? null
: new
{
x.ScheduleEntry.DayOfWeek,
x.ScheduleEntry.StartPeriod,
x.ScheduleEntry.PeriodCount
},
Sheet = x.GradeSheet == null Sheet = x.GradeSheet == null
? null ? null
: new : new
@@ -356,6 +369,15 @@ public sealed class ExperimentGradesController(
ProjectCode = x.ExperimentProject!.Code, ProjectCode = x.ExperimentProject!.Code,
ProjectName = x.ExperimentProject.Name, ProjectName = x.ExperimentProject.Name,
x.ExperimentProject.ArrangementMode, x.ExperimentProject.ArrangementMode,
x.ExperimentProject.ScheduleWeek,
ScheduleEntry = x.ExperimentProject.ScheduleEntry == null
? null
: new
{
x.ExperimentProject.ScheduleEntry.DayOfWeek,
x.ExperimentProject.ScheduleEntry.StartPeriod,
x.ExperimentProject.ScheduleEntry.PeriodCount
},
x.ExperimentProject.TeachingTaskId, x.ExperimentProject.TeachingTaskId,
x.ExperimentProject.TeachingTask!.TaskNumber, x.ExperimentProject.TeachingTask!.TaskNumber,
TaskName = x.ExperimentProject.TeachingTask.Name, TaskName = x.ExperimentProject.TeachingTask.Name,
@@ -215,6 +215,89 @@ public sealed class ExperimentGradesControllerTests
Assert.Equal(1, Property<int>(filtered.Value, "Total")); Assert.Equal(1, Property<int>(filtered.Value, "Total"));
} }
[Fact]
public async Task ManagementList_OrdersProjectsByScheduledWeekAndPeriod()
{
await using var fixture = await ExperimentGradeFixture.CreateAsync();
var plan = new SchedulePlan
{
AcademicTermId = fixture.Term.Id,
Name = "实验课表",
Version = "v1",
Status = SchedulePlanStatus.Published
};
var laterEntry = new ScheduleEntry
{
SchedulePlanId = plan.Id,
TeachingTaskId = fixture.Task.Id,
ClassroomId = fixture.Classroom.Id,
Kind = ScheduleEntryKind.Experiment,
DayOfWeek = 4,
StartPeriod = 5,
PeriodCount = 2,
StartWeek = 5,
EndWeek = 5,
WeekPattern = WeekPattern.All
};
var earlierEntry = new ScheduleEntry
{
SchedulePlanId = plan.Id,
TeachingTaskId = fixture.Task.Id,
ClassroomId = fixture.Classroom.Id,
Kind = ScheduleEntryKind.Experiment,
DayOfWeek = 2,
StartPeriod = 3,
PeriodCount = 2,
StartWeek = 2,
EndWeek = 2,
WeekPattern = WeekPattern.All
};
fixture.Db.AddRange(plan, laterEntry, earlierEntry);
fixture.Db.AddRange(
new ExperimentProject
{
TeachingTaskId = fixture.Task.Id,
ScheduleEntryId = laterEntry.Id,
ScheduleWeek = 5,
Code = "LAB-A",
Name = "后面的实验",
ArrangementMode = ExperimentArrangementMode.Centralized,
StartDate = fixture.Term.StartDate,
EndDate = fixture.Term.EndDate,
Status = ExperimentProjectStatus.Published
},
new ExperimentProject
{
TeachingTaskId = fixture.Task.Id,
ScheduleEntryId = earlierEntry.Id,
ScheduleWeek = 2,
Code = "LAB-Z",
Name = "前面的实验",
ArrangementMode = ExperimentArrangementMode.Centralized,
StartDate = fixture.Term.StartDate,
EndDate = fixture.Term.EndDate,
Status = ExperimentProjectStatus.Published
});
await fixture.Db.SaveChangesAsync();
fixture.Db.ChangeTracker.Clear();
var result = Assert.IsType<OkObjectResult>(await fixture.ExperimentGrades(
fixture.AdminScope).GetManagement(
fixture.Term.Id, null, null, null, 1, 10, CancellationToken.None));
var course = Assert.Single(Property<System.Collections.IEnumerable>(
result.Value, "Items").Cast<object>());
var projects = Property<System.Collections.IEnumerable>(course, "Projects")
.Cast<object>()
.ToList();
Assert.Equal(["前面的实验", "后面的实验"],
projects.Select(project => Property<string>(project, "Name")));
var schedule = Property<object>(projects[0], "ScheduleEntry");
Assert.Equal(2, Property<int>(projects[0], "ScheduleWeek"));
Assert.Equal(2, Property<int>(schedule, "DayOfWeek"));
Assert.Equal(3, Property<int>(schedule, "StartPeriod"));
}
private static T Property<T>(object? value, string name) => private static T Property<T>(object? value, string name) =>
Assert.IsAssignableFrom<T>( Assert.IsAssignableFrom<T>(
value!.GetType().GetProperty(name)!.GetValue(value)); value!.GetType().GetProperty(name)!.GetValue(value));
+14 -1
View File
@@ -90,6 +90,18 @@ const modeLabels: Record<string, string> = {
Centralized: '集中安排', Centralized: '集中安排',
SelfScheduled: '自主预约', SelfScheduled: '自主预约',
} }
const weekdayLabels = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日']
function projectScheduleLabel(project: any) {
const entry = project.scheduleEntry
if (!entry) return modeLabels[project.arrangementMode] ?? '待安排'
const week = project.scheduleWeek ?? '—'
const endPeriod = entry.startPeriod + entry.periodCount - 1
const periods = entry.periodCount === 1
? `${entry.startPeriod}`
: `${entry.startPeriod}${endPeriod}`
return `${week} 周 · ${weekdayLabels[entry.dayOfWeek] ?? `${entry.dayOfWeek}`} · ${periods}`
}
const schemeWeightTotal = computed(() => const schemeWeightTotal = computed(() =>
schemeForm.items.reduce((sum, item) => sum + Number(item.weight || 0), 0), schemeForm.items.reduce((sum, item) => sum + Number(item.weight || 0), 0),
@@ -583,7 +595,7 @@ onMounted(async () => {
<template #default="{ row }"> <template #default="{ row }">
<div class="course-project-name"> <div class="course-project-name">
<b>{{ row.code }} · {{ row.name }}</b> <b>{{ row.code }} · {{ row.name }}</b>
<small>{{ modeLabels[row.arrangementMode] }}</small> <small>{{ projectScheduleLabel(row) }}</small>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -680,6 +692,7 @@ onMounted(async () => {
<span>{{ detail.projectCode }} · {{ modeLabels[detail.arrangementMode] }}</span> <span>{{ detail.projectCode }} · {{ modeLabels[detail.arrangementMode] }}</span>
<h3>{{ detail.projectName }}</h3> <h3>{{ detail.projectName }}</h3>
<p>{{ detail.courseCode }} · {{ detail.courseName }} · {{ detail.termName }}</p> <p>{{ detail.courseCode }} · {{ detail.courseName }} · {{ detail.termName }}</p>
<p class="project-schedule">{{ projectScheduleLabel(detail) }}</p>
</div> </div>
<div class="workbench-status"> <div class="workbench-status">
<el-tag :type="statusTypes[detail.status] as any" effect="dark">{{ statusLabels[detail.status] }}</el-tag> <el-tag :type="statusTypes[detail.status] as any" effect="dark">{{ statusLabels[detail.status] }}</el-tag>