fix: align desktop habit detail with task pane
ci / gitleaks (push) Successful in 7s
ci / docker (push) Successful in 3m33s

This commit is contained in:
2026-09-20 10:52:39 +08:00
parent 5a3620bf8a
commit 3a23c6cd44
6 changed files with 70 additions and 18 deletions
+10 -4
View File
@@ -168,6 +168,7 @@ const countdownComposer = ref<InstanceType<typeof CountdownPanel> | null>(null)
const memoPanel = ref<InstanceType<typeof MemoPanel> | null>(null)
const memoTrash = ref(false)
const memoDetailOpen = ref(false)
const habitDetailOpen = ref(false)
const compactLayout = ref(window.innerWidth <= 930)
const memoShellState = computed(() => deriveMemoShellState({ view: activeView.value, detailOpen: memoDetailOpen.value, compact: compactLayout.value, trash: memoTrash.value }))
const memoBackgroundInert = computed(() => memoShellState.value.backgroundInert)
@@ -696,7 +697,7 @@ async function switchView(view: View, listId?: string) {
if (listId) activeList.value = listId
writeStoredNavigation(window.localStorage, NAVIGATION_STORAGE_KEY, view, activeList.value)
page.value = 1
selectedTask.value = null; taskSelectionGeneration.value += 1; mobileSidebar.value = false; mobileDetail.value = false; taskComposeGeneration.value += 1; taskComposeOpen.value = false; sidebarCreateOpen.value = false; sidebarAction.value = null
habitComposer.value?.closeHabitDetail(true); selectedTask.value = null; habitDetailOpen.value = false; taskSelectionGeneration.value += 1; mobileSidebar.value = false; mobileDetail.value = false; taskComposeGeneration.value += 1; taskComposeOpen.value = false; sidebarCreateOpen.value = false; sidebarAction.value = null
if (view !== 'memos') memoDetailOpen.value = false
if (view === 'trash') await loadTrash()
else if (view === 'today') await loadTodayView()
@@ -1159,6 +1160,10 @@ function closeTaskDetail() {
mobileDetail.value = false
selectedTask.value = null
}
function handleHabitDetail(open: boolean) {
if (open) closeTaskDetail()
habitDetailOpen.value = open
}
function addSelectedDueTime() {
selectedDueHasTime.value = true
nextTick(() => {
@@ -1172,6 +1177,7 @@ function clearSelectedDueDate() {
selectedTaskRepeat.value = 'none'
}
function selectTask(task: Task) {
habitComposer.value?.closeHabitDetail(true)
taskSelectionGeneration.value += 1
selectedTask.value = { ...task, subtasks: task.subtasks ? [...task.subtasks] : [] }
const due = parseTaskDueDraft(task.due_at, task.due_has_time)
@@ -1521,7 +1527,7 @@ onUnmounted(() => {
<p v-if="initialized" class="auth-footnote">登录后继续你的清单</p>
</form>
</div>
<div v-else class="shell" :class="{ 'today-active': activeView==='today', 'sidebar-collapsed': sidebarCollapsed, 'detail-open': Boolean(selectedTask), 'memo-detail-open': activeView==='memos' && memoDetailOpen, 'mobile-sidebar-open': mobileSidebar }">
<div v-else class="shell" :class="{ 'today-active': activeView==='today', 'sidebar-collapsed': sidebarCollapsed, 'detail-open': Boolean(selectedTask) || habitDetailOpen, 'memo-detail-open': activeView==='memos' && memoDetailOpen, 'mobile-sidebar-open': mobileSidebar }">
<div v-if="mobileSidebar || mobileDetail" class="scrim" @click="mobileSidebar=false;mobileDetail=false" />
<aside class="sidebar" :inert="memoBackgroundInert ? true : undefined" :class="{ open: mobileSidebar }" @keydown.esc="sidebarCreateOpen=false;closeArchivedListAction();closeSidebarAction()">
<div class="brand-row"><div class="brand small brand-lockup"><img class="brand-logo" src="/dodo-logo.svg" alt=""><span class="brand-wordmark">dodo</span></div><button class="icon mobile-only" aria-label="关闭菜单" @click="mobileSidebar=false"><X /></button></div>
@@ -1599,7 +1605,7 @@ onUnmounted(() => {
<div v-if="!['today','tasks','upcoming','habits','settings'].includes(activeView)" class="topbar-title"><h1 :title="activeName">{{ activeName }}</h1></div>
</header>
<template v-if="['habits','settings'].includes(activeView)">
<MvpPanel ref="habitComposer" :key="activeView" :view="activeView as 'habits'|'settings'" :show-completed="showCompleted" @update:show-completed="showCompleted=$event" @changed="refreshAll" @notice="toast" @logout="completeLogout" />
<MvpPanel ref="habitComposer" :key="activeView" :view="activeView as 'habits'|'settings'" :show-completed="showCompleted" :compact-layout="compactLayout" @update:show-completed="showCompleted=$event" @detail="handleHabitDetail" @changed="refreshAll" @notice="toast" @logout="completeLogout" />
</template>
<MemoPanel v-else-if="activeView==='memos'" ref="memoPanel" :request="api" @scope="memoTrash=$event==='trash'" @detail="memoDetailOpen=$event" @notice="toast" />
<CountdownPanel ref="countdownComposer" v-else-if="activeView==='countdowns'" @notice="toast" />
@@ -1645,7 +1651,7 @@ onUnmounted(() => {
<div v-if="activeView==='today'" class="today-habits-section today-section-anchor">
<button id="today-habits-heading" class="today-section-toggle" type="button" :aria-expanded="!todaySectionCollapse.habits" aria-controls="today-habits" @click="toggleTodaySection('habits')"><span class="today-section-title">习惯</span><span class="today-section-summary">{{todayHabitTotal}}</span><span class="today-section-chevron" aria-hidden="true">{{ todaySectionCollapse.habits ? '' : '⌄' }}</span></button>
<div v-show="!todaySectionCollapse.habits" id="today-habits" role="region" aria-labelledby="today-habits-heading">
<MvpPanel ref="habitComposer" view="today-habits" :show-completed="showCompleted" @changed="refreshAll" @notice="toast" @summary="updateTodayHabitSummary" />
<MvpPanel ref="habitComposer" view="today-habits" :show-completed="showCompleted" :compact-layout="compactLayout" @detail="handleHabitDetail" @changed="refreshAll" @notice="toast" @summary="updateTodayHabitSummary" />
</div>
</div>
</template>