fix: 周预览折叠改为折叠浅灰非工作时段区域, 而非整个网格

- 折叠对象改为 8:00 前/22:00 后的浅灰区域, 工作时段始终可见
- 浅灰区域无事件 -> 默认折叠; 有事件 -> 默认展开
- 按钮切换「折叠夜间/展开夜间」, 切换周时按是否有夜间事件重置
- 折叠时网格从 8:00 起 (gridStartMin), 事件与 hour-line 按行索引定位
This commit is contained in:
zikai
2026-07-22 07:29:45 +00:00
parent e64f9f4adf
commit 7edcf8c67a

View File

@@ -15,8 +15,10 @@ const headers = DOW.slice(1).concat(DOW[0]) // 周一..周日
const hours = Array.from({ length: 24 }, function (_, i) { return i }) const hours = Array.from({ length: 24 }, function (_, i) { return i })
// 非工作时段: 8:00 前 (0-7) 与 22:00 后 (22-23) 置淡灰 // 非工作时段: 8:00 前 (0-7) 与 22:00 后 (22-23) 置淡灰
const OFF_HOUR_START = 8 // 8:00 起为工作时段
const OFF_HOUR_END = 22 // 22:00 起为非工作时段
function isOffHour(h) { function isOffHour(h) {
return h < 8 || h >= 22 return h < OFF_HOUR_START || h >= OFF_HOUR_END
} }
// 当前周的 7 天日期 (周一~周日) // 当前周的 7 天日期 (周一~周日)
@@ -95,21 +97,55 @@ const hasEvents = computed(function () {
return false return false
}) })
// 折叠状态: 本周无日程时自动折叠; 按钮可强制切换 // 非工作时段 (8:00 前 / 22:00 后) 是否有事件落入
const collapsed = ref(false) // 事件开始 < 8:00 或 结束 > 22:00 即视为占用非工作时段
const offHoursHaveEvents = computed(function () {
const offStart = OFF_HOUR_START * 60
const offEnd = OFF_HOUR_END * 60
for (const items of laidOutByDate.value.values()) {
for (const it of items) {
if (it.startMin < offStart || it.endMin > offEnd) return true
}
}
return false
})
// 折叠状态: 折叠的是非工作时段 (浅灰区域), 不是整个网格。
// 默认: 非工作时段无事件 -> 折叠; 有事件 -> 展开。按钮可强制切换。
const offHoursCollapsed = ref(false)
watch(weekLabel, function () { watch(weekLabel, function () {
// 切换周时, 若新周无日程则自动折叠 // 切换周时, 按非工作时段是否有事件重置折叠状态
collapsed.value = !hasEvents.value offHoursCollapsed.value = !offHoursHaveEvents.value
}, { immediate: true }) }, { immediate: true })
function toggleCollapse() { function toggleCollapse() {
collapsed.value = !collapsed.value offHoursCollapsed.value = !offHoursCollapsed.value
} }
// 渲染的小时列表: 折叠时仅显示工作时段 (8-21), 展开时显示全天 (0-23)
const displayHours = computed(function () {
if (offHoursCollapsed.value) {
return hours.filter(function (h) { return !isOffHour(h) })
}
return hours
})
// 网格起始分钟: 折叠时从 8:00 (480) 起, 展开时从 0:00 起。
// 事件与 hour-line 的 top 均相对于此基准。
const gridStartMin = computed(function () {
return offHoursCollapsed.value ? OFF_HOUR_START * 60 : 0
})
// 折叠时 day-body 的高度 (仅工作时段) 与展开时 (全天) 不同
const dayBodyHeight = computed(function () {
return displayHours.value.length * HOUR_HEIGHT
})
function eventStyle(occ) { function eventStyle(occ) {
const c = eventColor(occ.event.uid) const c = eventColor(occ.event.uid)
const top = occ.startMin * (HOUR_HEIGHT / 60) const pxPerMin = HOUR_HEIGHT / 60
const height = Math.max((occ.endMin - occ.startMin) * (HOUR_HEIGHT / 60), 18) const top = (occ.startMin - gridStartMin.value) * pxPerMin
const height = Math.max((occ.endMin - occ.startMin) * pxPerMin, 18)
const widthPct = 100 / occ.totalCols const widthPct = 100 / occ.totalCols
return { return {
top: top + 'px', top: top + 'px',
@@ -139,17 +175,17 @@ function tooltip(ev) {
<button class="nav-btn" @click="nextWeek">下一周</button> <button class="nav-btn" @click="nextWeek">下一周</button>
<button class="nav-btn today-btn" @click="goToToday">回到今日</button> <button class="nav-btn today-btn" @click="goToToday">回到今日</button>
<button class="nav-btn collapse-btn" @click="toggleCollapse"> <button class="nav-btn collapse-btn" @click="toggleCollapse">
{{ collapsed ? '展开' : '折叠' }} {{ offHoursCollapsed ? '展开夜间' : '折叠夜间' }}
</button> </button>
<span v-if="!hasEvents" class="empty-hint">本周无日程</span> <span v-if="!hasEvents" class="empty-hint">本周无日程</span>
</div> </div>
<div v-show="!collapsed" class="week-grid-wrapper"> <div class="week-grid-wrapper">
<div class="week-grid"> <div class="week-grid">
<!-- 左侧时间标签列 --> <!-- 左侧时间标签列 -->
<div class="time-col"> <div class="time-col">
<div class="time-spacer"></div> <div class="time-spacer"></div>
<div <div
v-for="h in hours" v-for="h in displayHours"
:key="h" :key="h"
class="time-slot" class="time-slot"
:class="{ 'off-hour': isOffHour(h) }" :class="{ 'off-hour': isOffHour(h) }"
@@ -164,13 +200,13 @@ function tooltip(ev) {
{{ headers[idx] }} {{ headers[idx] }}
<span class="day-date">{{ parseISODate(dateStr).day }}</span> <span class="day-date">{{ parseISODate(dateStr).day }}</span>
</div> </div>
<div class="day-body" :style="{ height: 24 * HOUR_HEIGHT + 'px' }"> <div class="day-body" :style="{ height: dayBodyHeight + 'px' }">
<div <div
v-for="h in hours" v-for="(h, i) in displayHours"
:key="h" :key="h"
class="hour-line" class="hour-line"
:class="{ 'off-hour': isOffHour(h) }" :class="{ 'off-hour': isOffHour(h) }"
:style="{ top: h * HOUR_HEIGHT + 'px', height: HOUR_HEIGHT + 'px' }" :style="{ top: i * HOUR_HEIGHT + 'px', height: HOUR_HEIGHT + 'px' }"
></div> ></div>
<div <div
v-for="occ in (laidOutByDate.get(dateStr) || [])" v-for="occ in (laidOutByDate.get(dateStr) || [])"
@@ -186,10 +222,6 @@ function tooltip(ev) {
</div> </div>
</div> </div>
</div> </div>
<div v-if="collapsed" class="collapsed-placeholder">
<span v-if="!hasEvents">本周无日程</span>
<span v-else>本周视图已折叠</span>
</div>
</div> </div>
</template> </template>
@@ -302,16 +334,6 @@ function tooltip(ev) {
.hour-line.off-hour { .hour-line.off-hour {
background: #f7f8f9; background: #f7f8f9;
} }
.collapsed-placeholder {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: #95a5a6;
font-size: 14px;
font-style: italic;
padding: 40px;
}
.event-block { .event-block {
position: absolute; position: absolute;
border-radius: 4px; border-radius: 4px;