考试按实际日期、周次和节次融入周/日/总视图,并用橙色“考试”卡片区分。
即使固定课表尚未发布,只要考试已发布,也能正常显示。 考试计划、考场、监考教师和日期都会显示。 Excel 导出同步包含考试安排。
This commit is contained in:
@@ -25,9 +25,14 @@ public static class TimetableExcelExporter
|
|||||||
sheet.Row(1).Height = 34;
|
sheet.Row(1).Height = 34;
|
||||||
|
|
||||||
sheet.Range("A2:H2").Merge();
|
sheet.Range("A2:H2").Merge();
|
||||||
|
var examSummary = timetable.ExamEntries.Count > 0
|
||||||
|
? $" · 已融入 {timetable.ExamEntries.Count} 场考试"
|
||||||
|
: "";
|
||||||
sheet.Cell("A2").Value = timetable.Plan is null
|
sheet.Cell("A2").Value = timetable.Plan is null
|
||||||
? "本学期暂无可用课表版本"
|
? timetable.ExamEntries.Count > 0
|
||||||
: $"版本:{timetable.Plan.Version} · 状态:{PlanStatus(timetable.Plan.Status)} · 导出时间:{DateTime.Now:yyyy-MM-dd HH:mm}";
|
? $"固定课表尚未发布{examSummary} · 导出时间:{DateTime.Now:yyyy-MM-dd HH:mm}"
|
||||||
|
: "本学期暂无可用课表版本"
|
||||||
|
: $"版本:{timetable.Plan.Version} · 状态:{PlanStatus(timetable.Plan.Status)}{examSummary} · 导出时间:{DateTime.Now:yyyy-MM-dd HH:mm}";
|
||||||
sheet.Cell("A2").Style
|
sheet.Cell("A2").Style
|
||||||
.Font.SetFontColor(XLColor.FromHtml("#52677A"))
|
.Font.SetFontColor(XLColor.FromHtml("#52677A"))
|
||||||
.Fill.SetBackgroundColor(XLColor.FromHtml("#EFF3F6"));
|
.Fill.SetBackgroundColor(XLColor.FromHtml("#EFF3F6"));
|
||||||
@@ -48,8 +53,11 @@ public static class TimetableExcelExporter
|
|||||||
}
|
}
|
||||||
sheet.Row(headerRow).Height = 26;
|
sheet.Row(headerRow).Height = 26;
|
||||||
|
|
||||||
|
var timedEntries = timetable.Entries
|
||||||
|
.Concat(timetable.ExamEntries)
|
||||||
|
.ToArray();
|
||||||
var periods = timetable.Slots.Select(x => x.PeriodNumber)
|
var periods = timetable.Slots.Select(x => x.PeriodNumber)
|
||||||
.Concat(timetable.Entries.SelectMany(x =>
|
.Concat(timedEntries.SelectMany(x =>
|
||||||
Enumerable.Range(x.StartPeriod, x.PeriodCount)))
|
Enumerable.Range(x.StartPeriod, x.PeriodCount)))
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Order()
|
.Order()
|
||||||
@@ -72,7 +80,7 @@ public static class TimetableExcelExporter
|
|||||||
|
|
||||||
for (var day = 1; day <= 7; day++)
|
for (var day = 1; day <= 7; day++)
|
||||||
{
|
{
|
||||||
var entries = timetable.Entries
|
var entries = timedEntries
|
||||||
.Where(x =>
|
.Where(x =>
|
||||||
x.DayOfWeek == day &&
|
x.DayOfWeek == day &&
|
||||||
x.StartPeriod <= period &&
|
x.StartPeriod <= period &&
|
||||||
@@ -86,7 +94,8 @@ public static class TimetableExcelExporter
|
|||||||
if (entries.Count > 0)
|
if (entries.Count > 0)
|
||||||
{
|
{
|
||||||
cell.Style
|
cell.Style
|
||||||
.Fill.SetBackgroundColor(XLColor.FromHtml("#E8F3F2"))
|
.Fill.SetBackgroundColor(XLColor.FromHtml(
|
||||||
|
entries.Any(x => x.IsExam) ? "#FFF1E8" : "#E8F3F2"))
|
||||||
.Font.SetFontColor(XLColor.FromHtml("#173F4C"));
|
.Font.SetFontColor(XLColor.FromHtml("#173F4C"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +147,14 @@ public static class TimetableExcelExporter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static string EntryText(TimetableEntryDto entry) =>
|
private static string EntryText(TimetableEntryDto entry) =>
|
||||||
$"{entry.CourseName}\n" +
|
entry.IsExam
|
||||||
|
? $"【考试】{entry.CourseName}\n" +
|
||||||
|
$"{entry.ExamPlanName}\n" +
|
||||||
|
$"{string.Join('、', entry.TeacherNames)}\n" +
|
||||||
|
$"{Location(entry)}\n" +
|
||||||
|
$"{entry.ExamDate:yyyy-MM-dd} · 第 {entry.StartPeriod}-" +
|
||||||
|
$"{entry.StartPeriod + entry.PeriodCount - 1} 节"
|
||||||
|
: $"{entry.CourseName}\n" +
|
||||||
$"{string.Join('、', entry.TeacherNames)}\n" +
|
$"{string.Join('、', entry.TeacherNames)}\n" +
|
||||||
$"{Location(entry)}\n" +
|
$"{Location(entry)}\n" +
|
||||||
$"{entry.StartWeek}-{entry.EndWeek} 周";
|
$"{entry.StartWeek}-{entry.EndWeek} 周";
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Jiaowu.Api.Tests;
|
|||||||
public sealed class TimetableExcelExporterTests
|
public sealed class TimetableExcelExporterTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Create_builds_week_grid_and_flexible_course_section()
|
public void Create_builds_week_grid_with_exam_and_flexible_course_section()
|
||||||
{
|
{
|
||||||
var timetable = new TimetableData(
|
var timetable = new TimetableData(
|
||||||
new TimetableTermDto(
|
new TimetableTermDto(
|
||||||
@@ -84,7 +84,31 @@ public sealed class TimetableExcelExporterTests
|
|||||||
["计算机科学 2601 班"],
|
["计算机科学 2601 班"],
|
||||||
null)
|
null)
|
||||||
],
|
],
|
||||||
[]);
|
[
|
||||||
|
new TimetableEntryDto(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"TASK-EXAM",
|
||||||
|
"高等数学期末考试",
|
||||||
|
"MATH101",
|
||||||
|
"高等数学",
|
||||||
|
["张老师", "监考:李老师"],
|
||||||
|
["计算机科学 2601 班"],
|
||||||
|
"201",
|
||||||
|
"第二教学楼",
|
||||||
|
"主校区",
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
WeekPattern.All,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
"2026-2027 学年第一学期期末考试",
|
||||||
|
new DateOnly(2027, 1, 5),
|
||||||
|
DateTime.UtcNow)
|
||||||
|
]);
|
||||||
|
|
||||||
var bytes = TimetableExcelExporter.Create(timetable);
|
var bytes = TimetableExcelExporter.Create(timetable);
|
||||||
|
|
||||||
@@ -92,7 +116,10 @@ public sealed class TimetableExcelExporterTests
|
|||||||
using var workbook = new XLWorkbook(stream);
|
using var workbook = new XLWorkbook(stream);
|
||||||
var sheet = workbook.Worksheet("课表");
|
var sheet = workbook.Worksheet("课表");
|
||||||
Assert.Contains("计算机科学 2601 班", sheet.Cell("A1").GetString());
|
Assert.Contains("计算机科学 2601 班", sheet.Cell("A1").GetString());
|
||||||
|
Assert.Contains("已融入 1 场考试", sheet.Cell("A2").GetString());
|
||||||
Assert.Contains("高等数学", sheet.Cell(5, 2).GetString());
|
Assert.Contains("高等数学", sheet.Cell(5, 2).GetString());
|
||||||
|
Assert.Contains("【考试】高等数学", sheet.Cell(5, 3).GetString());
|
||||||
|
Assert.Contains("2027-01-05", sheet.Cell(5, 3).GetString());
|
||||||
Assert.Contains(
|
Assert.Contains(
|
||||||
sheet.CellsUsed(),
|
sheet.CellsUsed(),
|
||||||
cell => cell.GetString().Contains("非排时课程"));
|
cell => cell.GetString().Contains("非排时课程"));
|
||||||
|
|||||||
@@ -100,14 +100,6 @@ const selectedResourceId = computed(() => {
|
|||||||
if (resourceType.value === 'Classroom') return classroomId.value
|
if (resourceType.value === 'Classroom') return classroomId.value
|
||||||
return classId.value
|
return classId.value
|
||||||
})
|
})
|
||||||
const maxPeriods = computed(() => {
|
|
||||||
const slotMaximum = Math.max(0, ...((timetable.value?.slots ?? []).map((x: any) => x.periodNumber)))
|
|
||||||
const entryMaximum = Math.max(
|
|
||||||
0,
|
|
||||||
...((timetable.value?.entries ?? []).map((x: any) => x.startPeriod + x.periodCount - 1)),
|
|
||||||
)
|
|
||||||
return Math.max(8, slotMaximum, entryMaximum)
|
|
||||||
})
|
|
||||||
const slotMap = computed<Map<number, any>>(() =>
|
const slotMap = computed<Map<number, any>>(() =>
|
||||||
new Map<number, any>(
|
new Map<number, any>(
|
||||||
(timetable.value?.slots ?? []).map((item: any) => [item.periodNumber, item]),
|
(timetable.value?.slots ?? []).map((item: any) => [item.periodNumber, item]),
|
||||||
@@ -119,6 +111,36 @@ const termMonday = computed(() => {
|
|||||||
const offset = start.getDay() === 0 ? 6 : start.getDay() - 1
|
const offset = start.getDay() === 0 ? 6 : start.getDay() - 1
|
||||||
return addDays(start, -offset)
|
return addDays(start, -offset)
|
||||||
})
|
})
|
||||||
|
const examEntries = computed(() => {
|
||||||
|
if (!termMonday.value) return []
|
||||||
|
return (timetable.value?.examEntries ?? []).flatMap((entry: any) => {
|
||||||
|
const examDate = parseDate(entry.examDate)
|
||||||
|
if (!examDate) return []
|
||||||
|
const week = Math.floor(
|
||||||
|
(examDate.getTime() - termMonday.value!.getTime()) / (7 * 86400000),
|
||||||
|
) + 1
|
||||||
|
if (week < 1) return []
|
||||||
|
return [{
|
||||||
|
...entry,
|
||||||
|
startWeek: week,
|
||||||
|
endWeek: week,
|
||||||
|
weekPattern: 'All',
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const timedEntries = computed(() => [
|
||||||
|
...(timetable.value?.entries ?? []),
|
||||||
|
...examEntries.value,
|
||||||
|
])
|
||||||
|
const hasTimedEntries = computed(() => timedEntries.value.length > 0)
|
||||||
|
const maxPeriods = computed(() => {
|
||||||
|
const slotMaximum = Math.max(0, ...((timetable.value?.slots ?? []).map((x: any) => x.periodNumber)))
|
||||||
|
const entryMaximum = Math.max(
|
||||||
|
0,
|
||||||
|
...(timedEntries.value.map((x: any) => x.startPeriod + x.periodCount - 1)),
|
||||||
|
)
|
||||||
|
return Math.max(8, slotMaximum, entryMaximum)
|
||||||
|
})
|
||||||
const totalWeeks = computed(() => {
|
const totalWeeks = computed(() => {
|
||||||
const end = parseDate(timetable.value?.term?.endDate)
|
const end = parseDate(timetable.value?.term?.endDate)
|
||||||
if (!termMonday.value || !end) return 1
|
if (!termMonday.value || !end) return 1
|
||||||
@@ -126,7 +148,7 @@ const totalWeeks = computed(() => {
|
|||||||
(7 * 86400000))
|
(7 * 86400000))
|
||||||
const entryWeeks = Math.max(
|
const entryWeeks = Math.max(
|
||||||
1,
|
1,
|
||||||
...((timetable.value?.entries ?? []).map((entry: any) => entry.endWeek)),
|
...(timedEntries.value.map((entry: any) => entry.endWeek)),
|
||||||
)
|
)
|
||||||
return Math.max(1, dateWeeks, entryWeeks)
|
return Math.max(1, dateWeeks, entryWeeks)
|
||||||
})
|
})
|
||||||
@@ -139,7 +161,7 @@ const currentTermWeek = computed(() => {
|
|||||||
return week >= 1 && week <= totalWeeks.value ? week : null
|
return week >= 1 && week <= totalWeeks.value ? week : null
|
||||||
})
|
})
|
||||||
const weekEntries = computed(() =>
|
const weekEntries = computed(() =>
|
||||||
(timetable.value?.entries ?? []).filter((entry: any) =>
|
timedEntries.value.filter((entry: any) =>
|
||||||
occursInWeek(entry, selectedWeek.value)),
|
occursInWeek(entry, selectedWeek.value)),
|
||||||
)
|
)
|
||||||
const dayEntries = computed(() =>
|
const dayEntries = computed(() =>
|
||||||
@@ -147,7 +169,7 @@ const dayEntries = computed(() =>
|
|||||||
)
|
)
|
||||||
const visibleGridEntries = computed(() =>
|
const visibleGridEntries = computed(() =>
|
||||||
viewMode.value === 'overview'
|
viewMode.value === 'overview'
|
||||||
? (timetable.value?.entries ?? [])
|
? timedEntries.value
|
||||||
: weekEntries.value,
|
: weekEntries.value,
|
||||||
)
|
)
|
||||||
const selectedWeekLabel = computed(() => weekLabel(selectedWeek.value))
|
const selectedWeekLabel = computed(() => weekLabel(selectedWeek.value))
|
||||||
@@ -182,6 +204,22 @@ function weeks(entry: any) {
|
|||||||
return `${entry.startWeek}—${entry.endWeek}周${pattern === '每周' ? '' : ` · ${pattern}`}`
|
return `${entry.startWeek}—${entry.endWeek}周${pattern === '每周' ? '' : ` · ${pattern}`}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function entryKey(entry: any) {
|
||||||
|
return `${entry.isExam ? 'exam' : 'course'}-${entry.id}-${entry.teachingTaskId ?? ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryTiming(entry: any) {
|
||||||
|
const periods = `第 ${entry.startPeriod}—${entry.startPeriod + entry.periodCount - 1} 节`
|
||||||
|
return entry.isExam
|
||||||
|
? `${formatExamDate(entry.examDate)} · ${periods}`
|
||||||
|
: `${weeks(entry)} · ${periods}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatExamDate(value?: string) {
|
||||||
|
const date = parseDate(value)
|
||||||
|
return date ? `${date.getMonth() + 1}月${date.getDate()}日` : '考试日期待定'
|
||||||
|
}
|
||||||
|
|
||||||
function location(entry: any) {
|
function location(entry: any) {
|
||||||
return [entry.campusName, entry.buildingName, entry.classroomName].filter(Boolean).join(' · ')
|
return [entry.campusName, entry.buildingName, entry.classroomName].filter(Boolean).join(' · ')
|
||||||
}
|
}
|
||||||
@@ -255,7 +293,7 @@ function laneFor(entry: any, entries: any[]) {
|
|||||||
return { item, index }
|
return { item, index }
|
||||||
})
|
})
|
||||||
const count = Math.max(1, laneEnds.length)
|
const count = Math.max(1, laneEnds.length)
|
||||||
placed.forEach(({ item, index }) => layouts.set(String(item.id), { index, count }))
|
placed.forEach(({ item, index }) => layouts.set(entryKey(item), { index, count }))
|
||||||
}
|
}
|
||||||
|
|
||||||
sameDay.forEach((item: any) => {
|
sameDay.forEach((item: any) => {
|
||||||
@@ -269,7 +307,7 @@ function laneFor(entry: any, entries: any[]) {
|
|||||||
clusterEnd = Math.max(clusterEnd, itemEnd)
|
clusterEnd = Math.max(clusterEnd, itemEnd)
|
||||||
})
|
})
|
||||||
placeCluster()
|
placeCluster()
|
||||||
return layouts.get(String(entry.id)) ?? { index: 0, count: 1 }
|
return layouts.get(entryKey(entry)) ?? { index: 0, count: 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
function withLaneStyle(entry: any, entries: any[], base: Record<string, string>) {
|
function withLaneStyle(entry: any, entries: any[], base: Record<string, string>) {
|
||||||
@@ -670,7 +708,7 @@ onMounted(async () => {
|
|||||||
<p v-else-if="isMine">行政班课程与本人已选课程统一展示,仅采用教务处已发布课表。</p>
|
<p v-else-if="isMine">行政班课程与本人已选课程统一展示,仅采用教务处已发布课表。</p>
|
||||||
<p v-else-if="isTeacherView">查看指定教师本学期的授课安排。</p>
|
<p v-else-if="isTeacherView">查看指定教师本学期的授课安排。</p>
|
||||||
<p v-else-if="isManager">查询班级、教师与场地课表,可切换草稿或正式发布版本。</p>
|
<p v-else-if="isManager">查询班级、教师与场地课表,可切换草稿或正式发布版本。</p>
|
||||||
<p v-else>按年级、学院、专业和班级分类查询正式发布的课程安排。</p>
|
<p v-else>按年级、学院、专业和班级分类查询正式发布的课程与考试安排。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="timetable-filters">
|
<div class="timetable-filters">
|
||||||
<el-select v-model="termId" filterable placeholder="选择学期" @change="onTermChanged">
|
<el-select v-model="termId" filterable placeholder="选择学期" @change="onTermChanged">
|
||||||
@@ -839,6 +877,12 @@ onMounted(async () => {
|
|||||||
<template v-if="timetable.plan.publishedAt">
|
<template v-if="timetable.plan.publishedAt">
|
||||||
· 发布于 {{ new Date(timetable.plan.publishedAt).toLocaleString('zh-CN') }}
|
· 发布于 {{ new Date(timetable.plan.publishedAt).toLocaleString('zh-CN') }}
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="examEntries.length">
|
||||||
|
· 已融入 {{ examEntries.length }} 场考试
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
<span v-else-if="examEntries.length">
|
||||||
|
固定课表尚未发布 · 已载入 {{ examEntries.length }} 场考试
|
||||||
</span>
|
</span>
|
||||||
<span v-else>本学期课表尚未发布</span>
|
<span v-else>本学期课表尚未发布</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -866,7 +910,7 @@ onMounted(async () => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="(viewMode === 'overview' || viewMode === 'week') && timetable?.plan && timetable.entries.length"
|
v-if="(viewMode === 'overview' || viewMode === 'week') && hasTimedEntries"
|
||||||
class="timetable-view-section"
|
class="timetable-view-section"
|
||||||
>
|
>
|
||||||
<div class="view-context">
|
<div class="view-context">
|
||||||
@@ -874,12 +918,12 @@ onMounted(async () => {
|
|||||||
<strong>{{ viewMode === 'overview' ? '全学期总览' : selectedWeekLabel }}</strong>
|
<strong>{{ viewMode === 'overview' ? '全学期总览' : selectedWeekLabel }}</strong>
|
||||||
<span>
|
<span>
|
||||||
{{ viewMode === 'overview'
|
{{ viewMode === 'overview'
|
||||||
? '同时段但不同周次的课程并列显示'
|
? '固定课程与已发布考试按节次合并显示'
|
||||||
: `本周共 ${weekEntries.length} 项课程安排` }}
|
: `本周共 ${weekEntries.length} 项课程、考试安排` }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<small v-if="viewMode === 'overview'">课程卡片保留起止周与单双周信息</small>
|
<small v-if="viewMode === 'overview'">考试卡片显示具体日期,课程卡片保留周次信息</small>
|
||||||
<small v-else-if="!weekEntries.length">本周没有课程安排</small>
|
<small v-else-if="!weekEntries.length">本周没有课程或考试安排</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="timetable-scroll">
|
<div class="timetable-scroll">
|
||||||
<div
|
<div
|
||||||
@@ -907,19 +951,24 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
<article
|
<article
|
||||||
v-for="entry in visibleGridEntries"
|
v-for="entry in visibleGridEntries"
|
||||||
:key="entry.id"
|
:key="entryKey(entry)"
|
||||||
class="course-block"
|
class="course-block"
|
||||||
|
:class="{ 'exam-block': entry.isExam }"
|
||||||
:style="gridEntryStyle(entry)"
|
:style="gridEntryStyle(entry)"
|
||||||
>
|
>
|
||||||
<strong>{{ entry.courseName }}</strong>
|
<strong>
|
||||||
|
<span v-if="entry.isExam" class="entry-kind">考试</span>
|
||||||
|
{{ entry.courseName }}
|
||||||
|
</strong>
|
||||||
|
<span v-if="entry.isExam && entry.examPlanName">{{ entry.examPlanName }}</span>
|
||||||
<span>{{ entry.teacherNames.join('、') || '教师待定' }}</span>
|
<span>{{ entry.teacherNames.join('、') || '教师待定' }}</span>
|
||||||
<span>{{ location(entry) }}</span>
|
<span>{{ location(entry) }}</span>
|
||||||
<small>{{ weeks(entry) }} · 第 {{ entry.startPeriod }}—{{ entry.startPeriod + entry.periodCount - 1 }} 节</small>
|
<small>{{ entryTiming(entry) }}</small>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="viewMode === 'day' && timetable?.plan" class="day-view">
|
<div v-else-if="viewMode === 'day' && hasTimedEntries" class="day-view">
|
||||||
<header>
|
<header>
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ weekdays[selectedDay] }} · {{ selectedDayDate }}</strong>
|
<strong>{{ weekdays[selectedDay] }} · {{ selectedDayDate }}</strong>
|
||||||
@@ -945,21 +994,26 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
<article
|
<article
|
||||||
v-for="entry in dayEntries"
|
v-for="entry in dayEntries"
|
||||||
:key="entry.id"
|
:key="entryKey(entry)"
|
||||||
class="day-course-block"
|
class="day-course-block"
|
||||||
|
:class="{ 'exam-block': entry.isExam }"
|
||||||
:style="dayEntryStyle(entry)"
|
:style="dayEntryStyle(entry)"
|
||||||
>
|
>
|
||||||
<strong>{{ entry.courseName }}</strong>
|
<strong>
|
||||||
|
<span v-if="entry.isExam" class="entry-kind">考试</span>
|
||||||
|
{{ entry.courseName }}
|
||||||
|
</strong>
|
||||||
|
<span v-if="entry.isExam && entry.examPlanName">{{ entry.examPlanName }}</span>
|
||||||
<span>{{ entry.teacherNames.join('、') || '教师待定' }}</span>
|
<span>{{ entry.teacherNames.join('、') || '教师待定' }}</span>
|
||||||
<span>{{ location(entry) }}</span>
|
<span>{{ location(entry) }}</span>
|
||||||
<small>{{ weeks(entry) }} · 第 {{ entry.startPeriod }}—{{ entry.startPeriod + entry.periodCount - 1 }} 节</small>
|
<small>{{ entryTiming(entry) }}</small>
|
||||||
</article>
|
</article>
|
||||||
<span v-if="!dayEntries.length" class="day-no-courses">当日无课程安排</span>
|
<span v-if="!dayEntries.length" class="day-no-courses">当日无课程安排</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-empty
|
<el-empty
|
||||||
v-else-if="timetable && !loading && !timetable.flexibleCourses?.length"
|
v-else-if="timetable && !loading && !timetable.flexibleCourses?.length"
|
||||||
:description="timetable.plan ? '该课表暂时没有课程安排' : '所选学期尚未发布课表'"
|
:description="timetable.plan ? '该课表暂时没有课程或考试安排' : '所选学期尚未发布课表或考试安排'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -1131,6 +1185,10 @@ onMounted(async () => {
|
|||||||
.course-block strong { color: #123a4b; font-size: 14px; }
|
.course-block strong { color: #123a4b; font-size: 14px; }
|
||||||
.course-block span { font-size: 12px; }
|
.course-block span { font-size: 12px; }
|
||||||
.course-block small { margin-top: auto; color: #5f7885; font-size: 11px; }
|
.course-block small { margin-top: auto; color: #5f7885; font-size: 11px; }
|
||||||
|
.course-block.exam-block, .day-course-block.exam-block { border-left-color: #b65b32; background: #fff1e8; color: #70442f; }
|
||||||
|
.course-block.exam-block strong, .day-course-block.exam-block strong { color: #78391e; }
|
||||||
|
.course-block.exam-block small, .day-course-block.exam-block small { color: #8b5b43; }
|
||||||
|
.entry-kind { display: inline-block; margin-right: 5px; padding: 1px 5px; border-radius: 2px; background: #b65b32; color: #fff; font-size: 10px !important; line-height: 1.5; vertical-align: 1px; }
|
||||||
.day-view { border: 1px solid #dce4eb; }
|
.day-view { border: 1px solid #dce4eb; }
|
||||||
.day-view > header { padding: 14px 16px; display: flex; align-items: baseline; justify-content: space-between; background: #173e72; color: #fff; }
|
.day-view > header { padding: 14px 16px; display: flex; align-items: baseline; justify-content: space-between; background: #173e72; color: #fff; }
|
||||||
.day-view > header > div { display: grid; gap: 3px; }
|
.day-view > header > div { display: grid; gap: 3px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user