fix: dynamic time slot columns from events + detailed event blocks
- Only show columns for time slots that have actual courses - Event blocks show: lesson period, time range, teacher, students - Horizontal scroll when many slot columns Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
<template>
|
||||
<div class="matrix-wrapper">
|
||||
<div class="matrix-grid">
|
||||
<div v-if="!timeSlots.length" class="matrix-empty">
|
||||
本周暂无排课数据
|
||||
</div>
|
||||
|
||||
<div v-else class="matrix-grid" :style="gridStyle">
|
||||
<!-- Corner -->
|
||||
<div class="grid-cell corner-cell"></div>
|
||||
|
||||
<!-- Header: time slots -->
|
||||
<!-- Header: time slots (动态) -->
|
||||
<div
|
||||
v-for="slot in timeSlots"
|
||||
:key="slot.key"
|
||||
@@ -37,8 +41,9 @@
|
||||
@click.stop="onEventClick(ev)"
|
||||
>
|
||||
<div class="ev-title">{{ ev.clazzName }}</div>
|
||||
<div class="ev-teacher" v-if="ev.teacherName">{{ ev.teacherName }}</div>
|
||||
<div class="ev-students" v-if="ev.studentNames.length">{{ ev.studentNames.join('、') }}</div>
|
||||
<div class="ev-meta">{{ slot.label }} {{ fmtTime(ev.startAt) }}-{{ fmtTime(ev.endAt) }}</div>
|
||||
<div class="ev-teacher">{{ ev.teacherName || '未设置老师' }}</div>
|
||||
<div class="ev-students">{{ ev.studentNames.length ? ev.studentNames.join('、') : '未设置学生' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="cell-empty" @click="onCellClick(day.dateKey, slot.key)">+</div>
|
||||
@@ -69,18 +74,16 @@ interface WeekDay {
|
||||
|
||||
const WEEKDAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
|
||||
const timeSlotsData: TimeSlot[] = DEFAULT_TIME_SLOTS.map((t) => ({
|
||||
key: t,
|
||||
label: SLOT_LABELS[t] || t,
|
||||
time: t,
|
||||
}));
|
||||
|
||||
function isSameDay(a: Date, b: Date): boolean {
|
||||
return a.getFullYear() === b.getFullYear()
|
||||
&& a.getMonth() === b.getMonth()
|
||||
&& a.getDate() === b.getDate();
|
||||
}
|
||||
|
||||
function fmtTime(d: Date): string {
|
||||
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ClazzMatrixView',
|
||||
props: {
|
||||
@@ -90,7 +93,6 @@ export default defineComponent({
|
||||
},
|
||||
emits: ['cell-click', 'event-click'],
|
||||
setup(props, { emit }) {
|
||||
const timeSlots = timeSlotsData;
|
||||
const today = computed(() => new Date());
|
||||
|
||||
const weekDays = computed<WeekDay[]>(() => {
|
||||
@@ -109,6 +111,26 @@ export default defineComponent({
|
||||
return days;
|
||||
});
|
||||
|
||||
/** 从排课数据动态计算有时段的列 */
|
||||
const timeSlots = computed<TimeSlot[]>(() => {
|
||||
const keys = new Set<string>();
|
||||
for (const ev of props.events) {
|
||||
if (!ev.visible) continue;
|
||||
keys.add(ev.timeSlotKey);
|
||||
}
|
||||
const sorted = Array.from(keys).sort();
|
||||
return sorted.map((key) => ({
|
||||
key,
|
||||
label: SLOT_LABELS[key] || key,
|
||||
time: key,
|
||||
}));
|
||||
});
|
||||
|
||||
/** CSS Grid 列模板:左侧日期 + 每时段列至少 1.8rem */
|
||||
const gridStyle = computed(() => ({
|
||||
gridTemplateColumns: `1.2rem repeat(${timeSlots.value.length}, minmax(1.8rem, 1fr))`,
|
||||
}));
|
||||
|
||||
const eventMap = computed(() => {
|
||||
const map: Record<string, Record<string, NormalizedClazzEvent[]>> = {};
|
||||
for (const ev of props.events) {
|
||||
@@ -135,7 +157,7 @@ export default defineComponent({
|
||||
emit('event-click', ev);
|
||||
}
|
||||
|
||||
return { timeSlots, weekDays, eventMap, eventStyle, onCellClick, onEventClick };
|
||||
return { timeSlots, gridStyle, weekDays, eventMap, fmtTime, eventStyle, onCellClick, onEventClick };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -144,14 +166,26 @@ export default defineComponent({
|
||||
.matrix-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.matrix-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
font-size: 0.28rem;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.matrix-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2rem repeat(4, 1fr);
|
||||
grid-template-rows: auto repeat(7, 1fr);
|
||||
/* gridTemplateColumns set dynamically via inline style */
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
@@ -240,6 +274,15 @@ export default defineComponent({
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ev-meta {
|
||||
font-size: 0.16rem;
|
||||
color: #888;
|
||||
line-height: 1.3;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ev-teacher {
|
||||
font-size: 0.16rem;
|
||||
color: #555;
|
||||
|
||||
@@ -4,6 +4,9 @@ export const DEFAULT_TIME_SLOTS = [
|
||||
'10:10',
|
||||
'14:00',
|
||||
'15:30',
|
||||
'17:00',
|
||||
'18:30',
|
||||
'20:00',
|
||||
];
|
||||
|
||||
/** 时段显示名称映射 */
|
||||
@@ -12,6 +15,9 @@ export const SLOT_LABELS: Record<string, string> = {
|
||||
'10:10': '第二节',
|
||||
'14:00': '第三节',
|
||||
'15:30': '第四节',
|
||||
'17:00': '第五节',
|
||||
'18:30': '第六节',
|
||||
'20:00': '第七节',
|
||||
};
|
||||
|
||||
/** 视图模式 */
|
||||
|
||||
Reference in New Issue
Block a user