fix: align today habit rows with swipe interactions
ci / docker (push) Successful in 3m26s

This commit is contained in:
2026-09-06 10:13:46 +08:00
parent 6b0cc3213d
commit cc98bc53e9
4 changed files with 26 additions and 14 deletions
+21 -9
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { Activity, ArchiveRestore, Download, FileJson, LogOut, Plus, RefreshCw, Trash2, Upload } from 'lucide-vue-next'
import { dateKey, isHabitComplete, mergePage, numericHabitInputValue, shouldToggleRowSwipe } from './lib/mvp-utils'
import { csrfHeader } from './lib/csrf'
@@ -30,6 +30,7 @@ const numericValues = ref<Record<string, number | string>>({})
const todayKey = ref(dateKey(new Date()))
const habitSwipeStart = ref<{ id: string; x: number; y: number } | null>(null)
const habitSwipeOffsets = ref<Record<string, number>>({})
const todayHabits = computed(() => habits.value.filter((item) => item.kind === 'boolean' || (item.cells ?? []).some((cell) => cell.day === todayKey.value && cell.scheduled)))
let dayRolloverTimer: ReturnType<typeof setInterval> | undefined
function formatErrorMessage(detail: unknown): string {
@@ -264,17 +265,28 @@ onBeforeUnmount(() => {
<!-- 习惯TickTick 风格一次只操作一个习惯不再逐格小按钮误触 -->
<template v-if="view === 'habits' || view === 'today-habits'">
<header class="view-intro">
<div><small>{{ view === 'today-habits' ? '今天习惯单独看,一眼就知道还差什么' : '把想坚持的事变成每天的日常' }}</small><h2>{{ view === 'today-habits' ? '今天的习惯' : '习惯' }}</h2></div>
<header v-if="view === 'habits'" class="view-intro">
<div><small>把想坚持的事变成每天的日常</small><h2>习惯</h2></div>
<button class="soft-button" @click="loadHabits"><RefreshCw />刷新</button>
</header>
<!-- 今日习惯只展示今天该做的习惯 -->
<div v-if="view === 'today-habits' && !busy" class="today-strip">
<template v-for="h in habits.filter((item) => item.kind === 'boolean' ? !isDone(item, todayKey) : (item.cells ?? []).find((cell) => cell.day === todayKey && cell.scheduled) !== undefined)" :key="h.id">
<button class="today-chip" :class="{ done: isDone(h, todayKey) }" :aria-label="isDone(h, todayKey) ? `完成${h.name}打卡` : `打卡${h.name}`" @click="toggleHabitFromButton(h)"><Repeat2 />{{ h.name }}</button>
</template>
<div v-if="!habits.some((item) => item.kind === 'boolean' ? !isDone(item, todayKey) : (item.cells ?? []).find((cell) => cell.day === todayKey && cell.scheduled) !== undefined)" class="empty-panel">今天的习惯都完成了 🎉</div>
<!-- 今日习惯只展示今天该做的习惯复用正式习惯行样式 -->
<div v-if="view === 'today-habits' && !busy" class="habit-list today-habit-list">
<article v-for="h in todayHabits" :key="h.id" class="habit-row" :class="{ done: isDone(h, todayKey), swipeable: h.kind !== 'numeric', ready: Math.abs(habitSwipeOffsets[h.id] ?? 0) >= 64 }" :style="{ '--swipe-x': `${habitSwipeOffsets[h.id] ?? 0}px`, '--swipe-width': `${Math.abs(habitSwipeOffsets[h.id] ?? 0)}px` }" @touchstart.passive="startHabitSwipe(h, $event)" @touchmove.passive="moveHabitSwipe(h, $event)" @touchend="finishHabitSwipe(h, $event)" @touchcancel="cancelHabitSwipe(h)">
<span v-if="h.kind !== 'numeric'" class="swipe-bg" :class="{ cancel: isDone(h, todayKey) }">{{ isDone(h, todayKey) ? ' 未完成' : ' 打卡' }}</span>
<div v-if="h.kind !== 'numeric'" class="habit-main">
<span><span class="habit-name">{{ h.name }}</span><small>{{ isDone(h, todayKey) ? '今天已完成 · 左滑取消' : '右滑整行打卡' }}</small></span>
<button class="habit-swipe-hint" :aria-label="isDone(h, todayKey) ? `取消${h.name}今天的打卡` : `完成${h.name}今天的打卡`" :aria-pressed="isDone(h, todayKey)" @click.stop="toggleHabitFromButton(h)">{{ isDone(h, todayKey) ? '已完成' : '打卡' }}</button>
</div>
<div v-else class="habit-main numeric-habit">
<span><span class="habit-name">{{ h.name }}</span><small>今天 {{ logFor(h, todayKey)?.value || 0 }} / {{ h.target || 1 }}{{ h.unit || '' }}</small></span>
<span class="numeric-action">
<input v-model.number="numericValues[h.id]" type="number" min="0" :max="h.max_value ?? undefined" step="any" :placeholder="String(h.target || 1)" :aria-label="`${h.name}今日数值`">
<button class="soft-button" @click="recordNumeric(h, todayKey)">记录</button>
</span>
</div>
</article>
<div v-if="!todayHabits.length" class="empty-panel">今天没有安排习惯轻松一下吧</div>
</div>
<div v-else-if="view === 'today-habits' && busy" class="empty-panel">加载中</div>