Files
Kanban/src/App.vue
T
chenshoulin 4aba003a9e fix(ui): Toast 撤销可暂停,存储告警提升为全局
- Toast 自动关闭支持悬停/聚焦暂停,移开后按剩余时间继续
- 存储异常提示移到 App 级,看板与今日视图都能看到
- 归档隐藏与筛选隐藏分开说明,并提示列内排序暂停原因
2026-10-19 17:21:05 +08:00

109 lines
3.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { onMounted } from 'vue'
import { RouterView } from 'vue-router'
import AppNav from '@/components/AppNav.vue'
import BackupPanel from '@/components/BackupPanel.vue'
import PwaUpdatePrompt from '@/components/PwaUpdatePrompt.vue'
import SideDrawer from '@/components/SideDrawer.vue'
import ToastHost from '@/components/ToastHost.vue'
import { useKeyboardShortcuts } from '@/composables/useKeyboardShortcuts'
import { useTaskStore } from '@/stores/tasks'
import { useThemeStore } from '@/stores/theme'
import { useUiStore } from '@/stores/ui'
const SHORTCUTS = [
{ keys: 'N', description: '聚焦「待办」列的快速新增输入框' },
{ keys: '/', description: '聚焦搜索框' },
{ keys: '?', description: '打开本帮助' },
{ keys: 'Esc', description: '关闭当前面板' },
] as const
/** 这些只在卡片获得焦点后生效 */
const CARD_SHORTCUTS = [
{ keys: 'Tab', description: '依次聚焦到任务卡片' },
{ keys: '← →', description: '把卡片移到上一列 / 下一列' },
{ keys: '↑ ↓', description: '在列内上移 / 下移' },
{ keys: 'Enter', description: '打开任务详情' },
] as const
const store = useTaskStore()
const ui = useUiStore()
const theme = useThemeStore()
const { notice } = storeToRefs(store)
onMounted(() => {
store.init()
theme.init()
})
// 帮助面板是应用级的,因此在任何视图下都能用 ?
useKeyboardShortcuts({
'?': () => {
if (!ui.anyPanelOpen) ui.openHelp()
},
})
</script>
<template>
<div class="flex h-full min-h-0 flex-col bg-slate-100 dark:bg-zinc-950">
<AppNav />
<!--
存储异常必须在两个视图都可见:今日视图同样会丢数据,
把提示放在应用级而不是看板里,避免「切到今日就看不见告警」。
-->
<p
v-if="notice"
role="alert"
class="border-b border-amber-300 bg-amber-50 px-4 py-2 text-sm text-amber-800 dark:border-amber-800 dark:bg-amber-950/60 dark:text-amber-300 md:px-6"
>
{{ notice }}
</p>
<main class="flex min-h-0 flex-1 flex-col">
<RouterView />
</main>
</div>
<BackupPanel :open="ui.backupOpen" @close="ui.closeBackup()" />
<SideDrawer :open="ui.helpOpen" title="快捷键" @close="ui.closeHelp()">
<h3 class="mb-2 text-xs font-medium tracking-wide text-slate-400 uppercase dark:text-zinc-500">
全局
</h3>
<dl class="flex flex-col gap-3 text-sm">
<div v-for="item in SHORTCUTS" :key="item.keys" class="flex items-center gap-3">
<dt
class="w-14 shrink-0 rounded-md border border-slate-200 bg-slate-50 px-2 py-0.5 text-center font-mono text-xs text-slate-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"
>
{{ item.keys }}
</dt>
<dd class="text-slate-600 dark:text-zinc-300">{{ item.description }}</dd>
</div>
</dl>
<h3
class="mt-6 mb-2 text-xs font-medium tracking-wide text-slate-400 uppercase dark:text-zinc-500"
>
卡片聚焦后
</h3>
<dl class="flex flex-col gap-3 text-sm">
<div v-for="item in CARD_SHORTCUTS" :key="item.keys" class="flex items-center gap-3">
<dt
class="w-14 shrink-0 rounded-md border border-slate-200 bg-slate-50 px-2 py-0.5 text-center font-mono text-xs text-slate-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"
>
{{ item.keys }}
</dt>
<dd class="text-slate-600 dark:text-zinc-300">{{ item.description }}</dd>
</div>
</dl>
<p class="mt-4 text-xs text-slate-400 dark:text-zinc-500">在输入框内输入时不会触发快捷键。</p>
</SideDrawer>
<ToastHost />
<PwaUpdatePrompt />
</template>