feat: 更新统计卡片与分组对应关系,优化样式与可访问性
This commit is contained in:
+4
-2
@@ -159,8 +159,10 @@ describe('App 路由', () => {
|
||||
await nextTick()
|
||||
|
||||
const values = wrapper.findAll('dd').map((node) => node.text())
|
||||
// [今日待办, 逾期, 今天到期, 本周完成]
|
||||
expect(values[3]).toBe('1')
|
||||
// 统计带 = [逾期, 今天到期, 进行中, 待办]:刚完成的任务不进任何分组,四格都应是 0
|
||||
expect(values).toEqual(['0', '0', '0', '0'])
|
||||
// 「本周完成」已移到页头副标题,不再占统计格
|
||||
expect(wrapper.find('[data-role="completed-this-week"]').text()).toBe('1')
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ const LINKS = [
|
||||
>
|
||||
<a
|
||||
:href="href"
|
||||
class="rounded-md px-3 py-1.5 text-sm transition"
|
||||
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'
|
||||
@@ -62,7 +62,7 @@ const LINKS = [
|
||||
</RouterLink>
|
||||
</nav>
|
||||
|
||||
<div class="ml-auto flex items-center gap-1.5">
|
||||
<div class="ml-auto flex flex-none items-center gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-8 items-center justify-center rounded-md text-slate-400 transition hover:bg-slate-100 hover:text-slate-700 dark:text-zinc-500 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
||||
@@ -118,9 +118,14 @@ const LINKS = [
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!--
|
||||
320px 是 WCAG 1.4.10 要求的最小重排宽度,此时单行放不下四个操作按钮,
|
||||
会横向溢出 20px。快捷键帮助是纯桌面向的次要入口(手机上也没有物理键盘),
|
||||
在 <360 收起,优先保住「不横向滚动」。
|
||||
-->
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-8 items-center justify-center rounded-md text-slate-400 transition hover:bg-slate-100 hover:text-slate-700 dark:text-zinc-500 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
||||
class="flex size-8 items-center justify-center rounded-md text-slate-400 transition max-[359px]:hidden hover:bg-slate-100 hover:text-slate-700 dark:text-zinc-500 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
||||
aria-label="快捷键帮助"
|
||||
@click="ui.openHelp()"
|
||||
>
|
||||
@@ -172,6 +177,7 @@ const LINKS = [
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-1.5 rounded-md border border-slate-200 px-2.5 py-1.5 text-sm text-slate-600 transition hover:border-slate-300 hover:bg-slate-50 hover:text-slate-900 dark:border-zinc-700 dark:text-zinc-300 dark:hover:border-zinc-600 dark:hover:bg-zinc-800"
|
||||
aria-label="备份"
|
||||
@click="ui.openBackup()"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" class="size-4" fill="none" aria-hidden="true">
|
||||
@@ -189,7 +195,7 @@ const LINKS = [
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
备份
|
||||
<span class="hidden sm:inline">备份</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -66,8 +66,14 @@ useDraggable(listRef, {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!--
|
||||
列容器是「托盘」:页面 slate-100 → 列 slate-50 → 卡片 white,三级递增。
|
||||
早先是 bg-slate-100/70,叠在同样是 slate-100 的页面上等于同色(差值为 0),
|
||||
列只剩一根 1px 边框,看不到边界;深色下 zinc-900/60 叠 zinc-950 反而是分层的,
|
||||
两套模式就出现了「深色有容器、浅色没有」的不对等。
|
||||
-->
|
||||
<section
|
||||
class="flex flex-col rounded-xl border border-slate-200 bg-slate-100/70 md:min-h-0 dark:border-zinc-800 dark:bg-zinc-900/60"
|
||||
class="flex flex-col rounded-xl border border-slate-200 bg-slate-50 md:min-h-0 dark:border-zinc-800 dark:bg-zinc-900/60"
|
||||
>
|
||||
<header class="flex items-center gap-2 px-3 pt-3 pb-2">
|
||||
<span class="size-1.5 shrink-0 rounded-full" :class="dotClass" aria-hidden="true" />
|
||||
|
||||
@@ -113,10 +113,26 @@ describe('截止日期', () => {
|
||||
})
|
||||
|
||||
describe('已完成样式', () => {
|
||||
it('标题加删除线并整体降饱和', () => {
|
||||
it('标题加删除线,但不再整卡降透明度', () => {
|
||||
const wrapper = mountCard({ status: 'done' })
|
||||
const classes = wrapper.find('article').classes().join(' ')
|
||||
|
||||
expect(wrapper.find('h3').classes().join(' ')).toContain('line-through')
|
||||
expect(wrapper.find('article').classes().join(' ')).toContain('opacity-60')
|
||||
// 整卡 opacity-60 会把标题压到 1.69:1、标签压到 1.61:1(WCAG AA 要求 4.5:1)
|
||||
expect(classes).not.toContain('opacity-60')
|
||||
expect(classes).toContain('bg-white')
|
||||
})
|
||||
|
||||
it('已完成卡片的文字不低于 slate-500', () => {
|
||||
const wrapper = mountCard({ status: 'done', priority: 1, tags: ['设计'] })
|
||||
const html = wrapper.html()
|
||||
|
||||
expect(wrapper.find('h3').classes().join(' ')).toContain('text-slate-500')
|
||||
// 标签与「低」优先级曾用 slate-400(白底 2.56:1),统一提到 slate-500(4.76:1)
|
||||
expect(html).not.toContain('text-slate-400')
|
||||
expect(html).toContain('text-slate-500')
|
||||
// 完成日期曾带 /80 透明度,混色后 3.75:1
|
||||
expect(html).not.toContain('emerald-700/80')
|
||||
})
|
||||
|
||||
it('未完成时不加删除线', () => {
|
||||
@@ -189,6 +205,15 @@ describe('交互', () => {
|
||||
expect(mountCard({ status: 'done' }).find('button').attributes('aria-pressed')).toBe('true')
|
||||
})
|
||||
|
||||
it('勾选按钮的命中区不小于 24×24(视觉仍是 16px 圆点)', () => {
|
||||
const button = mountCard().find('button')
|
||||
|
||||
// WCAG 2.5.8:命中区 24×24 起;视觉尺寸由内层 span 保持 size-4
|
||||
expect(button.classes().join(' ')).toContain('h-9')
|
||||
expect(button.classes().join(' ')).toContain('w-8')
|
||||
expect(button.find('span').classes().join(' ')).toContain('size-4')
|
||||
})
|
||||
|
||||
it('点击卡片 emit open 而不是 toggle', async () => {
|
||||
const wrapper = mountCard({ id: 'task-7' })
|
||||
await wrapper.find('article').trigger('click')
|
||||
|
||||
+37
-21
@@ -91,21 +91,28 @@ const timeTitle = computed(() => {
|
||||
/**
|
||||
* 卡片外观。选中态与「已完成」都要覆盖边框/背景,
|
||||
* 因此这些颜色放在条件分支里,而不是和基础类各写一份靠优先级取胜。
|
||||
*
|
||||
* 已完成不再整卡降透明度:opacity-60 会把标题压到 1.69:1、标签压到 1.61:1,
|
||||
* 回看上周做了什么、或者要取消勾选时都读不清。改为「删除线 + 收敛文字色 +
|
||||
* 实心绿勾 + 完成日期」四个局部信号,底色保持白色。
|
||||
*/
|
||||
const cardClass = computed(() => {
|
||||
if (props.selected) {
|
||||
return 'border-sky-400 bg-sky-50 dark:border-sky-600 dark:bg-sky-950/40'
|
||||
}
|
||||
return isDone.value
|
||||
? 'border-slate-200 bg-white opacity-60 dark:border-zinc-800 dark:bg-zinc-900'
|
||||
? 'border-slate-200 bg-white dark:border-zinc-800 dark:bg-zinc-900'
|
||||
: 'border-slate-200 bg-white hover:-translate-y-px hover:border-slate-300 hover:shadow-card-hover dark:border-zinc-800 dark:bg-zinc-900 dark:hover:border-zinc-700 dark:hover:bg-zinc-800/25'
|
||||
})
|
||||
|
||||
/**
|
||||
* 优先级用左侧色条表达,而不是一枚彩色胶囊。
|
||||
* 色条几乎不占空间,扫视一列卡片时却比文字更快分辨出轻重。
|
||||
* 已完成的任务把色条降为中性灰:完成态再挂一条饱和色条,
|
||||
* 会和绿勾、删除线一起抢注意力,看起来像还在报警。
|
||||
*/
|
||||
const priorityBarClass = computed(() => {
|
||||
if (isDone.value) return 'bg-slate-300 dark:bg-zinc-600'
|
||||
switch (props.task.priority) {
|
||||
case 3:
|
||||
return 'bg-rose-400'
|
||||
@@ -123,7 +130,7 @@ const priorityTextClass = computed(() => {
|
||||
case 2:
|
||||
return 'text-amber-700 dark:text-amber-400'
|
||||
default:
|
||||
return 'text-slate-400 dark:text-zinc-500'
|
||||
return 'text-slate-500 dark:text-zinc-400'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -170,35 +177,44 @@ const priorityTextClass = computed(() => {
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<!--
|
||||
命中区 32×36,视觉仍是 16px 圆点:靠负 margin 把按钮撑大,再用内层 span 保持圆点尺寸。
|
||||
WCAG 2.5.8(AA)要求 24×24,原来 16×16 不合格;卡片只有 59px 高,
|
||||
再往上撑到 44 就会把整条左边缘都变成「勾选」,容易误触,36 是这里的实际上限。
|
||||
-->
|
||||
<button
|
||||
v-else
|
||||
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 dark:border-zinc-600 dark:hover:border-emerald-500 dark:hover:bg-emerald-950'
|
||||
"
|
||||
class="group -mx-2 -my-2 flex h-9 w-8 shrink-0 items-center justify-center rounded-full"
|
||||
: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>
|
||||
<span
|
||||
class="flex size-4 items-center justify-center rounded-full border transition"
|
||||
:class="
|
||||
isDone
|
||||
? 'border-emerald-600 bg-emerald-600 text-white'
|
||||
: 'border-slate-300 group-hover:border-emerald-500 group-hover:bg-emerald-50 dark:border-zinc-600 dark:group-hover:border-emerald-500 dark:group-hover:bg-emerald-950'
|
||||
"
|
||||
>
|
||||
<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>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<h3
|
||||
class="min-w-0 flex-1 text-sm leading-snug wrap-break-word"
|
||||
:class="
|
||||
isDone
|
||||
? 'text-slate-400 line-through dark:text-zinc-500'
|
||||
? 'text-slate-500 line-through dark:text-zinc-400'
|
||||
: 'text-slate-800 dark:text-zinc-100'
|
||||
"
|
||||
>
|
||||
@@ -222,10 +238,10 @@ const priorityTextClass = computed(() => {
|
||||
{{ dueLabel }}
|
||||
</span>
|
||||
|
||||
<span v-for="tag in visibleTags" :key="tag" class="text-slate-400 dark:text-zinc-500">
|
||||
<span v-for="tag in visibleTags" :key="tag" class="text-slate-500 dark:text-zinc-400">
|
||||
#{{ tag }}
|
||||
</span>
|
||||
<span v-if="hiddenTagCount > 0" class="text-slate-400 dark:text-zinc-500">
|
||||
<span v-if="hiddenTagCount > 0" class="text-slate-500 dark:text-zinc-400">
|
||||
+{{ hiddenTagCount }}
|
||||
</span>
|
||||
|
||||
@@ -233,7 +249,7 @@ const priorityTextClass = computed(() => {
|
||||
v-if="completedAtShort"
|
||||
data-role="completed-at"
|
||||
:title="`完成于 ${completedAtFull}`"
|
||||
class="ml-auto text-emerald-700/80 dark:text-emerald-400/80"
|
||||
class="ml-auto text-emerald-700 dark:text-emerald-400"
|
||||
>
|
||||
完成于 {{ completedAtShort }}
|
||||
</span>
|
||||
|
||||
@@ -51,10 +51,10 @@ describe('TodayView 顶部', () => {
|
||||
expect(wrapper.text()).toContain(dayjs().format('YYYY年M月D日'))
|
||||
})
|
||||
|
||||
it('显示四张统计卡片', () => {
|
||||
it('显示四张统计卡片,与下方四个分组一一对应', () => {
|
||||
const { wrapper } = setup()
|
||||
const labels = wrapper.findAll('dt').map((node) => node.text())
|
||||
expect(labels).toEqual(['逾期', '今天到期', '进行中', '本周完成'])
|
||||
expect(labels).toEqual(['逾期', '今天到期', '进行中', '待办'])
|
||||
})
|
||||
|
||||
it('统计卡片数值正确', () => {
|
||||
@@ -66,8 +66,31 @@ describe('TodayView 顶部', () => {
|
||||
])
|
||||
|
||||
const values = wrapper.findAll('dd').map((node) => node.text())
|
||||
// 逾期 1、今天到期 1、进行中 1、本周完成 1
|
||||
expect(values).toEqual(['1', '1', '1', '1'])
|
||||
// 逾期 1、今天到期 1、进行中 1、待办 0(已完成的不进任何分组)
|
||||
expect(values).toEqual(['1', '1', '1', '0'])
|
||||
})
|
||||
|
||||
it('「本周完成」放在页头,不占统计格', () => {
|
||||
const { wrapper } = setup([{ title: '刚做完', status: 'done' }])
|
||||
|
||||
expect(wrapper.find('[data-role="completed-this-week"]').text()).toBe('1')
|
||||
expect(wrapper.findAll('dt').map((node) => node.text())).not.toContain('本周完成')
|
||||
})
|
||||
|
||||
it('统计带四个数字等于对应分组的条数(同一屏不重复强调)', () => {
|
||||
const { wrapper } = setup([
|
||||
{ title: '逾期 A', dueDate: yesterday },
|
||||
{ title: '逾期 B', dueDate: yesterday },
|
||||
{ title: '今天', dueDate: today },
|
||||
{ title: '进行中', status: 'doing' },
|
||||
{ title: '待办 A' },
|
||||
{ title: '待办 B' },
|
||||
])
|
||||
|
||||
const stats = wrapper.findAll('dd').map((node) => node.text())
|
||||
const counts = wrapper.findAll('[data-role="section-count"]').map((node) => node.text())
|
||||
expect(stats).toEqual(['2', '1', '1', '2'])
|
||||
expect(counts).toEqual(['2 项', '1 项', '1 项', '2 项'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -91,7 +114,7 @@ describe('TodayView 分组', () => {
|
||||
|
||||
// 每组各 1 条
|
||||
const badges = wrapper.findAll('[data-role="section-count"]').map((node) => node.text())
|
||||
expect(badges).toEqual(['1', '1', '1'])
|
||||
expect(badges).toEqual(['1 项', '1 项', '1 项'])
|
||||
})
|
||||
|
||||
it('没有任务的分组不渲染', () => {
|
||||
|
||||
+45
-63
@@ -12,28 +12,16 @@ import { buildTodayBuckets, countTodayTasks } from '@/utils/today'
|
||||
|
||||
const WEEKDAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'] as const
|
||||
|
||||
/** 用完整类名字符串,避免 Tailwind 扫描不到动态拼接的类 */
|
||||
/**
|
||||
* 用完整类名字符串,避免 Tailwind 扫描不到动态拼接的类。
|
||||
* 只保留色点:分组计数已经降为纯文字,彩色徽章会把权重抬到标题之上,
|
||||
* 而且和上方统计带说的还是同一个数字。
|
||||
*/
|
||||
const TONES = {
|
||||
rose: {
|
||||
dot: 'bg-rose-500',
|
||||
badge:
|
||||
'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-800 dark:bg-rose-950 dark:text-rose-300',
|
||||
},
|
||||
amber: {
|
||||
dot: 'bg-amber-500',
|
||||
badge:
|
||||
'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-300',
|
||||
},
|
||||
sky: {
|
||||
dot: 'bg-sky-500',
|
||||
badge:
|
||||
'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-800 dark:bg-sky-950 dark:text-sky-300',
|
||||
},
|
||||
slate: {
|
||||
dot: 'bg-slate-400',
|
||||
badge:
|
||||
'border-slate-200 bg-slate-50 text-slate-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300',
|
||||
},
|
||||
rose: { dot: 'bg-rose-500' },
|
||||
amber: { dot: 'bg-amber-500' },
|
||||
sky: { dot: 'bg-sky-500' },
|
||||
slate: { dot: 'bg-slate-400' },
|
||||
} as const
|
||||
|
||||
const store = useTaskStore()
|
||||
@@ -49,13 +37,18 @@ const stats = computed(() => computeStats(activeTasks.value))
|
||||
const todayCount = computed(() => countTodayTasks(buckets.value))
|
||||
|
||||
/**
|
||||
* 统计卡片与下方分组一一对应,避免「数字和列表对不上」的困惑。
|
||||
* 统计带与下方分组一一对应:四个数字就是四组任务量。
|
||||
*
|
||||
* 早先这里放的是「逾期 / 今天到期 / 进行中 / 本周完成」——「本周完成」在下方
|
||||
* 没有对应分组,而「待办」这一组又没有统计卡,所谓「一一对应」并不成立;
|
||||
* 三个数字还会在分组徽章上再说一遍。
|
||||
* 现在「本周完成」属于回顾性指标,移到页头副标题;分组头只留纯文字「N 项」。
|
||||
*/
|
||||
const statCards = computed(() => [
|
||||
{ label: '逾期', value: stats.value.overdue, dot: 'bg-rose-500' },
|
||||
{ label: '今天到期', value: stats.value.dueToday, dot: 'bg-amber-500' },
|
||||
{ label: '逾期', value: buckets.value.overdue.length, dot: 'bg-rose-500' },
|
||||
{ label: '今天到期', value: buckets.value.dueToday.length, dot: 'bg-amber-500' },
|
||||
{ label: '进行中', value: buckets.value.inProgress.length, dot: 'bg-sky-500' },
|
||||
{ label: '本周完成', value: stats.value.completedThisWeek, dot: 'bg-emerald-500' },
|
||||
{ label: '待办', value: buckets.value.pending.length, dot: 'bg-slate-400' },
|
||||
])
|
||||
|
||||
/**
|
||||
@@ -65,34 +58,10 @@ const statCards = computed(() => [
|
||||
*/
|
||||
const sections = computed(() =>
|
||||
[
|
||||
{
|
||||
key: 'overdue',
|
||||
title: '已逾期',
|
||||
hint: '建议优先处理',
|
||||
tone: 'rose' as const,
|
||||
items: buckets.value.overdue,
|
||||
},
|
||||
{
|
||||
key: 'dueToday',
|
||||
title: '今天到期',
|
||||
hint: '今天要完成',
|
||||
tone: 'amber' as const,
|
||||
items: buckets.value.dueToday,
|
||||
},
|
||||
{
|
||||
key: 'inProgress',
|
||||
title: '进行中',
|
||||
hint: '已开始,尚无紧迫截止日期',
|
||||
tone: 'sky' as const,
|
||||
items: buckets.value.inProgress,
|
||||
},
|
||||
{
|
||||
key: 'pending',
|
||||
title: '待办',
|
||||
hint: '没有紧迫截止时间,有空再做',
|
||||
tone: 'slate' as const,
|
||||
items: buckets.value.pending,
|
||||
},
|
||||
{ key: 'overdue', title: '已逾期', tone: 'rose' as const, items: buckets.value.overdue },
|
||||
{ key: 'dueToday', title: '今天到期', tone: 'amber' as const, items: buckets.value.dueToday },
|
||||
{ key: 'inProgress', title: '进行中', tone: 'sky' as const, items: buckets.value.inProgress },
|
||||
{ key: 'pending', title: '待办', tone: 'slate' as const, items: buckets.value.pending },
|
||||
].filter((section) => section.items.length > 0),
|
||||
)
|
||||
|
||||
@@ -128,20 +97,37 @@ function handleKeyMove(payload: { id: string; direction: MoveDirection }): void
|
||||
<h1 class="text-xl font-semibold tracking-tight text-slate-900 md:text-2xl dark:text-zinc-100">
|
||||
今日
|
||||
</h1>
|
||||
<p class="text-sm text-slate-500 dark:text-zinc-400">{{ todayLabel }}</p>
|
||||
<!-- 「本周完成」在下方没有对应分组,属于回顾性指标,放页头而不是占一格统计 -->
|
||||
<p class="text-sm text-slate-500 dark:text-zinc-400">
|
||||
{{ todayLabel }} · 本周完成
|
||||
<span
|
||||
data-role="completed-this-week"
|
||||
class="font-medium tabular-nums text-slate-600 dark:text-zinc-300"
|
||||
>{{ stats.completedThisWeek }}</span
|
||||
>
|
||||
个
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<dl class="grid grid-cols-2 gap-3 md:grid-cols-4">
|
||||
<!--
|
||||
四格合成一条带:gap-px + 露出父级底色,得到 1px 分隔线。
|
||||
比四张独立卡片少三道投影、四条边框,读起来是「一个总览」而不是四个小组件。
|
||||
-->
|
||||
<dl
|
||||
class="grid grid-cols-2 gap-px overflow-hidden rounded-xl border border-slate-200 bg-slate-200 md:grid-cols-4 dark:border-zinc-800 dark:bg-zinc-800"
|
||||
>
|
||||
<div
|
||||
v-for="card in statCards"
|
||||
:key="card.label"
|
||||
class="rounded-xl border border-slate-200 bg-white px-4 py-3.5 shadow-card dark:border-zinc-800 dark:bg-zinc-900"
|
||||
class="bg-white px-4 py-3.5 dark:bg-zinc-900"
|
||||
>
|
||||
<dt class="flex items-center gap-1.5 text-xs text-slate-500 dark:text-zinc-400">
|
||||
<span class="size-1.5 rounded-full" :class="card.dot" aria-hidden="true" />
|
||||
{{ card.label }}
|
||||
</dt>
|
||||
<dd class="mt-1.5 text-2xl font-semibold tracking-tight text-slate-900 dark:text-zinc-100">
|
||||
<dd
|
||||
class="mt-1.5 text-2xl font-semibold tracking-tight tabular-nums text-slate-900 dark:text-zinc-100"
|
||||
>
|
||||
{{ card.value }}
|
||||
</dd>
|
||||
</div>
|
||||
@@ -162,13 +148,9 @@ function handleKeyMove(payload: { id: string; direction: MoveDirection }): void
|
||||
{{ section.title }}
|
||||
<span
|
||||
data-role="section-count"
|
||||
class="rounded-full border px-2 py-0.5 text-xs"
|
||||
:class="TONES[section.tone].badge"
|
||||
class="text-xs font-normal tabular-nums text-slate-500 dark:text-zinc-400"
|
||||
>
|
||||
{{ section.items.length }}
|
||||
</span>
|
||||
<span class="text-xs font-normal text-slate-400 dark:text-zinc-500">
|
||||
{{ section.hint }}
|
||||
{{ section.items.length }} 项
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user