+22
-7
@@ -73,6 +73,7 @@ function openTaskCompose() {
|
||||
taskComposeOpen.value = true
|
||||
nextTick(() => document.querySelector<HTMLInputElement>('.task-compose-input')?.focus())
|
||||
}
|
||||
const taskComposeTitle = computed(() => activeView.value === 'today' ? '添加今天任务' : '添加任务')
|
||||
function closeTaskCompose() { taskComposeOpen.value = false }
|
||||
function activateFloatingAdd(origin: { x: number; y: number }) {
|
||||
composeOrigin.value = origin
|
||||
@@ -143,6 +144,14 @@ const activeName = computed(() => {
|
||||
if (activeView.value === 'settings') return '设置与数据'
|
||||
return lists.value.find((item) => item.id === activeList.value)?.name || '收集箱'
|
||||
})
|
||||
const todayCompletedTasks = computed(() => activeView.value === 'today' ? tasks.value.filter((task) => task.completed).length : 0)
|
||||
const todayOpenTasks = computed(() => activeView.value === 'today' ? Math.max(totalTasks.value - todayCompletedTasks.value, 0) : 0)
|
||||
const todayProgressText = computed(() => {
|
||||
if (activeView.value !== 'today') return ''
|
||||
if (!totalTasks.value) return '今天还没有任务'
|
||||
if (!todayOpenTasks.value) return '今天任务都清掉了'
|
||||
return `还有 ${todayOpenTasks.value} 件事要处理`
|
||||
})
|
||||
const sourceTasks = computed(() => activeView.value === 'trash' ? trash.value : tasks.value)
|
||||
const visibleTasks = computed(() => {
|
||||
const now = new Date()
|
||||
@@ -513,9 +522,9 @@ onMounted(bootstrap)
|
||||
</aside>
|
||||
|
||||
<main>
|
||||
<header class="topbar">
|
||||
<header class="topbar" :class="{ 'today-topbar': activeView==='today' }">
|
||||
<button class="icon" :aria-label="(sidebarCollapsed ? '展开' : '收起') + '菜单'" :aria-expanded="sidebarCollapsed ? 'false' : 'true'" @click="toggleSidebar"><Menu /></button>
|
||||
<div><h1>{{ activeName }}</h1></div>
|
||||
<div><h1>{{ activeName }}</h1><p v-if="activeView==='today'">{{ todayProgressText }},慢慢来。</p></div>
|
||||
<label v-if="['tasks','today','upcoming','trash'].includes(activeView)" class="search"><Search/><input v-model="query" placeholder="搜索任务…" aria-label="搜索任务"><kbd>⌘ K</kbd></label>
|
||||
</header>
|
||||
<template v-if="['habits','settings'].includes(activeView)">
|
||||
@@ -523,10 +532,15 @@ onMounted(bootstrap)
|
||||
</template>
|
||||
<CountdownPanel ref="countdownComposer" v-else-if="activeView==='countdowns'" @notice="toast" />
|
||||
<template v-else>
|
||||
<section v-if="activeView==='today'" class="today-board" aria-label="今日概览">
|
||||
<div class="today-stat"><strong>{{ todayOpenTasks }}</strong><span>待处理</span></div>
|
||||
<div class="today-stat"><strong>{{ todayCompletedTasks }}</strong><span>已完成</span></div>
|
||||
<div class="today-stat muted"><strong>{{ totalTasks }}</strong><span>今日任务</span></div>
|
||||
</section>
|
||||
<template v-if="activeView==='today'">
|
||||
<h3 class="section-heading"><ListTodo/>今日任务</h3>
|
||||
<h3 class="section-heading"><ListTodo/>任务</h3>
|
||||
</template>
|
||||
<div class="list-toolbar"><label v-if="activeView!=='trash'"><input v-model="showCompleted" type="checkbox"> 显示已完成</label><span>第 {{page}} / {{totalPages}} 页 · 共 {{totalTasks}} 项</span><button v-if="query" class="link" @click="query=''">清除搜索</button></div>
|
||||
<div class="list-toolbar"><label v-if="activeView!=='trash'"><input v-model="showCompleted" type="checkbox"> 显示已完成</label><span v-if="activeView==='trash' || totalPages > 1 || totalTasks > 0">{{ totalPages > 1 ? `第 ${page} / ${totalPages} 页 · ` : '' }}共 {{totalTasks}} 项</span><button v-if="query" class="link" @click="query=''">清除搜索</button></div>
|
||||
<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">
|
||||
@@ -540,10 +554,10 @@ onMounted(bootstrap)
|
||||
</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` }" @pointerdown="startTaskPointer(subtask, $event)" @pointermove="moveTaskPointer(subtask, $event)" @pointerup="finishTaskPointer(subtask, $event)" @pointercancel="cancelTaskPointer(subtask)" @touchstart.passive="startTaskSwipe(subtask, $event)" @touchmove.passive="moveTaskSwipe(subtask, $event)" @touchend="finishTaskSwipe(subtask, $event)" @touchcancel="cancelTaskSwipe(subtask)"><GripVertical/><button class="task-check" :aria-label="subtask.completed ? `重新打开${subtask.title}` : `完成${subtask.title}`" :aria-pressed="subtask.completed" @click.stop="toggle(subtask)"><span class="task-check-mark" :class="`p${subtask.priority}`"><Check v-if="subtask.completed" /></span></button><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></article>
|
||||
</template>
|
||||
<div v-if="!visibleTasks.length&&!loading" class="empty"><ListTodo/><b>{{query?'没有匹配的任务':'这里还很安静'}}</b><span>{{query?'换个关键词试试':'写下第一件想完成的小事吧'}}</span></div>
|
||||
<div v-if="!visibleTasks.length&&!loading" class="empty"><ListTodo/><b>{{query?'没有匹配的任务':'这里还很安静'}}</b><span>{{query?'换个关键词试试':'写下第一件想完成的小事吧'}}</span><button v-if="activeView==='today' && !query" class="soft-button empty-action" @click="openTaskCompose"><Plus/>添加今天任务</button></div>
|
||||
</section>
|
||||
<div v-if="activeView==='today'">
|
||||
<h3 class="section-heading"><Repeat2/>今日习惯</h3>
|
||||
<h3 class="section-heading"><Repeat2/>习惯</h3>
|
||||
<MvpPanel ref="habitComposer" view="today-habits" @changed="refreshAll" @notice="toast" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -562,6 +576,7 @@ onMounted(bootstrap)
|
||||
</div></details>
|
||||
<div class="detail-actions"><button class="secondary" @click="saveTask">保存更改</button><button class="danger-text" @click="removeTask(selectedTask)"><Trash2/>移到回收站</button></div>
|
||||
</div>
|
||||
<div v-else-if="activeView==='today'" class="paper today-summary"><ListTodo/><b>{{ todayProgressText }}</b><p>点左侧任务看详情;没任务时就从今天加一件小事开始。</p><div class="today-summary-grid"><span><strong>{{ todayOpenTasks }}</strong>待处理</span><span><strong>{{ todayCompletedTasks }}</strong>已完成</span></div></div>
|
||||
<div v-else class="paper"><ListChecks/><b>选中一个任务</b><p>日期、优先级、子任务和 Markdown 备注会出现在这里。</p></div>
|
||||
</aside>
|
||||
|
||||
@@ -571,7 +586,7 @@ onMounted(bootstrap)
|
||||
<Transition name="task-compose">
|
||||
<div v-if="taskComposeOpen" class="task-compose-mask" @click.self="closeTaskCompose">
|
||||
<form class="task-compose-sheet" :style="taskComposeStyle" role="dialog" aria-modal="true" aria-labelledby="task-compose-title" @submit.prevent="submitTaskCompose">
|
||||
<header><div><small>NEW TASK</small><h2 id="task-compose-title">添加任务</h2></div><button class="icon" type="button" aria-label="关闭添加任务" @click="closeTaskCompose"><X/></button></header>
|
||||
<header><div><small>NEW TASK</small><h2 id="task-compose-title">{{ taskComposeTitle }}</h2></div><button class="icon" type="button" aria-label="关闭添加任务" @click="closeTaskCompose"><X/></button></header>
|
||||
<label>任务名称<input v-model="composeTitle" class="task-compose-input" placeholder="准备做点什么?" autocomplete="off"></label>
|
||||
<div class="task-compose-row"><label>清单<select v-model="composeListId"><option v-for="list in lists" :key="list.id" :value="list.id">{{list.name}}</option></select></label><label>优先级<select v-model.number="composePriority"><option :value="0">无</option><option :value="1">低</option><option :value="2">中</option><option :value="3">高</option></select></label></div>
|
||||
<label>截止时间<input v-model="composeDueAt" type="datetime-local"></label>
|
||||
|
||||
Reference in New Issue
Block a user