前端: 按 Web Interface Guidelines 全面重做

组件:
- Button: transition-all→明确属性列表, touch-action, aria-busy
- Input: name/spellcheck/inputmode/aria-invalid/aria-describedby, focus-visible ring,
  label 关联, 暴露 focus() 供错误定位, required 标记
- Modal: Escape 关闭 + focus trap + 焦点归还, overscroll-behavior: contain,
  aria-labelledby, body 滚动锁, touch-action
- 新增 Toast(aria-live polite) + pinia toast store

页面:
- ConsoleLayout: skip link, <main> 语义标签, aside/nav aria-label, aria-current,
  装饰 SVG aria-hidden, Intl 金额, translate=no 品牌/代码
- Dashboard: Intl.NumberFormat, 骨架屏 loading, aria-live 错误
- Keys: 字段级错误+焦点定位, toast 反馈(复制/吊销), name 属性
- Usage: select 加 label, 分页/筛选同步 URL(可深链), Intl 日期/金额, 表格 caption
- Login/Register: 字段级校验+错误定位, spellcheck=false, inputmode, autocomplete
- Landing: router-link 替换 href, aria-hidden, text-balance, scroll-mt
- index.html: theme-color + color-scheme

验证: playwright 22 项断言全过, WCAG AA 对比度 9 组全过, 零控制台错误
This commit is contained in:
Sakurasan
2026-08-15 13:32:18 +08:00
parent 5b67b66611
commit b25e9ec8a7
14 changed files with 646 additions and 185 deletions
+13 -2
View File
@@ -6,16 +6,22 @@ withDefaults(
type?: 'button' | 'submit'
loading?: boolean
disabled?: boolean
ariaLabel?: string
}>(),
{ variant: 'primary', size: 'md', type: 'button', loading: false, disabled: false },
)
defineEmits<{ (e: 'click', ev: MouseEvent): void }>()
</script>
<template>
<button
:type="type"
:disabled="disabled || loading"
class="inline-flex items-center justify-center gap-2 font-medium transition-all select-none
:aria-label="ariaLabel"
:aria-busy="loading || undefined"
class="inline-flex touch-manipulation items-center justify-center gap-2 font-medium select-none
transition-[background-color,border-color,color,transform,opacity] duration-150
active:scale-[0.98] disabled:opacity-50 disabled:pointer-events-none cursor-pointer"
:class="[
size === 'sm' ? 'h-8 px-3 text-[13px] rounded-md' : 'h-10 px-4 text-sm rounded-md',
@@ -24,8 +30,13 @@ withDefaults(
variant === 'ghost' && 'text-paper-500 hover:text-paper-100 hover:bg-ink-800',
variant === 'danger' && 'bg-ember-500/90 text-white hover:bg-ember-400',
]"
@click="$emit('click', $event)"
>
<span v-if="loading" class="h-3.5 w-3.5 animate-spin rounded-full border-2 border-current border-t-transparent" />
<span
v-if="loading"
class="h-3.5 w-3.5 animate-spin rounded-full border-2 border-current border-t-transparent"
aria-hidden="true"
/>
<slot />
</button>
</template>