修复其他考试提交
This commit is contained in:
@@ -699,7 +699,7 @@ public sealed class GradesController(
|
||||
var itemNames = sheet.Items.Select(i => i.Name).ToList();
|
||||
var headers = new List<string> { "学号", "姓名", "班级", "平时成绩" };
|
||||
headers.AddRange(itemNames);
|
||||
headers.AddRange(["期末成绩", "考试状态", "备注"]);
|
||||
headers.AddRange(["总分(自动计算)", "期末成绩", "考试状态", "备注"]);
|
||||
|
||||
var rows = sheet.Records.Select(record =>
|
||||
{
|
||||
@@ -716,6 +716,7 @@ public sealed class GradesController(
|
||||
.FirstOrDefault(s => s.GradeItemId == item.Id)?.Score;
|
||||
values.Add(score);
|
||||
}
|
||||
values.Add(null);
|
||||
values.Add(record.FinalScore);
|
||||
values.Add(record.ExamStatus == GradeExamStatus.Normal ? "正常" :
|
||||
record.ExamStatus == GradeExamStatus.Absent ? "缺考" :
|
||||
@@ -729,13 +730,56 @@ public sealed class GradesController(
|
||||
{
|
||||
"请勿修改第一行列名;学号、姓名、班级列请勿修改,用于匹配学生。",
|
||||
"成绩列填写 0—100 的数值,留空表示暂未录入。",
|
||||
"总分(自动计算)列由 Excel 按各部分比例自动计算,仅供填写时预览;上传时系统不会采用该列结果。",
|
||||
"考试状态填写:正常、缺考、缓考 或 免修,留空默认为正常。",
|
||||
$"本成绩单共 {sheet.Items.Count} 个分项:{string.Join("、", itemNames)}。",
|
||||
"导入后会自动重新计算总评成绩和绩点。"
|
||||
};
|
||||
|
||||
var regularColumn = 4;
|
||||
var itemColumns = Enumerable.Range(5, sheet.Items.Count).ToArray();
|
||||
var totalColumn = regularColumn + itemColumns.Length + 1;
|
||||
var finalColumn = totalColumn + 1;
|
||||
var statusColumn = finalColumn + 1;
|
||||
var weightedColumns = new List<(int Column, decimal Weight)> { (regularColumn, sheet.RegularWeight) };
|
||||
weightedColumns.AddRange(sheet.Items.Select((item, index) =>
|
||||
(itemColumns[index], item.Weight)));
|
||||
weightedColumns.Add((finalColumn, sheet.FinalWeight));
|
||||
var requiredColumns = weightedColumns.Where(x => x.Weight > 0).ToArray();
|
||||
var scoreColumns = weightedColumns.Select(x => x.Column)
|
||||
.Append(totalColumn)
|
||||
.ToArray();
|
||||
|
||||
var bytes = ExcelWorkbookHelper.Create(
|
||||
"成绩导入", headers, rows, instructions);
|
||||
"成绩导入", headers, rows, instructions,
|
||||
(worksheet, rowNumber) =>
|
||||
{
|
||||
var componentReferences = requiredColumns
|
||||
.Select(x => $"{ColumnLetter(x.Column)}{rowNumber}")
|
||||
.ToArray();
|
||||
var weightedExpression = string.Join("+", weightedColumns.Select(x =>
|
||||
$"{ColumnLetter(x.Column)}{rowNumber}*{x.Weight.ToString(CultureInfo.InvariantCulture)}/100"));
|
||||
var statusReference = $"{ColumnLetter(statusColumn)}{rowNumber}";
|
||||
var formula =
|
||||
$"=IF(OR({statusReference}=\"缺考\",{statusReference}=\"缓考\",{statusReference}=\"免修\"),\"\",IF(COUNT({string.Join(",", componentReferences)})={requiredColumns.Length},ROUND({weightedExpression},1),\"\"))";
|
||||
var cell = worksheet.Cell(rowNumber, totalColumn);
|
||||
cell.FormulaA1 = formula;
|
||||
cell.Style.NumberFormat.Format = "0.0";
|
||||
cell.Style.Font.Bold = true;
|
||||
cell.Style.Fill.BackgroundColor = ClosedXML.Excel.XLColor.FromHtml("#E8F1FB");
|
||||
|
||||
foreach (var scoreColumn in scoreColumns)
|
||||
{
|
||||
var conditionalFormat = worksheet
|
||||
.Range(rowNumber, scoreColumn, rowNumber, scoreColumn)
|
||||
.AddConditionalFormat();
|
||||
var failingScoreFormat = conditionalFormat.WhenLessThan(60);
|
||||
failingScoreFormat.Fill.BackgroundColor =
|
||||
ClosedXML.Excel.XLColor.FromHtml("#FDECEC");
|
||||
failingScoreFormat.Font.FontColor =
|
||||
ClosedXML.Excel.XLColor.FromHtml("#B42318");
|
||||
}
|
||||
});
|
||||
var taskName = sheet.TeachingTask!.Name;
|
||||
return File(bytes, ExcelWorkbookHelper.ContentType,
|
||||
$"成绩导入模板-{taskName}.xlsx");
|
||||
@@ -768,13 +812,15 @@ public sealed class GradesController(
|
||||
var itemNames = sheet.Items.Select(i => i.Name).ToList();
|
||||
var headers = new List<string> { "学号", "姓名", "班级", "平时成绩" };
|
||||
headers.AddRange(itemNames);
|
||||
headers.AddRange(["期末成绩", "考试状态", "备注"]);
|
||||
headers.AddRange(["总分(自动计算)", "期末成绩", "考试状态", "备注"]);
|
||||
|
||||
IReadOnlyList<ExcelRow> rows;
|
||||
try
|
||||
{
|
||||
rows = await ExcelWorkbookHelper.ReadAsync(
|
||||
file, headers, cancellationToken);
|
||||
file,
|
||||
headers.Where(x => x != "总分(自动计算)").ToArray(),
|
||||
cancellationToken);
|
||||
}
|
||||
catch (InvalidDataException exception)
|
||||
{
|
||||
@@ -810,7 +856,7 @@ public sealed class GradesController(
|
||||
var regularScore = ParseOptionalDecimal(row, "平时成绩", 0, 100, errors);
|
||||
if (errors.Count > 0 && errors[^1].Contains(row.RowNumber.ToString())) continue;
|
||||
|
||||
// Parse final score
|
||||
// Parse final score. The formula-driven total column is intentionally ignored.
|
||||
var finalScore = ParseOptionalDecimal(row, "期末成绩", 0, 100, errors);
|
||||
if (errors.Count > 0 && errors[^1].Contains(row.RowNumber.ToString())) continue;
|
||||
|
||||
@@ -1041,6 +1087,18 @@ public sealed class GradesController(
|
||||
|
||||
private static string? Normalize(string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
|
||||
private static string ColumnLetter(int column)
|
||||
{
|
||||
var result = string.Empty;
|
||||
while (column > 0)
|
||||
{
|
||||
column--;
|
||||
result = (char)('A' + column % 26) + result;
|
||||
column /= 26;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record GradeSheetRequest(
|
||||
|
||||
@@ -146,7 +146,10 @@ public sealed class OtherExamsController(AppDbContext db, ICurrentUserDataScope
|
||||
{
|
||||
var numbers = inputs.Select(x => x.StudentNumber.Trim()).ToList();
|
||||
if (numbers.Count != numbers.Distinct(StringComparer.OrdinalIgnoreCase).Count()) return ValidationProblem("同一考试批次中学生不能重复出现。");
|
||||
var students = await db.Students.Where(x => numbers.Contains(x.StudentNumber)).ToDictionaryAsync(x => x.StudentNumber, StringComparer.OrdinalIgnoreCase, ct);
|
||||
var studentRows = await db.Students
|
||||
.Where(x => numbers.Contains(x.StudentNumber))
|
||||
.ToListAsync(ct);
|
||||
var students = studentRows.ToDictionary(x => x.StudentNumber, StringComparer.OrdinalIgnoreCase);
|
||||
if (students.Count != numbers.Count) return ValidationProblem("存在不存在的学号,请先检查学生档案。");
|
||||
foreach (var item in inputs)
|
||||
{
|
||||
@@ -157,11 +160,30 @@ public sealed class OtherExamsController(AppDbContext db, ICurrentUserDataScope
|
||||
var beforeCount = await db.OtherExamResults.AsNoTracking()
|
||||
.Where(x => x.OtherExamBatchId != batch.Id && studentIds.Contains(x.StudentId) && (x.OtherExamBatch!.ExamCode == batch.ExamCode || (x.OtherExamBatch.ExamCode == null && batch.ExamCode == null && x.OtherExamBatch.Name == batch.Name)) && (x.OtherExamBatch.ExamDate < batch.ExamDate || (x.OtherExamBatch.ExamDate == batch.ExamDate && x.OtherExamBatch.CreatedAt < batch.CreatedAt)))
|
||||
.GroupBy(x => x.StudentId).Select(x => new { StudentId = x.Key, Count = x.Count() }).ToDictionaryAsync(x => x.StudentId, x => x.Count, ct);
|
||||
return await db.ExecuteInRetriableTransactionAsync<ActionResult?>(async transaction =>
|
||||
{
|
||||
db.OtherExamResults.RemoveRange(batch.Results);
|
||||
batch.Status = OtherExamBatchStatus.Draft;
|
||||
batch.Results = inputs.Select(x => { var student = students[x.StudentNumber.Trim()]; return new OtherExamResult { OtherExamBatchId = batch.Id, StudentId = student.Id, AttemptNumber = (beforeCount.GetValueOrDefault(student.Id) + 1), Score = x.Score, Level = Normalize(x.Level), IsPassed = x.IsPassed, Notes = Normalize(x.Notes) }; }).ToList();
|
||||
await db.SaveChangesAsync(ct);
|
||||
|
||||
batch.Results = inputs.Select(x =>
|
||||
{
|
||||
var student = students[x.StudentNumber.Trim()];
|
||||
return new OtherExamResult
|
||||
{
|
||||
OtherExamBatchId = batch.Id,
|
||||
StudentId = student.Id,
|
||||
AttemptNumber = beforeCount.GetValueOrDefault(student.Id) + 1,
|
||||
Score = x.Score,
|
||||
Level = Normalize(x.Level),
|
||||
IsPassed = x.IsPassed,
|
||||
Notes = Normalize(x.Notes)
|
||||
};
|
||||
}).ToList();
|
||||
await db.SaveChangesAsync(ct);
|
||||
await transaction.CommitAsync(ct);
|
||||
return null;
|
||||
}, ct);
|
||||
}
|
||||
|
||||
private static string[] HeadersFor(OtherExamBatch batch) => batch.MetricKind switch
|
||||
|
||||
@@ -11,7 +11,8 @@ public static class ExcelWorkbookHelper
|
||||
string sheetName,
|
||||
IReadOnlyList<string> headers,
|
||||
IEnumerable<IReadOnlyList<object?>> rows,
|
||||
IReadOnlyList<string>? instructions = null)
|
||||
IReadOnlyList<string>? instructions = null,
|
||||
Action<IXLWorksheet, int>? configureRow = null)
|
||||
{
|
||||
using var workbook = new XLWorkbook();
|
||||
var sheet = workbook.Worksheets.Add(sheetName);
|
||||
@@ -33,6 +34,7 @@ public static class ExcelWorkbookHelper
|
||||
{
|
||||
SetCellValue(sheet.Cell(rowNumber, column + 1), row[column]);
|
||||
}
|
||||
configureRow?.Invoke(sheet, rowNumber);
|
||||
rowNumber++;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Version>2.3.0-rc3</Version>
|
||||
<Version>2.3.1-beta.1</Version>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SpaRoot>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/../../web'))</SpaRoot>
|
||||
@@ -32,7 +32,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.10" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="10.0.7" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="10.0.9" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.17.0" />
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "web",
|
||||
"private": true,
|
||||
"version": "2.3.0-rc4",
|
||||
"version": "2.3.1-beta.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -384,6 +384,10 @@ function scoreClass(score: number | null) {
|
||||
return ''
|
||||
}
|
||||
|
||||
function isFailingScore(score: number | string | null | undefined) {
|
||||
return score != null && score !== '' && Number(score) < 60
|
||||
}
|
||||
|
||||
function itemScore(record: any, gradeItemId: string) {
|
||||
return record.itemScores?.find((itemScore: any) => itemScore.gradeItemId === gradeItemId)
|
||||
}
|
||||
@@ -473,7 +477,7 @@ onMounted(async () => {
|
||||
:style="{ left: `${record.totalScore}%` }"
|
||||
>{{ record.totalScore }}</span>
|
||||
</div>
|
||||
<div class="transcript-result">
|
||||
<div class="transcript-result" :class="{ 'is-failed': isFailingScore(record.totalScore) }">
|
||||
<span>{{ examStatusLabels[record.examStatus] }}</span>
|
||||
<b v-if="record.totalScore != null">{{ record.totalScore }}</b>
|
||||
<b v-else>—</b>
|
||||
@@ -602,12 +606,14 @@ onMounted(async () => {
|
||||
</el-table-column>
|
||||
<el-table-column :label="`平时 ${detail.regularWeight}%`" width="110">
|
||||
<template #default="{ row }">
|
||||
<div class="score-cell" :class="{ 'is-failed': isFailingScore(row.regularScore) }">
|
||||
<el-input-number
|
||||
v-if="detail.canEdit && row.examStatus === 'Normal'"
|
||||
v-model="row.regularScore"
|
||||
:min="0" :max="100" :controls="false" size="small"
|
||||
/>
|
||||
<span v-else>{{ row.regularScore ?? '—' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -617,6 +623,7 @@ onMounted(async () => {
|
||||
:width="item.sourceType === 'ExperimentSummary' ? 150 : 110"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="score-cell" :class="{ 'is-failed': isFailingScore(itemScore(row, item.id)?.score) }">
|
||||
<el-input-number
|
||||
v-if="detail.canEdit && row.examStatus === 'Normal' && item.sourceType !== 'ExperimentSummary'"
|
||||
:model-value="itemScore(row, item.id)?.score"
|
||||
@@ -627,26 +634,34 @@ onMounted(async () => {
|
||||
{{ itemScore(row, item.id)?.score ?? '—' }}
|
||||
<small v-if="item.sourceType === 'ExperimentSummary'">已锁定</small>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="`期末 ${detail.finalWeight}%`" width="110">
|
||||
<template #default="{ row }">
|
||||
<div class="score-cell" :class="{ 'is-failed': isFailingScore(row.finalScore) }">
|
||||
<el-input-number
|
||||
v-if="detail.canEdit && row.examStatus === 'Normal'"
|
||||
v-model="row.finalScore"
|
||||
:min="0" :max="100" :controls="false" size="small"
|
||||
/>
|
||||
<span v-else>{{ row.finalScore ?? '—' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总评" width="90">
|
||||
<template #default="{ row }">
|
||||
<div
|
||||
class="score-cell"
|
||||
:class="{ 'is-failed': isFailingScore(detail.canEdit && row.examStatus === 'Normal' ? calcPreviewTotal(row) : row.totalScore) }"
|
||||
>
|
||||
<template v-if="detail.canEdit && row.examStatus === 'Normal'">
|
||||
<b class="total-score preview" :class="scoreClass(calcPreviewTotal(row))">
|
||||
{{ calcPreviewTotal(row) ?? '—' }}
|
||||
</b>
|
||||
</template>
|
||||
<b v-else class="total-score" :class="scoreClass(row.totalScore)">{{ row.totalScore ?? '—' }}</b>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="考试状态" width="115">
|
||||
@@ -795,6 +810,32 @@ onMounted(async () => {
|
||||
.item-row > span:first-child { flex: 1; font-size: 13px; font-weight: 650; }
|
||||
.add-item-row { display: flex; align-items: center; gap: 8px; }
|
||||
|
||||
.score-cell {
|
||||
min-height: 30px;
|
||||
margin: -5px;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.score-cell.is-failed {
|
||||
color: #b42318;
|
||||
background: #fdecec;
|
||||
border-color: #f4b8b2;
|
||||
font-weight: 700;
|
||||
}
|
||||
.score-cell.is-failed :deep(input) { color: #b42318; font-weight: 700; }
|
||||
.transcript-result.is-failed {
|
||||
color: #b42318;
|
||||
background: #fdecec;
|
||||
border: 1px solid #f4b8b2;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
}
|
||||
.transcript-result.is-failed b { color: #b42318; }
|
||||
|
||||
/* Preview total score */
|
||||
.total-score.preview {
|
||||
text-decoration: underline dashed;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Download, Plus, Promotion, Upload } from '@element-plus/icons-vue'
|
||||
import http, { apiErrorMessage } from '../api/http'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
@@ -14,6 +14,7 @@ const selected = ref<any>(null)
|
||||
const results = ref<ResultRow[]>([])
|
||||
const mine = reactive({ best: [] as any[], history: [] as any[] })
|
||||
const dialog = ref(false)
|
||||
const publishDialog = ref(false)
|
||||
const saving = ref(false)
|
||||
const form = reactive({ examCode: '', name: '', organizer: '', examDate: new Date().toISOString().slice(0, 10), metricKind: 3, maxScore: 100, levelOptions: '' })
|
||||
const selectedMetricKind = computed(() => Number(selected.value?.metricKind ?? 0))
|
||||
@@ -54,11 +55,19 @@ async function saveResults() {
|
||||
} catch (error) { ElMessage.error(apiErrorMessage(error)) } finally { saving.value = false }
|
||||
}
|
||||
async function publish() {
|
||||
publishDialog.value = true
|
||||
}
|
||||
async function confirmPublish() {
|
||||
saving.value = true
|
||||
try {
|
||||
await ElMessageBox.confirm('确认发布本批次成绩?发布后学生可查看;修改后可再次发布。', '发布其他考试成绩')
|
||||
await http.post(`/other-exams/batches/${selected.value.id}/publish`)
|
||||
ElMessage.success('成绩已发布'); await openBatch(selected.value); await load()
|
||||
} catch (error: any) { if (error !== 'cancel' && error !== 'close') ElMessage.error(apiErrorMessage(error)) }
|
||||
publishDialog.value = false
|
||||
ElMessage.success('成绩已发布')
|
||||
await openBatch(selected.value)
|
||||
await load()
|
||||
} catch (error) {
|
||||
ElMessage.error(`发布失败:${apiErrorMessage(error)}`)
|
||||
} finally { saving.value = false }
|
||||
}
|
||||
async function createBatch() {
|
||||
try { await http.post('/other-exams/batches', form); dialog.value = false; ElMessage.success('考试批次已建立'); await load() }
|
||||
@@ -94,6 +103,7 @@ onMounted(load)
|
||||
<section v-if="selected" class="editor-panel"><div class="editor-heading"><div><span class="kicker">{{ selected.examCode }}</span><h2>{{ selected.name }}</h2><p>{{ selected.organizer || '未填写组织方' }} · {{ selected.examDate }} · {{ metricName(selected.metricKind) }}{{ selected.metricKind === 3 ? ` · 满分 ${selected.maxScore}` : '' }}</p></div><div class="editor-actions"><el-button :icon="Download" @click="downloadTemplate">下载导入模板</el-button><label class="upload-button"><Upload />批量导入<input type="file" accept=".xlsx,.xls" @change="importExcel" /></label><el-button :icon="Plus" @click="addResult">新增一行</el-button><el-button type="primary" :loading="saving" @click="saveResults">保存记录</el-button><el-button type="success" :icon="Promotion" @click="publish">发布</el-button></div></div><div class="editor-hint">学号输入完成后离开输入框,系统自动检索姓名、学院和班级;参加次数不需要填写,由系统按考试编码和考试日期自动计算。</div><el-table :data="results" class="score-table"><el-table-column label="学号" min-width="150" fixed><template #default="{ row }"><el-input v-model="row.studentNumber" placeholder="输入学号" @blur="lookupStudent(row)" /></template></el-table-column><el-table-column label="姓名" width="110"><template #default="{ row }"><span :class="{ 'unresolved': row.studentNumber && !row.studentName }">{{ row.studentName || '待检索' }}</span></template></el-table-column><el-table-column label="学院" min-width="150"><template #default="{ row }">{{ row.collegeName || '—' }}</template></el-table-column><el-table-column label="班级" min-width="150"><template #default="{ row }">{{ row.className || '—' }}</template></el-table-column><el-table-column v-if="selected.metricKind === 3" label="成绩" width="150"><template #default="{ row }"><el-input-number v-model="row.score" :min="0" :max="selected.maxScore" :precision="2" controls-position="right" placeholder="请输入分数" /></template></el-table-column><el-table-column v-if="selected.metricKind === 2" label="等级" width="150"><template #default="{ row }"><el-select v-model="row.level" placeholder="选择等级"><el-option v-for="level in (selected.levelOptions || '').split(',').filter(Boolean)" :key="level" :label="level" :value="level" /></el-select></template></el-table-column><el-table-column v-if="selected.metricKind === 1" label="考试结果" width="150"><template #default="{ row }"><el-select v-model="row.isPassed" placeholder="选择结果"><el-option label="合格" :value="true"/><el-option label="不合格" :value="false"/></el-select></template></el-table-column><el-table-column label="参加次数" width="100"><template #default="{ row }"><span class="auto-attempt">{{ row.attemptNumber || '自动' }}</span></template></el-table-column><el-table-column label="备注" min-width="180"><template #default="{ row }"><el-input v-model="row.notes" placeholder="可选" /></template></el-table-column></el-table><el-empty v-if="!results.length" description="还没有成绩记录,点击“新增一行”或使用批量导入" /></section>
|
||||
</template>
|
||||
<el-dialog v-model="dialog" title="新建其他考试场次" width="600px"><el-form label-width="100px"><el-form-item label="考试编码" required><el-input v-model="form.examCode" placeholder="如 CET4、IELTS、计算机二级;同一考试始终使用相同编码" /></el-form-item><el-form-item label="考试名称" required><el-input v-model="form.name" placeholder="如 大学英语四级" /></el-form-item><el-form-item label="组织方"><el-input v-model="form.organizer" /></el-form-item><el-form-item label="考试日期"><el-date-picker v-model="form.examDate" type="date" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="评价方式"><el-select v-model="form.metricKind"><el-option label="分数制" :value="3"/><el-option label="等级制" :value="2"/><el-option label="合格/不合格" :value="1"/></el-select></el-form-item><el-form-item v-if="form.metricKind === 3" label="满分"><el-input-number v-model="form.maxScore" :min="1" /></el-form-item><el-form-item v-if="form.metricKind === 2" label="等级顺序"><el-input v-model="form.levelOptions" placeholder="按最优到最差填写,如 A+,A,B,C,D" /></el-form-item></el-form><template #footer><el-button @click="dialog = false">取消</el-button><el-button type="primary" @click="createBatch">建立场次</el-button></template></el-dialog>
|
||||
<el-dialog v-model="publishDialog" title="发布其他考试成绩" width="460px" :close-on-click-modal="false"><div class="publish-confirm"><strong>确认发布“{{ selected?.name }}”成绩?</strong><p>发布后学生可以查看本批次成绩;如需更正,可以修改后再次发布。</p></div><template #footer><el-button @click="publishDialog = false">取消</el-button><el-button type="success" :loading="saving" @click="confirmPublish">确认发布</el-button></template></el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -105,4 +115,5 @@ onMounted(load)
|
||||
.editor-heading > div:first-child { min-width: 0; }
|
||||
.editor-actions { flex: 0 0 auto; }
|
||||
.upload-button { flex: 0 0 auto; min-width: 112px; white-space: nowrap; justify-content: center; line-height: 1.4; }
|
||||
.publish-confirm { color:var(--ink); line-height:1.7; }.publish-confirm p { margin:8px 0 0; color:var(--muted); font-size:13px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user