ExamEdit&Check

This commit is contained in:
SpecialX
2025-06-12 19:01:05 +08:00
parent e26881ec2f
commit b77ed0b30f
18 changed files with 729 additions and 328 deletions

View File

@@ -0,0 +1,223 @@
@using Entities.DTO
@using TechHelper.Client.Exam
@page "/exam/check/{ExamID}"
<MudText Typo="Typo.h4" Class="mb-4">试卷批改预览: @ExamDto.AssignmentTitle</MudText>
<MudDivider Class="my-4" />
@if (_isLoading)
{
<MudProgressCircular Indeterminate="true" Color="Color.Primary" Class="d-flex justify-center my-8" />
<MudText Class="text-center">正在加载试卷和学生数据...</MudText>
}
else if (_questionsForTable.Any() && _students.Any())
{
<MudTable @ref="_table" T="QuestionRowData" Items="@_questionsForTable" Hover="true" Breakpoint="Breakpoint.Sm" Class="mud-elevation-2" Dense="true">
<HeaderContent>
<MudTh Style="width:100px;">序号</MudTh>
<MudTh Style="width:80px; text-align:center;">分值</MudTh>
@foreach (var student in _students)
{
<MudTh Style="width:120px; text-align:center;">
@student.Name
<MudTooltip Text="点击以切换此学生所有题目的对错">
<MudIconButton Icon="@Icons.Material.Filled.Info" Size="Size.Small" Class="ml-1"
@onclick="() => ToggleStudentAllAnswers(student.Id)" />
</MudTooltip>
</MudTh>
}
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="序号">@context.QuestionItem.Sequence</MudTd>
<MudTd DataLabel="分值" Style="text-align:center;">@context.QuestionItem.Score</MudTd>
@foreach (var student in _students)
{
<MudTd DataLabel="@student.Name" Style="text-align:center;">
@if (context.StudentAnswers.ContainsKey(student.Id))
{
<MudCheckBox @bind-Value="context.StudentAnswers[student.Id]" Size="Size.Small" Color="Color.Primary"></MudCheckBox>
}
else
{
<MudText Color="Color.Warning">N/A</MudText>
}
</MudTd>
}
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
<MudPaper Class="pa-4 mt-4 mud-elevation-2 d-flex flex-column align-end">
<MudText Typo="Typo.h6">学生总分预览:</MudText>
@foreach (var student in _students)
{
<MudText Typo="Typo.subtitle1">
@student.Name: <MudText Typo="Typo.h5" Color="Color.Primary" Class="d-inline-block ml-2">@GetStudentTotalScore(student.Id)</MudText>
</MudText>
}
<MudButton Variant="Variant.Filled" Color="Color.Success" Class="mt-4" @onclick="SubmitGrading">
提交批改结果 (模拟)
</MudButton>
</MudPaper>
}
else
{
<MudAlert Severity="Severity.Info" Class="mt-4">无法加载试卷或题目信息。</MudAlert>
<MudButton Variant="Variant.Text" Color="Color.Primary" Class="mt-4" >返回试卷列表</MudButton>
}
@code {
[Parameter]
public string ExamId { get; set; } // 从路由获取的试卷ID
[Inject]
public IExamService ExamService { get; set; } // 注入试卷服务
[Inject]
private ISnackbar Snackbar { get; set; } // 注入 Snackbar 用于消息提示
[Inject]
private NavigationManager Navigation { get; set; } // 注入导航管理器
private MudTable<QuestionRowData> _table = new(); // MudTable 实例引用
private ExamDto ExamDto { get; set; } = new ExamDto(); // 原始试卷数据
private ExamStruct _examStruct = new ExamStruct(); // 处理后的试卷结构,包含带序号的题目
private List<Student> _students = new List<Student>(); // 临时生成的学生列表
private List<QuestionRowData> _questionsForTable = new List<QuestionRowData>(); // 用于 MudTable 的数据源
private bool _isLoading = true; // 加载状态
// 在组件初始化时加载数据
protected override async Task OnInitializedAsync()
{
_isLoading = true;
await LoadExamData();
GenerateTemporaryStudentsAndAnswers(); // 生成学生和初始作答数据
_isLoading = false;
}
// 加载试卷数据的方法
private async Task LoadExamData()
{
if (Guid.TryParse(ExamId, out Guid parsedExamId))
{
try
{
var result = await ExamService.GetExam(parsedExamId);
if (result.Status)
{
ExamDto = result.Result as ExamDto ?? new ExamDto();
_examStruct = ExamDto.GetStruct(); // 将 ExamDto 转换为 ExamStruct
}
else
{
Snackbar?.Add($"获取试卷失败: {result.Message}", Severity.Error);
Navigation.NavigateTo("/exam/manager"); // 导航回管理页
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"获取试卷时发生错误: {ex.Message}");
Snackbar?.Add($"获取试卷失败: {ex.Message}", Severity.Error);
Navigation.NavigateTo("/exam/manager");
}
}
else
{
Console.Error.WriteLine($"错误:路由参数 ExamId '{ExamId}' 不是一个有效的 GUID 格式。");
Snackbar?.Add("无效的试卷ID无法加载。", Severity.Error);
Navigation.NavigateTo("/exam/manager");
}
}
// 生成临时学生和作答数据
private void GenerateTemporaryStudentsAndAnswers()
{
_students = new List<Student>();
// 生成 40 个学生
for (int i = 1; i <= 40; i++)
{
_students.Add(new Student { Name = $"学生{i}" });
}
_questionsForTable = _examStruct.Questions.Select(qItem =>
{
var rowData = new QuestionRowData
{
QuestionItem = qItem,
StudentAnswers = new Dictionary<Guid, bool>()
};
// 为每个学生随机生成初始的对错状态
var random = new Random();
foreach (var student in _students)
{
// 模拟随机对错50%的概率
rowData.StudentAnswers[student.Id] = random.Next(0, 2) == 1;
}
return rowData;
}).ToList();
}
// 当某个学生的某个题目的作答状态改变时触发
private void OnAnswerChanged(string questionSequence, Guid studentId, bool isCorrect)
{
// 可以在这里添加额外的逻辑,例如记录更改
Console.WriteLine($"题目 {questionSequence}, 学生 {studentId} 的答案变为: {isCorrect}");
// 由于是 @bind-Checked数据模型已经自动更新这里只是日志
}
// 计算某个学生的总分
private float GetStudentTotalScore(Guid studentId)
{
float totalScore = 0;
foreach (var row in _questionsForTable)
{
if (row.StudentAnswers.TryGetValue(studentId, out bool isCorrect) && isCorrect)
{
totalScore += row.QuestionItem.Score;
}
}
return totalScore;
}
// 切换某个学生所有题目的对错状态 (用于快速批改)
private void ToggleStudentAllAnswers(Guid studentId)
{
bool allCorrect = _questionsForTable.All(row => row.StudentAnswers.ContainsKey(studentId) && row.StudentAnswers[studentId]);
foreach (var row in _questionsForTable)
{
if (row.StudentAnswers.ContainsKey(studentId))
{
row.StudentAnswers[studentId] = !allCorrect; // 全部取反
}
}
StateHasChanged(); // 手动通知 Blazor 刷新 UI
}
// 提交批改结果(模拟)
private void SubmitGrading()
{
Console.WriteLine("--- 提交批改结果 ---");
foreach (var student in _students)
{
Console.WriteLine($"学生: {student.Name}, 总分: {GetStudentTotalScore(student.Id)}");
foreach (var row in _questionsForTable)
{
if (row.StudentAnswers.TryGetValue(student.Id, out bool isCorrect))
{
Console.WriteLine($" - 题目 {row.QuestionItem.Sequence}: {(isCorrect ? "正确" : "错误")}");
}
}
}
Snackbar?.Add("批改结果已提交(模拟)", Severity.Success);
// 实际应用中,这里会将 _questionsForTable 和 _students 的数据发送到后端API
}
}

