style: 重做界面视觉与信息层级
先截图审视现状,再针对性修改。主要问题与改动如下。 工具栏 - 标签筛选从「平铺全部标签」改为下拉面板,标签多时不再换行挤爆工具栏 - 备份与快捷键帮助移到导航栏 —— 它们是应用级操作,不该挤在看板工具栏里, 顺带修好了「? 帮助只在看板可用」的问题 - 到期统计(今日到期 / 逾期)合并为可点击的筛选入口,去掉两个独立控件 - 移动端工具栏从占半屏降到三行紧凑布局 卡片 - 优先级从彩色胶囊改为左侧色条,扫视一列时比读文字更快分辨轻重 - 元信息改为「优先级 · 日期 · 标签」的轻量文本行,不再三种胶囊叠在一起 - 完成态用勾选图标 + 降低不透明度 布局 - 今日视图限宽居中(max-w-3xl):此前 1440px 下卡片被拉满整屏, 标题与元信息之间出现大片空白 - 修复移动端卡片被压成细条:TaskCard 的 overflow-hidden(用于裁切左侧色条) 会让 flex 项的最小尺寸解析为 0,补 shrink-0 修正; 同时改为移动端按内容高度堆叠、桌面端列内滚动 其他 - 导航栏品牌改用看板图标,消除「看板」重复出现两次 - 统一卡片阴影、圆角与键盘焦点样式 - 新增 scripts/screenshot.mjs,便于后续对照审视视觉改动 验证:单元测试 452 个、端到端测试 23 个全部通过,lint 与构建无错误。
This commit is contained in:
+68
-44
@@ -15,67 +15,91 @@ const overdue = computed(() => isOverdue(props.task.dueDate, props.task.status))
|
||||
const visibleTags = computed(() => props.task.tags.slice(0, MAX_VISIBLE_TAGS))
|
||||
const hiddenTagCount = computed(() => Math.max(0, props.task.tags.length - MAX_VISIBLE_TAGS))
|
||||
|
||||
/** 三档优先级使用不同色系,保证一眼可区分 */
|
||||
const priorityClass = computed(() => {
|
||||
/**
|
||||
* 优先级用左侧色条表达,而不是一枚彩色胶囊。
|
||||
* 色条几乎不占空间,扫视一列卡片时却比文字更快分辨出轻重。
|
||||
*/
|
||||
const priorityBarClass = computed(() => {
|
||||
switch (props.task.priority) {
|
||||
case 3:
|
||||
return 'bg-rose-100 text-rose-700'
|
||||
return 'bg-rose-400'
|
||||
case 2:
|
||||
return 'bg-amber-100 text-amber-700'
|
||||
return 'bg-amber-400'
|
||||
default:
|
||||
return 'bg-slate-100 text-slate-500'
|
||||
return 'bg-slate-300'
|
||||
}
|
||||
})
|
||||
|
||||
const priorityTextClass = computed(() => {
|
||||
switch (props.task.priority) {
|
||||
case 3:
|
||||
return 'text-rose-600'
|
||||
case 2:
|
||||
return 'text-amber-700'
|
||||
default:
|
||||
return 'text-slate-400'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article
|
||||
class="cursor-pointer rounded-md border border-slate-200 bg-white px-2.5 py-2 shadow-sm transition"
|
||||
:class="isDone ? 'opacity-60' : 'hover:border-slate-300 hover:shadow'"
|
||||
class="relative shrink-0 cursor-pointer overflow-hidden rounded-lg border border-slate-200 bg-white shadow-card transition duration-150"
|
||||
:class="isDone ? 'opacity-60' : 'hover:-translate-y-px hover:border-slate-300 hover:shadow-card-hover'"
|
||||
:data-task-id="task.id"
|
||||
@click="emit('open', task.id)"
|
||||
>
|
||||
<div class="flex items-start gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="mt-0.5 size-4 shrink-0 rounded-full border transition"
|
||||
:class="
|
||||
isDone ? 'border-emerald-500 bg-emerald-500' : 'border-slate-300 hover:border-emerald-500'
|
||||
"
|
||||
:aria-label="isDone ? '标记为未完成' : '标记为已完成'"
|
||||
:aria-pressed="isDone"
|
||||
@click.stop="emit('toggle', task.id)"
|
||||
/>
|
||||
<span class="absolute inset-y-0 left-0 w-[3px]" :class="priorityBarClass" aria-hidden="true" />
|
||||
|
||||
<h3
|
||||
class="min-w-0 flex-1 text-sm leading-snug wrap-break-word"
|
||||
:class="isDone ? 'text-slate-400 line-through' : 'text-slate-800'"
|
||||
>
|
||||
{{ task.title }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="py-2 pr-2.5 pl-3.5">
|
||||
<div class="flex items-start gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full border transition"
|
||||
:class="
|
||||
isDone
|
||||
? 'border-emerald-500 bg-emerald-500 text-white'
|
||||
: 'border-slate-300 hover:border-emerald-500 hover:bg-emerald-50'
|
||||
"
|
||||
:aria-label="isDone ? '标记为未完成' : '标记为已完成'"
|
||||
:aria-pressed="isDone"
|
||||
@click.stop="emit('toggle', task.id)"
|
||||
>
|
||||
<svg v-if="isDone" viewBox="0 0 12 12" class="size-2.5" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="m2.5 6.2 2.2 2.2 4.8-4.8"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.8"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="mt-2 flex flex-wrap items-center gap-1.5 pl-6">
|
||||
<span class="rounded px-1.5 py-0.5 text-xs" :class="priorityClass">
|
||||
{{ PRIORITY_LABELS[task.priority] }}
|
||||
</span>
|
||||
<h3
|
||||
class="min-w-0 flex-1 text-sm leading-snug wrap-break-word"
|
||||
:class="isDone ? 'text-slate-400 line-through' : 'text-slate-800'"
|
||||
>
|
||||
{{ task.title }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="task.dueDate"
|
||||
class="rounded px-1.5 py-0.5 text-xs"
|
||||
:class="overdue ? 'bg-rose-100 font-medium text-rose-700' : 'bg-slate-100 text-slate-600'"
|
||||
>
|
||||
{{ overdue ? '⚠ ' : '' }}{{ formatDueDate(task.dueDate) }}
|
||||
</span>
|
||||
<div class="mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-1 pl-6 text-xs">
|
||||
<span class="font-medium" :class="priorityTextClass">
|
||||
{{ PRIORITY_LABELS[task.priority] }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-for="tag in visibleTags"
|
||||
:key="tag"
|
||||
class="rounded bg-sky-50 px-1.5 py-0.5 text-xs text-sky-700"
|
||||
>
|
||||
#{{ tag }}
|
||||
</span>
|
||||
<span v-if="hiddenTagCount > 0" class="text-xs text-slate-400">+{{ hiddenTagCount }}</span>
|
||||
<span
|
||||
v-if="task.dueDate"
|
||||
class="font-medium"
|
||||
:class="overdue ? 'text-rose-600' : 'text-slate-500'"
|
||||
>
|
||||
{{ formatDueDate(task.dueDate) }}
|
||||
</span>
|
||||
|
||||
<span v-for="tag in visibleTags" :key="tag" class="text-slate-400">#{{ tag }}</span>
|
||||
<span v-if="hiddenTagCount > 0" class="text-slate-400">+{{ hiddenTagCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user