4 Commits

Author SHA1 Message Date
weli c44d763e63 fix: adapt portrait matrix row heights without drift
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-02 22:46:42 +08:00
weli 8d4ed8099c fix: align portrait matrix sidebar with slots
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-02 22:40:05 +08:00
weli b02fa37350 fix: enable natural touch scroll in matrix landscape
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-02 22:33:20 +08:00
weli 2fb4e12ad5 fix: remove unrotated matrix landscape header
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-02 22:21:24 +08:00
2 changed files with 65 additions and 19 deletions
+56 -5
View File
@@ -79,7 +79,13 @@
<div class="day-date">{{ day.date }}</div> <div class="day-date">{{ day.date }}</div>
</div> </div>
</div> </div>
<div class="ml-body"> <div
ref="landscapeBodyRef"
class="ml-body"
@touchstart="onLandscapeBodyTouchStart"
@touchmove.prevent="onLandscapeBodyTouchMove"
@wheel="onLandscapeBodyWheel"
>
<div v-for="slot in timeSlots" :key="slot.key" class="ml-row"> <div v-for="slot in timeSlots" :key="slot.key" class="ml-row">
<div class="ml-label"> <div class="ml-label">
<div class="header-label">{{ slot.label }}</div> <div class="header-label">{{ slot.label }}</div>
@@ -114,7 +120,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, PropType } from 'vue'; import { computed, defineComponent, PropType, ref } from 'vue';
import { NormalizedClazzEvent } from '../useClazzViewModel'; import { NormalizedClazzEvent } from '../useClazzViewModel';
import { DateFormatter, formatDate, hexToRgb } from '~/utils'; import { DateFormatter, formatDate, hexToRgb } from '~/utils';
@@ -172,6 +178,8 @@ export default defineComponent({
emits: ['cell-click', 'event-click'], emits: ['cell-click', 'event-click'],
setup(props, { emit }) { setup(props, { emit }) {
const today = computed(() => new Date()); const today = computed(() => new Date());
const landscapeBodyRef = ref<HTMLElement | null>(null);
const landscapeTouchLastY = ref<number | null>(null);
const weekDays = computed<WeekDay[]>(() => { const weekDays = computed<WeekDay[]>(() => {
const days: WeekDay[] = []; const days: WeekDay[] = [];
@@ -253,7 +261,30 @@ export default defineComponent({
emit('event-click', ev); emit('event-click', ev);
} }
function onLandscapeBodyTouchStart(e: TouchEvent) {
if (!props.landscape) return;
landscapeTouchLastY.value = e.touches[0]?.clientY ?? null;
}
function onLandscapeBodyTouchMove(e: TouchEvent) {
if (!props.landscape) return;
const currentY = e.touches[0]?.clientY;
const previousY = landscapeTouchLastY.value;
const body = landscapeBodyRef.value;
if (currentY == null || previousY == null || !body) return;
body.scrollTop += currentY - previousY;
landscapeTouchLastY.value = currentY;
}
function onLandscapeBodyWheel(e: WheelEvent) {
if (!props.landscape) return;
const body = landscapeBodyRef.value;
if (!body) return;
body.scrollTop += e.deltaY;
}
return { return {
landscapeBodyRef,
timeSlots, timeSlots,
weekDays, weekDays,
eventMap, eventMap,
@@ -262,6 +293,9 @@ export default defineComponent({
eventStyle, eventStyle,
onCellClick, onCellClick,
onEventClick, onEventClick,
onLandscapeBodyTouchStart,
onLandscapeBodyTouchMove,
onLandscapeBodyWheel,
}; };
}, },
}); });
@@ -270,6 +304,8 @@ export default defineComponent({
<style scoped lang="less"> <style scoped lang="less">
@sidebar-width: 1.2rem; @sidebar-width: 1.2rem;
@cell-width: 1.8rem; @cell-width: 1.8rem;
@portrait-header-height: 0.72rem;
@portrait-day-row-min-height: 1.16rem;
.matrix-wrapper { .matrix-wrapper {
width: 100%; width: 100%;
@@ -300,9 +336,12 @@ export default defineComponent({
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box;
pointer-events: none; /* allow click-through to cells underneath */ pointer-events: none; /* allow click-through to cells underneath */
.sidebar-corner { .sidebar-corner {
height: @portrait-header-height;
box-sizing: border-box;
flex-shrink: 0; flex-shrink: 0;
background: #f7f8fa; background: #f7f8fa;
border-bottom: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8;
@@ -312,7 +351,8 @@ export default defineComponent({
} }
.sidebar-row { .sidebar-row {
flex: 1; flex: 1 0 @portrait-day-row-min-height;
min-height: @portrait-day-row-min-height;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -353,18 +393,20 @@ export default defineComponent({
flex-direction: column; flex-direction: column;
min-height: 100%; min-height: 100%;
min-width: fit-content; min-width: fit-content;
box-sizing: border-box;
} }
/* ─── 行:共用 ─── */ /* ─── 行:共用 ─── */
.sr-header { .sr-header {
display: flex; display: flex;
height: @portrait-header-height;
flex-shrink: 0; flex-shrink: 0;
} }
.sr-row { .sr-row {
display: flex; display: flex;
flex: 1; flex: 1 0 @portrait-day-row-min-height;
min-height: 0; min-height: @portrait-day-row-min-height;
} }
/* ─── Spacer(占位与 sidebar 同宽) ─── */ /* ─── Spacer(占位与 sidebar 同宽) ─── */
@@ -484,6 +526,8 @@ export default defineComponent({
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
min-height: 0;
overflow: hidden;
} }
.ml-header { .ml-header {
@@ -491,6 +535,9 @@ export default defineComponent({
flex-shrink: 0; flex-shrink: 0;
background: #f7f8fa; background: #f7f8fa;
border-bottom: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8;
position: sticky;
top: 0;
z-index: 3;
} }
.ml-corner { .ml-corner {
@@ -521,8 +568,12 @@ export default defineComponent({
.ml-body { .ml-body {
flex: 1; flex: 1;
min-height: 0;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
touch-action: none;
padding-bottom: 0.08rem; padding-bottom: 0.08rem;
} }
+9 -14
View File
@@ -69,18 +69,6 @@
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div class="landscape-matrix-header">
<div class="lmh-corner">时段</div>
<div
v-for="day in matrixWeekDays"
:key="day.dateKey"
class="lmh-day"
:class="{ 'lmh-day--today': day.isToday }"
>
<div class="day-name">{{ day.name }}</div>
<div class="day-date">{{ day.date }}</div>
</div>
</div>
<div class="landscape-stage-viewport landscape-stage-viewport--matrix"> <div class="landscape-stage-viewport landscape-stage-viewport--matrix">
<div class="landscape-stage"> <div class="landscape-stage">
<div class="matrix-container matrix-container--landscape"> <div class="matrix-container matrix-container--landscape">
@@ -102,7 +90,6 @@
:week-start="matrixWeekStart" :week-start="matrixWeekStart"
:week-end="matrixWeekEnd" :week-end="matrixWeekEnd"
:landscape="true" :landscape="true"
:hide-landscape-header="true"
@cell-click="onMatrixCellClick" @cell-click="onMatrixCellClick"
@event-click="onMatrixEventClick" @event-click="onMatrixEventClick"
/> />
@@ -1278,7 +1265,15 @@ export default defineComponent({
min-height: 0; min-height: 0;
} }
:deep(.matrix-sidebar) {
padding-bottom: calc(0.72rem + env(safe-area-inset-bottom));
}
:deep(.matrix-scroll) { :deep(.matrix-scroll) {
box-sizing: border-box;
}
:deep(.scroll-inner) {
padding-bottom: calc(0.72rem + env(safe-area-inset-bottom)); padding-bottom: calc(0.72rem + env(safe-area-inset-bottom));
box-sizing: border-box; box-sizing: border-box;
} }
@@ -1486,7 +1481,7 @@ export default defineComponent({
.landscape-stage-viewport--matrix { .landscape-stage-viewport--matrix {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
overflow: auto; overflow: hidden;
} }
.landscape-stage { .landscape-stage {