View File

@@ -1,15 +1,45 @@
@page "/exam/edit/{ExamId:Guid}"
@page "/exam/edit/{ExamId}"
@using Entities.DTO
@using TechHelper.Client.Exam
<ExamView ParsedExam="@ExamDto"/>
@code {
[Parameter]
public Guid ExamId { get; set; }
public string ExamId { get; set; }
[Inject]
public IExamService ExamService { get; set; }
[Inject]
private NavigationManager Navigation { get; set; }
[Inject]
private ISnackbar Snackbar { get; set; }
private ExamDto ExamDto { get; set; }
protected override async Task OnInitializedAsync()
{
await ExamService.GetExam(Guid.NewGuid());
if (Guid.TryParse(ExamId, out Guid parsedExamId))
{
Console.WriteLine($"ExamId 字符串成功解析为 Guid: {parsedExamId}");
try
{
var result = await ExamService.GetExam(parsedExamId);
if (result.Status) ExamDto = result.Result as ExamDto ?? new ExamDto();
}
catch (Exception ex)
{
Snackbar?.Add($"获取试卷失败: {ex.Message}", Severity.Error);
}
}
else
{
Console.Error.WriteLine($"错误:路由参数 ExamId '{ExamId}' 不是一个有效的 GUID 格式。无法获取试卷信息。");
Navigation.NavigateTo("/exam/manager");
Snackbar?.Add("无效的试卷ID无法加载。", Severity.Error);
}
}
}
}

View File

