This commit is contained in:
+12
-5
@@ -249,7 +249,11 @@ function moveTaskSwipe(task: Task, event: TouchEvent) {
|
||||
if (!start || start.id !== task.id || !touch) return
|
||||
const deltaX = touch.clientX - start.x
|
||||
const deltaY = touch.clientY - start.y
|
||||
if (deltaX > 0 && Math.abs(deltaX) > Math.abs(deltaY)) taskSwipeOffsets.value[task.id] = Math.min(deltaX, 92)
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
||||
if ((!task.completed && deltaX > 0) || (task.completed && deltaX < 0)) {
|
||||
taskSwipeOffsets.value[task.id] = Math.max(-92, Math.min(deltaX, 92))
|
||||
}
|
||||
}
|
||||
}
|
||||
async function finishTaskSwipe(task: Task, event: TouchEvent) {
|
||||
const start = taskSwipeStart.value
|
||||
@@ -258,7 +262,10 @@ async function finishTaskSwipe(task: Task, event: TouchEvent) {
|
||||
taskSwipeOffsets.value[task.id] = 0
|
||||
if (!start || start.id !== task.id || activeView.value === 'trash' || loading.value) return
|
||||
const touch = event.changedTouches[0]
|
||||
if (touch && shouldToggleRowSwipe(touch.clientX - start.x, touch.clientY - start.y)) {
|
||||
const deltaX = touch ? touch.clientX - start.x : 0
|
||||
const deltaY = touch ? touch.clientY - start.y : 0
|
||||
const expectedDirection = task.completed ? deltaX < 0 : deltaX > 0
|
||||
if (touch && expectedDirection && shouldToggleRowSwipe(Math.abs(deltaX), deltaY)) {
|
||||
suppressTaskClickId = task.id
|
||||
window.setTimeout(() => { if (suppressTaskClickId === task.id) suppressTaskClickId = '' }, 400)
|
||||
await toggle(task)
|
||||
@@ -437,8 +444,8 @@ onMounted(bootstrap)
|
||||
<div v-if="activeView!=='trash' && totalPages > 1" class="pager"><button class="secondary" :disabled="page<=1 || loading" @click="previousPage">上一页</button><span>{{page}} / {{totalPages}}</span><button class="secondary" :disabled="page>=totalPages || loading" @click="nextPage">下一页</button></div>
|
||||
<section class="task-list" :class="{loading}">
|
||||
<template v-for="node in taskTree" :key="node.task.id">
|
||||
<article class="task-row swipeable" :class="{done:node.task.completed,selected:selectedTask?.id===node.task.id,ready:(taskSwipeOffsets[node.task.id] ?? 0) >= 64}" :style="{ '--swipe-x': `${taskSwipeOffsets[node.task.id] ?? 0}px` }" @touchstart.passive="startTaskSwipe(node.task, $event)" @touchmove.passive="moveTaskSwipe(node.task, $event)" @touchend="finishTaskSwipe(node.task, $event)" @touchcancel="cancelTaskSwipe(node.task)">
|
||||
<span class="swipe-bg">✓ 完成</span>
|
||||
<article class="task-row swipeable" :class="{done:node.task.completed,selected:selectedTask?.id===node.task.id,ready:Math.abs(taskSwipeOffsets[node.task.id] ?? 0) >= 64}" :style="{ '--swipe-x': `${taskSwipeOffsets[node.task.id] ?? 0}px`, '--swipe-width': `${Math.abs(taskSwipeOffsets[node.task.id] ?? 0)}px` }" @touchstart.passive="startTaskSwipe(node.task, $event)" @touchmove.passive="moveTaskSwipe(node.task, $event)" @touchend="finishTaskSwipe(node.task, $event)" @touchcancel="cancelTaskSwipe(node.task)">
|
||||
<span class="swipe-bg" :class="{ cancel: node.task.completed }">{{ node.task.completed ? '↩ 未完成' : '✓ 完成' }}</span>
|
||||
<div class="task-main" role="button" tabindex="0" @click="activeView==='trash'?undefined:selectTaskUnlessSwiped(node.task)" @keydown.enter="activeView==='trash'?undefined:selectTaskUnlessSwiped(node.task)" @keydown.space.prevent="activeView==='trash'?undefined:selectTaskUnlessSwiped(node.task)"><strong>{{node.task.title}}</strong><span class="meta"><span v-if="node.task.due_at"><CalendarDays/>{{formatDue(node.task.due_at)}}</span><span v-if="node.subtasks.length"><ListChecks/>{{node.subtasks.filter(t=>t.completed).length}}/{{node.subtasks.length}}</span><i v-for="tag in node.task.tags" :key="tag.id" class="tag-dot" :style="{background:tag.color}" :title="tag.name"/></span></div>
|
||||
<button v-if="activeView!=='trash'" class="task-swipe-hint" :aria-label="node.task.completed ? `重新打开${node.task.title}` : `完成${node.task.title}`" :aria-pressed="node.task.completed" @click.stop="toggle(node.task)">{{ node.task.completed ? '已完成' : '完成' }}</button>
|
||||
<span v-if="node.task.priority" class="priority" :class="`p${node.task.priority}`">{{['','低','中','高'][node.task.priority]}}</span>
|
||||
@@ -446,7 +453,7 @@ onMounted(bootstrap)
|
||||
<button v-else class="icon ghost" aria-label="删除任务" @click.stop="removeTask(node.task)"><Trash2/></button>
|
||||
<button v-if="activeView==='trash'" class="icon danger ghost" aria-label="永久删除" @click.stop="purgeTask(node.task)"><X/></button>
|
||||
</article>
|
||||
<article v-for="subtask in node.subtasks" :key="subtask.id" class="task-row subtask swipeable" :class="{done:subtask.completed,ready:(taskSwipeOffsets[subtask.id] ?? 0) >= 64}" :style="{ '--swipe-x': `${taskSwipeOffsets[subtask.id] ?? 0}px` }" @touchstart.passive="startTaskSwipe(subtask, $event)" @touchmove.passive="moveTaskSwipe(subtask, $event)" @touchend="finishTaskSwipe(subtask, $event)" @touchcancel="cancelTaskSwipe(subtask)"><span class="swipe-bg">✓ 完成</span><GripVertical/><div class="task-main" role="button" tabindex="0" @click="selectTaskUnlessSwiped(subtask)" @keydown.enter="selectTaskUnlessSwiped(subtask)" @keydown.space.prevent="selectTaskUnlessSwiped(subtask)"><strong>{{subtask.title}}</strong></div><button class="task-swipe-hint" :aria-label="subtask.completed ? `重新打开${subtask.title}` : `完成${subtask.title}`" :aria-pressed="subtask.completed" @click.stop="toggle(subtask)">{{ subtask.completed ? '已完成' : '完成' }}</button></article>
|
||||
<article v-for="subtask in node.subtasks" :key="subtask.id" class="task-row subtask swipeable" :class="{done:subtask.completed,ready:Math.abs(taskSwipeOffsets[subtask.id] ?? 0) >= 64}" :style="{ '--swipe-x': `${taskSwipeOffsets[subtask.id] ?? 0}px`, '--swipe-width': `${Math.abs(taskSwipeOffsets[subtask.id] ?? 0)}px` }" @touchstart.passive="startTaskSwipe(subtask, $event)" @touchmove.passive="moveTaskSwipe(subtask, $event)" @touchend="finishTaskSwipe(subtask, $event)" @touchcancel="cancelTaskSwipe(subtask)"><span class="swipe-bg" :class="{ cancel: subtask.completed }">{{ subtask.completed ? '↩ 未完成' : '✓ 完成' }}</span><GripVertical/><div class="task-main" role="button" tabindex="0" @click="selectTaskUnlessSwiped(subtask)" @keydown.enter="selectTaskUnlessSwiped(subtask)" @keydown.space.prevent="selectTaskUnlessSwiped(subtask)"><strong>{{subtask.title}}</strong></div><button class="task-swipe-hint" :aria-label="subtask.completed ? `重新打开${subtask.title}` : `完成${subtask.title}`" :aria-pressed="subtask.completed" @click.stop="toggle(subtask)">{{ subtask.completed ? '已完成' : '完成' }}</button></article>
|
||||
</template>
|
||||
<div v-if="!visibleTasks.length&&!loading" class="empty"><ListTodo/><b>{{query?'没有匹配的任务':'这里还很安静'}}</b><span>{{query?'换个关键词试试':'写下第一件想完成的小事吧'}}</span></div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user