feat: add responsive task calendar picker
ci / gitleaks (push) Successful in 7s
ci / docker (push) Successful in 3m29s

This commit is contained in:
2026-09-09 17:44:49 +08:00
parent 33790aaeb7
commit 30705e1cec
8 changed files with 391 additions and 22 deletions
+10 -14
View File
@@ -12,6 +12,7 @@ import { createCompletionPulse } from './lib/completion-motion'
import MvpPanel from './MvpPanel.vue'
import CountdownPanel from './CountdownPanel.vue'
import FloatingAddButton from './components/FloatingAddButton.vue'
import CalendarPicker from './components/CalendarPicker.vue'
type FolderItem = { id: string; name: string }
type TaskList = { id: string; folder_id: string | null; name: string; is_inbox: boolean }
@@ -78,7 +79,8 @@ const composeListId = ref('')
const composeDueAt = ref('')
const composeHasTime = ref(false)
const composeTime = ref('12:00')
const composeDatePicker = ref<HTMLInputElement | null>(null)
const composeCalendarOpen = ref(false)
const composeDateButton = ref<HTMLButtonElement | null>(null)
const composeTimePicker = ref<HTMLInputElement | null>(null)
const composePriority = ref(0)
const composeDescription = ref('')
@@ -110,6 +112,7 @@ function openTaskCompose() {
composeDescription.value = ''
composeRepeat.value = 'none'
composeRepeatConfig.value = defaultRepeatConfig()
composeCalendarOpen.value = false
taskComposeOpen.value = true
nextTick(() => document.querySelector<HTMLInputElement>('.task-compose-input')?.focus())
}
@@ -120,16 +123,6 @@ const composeDueLabel = computed(() => {
const value = new Date(year, month - 1, day)
return Number.isNaN(value.getTime()) ? '设置截止日期' : new Intl.DateTimeFormat('zh-CN', { month: 'short', day: 'numeric', weekday: 'short' }).format(value)
})
function openComposeDuePicker() {
const picker = composeDatePicker.value as (HTMLInputElement & { showPicker?: () => void }) | null
if (!picker) return
try {
if (picker.showPicker) picker.showPicker()
else picker.click()
} catch {
picker.click()
}
}
function clearComposeDueDate() {
composeDueAt.value = ''
composeHasTime.value = false
@@ -142,7 +135,7 @@ function addComposeTime() {
try { picker?.showPicker?.() } catch { picker?.focus() }
})
}
function closeTaskCompose() { taskComposeOpen.value = false }
function closeTaskCompose() { composeCalendarOpen.value = false; taskComposeOpen.value = false }
function activateFloatingAdd(origin: { x: number; y: number }) {
composeOrigin.value = origin
if (activeView.value === 'habits') habitComposer.value?.openHabitComposer(origin)
@@ -305,6 +298,9 @@ const filteredTaskTree = computed(() => groupTaskTree(visibleTasks.value))
const taskTree = computed(() => filteredTaskTree.value)
const overdueTaskTree = computed(() => groupTaskTree(overdueTasks.value))
let searchTimer: number | undefined
watch(composeDueAt, (value) => {
if (!value) { composeHasTime.value = false; composeRepeat.value = 'none' }
})
watch(query, () => {
if (searchTimer) window.clearTimeout(searchTimer)
if (!isTaskView(activeView.value)) return
@@ -945,10 +941,10 @@ onMounted(bootstrap)
<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>
<div class="task-compose-date-actions" aria-label="截止时间">
<div class="task-compose-date-control">
<button class="task-compose-date-chip" :class="{selected:composeDueAt}" type="button" @click="openComposeDuePicker">
<button ref="composeDateButton" class="task-compose-date-chip" :class="{selected:composeDueAt}" type="button" aria-haspopup="dialog" :aria-expanded="composeCalendarOpen" @click="composeCalendarOpen=true">
<CalendarDays/><span>{{ composeDueLabel }}</span>
</button>
<input ref="composeDatePicker" v-model="composeDueAt" class="task-compose-native-picker" type="date" aria-label="选择截止日期" tabindex="-1">
<CalendarPicker v-model:open="composeCalendarOpen" v-model="composeDueAt" :anchor="composeDateButton"/>
<button v-if="composeDueAt" class="task-compose-date-clear" type="button" aria-label="清除截止日期" @click="clearComposeDueDate"><X/></button>
</div>
<button v-if="composeDueAt && !composeHasTime" class="task-compose-time-add" type="button" @click="addComposeTime">添加时间</button>