@@ -38,7 +38,7 @@
@code {
[Parameter]
public List<QuestionGroupDto> MajorQGList { get; set; }
public List<QuestionGroupDto> MajorQGList { get; set; } = new List<QuestionGroupDto>();
[Parameter]
public string Class { get; set; } = "my-2 pa-1";

View File

@@ -16,11 +16,12 @@ else
}
@foreach (var item in examDtos)
{
<ExamPreview examDto="item"> </ExamPreview>
}
<MudPaper Class="d-flex flex-wrap flex-grow-0 gap-4" Height="100%" Width="100%">
@foreach (var item in examDtos)
{
<ExamPreview examDto="item" Width="256px" Height="256px"> </ExamPreview>
}
</MudPaper>
@code {
[Inject]

View File

@@ -1,18 +1,25 @@
@using Entities.DTO
<MudPaper Width="@Width" Height="@Height" @onclick="ExamClick">
<MudCard>
<MudCardHeader>
<MudText> @examDto.AssignmentTitle </MudText>
</MudCardHeader>
<MudPaper Class="overflow-hidden " Style="background-color:pink" Width="@Width" Height="@Height" MaxHeight="@MaxHeight" MaxWidth="@MaxWidth">
<MudPaper Class="d-flex flex-column flex-grow-1 justify-content-between" Height="100%" Style="background-color:green">
<MudCardContent>
<MudText> @examDto.Description </MudText>
</MudCardContent>
</MudCard>
<MudText Typo="Typo.body2"> @examDto.AssignmentTitle </MudText>
<MudPaper>
<MudButtonGroup Size="Size.Small" Color="Color.Primary" Variant="Variant.Filled">
<MudButton OnClick="ExamClick"> 详情 </MudButton>
<MudIconButton Icon="@Icons.Material.Filled.Delete" aria-label="delete" />
<MudIconButton Icon="@Icons.Custom.Brands.GitHub" OnClick="CheckExam" Color="Color.Primary" aria-label="github" />
<MudIconButton Icon="@Icons.Material.Filled.Favorite" Color="Color.Secondary" aria-label="add to favorite" />
</MudButtonGroup>
</MudPaper>
</MudPaper>
</MudPaper>
@code {
[Inject]
@@ -23,15 +30,28 @@
[Parameter]
public string? Width { get; set; } = "200";
public string? Width { get; set; } = "256";
[Parameter]
public string? Height { get; set; } = "400";
public string? Height { get; set; } = "64";
[Parameter]
public string? MaxWidth { get; set; } = "256";
[Parameter]
public string? MaxHeight { get; set; } = "64";
private void ExamClick()
{
navigationManager.NavigateTo($"exam/Edit/{examDto.AssignmentId}");
navigationManager.NavigateTo($"exam/edit/{examDto.AssignmentId}");
}
private void CheckExam()
{
navigationManager.NavigateTo($"exam/check/{examDto.AssignmentId}");
}
}

View File

@@ -0,0 +1,16 @@
.hover-effect-paper {
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out; /* 添加平滑过渡效果 */
cursor: pointer;
}
.hover-effect-paper:hover {
transform: translateY(-5px) scale(1.02); /* 向上轻微移动并放大 */
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); /* 增加阴影,使其看起来浮起 */
/* 您还可以尝试改变背景色,例如: */
/* background-color: var(--mud-palette-action-hover); */
}
.test{
background-color: gray;
}

View File

@@ -1,12 +1,25 @@
@using Entities.DTO
@using TechHelper.Client.Exam
<MudPaper Height="@Height" Class="@Class" Style="@Style" Width="@Width">
<MudText Class="d-flex justify-content-center" Typo="Typo.h6"> @ParsedExam.AssignmentTitle </MudText>
<MudText Typo="Typo.body1"> @ParsedExam.Description </MudText>
<ExamGroupView MajorQGList="@ParsedExam.QuestionGroups.SubQuestionGroups" Elevation="1" Class="ma-0 pa-2" />
</MudPaper>
@if (ParsedExam != null)
{
<MudPaper Height="@Height" Class="@Class" Style="@Style" Width="@Width">
<MudText Class="d-flex justify-content-center" Typo="Typo.h6"> @ParsedExam.AssignmentTitle </MudText>
<MudText Typo="Typo.body1"> @ParsedExam.Description </MudText>
<ExamGroupView MajorQGList="@ParsedExam.QuestionGroups.SubQuestionGroups" Elevation="1" Class="ma-0 pa-2" />
</MudPaper>
}
else
{
<MudPaper Class="justify-content-center">
<MudProgressCircular Indeterminate="true" Color="Color.Primary" />
<MudText Class="ml-4">加载试卷中...</MudText>
</MudPaper>
}
@code {

View File

@@ -26,7 +26,7 @@
@code {
[Parameter]
public SubQuestionDto Question { get; set; }
public SubQuestionDto Question { get; set; } = new SubQuestionDto();
[Parameter]
public string Class { get; set; }