feat: 补齐今日视图待办组、卡片键盘操作、深色模式
今日视图 - 补上「待办」组。此前只收 逾期 / 今天到期 / 进行中,导致待办列里没设 截止日期的任务在今日视图里完全消失 —— 用户明明有一堆事没做,却看到 「今天没有待办事项」。现在只要任务未完成且未归档,就必定出现在某个 分组里(归属优先级:逾期 > 今天到期 > 进行中 > 待办)。 - 统计卡片改为与分组一一对应(逾期 / 今天到期 / 进行中 / 本周完成), 不再用「今日待办」这种把四组混在一起、与列表长度对不上的指标。 键盘操作(可访问性) - 卡片可聚焦(Tab / 点击),← → 换列、↑ ↓ 列内调序、Enter 打开详情。 - 换列时追加到目标列末尾:用全量长度而不是可见下标,筛选态下也安全; 列内调序要求该列渲染的是全量,否则禁用(与拖拽的规则一致)。 - 移动后手动恢复焦点 —— 卡片跨列会被重建,不恢复的话连续按键会失效。 深色模式 - 三态:跟随系统 / 浅色 / 深色,选择持久化到 localStorage。 - index.html 内联脚本在首帧之前应用主题,避免深色下闪白。 - 用 @custom-variant dark 做 class 控制(而不是纯 media query), 才能支持「强制浅色」;html 的 color-scheme 让原生控件与滚动条跟随。 验证:单元测试 480 个、端到端测试 25 个全部通过,lint 与构建无错误。 截图脚本新增深色与移动端深色两组,便于后续对照。
This commit is contained in:
@@ -88,30 +88,32 @@ function formatTime(iso: string): string {
|
||||
<template>
|
||||
<SideDrawer :open="props.open" title="备份与恢复" @close="emit('close')">
|
||||
<div class="flex flex-col gap-6">
|
||||
<p class="rounded-md bg-amber-50 px-3 py-2 text-xs leading-relaxed text-amber-800">
|
||||
<p
|
||||
class="rounded-md bg-amber-50 px-3 py-2 text-xs leading-relaxed text-amber-800 dark:bg-amber-950/60 dark:text-amber-300"
|
||||
>
|
||||
数据只保存在这台设备的浏览器里。清理浏览器数据会导致任务丢失,建议定期导出备份。
|
||||
</p>
|
||||
|
||||
<p
|
||||
v-if="message"
|
||||
role="status"
|
||||
class="rounded-md border border-emerald-300 bg-emerald-50 px-3 py-2 text-sm text-emerald-800"
|
||||
class="rounded-md border border-emerald-300 bg-emerald-50 px-3 py-2 text-sm text-emerald-800 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-300"
|
||||
>
|
||||
{{ message }}
|
||||
</p>
|
||||
<p
|
||||
v-if="error"
|
||||
role="alert"
|
||||
class="rounded-md border border-rose-300 bg-rose-50 px-3 py-2 text-sm text-rose-700"
|
||||
class="rounded-md border border-rose-300 bg-rose-50 px-3 py-2 text-sm text-rose-700 dark:border-rose-800 dark:bg-rose-950 dark:text-rose-300"
|
||||
>
|
||||
{{ error }}
|
||||
</p>
|
||||
|
||||
<section class="flex flex-col gap-2">
|
||||
<h3 class="text-sm font-medium text-slate-700">导出</h3>
|
||||
<h3 class="text-sm font-medium text-slate-700 dark:text-slate-300">导出</h3>
|
||||
<button
|
||||
type="button"
|
||||
class="self-start rounded-md bg-sky-600 px-3 py-1.5 text-sm font-medium text-white transition hover:bg-sky-700"
|
||||
class="self-start rounded-md bg-sky-600 px-3 py-1.5 text-sm font-medium text-white transition hover:bg-sky-700 dark:hover:bg-sky-500"
|
||||
@click="exportNow"
|
||||
>
|
||||
导出为 JSON
|
||||
@@ -119,35 +121,35 @@ function formatTime(iso: string): string {
|
||||
</section>
|
||||
|
||||
<section class="flex flex-col gap-2">
|
||||
<h3 class="text-sm font-medium text-slate-700">导入</h3>
|
||||
<h3 class="text-sm font-medium text-slate-700 dark:text-slate-300">导入</h3>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
accept="application/json,.json"
|
||||
class="block w-full text-sm text-slate-600 file:mr-3 file:rounded-md file:border file:border-slate-300 file:bg-white file:px-3 file:py-1.5 file:text-sm file:text-slate-600"
|
||||
class="block w-full text-sm text-slate-600 file:mr-3 file:rounded-md file:border file:border-slate-300 file:bg-white file:px-3 file:py-1.5 file:text-sm file:text-slate-600 dark:text-slate-400 dark:file:border-slate-600 dark:file:bg-slate-800 dark:file:text-slate-300"
|
||||
aria-label="选择导入文件"
|
||||
@change="onFileChange"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="pending"
|
||||
class="flex flex-col gap-2 rounded-md border border-amber-300 bg-amber-50 px-3 py-2"
|
||||
class="flex flex-col gap-2 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 dark:border-amber-800 dark:bg-amber-950/60"
|
||||
>
|
||||
<p class="text-sm text-amber-900">
|
||||
<p class="text-sm text-amber-900 dark:text-amber-200">
|
||||
将从「{{ pending.fileName }}」导入 {{ pending.count }} 个任务,
|
||||
<strong>并覆盖当前全部 {{ store.tasks.length }} 个任务</strong>。此操作不可撤销。
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-md bg-rose-600 px-3 py-1 text-sm font-medium text-white transition hover:bg-rose-700"
|
||||
class="rounded-md bg-rose-600 px-3 py-1 text-sm font-medium text-white transition hover:bg-rose-700 dark:hover:bg-rose-500"
|
||||
@click="confirmImport"
|
||||
>
|
||||
确认覆盖导入
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-md border border-slate-300 px-3 py-1 text-sm text-slate-600 transition hover:bg-white"
|
||||
class="rounded-md border border-slate-300 px-3 py-1 text-sm text-slate-600 transition hover:bg-white dark:border-slate-600 dark:text-slate-300 dark:hover:bg-slate-800"
|
||||
@click="pending = null"
|
||||
>
|
||||
取消
|
||||
@@ -157,26 +159,30 @@ function formatTime(iso: string): string {
|
||||
</section>
|
||||
|
||||
<section class="flex flex-col gap-2">
|
||||
<h3 class="text-sm font-medium text-slate-700">自动备份</h3>
|
||||
<p class="text-xs text-slate-500">每次打开看板时自动留一份,最多保留 3 份。</p>
|
||||
<h3 class="text-sm font-medium text-slate-700 dark:text-slate-300">自动备份</h3>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">
|
||||
每次打开看板时自动留一份,最多保留 3 份。
|
||||
</p>
|
||||
|
||||
<ul v-if="snapshots.length > 0" class="flex flex-col gap-2">
|
||||
<li
|
||||
v-for="item in snapshots"
|
||||
:key="item.key"
|
||||
class="flex items-center justify-between rounded-md border border-slate-200 px-3 py-2"
|
||||
class="flex items-center justify-between rounded-md border border-slate-200 px-3 py-2 dark:border-slate-700"
|
||||
>
|
||||
<span class="text-sm text-slate-600">{{ formatTime(item.savedAt) }}</span>
|
||||
<span class="text-sm text-slate-600 dark:text-slate-300">
|
||||
{{ formatTime(item.savedAt) }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-md px-2 py-0.5 text-sm text-sky-700 transition hover:bg-sky-50"
|
||||
class="rounded-md px-2 py-0.5 text-sm text-sky-700 transition hover:bg-sky-50 dark:text-sky-400 dark:hover:bg-sky-950"
|
||||
@click="restore(item.key)"
|
||||
>
|
||||
恢复
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<p v-else class="text-sm text-slate-400">暂无自动备份</p>
|
||||
<p v-else class="text-sm text-slate-400 dark:text-slate-500">暂无自动备份</p>
|
||||
</section>
|
||||
</div>
|
||||
</SideDrawer>
|
||||
|
||||
Reference in New Issue
Block a user