feat: 添加新建任务按钮功能,聚焦待办列输入框
CI / verify (push) Successful in 1m42s
CI / 部署到服务器 (push) Successful in 27s

This commit is contained in:
chenshoulin
2026-10-24 19:14:58 +08:00
parent 5133bf768c
commit 1d92ab2cb5
7 changed files with 117 additions and 13 deletions
+10
View File
@@ -46,6 +46,16 @@ describe('AppNav 导航', () => {
expect(links[1]?.attributes('aria-current')).toBe('page')
})
it('当前入口用主色(sky-700)铺底,与实心主按钮同一套色', async () => {
const { wrapper } = await setup('/today')
const [board, today] = wrapper.findAll('a')
expect(today?.classes()).toContain('bg-sky-700')
// 早先是接近黑的 slate-900,与页面主色脱节
expect(today?.classes()).not.toContain('bg-slate-900')
expect(board?.classes()).not.toContain('bg-sky-700')
})
it('点击可切换路由', async () => {
const { wrapper, router } = await setup('/')
+2 -2
View File
@@ -51,8 +51,8 @@ const LINKS = [
class="shrink-0 rounded-md px-3 py-1.5 text-sm whitespace-nowrap transition"
:class="
isExactActive
? 'bg-slate-900 font-medium text-white shadow-sm dark:bg-zinc-100 dark:text-zinc-900'
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100'
? 'bg-sky-700 font-medium text-white shadow-sm dark:bg-sky-500 dark:text-slate-950'
: 'text-slate-600 hover:bg-sky-50 hover:text-sky-800 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100'
"
:aria-current="isExactActive ? 'page' : undefined"
@click="navigate"
+35
View File
@@ -189,6 +189,41 @@ describe('BoardToolbar 归档', () => {
})
})
describe('BoardToolbar 主次按钮', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
it('点击「新建任务」emit new-task', async () => {
const { wrapper } = setup()
await buttonByText(wrapper, '新建任务')?.trigger('click')
expect(wrapper.emitted('new-task')).toHaveLength(1)
})
it('实心主按钮只有「新建任务」,「批量添加」降为描边次要按钮', () => {
const { wrapper } = setup()
const primary = buttonByText(wrapper, '新建任务')
const batch = buttonByText(wrapper, '批量添加')
expect(primary?.classes()).toContain('bg-sky-700')
expect(primary?.classes()).toContain('text-white')
expect(batch?.classes()).toContain('bg-white')
expect(batch?.classes()).not.toContain('bg-sky-700')
})
it('「新建任务」在平板上仍带文字,不退化成纯图标', () => {
const { wrapper } = setup()
const button = buttonByText(wrapper, '新建任务')
// 主操作只在手机(<640)收成图标
expect(button?.find('span').classes()).toContain('max-sm:hidden')
expect(button?.classes()).not.toContain('max-lg:w-9')
})
})
describe('BoardToolbar 批量添加', () => {
beforeEach(() => {
setActivePinia(createPinia())
+30 -2
View File
@@ -14,6 +14,7 @@ defineProps<{ hiddenNote?: string; selecting?: boolean }>()
const emit = defineEmits<{
archive: []
'new-task': []
'batch-add': []
'toggle-select': []
'manage-tags': []
@@ -177,11 +178,38 @@ defineExpose({ focusSearch })
</button>
</div>
<!-- 操作:主操作(批量添加)+ 两个视图开关,全部平铺,不再收进菜单 -->
<!--
操作:主操作(新建任务)+ 三个次要操作(批量添加 / 多选 / 显示归档)。
主次靠「实心 vs 描边」表达:真正高频的是「再加一条」,批量录入是偶发动作,
早先把它做成唯一实心按钮,把用户最常做的事藏进了每列的输入框。
新建任务在 ≥640 一律带文字(主操作不该在平板上退化成一个小图标),
三个次要操作在 <1024 收成图标,靠 title / aria-label 保留可访问名。
-->
<div class="ml-auto flex shrink-0 items-center gap-1.5 max-sm:order-2">
<button
type="button"
class="flex h-9 items-center gap-1.5 rounded-lg bg-sky-700 px-3 text-[13px] font-medium whitespace-nowrap text-white transition hover:bg-sky-800 active:scale-[0.98] max-lg:w-9 max-lg:justify-center max-lg:px-0 dark:bg-sky-500 dark:text-slate-950 dark:hover:bg-sky-400"
class="flex h-9 items-center gap-1.5 rounded-lg bg-sky-700 px-3 text-[13px] font-medium whitespace-nowrap text-white transition hover:bg-sky-800 active:scale-[0.98] dark:bg-sky-500 dark:text-slate-950 dark:hover:bg-sky-400"
aria-label="新建任务"
title="新建任务(N)"
@click="emit('new-task')"
>
<svg
viewBox="0 0 20 20"
class="size-4 shrink-0"
fill="none"
stroke="currentColor"
stroke-width="1.7"
stroke-linecap="round"
aria-hidden="true"
>
<path d="M10 4.5v11M4.5 10h11" />
</svg>
<span class="max-sm:hidden">新建任务</span>
</button>
<button
type="button"
class="flex h-9 items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-2.5 text-[13px] whitespace-nowrap text-slate-600 transition hover:border-slate-300 hover:bg-slate-50 max-lg:w-9 max-lg:justify-center max-lg:px-0 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300 dark:hover:border-zinc-600 dark:hover:bg-zinc-800"
aria-label="批量添加"
title="批量添加"
@click="emit('batch-add')"
+13 -9
View File
@@ -249,34 +249,38 @@ function remove(): void {
<TagInput v-model="tags" :suggestions="store.allTags" />
</div>
<section
class="flex flex-col gap-2 rounded-md border border-slate-200 bg-slate-50/70 px-3 py-2.5 dark:border-zinc-800 dark:bg-zinc-800/40"
aria-labelledby="task-time-heading"
>
<!--
时间:标题与「标签」同级平铺,明细单独用一个描边框圈起来。
只靠字号(标题 text-sm / 明细 text-xs)分层,框只负责把只读信息
和上方的可编辑字段区分开,不参与层级表达。
-->
<section class="flex flex-col gap-1.5" aria-labelledby="task-time-heading">
<h3 id="task-time-heading" class="text-sm font-medium text-slate-600 dark:text-zinc-400">
时间
</h3>
<dl class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-xs">
<dl
class="grid grid-cols-[auto_1fr] items-baseline gap-x-3 gap-y-0.5 rounded-md border border-slate-300 px-3 py-2 text-xs dark:border-zinc-700"
>
<dt class="text-slate-400 dark:text-zinc-500">创建</dt>
<dd data-role="created-at" class="text-slate-600 dark:text-zinc-300">
<dd data-role="created-at" class="tabular-nums text-slate-600 dark:text-zinc-300">
{{ createdAtText ?? '—' }}
</dd>
<dt class="text-slate-400 dark:text-zinc-500">完成</dt>
<dd data-role="completed-at" class="text-slate-600 dark:text-zinc-300">
<dd data-role="completed-at" class="tabular-nums text-slate-600 dark:text-zinc-300">
<span :class="completedAtText ? '' : 'text-slate-400 dark:text-zinc-500'">
{{ completedLabel }}
</span>
</dd>
<dt class="text-slate-400 dark:text-zinc-500">更新</dt>
<dd data-role="updated-at" class="text-slate-600 dark:text-zinc-300">
<dd data-role="updated-at" class="tabular-nums text-slate-600 dark:text-zinc-300">
{{ updatedAtText ?? '—' }}
</dd>
<template v-if="archivedAtText">
<dt class="text-slate-400 dark:text-zinc-500">归档</dt>
<dd data-role="archived-at" class="text-slate-600 dark:text-zinc-300">
<dd data-role="archived-at" class="tabular-nums text-slate-600 dark:text-zinc-300">
{{ archivedAtText }}
</dd>
</template>