feat: sort tasks by due time
This commit is contained in:
+35
-6
@@ -5,7 +5,7 @@ import {
|
||||
Ellipsis, GripVertical, Inbox, ListChecks, ListTodo, Menu, Pencil, Plus, Search,
|
||||
Settings, Trash2, X, Repeat2,
|
||||
} from 'lucide-vue-next'
|
||||
import { buildTaskRrule, defaultTaskDueAt, filterTasks, fromDateTimeLocal, groupTaskTree, moveItemWithinScope, parseTaskRrule, renderMarkdown, toDateTimeLocal, type TaskRepeatConfig } from './lib/task-utils'
|
||||
import { buildTaskRrule, defaultTaskDueAt, filterTasks, fromDateTimeLocal, groupTaskTree, isSameTaskSortTier, moveItemWithinScope, parseTaskRrule, renderMarkdown, toDateTimeLocal, type TaskRepeatConfig } from './lib/task-utils'
|
||||
import { beginLatestRequest, createMutationReconciler, formatApiErrorDetail, isLatestRequest, isTaskView, loadCountdownCache, nextTotalAfterLocalTaskAdd, nextTotalAfterLocalTaskRemoval, normalizeRequiredName, performTrashMutation, readStoredBoolean, readStoredNavigation, runLatestRequest, shouldToggleRowSwipe, startPrimaryWithBackground, writeStoredBoolean, writeStoredNavigation } from './lib/mvp-utils'
|
||||
import { csrfHeader } from './lib/csrf'
|
||||
import { createCompletionPulse } from './lib/completion-motion'
|
||||
@@ -100,6 +100,7 @@ const taskPointerStart = ref<{ id: string; x: number; y: number } | null>(null)
|
||||
const taskSwipeOffsets = ref<Record<string, number>>({})
|
||||
const taskReorder = ref<{ id: string; startY: number; offsetY: number } | null>(null)
|
||||
const taskReorderTarget = ref('')
|
||||
const taskReorderBlocked = ref(false)
|
||||
const taskComposeOpen = ref(false)
|
||||
const composeTitle = ref('')
|
||||
const composeTitleError = ref('')
|
||||
@@ -622,6 +623,7 @@ function startTaskReorder(task: Task, event: PointerEvent) {
|
||||
if (activeView.value === 'trash' || loading.value || query.value || totalPages.value > 1) return
|
||||
taskReorder.value = { id: task.id, startY: event.clientY, offsetY: 0 }
|
||||
taskReorderTarget.value = task.id
|
||||
taskReorderBlocked.value = false
|
||||
try { (event.currentTarget as Element).setPointerCapture(event.pointerId) } catch { /* synthetic events */ }
|
||||
}
|
||||
function taskById(id: string) {
|
||||
@@ -636,16 +638,33 @@ function moveTaskReorder(task: Task, event: PointerEvent) {
|
||||
const row = document.elementsFromPoint(event.clientX, event.clientY)
|
||||
.map((element) => element.closest<HTMLElement>('[data-task-id]'))
|
||||
.find((element) => element && element !== handle.closest('[data-task-id]'))
|
||||
if (row?.dataset.taskId) taskReorderTarget.value = row.dataset.taskId
|
||||
const target = row?.dataset.taskId ? taskById(row.dataset.taskId) : undefined
|
||||
if (target && target.list_id === task.list_id && (target.parent_id ?? null) === (task.parent_id ?? null) && isSameTaskSortTier(task, target)) {
|
||||
taskReorderTarget.value = target.id
|
||||
taskReorderBlocked.value = false
|
||||
} else {
|
||||
taskReorderTarget.value = ''
|
||||
taskReorderBlocked.value = Boolean(target)
|
||||
}
|
||||
}
|
||||
async function finishTaskReorder(task: Task, event: PointerEvent) {
|
||||
const drag = taskReorder.value
|
||||
const targetId = taskReorderTarget.value
|
||||
const blocked = taskReorderBlocked.value
|
||||
taskReorder.value = null
|
||||
taskReorderTarget.value = ''
|
||||
taskReorderBlocked.value = false
|
||||
if (blocked) {
|
||||
toast('只能调整相同完成状态和截止时间档的任务顺序')
|
||||
return
|
||||
}
|
||||
if (!drag || drag.id !== task.id || !targetId || targetId === task.id) return
|
||||
const target = taskById(targetId)
|
||||
if (!target || (target.parent_id ?? null) !== (task.parent_id ?? null)) return
|
||||
if (!target || target.list_id !== task.list_id || (target.parent_id ?? null) !== (task.parent_id ?? null)) return
|
||||
if (!isSameTaskSortTier(task, target)) {
|
||||
toast('只能调整相同完成状态和截止时间档的任务顺序')
|
||||
return
|
||||
}
|
||||
const placement = event.clientY >= drag.startY ? 'after' : 'before'
|
||||
const previous = tasks.value
|
||||
const previousSelected = selectedTask.value
|
||||
@@ -657,12 +676,12 @@ async function finishTaskReorder(task: Task, event: PointerEvent) {
|
||||
if (reordered === parent.subtasks) return
|
||||
tasks.value = tasks.value.map((item) => item.id === parent.id ? { ...item, subtasks: reordered } : item)
|
||||
if (selectedTask.value?.id === parent.id) selectedTask.value = { ...selectedTask.value, subtasks: reordered }
|
||||
ids = reordered.map((item) => item.id)
|
||||
ids = reordered.filter((item) => isSameTaskSortTier(task, item)).map((item) => item.id)
|
||||
} else {
|
||||
const next = moveItemWithinScope(previous, task.id, targetId, placement)
|
||||
if (next === previous) return
|
||||
tasks.value = next
|
||||
ids = next.map((item) => item.id)
|
||||
ids = next.filter((item) => isSameTaskSortTier(task, item)).map((item) => item.id)
|
||||
}
|
||||
try {
|
||||
await api('/tasks/reorder', { method: 'PUT', body: JSON.stringify({ task_ids: ids }) })
|
||||
@@ -676,6 +695,7 @@ async function finishTaskReorder(task: Task, event: PointerEvent) {
|
||||
function cancelTaskReorder() {
|
||||
taskReorder.value = null
|
||||
taskReorderTarget.value = ''
|
||||
taskReorderBlocked.value = false
|
||||
}
|
||||
|
||||
function startTaskSwipe(task: Task, event: TouchEvent) {
|
||||
@@ -771,6 +791,15 @@ function selectTaskUnlessSwiped(task: Task, toggleChildren = false) {
|
||||
if (toggleChildren) toggleTaskChildren(task)
|
||||
selectTask(task)
|
||||
}
|
||||
async function refreshTodayAfterTaskSave() {
|
||||
if (activeView.value !== 'today') return
|
||||
const request = beginLatestRequest('tasks')
|
||||
await Promise.all([
|
||||
loadTasksPage(request),
|
||||
loadOverdueTasks(request),
|
||||
loadTodayTaskSummary(),
|
||||
])
|
||||
}
|
||||
async function saveTask() {
|
||||
if (!selectedTask.value) return
|
||||
const normalized = normalizeRequiredName(selectedTask.value.title)
|
||||
@@ -793,7 +822,7 @@ async function saveTask() {
|
||||
}
|
||||
}
|
||||
if (selectedTask.value) selectedTask.value = { ...selectedTask.value, ...updated }
|
||||
if (activeView.value === 'today') void loadTodayTaskSummary()
|
||||
await refreshTodayAfterTaskSave()
|
||||
toast('已保存')
|
||||
} catch (reason) { fail(reason) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user