mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-03 00:02:28 +08:00
1533 lines
34 KiB
Markdown
1533 lines
34 KiB
Markdown
# Aether 设计系统 v2.3
|
||
|
||
> 基于 shadcn/ui 和书本纸张主题的完整前端设计规范
|
||
|
||
**版本**: 2.3.0
|
||
**最后更新**: 2025-11-18
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
本文档描述了 Aether 前端项目的设计系统,基于 shadcn/ui 和自定义主题构建。所有组件均已实现并在生产环境中使用。
|
||
|
||
### 核心理念
|
||
|
||
1. **一致性优先** - 所有组件遵循统一的视觉语言和交互模式
|
||
2. **响应式设计** - 组件自适应不同屏幕尺寸(移动端、平板、桌面)
|
||
3. **可访问性** - 遵循 WCAG 2.1 标准,支持键盘导航和屏幕阅读器
|
||
4. **性能优化** - 轻量级组件,按需加载,优化渲染性能
|
||
5. **开发体验** - TypeScript 类型安全,清晰的 API 设计,完善的文档
|
||
|
||
### 色彩体系
|
||
|
||
项目使用书本纸张主题色:
|
||
|
||
- **book-cloth** - 书籍封面布料色 (#cc785c / #d4a27f)
|
||
- **kraft** - 牛皮纸色 (#b97847 / #c9a26f)
|
||
- **manilla** - 马尼拉纸色 (#e8ddc5 / #d4c5a9)
|
||
- **cloud** - 云白色 (#f5f3ed / #2a2723)
|
||
|
||
详细配置见 [src/config/theme.ts](src/config/theme.ts)
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
- **Vue 3** - Composition API
|
||
- **TypeScript** - 类型安全
|
||
- **Tailwind CSS** - 原子化 CSS
|
||
- **shadcn/ui** - 基础组件库
|
||
- **lucide-vue-next** - 图标库
|
||
- **Vite** - 构建工具
|
||
|
||
---
|
||
|
||
## 主题系统
|
||
|
||
### 主题配置
|
||
|
||
主题配置位于 [src/config/theme.ts](src/config/theme.ts),包含:
|
||
|
||
```ts
|
||
export const theme = {
|
||
colors: themeColors, // 颜色系统
|
||
spacing, // 间距系统(基于 8px 网格)
|
||
radius, // 圆角系统
|
||
shadows, // 阴影系统
|
||
typography, // 字体系统
|
||
animations, // 动画系统
|
||
breakpoints, // 响应式断点
|
||
zIndex, // 层级管理
|
||
components: componentDefaults // 组件默认配置
|
||
}
|
||
```
|
||
|
||
### CSS 变量
|
||
|
||
全局 CSS 变量定义在 `src/assets/index.css`,使用 HSL 色彩空间:
|
||
|
||
```css
|
||
:root {
|
||
--background: 0 0% 100%;
|
||
--foreground: 20 14.3% 4.1%;
|
||
--primary: 15 55% 58%;
|
||
--border: 20 5.9% 90%;
|
||
--muted: 60 4.8% 95.9%;
|
||
--muted-foreground: 25 5.3% 44.7%;
|
||
/* ... 更多变量 */
|
||
}
|
||
|
||
.dark {
|
||
--background: 20 14.3% 4.1%;
|
||
--foreground: 0 0% 95%;
|
||
--primary: 15 45% 68%;
|
||
/* ... 暗色模式变量 */
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 组件库
|
||
|
||
### 基础组件 (shadcn/ui)
|
||
|
||
所有基础组件位于 [src/components/ui/](src/components/ui/):
|
||
|
||
#### 布局组件
|
||
- **Card** - 卡片容器
|
||
- 变体:`default`、`outline`、`ghost`、`interactive`
|
||
- **Separator** - 分隔线(水平/垂直)
|
||
- **Tabs** - 选项卡容器
|
||
|
||
#### 表单组件
|
||
- **Button** - 按钮
|
||
- 变体:`default`、`destructive`、`outline`、`secondary`、`ghost`、`link`
|
||
- 大小:`sm`、`md`、`lg`、`icon`
|
||
- **Input** - 输入框
|
||
- **Textarea** - 多行文本框
|
||
- **Select** - 下拉选择框
|
||
- **Checkbox** - 复选框
|
||
- **Switch** - 开关
|
||
- **Label** - 表单标签
|
||
|
||
#### 反馈组件
|
||
- **Badge** - 徽章标签
|
||
- **Skeleton** - 骨架屏
|
||
- **Toast** - 消息提示
|
||
- **Dialog** - 对话框/模态框
|
||
- **Alert** - 警告提示
|
||
|
||
#### 数据展示
|
||
- **Table** 系列 - 表格组件
|
||
- Table、TableHeader、TableBody、TableRow、TableHead、TableCell
|
||
- **Avatar** - 头像
|
||
- **Progress** - 进度条
|
||
|
||
---
|
||
|
||
### 布局组件 (Layout Components)
|
||
|
||
位于 [src/components/layout/](src/components/layout/),所有组件支持从 `@/components/layout` 统一导入:
|
||
|
||
```ts
|
||
import { PageHeader, PageContainer, Section, CardSection, Grid, StatCard } from '@/components/layout'
|
||
```
|
||
|
||
#### PageHeader
|
||
|
||
页面头部组件,支持标题、描述、图标和操作按钮。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { PageHeader } from '@/components/layout'
|
||
import { Settings } from 'lucide-vue-next'
|
||
</script>
|
||
|
||
<template>
|
||
<PageHeader
|
||
title="系统设置"
|
||
description="管理系统级别的配置和参数"
|
||
:icon="Settings"
|
||
>
|
||
<template #actions>
|
||
<Button @click="save">保存配置</Button>
|
||
</template>
|
||
</PageHeader>
|
||
</template>
|
||
```
|
||
|
||
**Props:**
|
||
- `title: string` - 页面标题(必填)
|
||
- `description?: string` - 页面描述
|
||
- `icon?: Component` - 图标组件
|
||
|
||
**Slots:**
|
||
- `icon` - 自定义图标区域
|
||
- `actions` - 右侧操作按钮
|
||
|
||
---
|
||
|
||
#### PageContainer
|
||
|
||
页面容器,提供响应式的最大宽度和内边距。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<template>
|
||
<PageContainer maxWidth="2xl" padding="md">
|
||
<!-- 页面内容 -->
|
||
</PageContainer>
|
||
</template>
|
||
```
|
||
|
||
**Props:**
|
||
- `maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'` - 最大宽度(默认: '2xl')
|
||
- `padding?: 'none' | 'sm' | 'md' | 'lg'` - 内边距(默认: 'md')
|
||
|
||
---
|
||
|
||
#### Section
|
||
|
||
区块容器,用于分隔页面不同区域。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<template>
|
||
<Section
|
||
title="用户信息"
|
||
description="管理用户基本资料"
|
||
spacing="md"
|
||
>
|
||
<template #actions>
|
||
<Button size="sm">编辑</Button>
|
||
</template>
|
||
|
||
<!-- 区块内容 -->
|
||
</Section>
|
||
</template>
|
||
```
|
||
|
||
**Props:**
|
||
- `title?: string` - 区块标题
|
||
- `description?: string` - 区块描述
|
||
- `spacing?: 'none' | 'sm' | 'md' | 'lg'` - 底部间距(默认: 'md')
|
||
|
||
**Slots:**
|
||
- `header` - 自定义头部
|
||
- `actions` - 右侧操作按钮
|
||
- `default` - 主内容
|
||
|
||
---
|
||
|
||
#### CardSection
|
||
|
||
卡片区块,基于 Card 组件的增强版。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<template>
|
||
<CardSection
|
||
title="系统配置"
|
||
description="配置系统默认参数"
|
||
variant="elevated"
|
||
padding="lg"
|
||
>
|
||
<template #actions>
|
||
<Button size="sm" variant="ghost">重置</Button>
|
||
</template>
|
||
|
||
<template #footer>
|
||
<Button>保存</Button>
|
||
</template>
|
||
|
||
<!-- 卡片内容 -->
|
||
</CardSection>
|
||
</template>
|
||
```
|
||
|
||
**Props:**
|
||
- `title?: string` - 卡片标题
|
||
- `description?: string` - 卡片描述
|
||
- `variant?: 'default' | 'elevated' | 'glass'` - 卡片样式(默认: 'default')
|
||
- `padding?: 'none' | 'sm' | 'md' | 'lg'` - 内边距(默认: 'md')
|
||
|
||
**Slots:**
|
||
- `header` - 自定义头部
|
||
- `actions` - 头部右侧操作
|
||
- `default` - 主内容
|
||
- `footer` - 底部内容
|
||
|
||
---
|
||
|
||
#### Grid
|
||
|
||
响应式网格布局。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<template>
|
||
<Grid :cols="{ sm: 1, md: 2, lg: 3 }" gap="md">
|
||
<Card>项目 1</Card>
|
||
<Card>项目 2</Card>
|
||
<Card>项目 3</Card>
|
||
</Grid>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
#### StatCard
|
||
|
||
统计卡片,用于展示关键指标。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { StatCard } from '@/components/layout'
|
||
import { Users } from 'lucide-vue-next'
|
||
</script>
|
||
|
||
<template>
|
||
<StatCard
|
||
title="总用户数"
|
||
:value="1234"
|
||
:icon="Users"
|
||
trend="up"
|
||
:trendValue="12.5"
|
||
trendLabel="较上月"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
#### ShellHeader (待废弃)
|
||
|
||
旧版页面头部组件,建议迁移到 `PageHeader`。
|
||
|
||
---
|
||
|
||
### 业务组件 (Common Components)
|
||
|
||
位于 [src/components/common/](src/components/common/):
|
||
|
||
#### 1. PageLayout
|
||
|
||
页面布局容器,集成标题、筛选、分页等功能。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import PageLayout from '@/components/common/PageLayout.vue'
|
||
import DataTable from '@/components/common/DataTable.vue'
|
||
|
||
const searchQuery = ref('')
|
||
const currentPage = ref(1)
|
||
const pageSize = ref(20)
|
||
const roleFilter = ref('')
|
||
</script>
|
||
|
||
<template>
|
||
<PageLayout
|
||
title="用户管理"
|
||
subtitle="管理系统用户和权限"
|
||
:showFilters="true"
|
||
:showPagination="true"
|
||
v-model:searchQuery="searchQuery"
|
||
v-model:currentPage="currentPage"
|
||
v-model:pageSize="pageSize"
|
||
:total="totalUsers"
|
||
spacing="normal"
|
||
maxWidth="full"
|
||
>
|
||
<template #toolbar>
|
||
<Button @click="openAddDialog">添加用户</Button>
|
||
</template>
|
||
|
||
<template #filters>
|
||
<Select v-model="roleFilter">
|
||
<SelectTrigger><SelectValue placeholder="角色筛选" /></SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="admin">管理员</SelectItem>
|
||
<SelectItem value="user">普通用户</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</template>
|
||
|
||
<DataTable :columns="columns" :data="users" />
|
||
</PageLayout>
|
||
</template>
|
||
```
|
||
|
||
**主要 Props:**
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `title` | `string` | - | 页面标题(必填) |
|
||
| `subtitle` | `string` | - | 页面副标题 |
|
||
| `showHeader` | `boolean` | `true` | 是否显示页面头部 |
|
||
| `showBackButton` | `boolean` | `false` | 是否显示返回按钮 |
|
||
| `maxWidth` | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | `'full'` | 内容最大宽度 |
|
||
| `spacing` | `'tight' \| 'normal' \| 'relaxed'` | `'normal'` | 内容间距 |
|
||
| `showFilters` | `boolean` | `false` | 是否显示筛选栏 |
|
||
| `showPagination` | `boolean` | `false` | 是否显示分页 |
|
||
|
||
**主要 Slots:**
|
||
|
||
- `toolbar` - 页面右上角工具栏
|
||
- `headerExtra` - 头部额外内容
|
||
- `filters` - 筛选条件
|
||
- `filterLeft` / `filterRight` - 筛选栏左右插槽
|
||
- `default` - 主内容区
|
||
- `footer` - 页面底部
|
||
|
||
---
|
||
|
||
#### 2. DataTable
|
||
|
||
响应式数据表格,桌面端显示表格,移动端自动切换为卡片视图。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import DataTable, { type DataTableColumn } from '@/components/common/DataTable.vue'
|
||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||
|
||
const columns: DataTableColumn[] = [
|
||
{
|
||
key: 'name',
|
||
label: '名称',
|
||
sortable: true,
|
||
width: '200px',
|
||
showOnMobile: true
|
||
},
|
||
{
|
||
key: 'email',
|
||
label: '邮箱',
|
||
align: 'left',
|
||
showOnMobile: true
|
||
},
|
||
{
|
||
key: 'status',
|
||
label: '状态',
|
||
align: 'center',
|
||
showOnMobile: true
|
||
},
|
||
{
|
||
key: 'created_at',
|
||
label: '创建时间',
|
||
formatter: (value) => new Date(value).toLocaleDateString(),
|
||
showOnMobile: false
|
||
},
|
||
{
|
||
key: 'actions',
|
||
label: '操作',
|
||
align: 'right',
|
||
showOnMobile: true
|
||
}
|
||
]
|
||
|
||
const handleRowClick = (row, index) => {
|
||
console.log('点击行:', row)
|
||
}
|
||
|
||
const handleSort = (sortBy, sortOrder) => {
|
||
// 处理排序
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<DataTable
|
||
:columns="columns"
|
||
:data="tableData"
|
||
:loading="loading"
|
||
:clickable="true"
|
||
rowKey="id"
|
||
@rowClick="handleRowClick"
|
||
@sort="handleSort"
|
||
>
|
||
<template #cell-status="{ value }">
|
||
<StatusBadge :status="value" />
|
||
</template>
|
||
|
||
<template #cell-actions="{ row }">
|
||
<div class="flex gap-2">
|
||
<Button size="sm" variant="outline" @click.stop="editRow(row)">
|
||
编辑
|
||
</Button>
|
||
<Button size="sm" variant="destructive" @click.stop="deleteRow(row)">
|
||
删除
|
||
</Button>
|
||
</div>
|
||
</template>
|
||
|
||
<template #mobile-card="{ row }">
|
||
<!-- 自定义移动端卡片布局 -->
|
||
<div class="p-4 space-y-2">
|
||
<div class="font-semibold">{{ row.name }}</div>
|
||
<div class="text-sm text-muted-foreground">{{ row.email }}</div>
|
||
<StatusBadge :status="row.status" />
|
||
</div>
|
||
</template>
|
||
|
||
<template #empty>
|
||
<EmptyState
|
||
type="empty"
|
||
title="暂无用户"
|
||
description="点击右上角按钮添加第一个用户"
|
||
/>
|
||
</template>
|
||
</DataTable>
|
||
</template>
|
||
```
|
||
|
||
**主要 Props:**
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `columns` | `DataTableColumn[]` | - | 列配置(必填) |
|
||
| `data` | `T[]` | - | 数据源(必填) |
|
||
| `rowKey` | `string` | `'id'` | 行唯一标识字段 |
|
||
| `loading` | `boolean` | `false` | 是否加载中 |
|
||
| `clickable` | `boolean` | `false` | 是否可点击行 |
|
||
| `emptyTitle` | `string` | `'暂无数据'` | 空状态标题 |
|
||
|
||
**列配置(DataTableColumn):**
|
||
|
||
```ts
|
||
interface DataTableColumn<T = any> {
|
||
key: string // 列标识(对应数据字段)
|
||
label: string // 列标题
|
||
width?: string // 列宽度
|
||
align?: 'left' | 'center' | 'right' // 对齐方式
|
||
sortable?: boolean // 是否可排序
|
||
formatter?: (value: any, row: T, index: number) => string // 值格式化
|
||
headerClass?: string // 表头样式类
|
||
cellClass?: string // 单元格样式类
|
||
showOnMobile?: boolean // 是否在移动端显示(默认 true)
|
||
}
|
||
```
|
||
|
||
**主要 Events:**
|
||
|
||
- `rowClick(row, index)` - 行点击事件
|
||
- `sort(sortBy, sortOrder)` - 排序事件
|
||
|
||
**主要 Slots:**
|
||
|
||
- `cell-{key}` - 自定义单元格内容(接收 `{ row, column, index, value }` 参数)
|
||
- `mobile-card` - 自定义移动端卡片布局(接收 `{ row, index }` 参数)
|
||
- `empty` - 自定义空状态
|
||
- `footer` - 表格底部内容
|
||
|
||
---
|
||
|
||
#### 3. SearchInput
|
||
|
||
智能搜索输入框,支持防抖、清除、建议列表。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
const searchQuery = ref('')
|
||
const searchSuggestions = ['用户名', '邮箱', 'ID']
|
||
const searching = ref(false)
|
||
|
||
const handleSearch = async (value: string) => {
|
||
searching.value = true
|
||
try {
|
||
await performSearch(value)
|
||
} finally {
|
||
searching.value = false
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<SearchInput
|
||
v-model="searchQuery"
|
||
placeholder="搜索用户名、邮箱..."
|
||
:suggestions="searchSuggestions"
|
||
:loading="searching"
|
||
:debounce="500"
|
||
size="md"
|
||
@search="handleSearch"
|
||
@clear="searchQuery = ''"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
**主要 Props:**
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `modelValue` | `string` | - | 输入值(必填) |
|
||
| `placeholder` | `string` | `'搜索...'` | 占位符 |
|
||
| `clearable` | `boolean` | `true` | 是否显示清除按钮 |
|
||
| `loading` | `boolean` | `false` | 是否显示加载图标 |
|
||
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 大小 |
|
||
| `suggestions` | `string[]` | `[]` | 搜索建议列表 |
|
||
| `debounce` | `number` | `300` | 防抖延迟(毫秒) |
|
||
|
||
---
|
||
|
||
#### 4. FilterBar
|
||
|
||
筛选栏容器,集成搜索和筛选条件。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<template>
|
||
<FilterBar
|
||
v-model:searchQuery="searchQuery"
|
||
:showSearch="true"
|
||
:hasActiveFilters="hasFilters"
|
||
searchPlaceholder="搜索..."
|
||
@searchChange="handleSearch"
|
||
@reset="resetFilters"
|
||
>
|
||
<template #filters>
|
||
<Select v-model="statusFilter">
|
||
<SelectTrigger class="w-36">
|
||
<SelectValue placeholder="状态" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="all">全部</SelectItem>
|
||
<SelectItem value="active">活跃</SelectItem>
|
||
<SelectItem value="inactive">禁用</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
|
||
<Select v-model="roleFilter">
|
||
<SelectTrigger class="w-36">
|
||
<SelectValue placeholder="角色" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="all">全部</SelectItem>
|
||
<SelectItem value="admin">管理员</SelectItem>
|
||
<SelectItem value="user">用户</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</template>
|
||
</FilterBar>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
#### 5. Pagination
|
||
|
||
分页组件,支持页码导航和每页数量选择。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
const currentPage = ref(1)
|
||
const pageSize = ref(20)
|
||
const totalRecords = ref(1000)
|
||
|
||
const loadData = () => {
|
||
// 重新加载数据
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<Pagination
|
||
v-model:currentPage="currentPage"
|
||
v-model:pageSize="pageSize"
|
||
:total="totalRecords"
|
||
:showPageSizeSelector="true"
|
||
:pageSizeOptions="[10, 20, 50, 100]"
|
||
@pageChange="loadData"
|
||
@pageSizeChange="loadData"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
**主要 Props:**
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `currentPage` | `number` | `1` | 当前页码 |
|
||
| `pageSize` | `number` | `20` | 每页显示数量 |
|
||
| `total` | `number` | `0` | 总记录数 |
|
||
| `showPageSizeSelector` | `boolean` | `true` | 是否显示页面大小选择器 |
|
||
| `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | 每页数量选项 |
|
||
|
||
---
|
||
|
||
#### 6. EmptyState
|
||
|
||
空状态组件,支持多种类型和自定义内容。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { RefreshCw, Plus } from 'lucide-vue-next'
|
||
</script>
|
||
|
||
<template>
|
||
<!-- 搜索无结果 -->
|
||
<EmptyState
|
||
type="search"
|
||
title="未找到结果"
|
||
description="尝试使用不同的关键词搜索"
|
||
actionText="清空筛选"
|
||
:actionIcon="RefreshCw"
|
||
@action="resetSearch"
|
||
/>
|
||
|
||
<!-- 筛选无结果 -->
|
||
<EmptyState
|
||
type="filter"
|
||
title="无匹配结果"
|
||
description="没有符合当前筛选条件的数据"
|
||
actionText="重置筛选"
|
||
@action="resetFilters"
|
||
/>
|
||
|
||
<!-- 空数据 -->
|
||
<EmptyState
|
||
type="empty"
|
||
size="lg"
|
||
title="暂无用户"
|
||
description="点击下方按钮添加第一个用户"
|
||
actionText="添加用户"
|
||
:actionIcon="Plus"
|
||
actionVariant="default"
|
||
@action="openAddDialog"
|
||
/>
|
||
|
||
<!-- 加载错误 -->
|
||
<EmptyState
|
||
type="error"
|
||
title="加载失败"
|
||
description="数据加载过程中出现错误,请稍后重试"
|
||
actionText="重新加载"
|
||
@action="retry"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
**类型(type):**
|
||
|
||
- `default` - 默认空状态
|
||
- `search` - 搜索无结果
|
||
- `filter` - 筛选无结果
|
||
- `error` - 加载错误
|
||
- `empty` - 空空如也
|
||
- `notFound` - 未找到资源
|
||
|
||
**主要 Props:**
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `type` | `EmptyStateType` | `'default'` | 空状态类型 |
|
||
| `title` | `string` | - | 标题(自动根据类型设置) |
|
||
| `description` | `string` | - | 描述(自动根据类型设置) |
|
||
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 大小 |
|
||
| `actionText` | `string` | - | 操作按钮文本 |
|
||
| `actionIcon` | `Component` | - | 操作按钮图标 |
|
||
| `actionVariant` | `ButtonVariant` | `'default'` | 按钮变体 |
|
||
|
||
---
|
||
|
||
#### 7. StatusBadge
|
||
|
||
状态徽章组件,用于显示不同状态。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 成功状态 -->
|
||
<StatusBadge status="success" label="已激活" :showIcon="true" />
|
||
|
||
<!-- 错误状态 -->
|
||
<StatusBadge status="error" label="已禁用" variant="solid" />
|
||
|
||
<!-- 警告状态 -->
|
||
<StatusBadge status="warning" label="待审核" variant="soft" />
|
||
|
||
<!-- 信息状态 -->
|
||
<StatusBadge status="info" label="处理中" />
|
||
|
||
<!-- 待处理 -->
|
||
<StatusBadge status="pending" label="排队中" />
|
||
|
||
<!-- 活跃 -->
|
||
<StatusBadge status="active" label="在线" />
|
||
|
||
<!-- 未激活 -->
|
||
<StatusBadge status="inactive" label="离线" />
|
||
</template>
|
||
```
|
||
|
||
**状态类型(status):**
|
||
|
||
| 状态 | 颜色 | 图标 | 用途 |
|
||
|------|------|------|------|
|
||
| `success` | 绿色 | CheckCircle2 | 成功、完成、已激活 |
|
||
| `error` | 红色 | XCircle | 错误、失败、已禁用 |
|
||
| `warning` | 黄色 | AlertCircle | 警告、待审核、需注意 |
|
||
| `info` | 蓝色 | Info | 信息、提示 |
|
||
| `pending` | 灰色 | Clock | 待处理、排队中 |
|
||
| `neutral` | 灰色 | Minus | 中性状态 |
|
||
| `active` | 主题色 | CheckCircle2 | 活跃、在线 |
|
||
| `inactive` | 灰色 | Minus | 未激活、离线 |
|
||
|
||
**变体(variant):**
|
||
|
||
- `solid` - 实心背景
|
||
- `soft` - 柔和背景(默认)
|
||
- `outline` - 描边样式
|
||
|
||
---
|
||
|
||
#### 8. LoadingState
|
||
|
||
加载状态组件,支持多种加载样式。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 旋转加载器 -->
|
||
<LoadingState
|
||
variant="spinner"
|
||
message="加载中,请稍候..."
|
||
size="md"
|
||
/>
|
||
|
||
<!-- 骨架屏 -->
|
||
<LoadingState
|
||
variant="skeleton"
|
||
size="lg"
|
||
:fullHeight="true"
|
||
/>
|
||
|
||
<!-- 脉冲点 -->
|
||
<LoadingState
|
||
variant="pulse"
|
||
message="正在加载数据..."
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
**变体(variant):**
|
||
|
||
- `spinner` - 旋转加载器(默认)
|
||
- `skeleton` - 骨架屏
|
||
- `pulse` - 脉冲点动画
|
||
|
||
---
|
||
|
||
#### 9. ConfirmButton
|
||
|
||
带确认对话框的按钮组件,简化危险操作的确认流程。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { ConfirmButton } from '@/components/common'
|
||
import { Trash2 } from 'lucide-vue-next'
|
||
|
||
const handleDelete = async () => {
|
||
await deleteItem()
|
||
console.log('删除成功')
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<!-- 危险操作确认 -->
|
||
<ConfirmButton
|
||
variant="destructive"
|
||
:icon="Trash2"
|
||
confirm-type="danger"
|
||
confirm-title="确认删除"
|
||
confirm-message="此操作不可撤销,确定要删除吗?"
|
||
@confirmed="handleDelete"
|
||
>
|
||
删除
|
||
</ConfirmButton>
|
||
|
||
<!-- 警告确认 -->
|
||
<ConfirmButton
|
||
confirm-type="warning"
|
||
confirm-title="重置配置"
|
||
confirm-message="将重置所有配置到默认值"
|
||
@confirmed="resetConfig"
|
||
>
|
||
重置
|
||
</ConfirmButton>
|
||
|
||
<!-- 无需确认直接执行 -->
|
||
<ConfirmButton
|
||
:require-confirm="false"
|
||
@click="handleClick"
|
||
>
|
||
普通按钮
|
||
</ConfirmButton>
|
||
</template>
|
||
```
|
||
|
||
**主要 Props:**
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `text` | `string` | - | 按钮文本 |
|
||
| `variant` | `ButtonVariant` | `'default'` | 按钮样式 |
|
||
| `size` | `ButtonSize` | `'md'` | 按钮大小 |
|
||
| `icon` | `Component` | - | 图标组件 |
|
||
| `disabled` | `boolean` | `false` | 是否禁用 |
|
||
| `confirmTitle` | `string` | `'确认操作'` | 确认对话框标题 |
|
||
| `confirmMessage` | `string` | `'确定要执行此操作吗?'` | 确认消息 |
|
||
| `confirmType` | `'default' \| 'danger' \| 'warning'` | `'default'` | 确认类型 |
|
||
| `requireConfirm` | `boolean` | `true` | 是否需要确认 |
|
||
|
||
**Events:**
|
||
- `click` - 不需要确认时触发
|
||
- `confirmed` - 确认后触发
|
||
- `cancelled` - 取消确认时触发
|
||
|
||
---
|
||
|
||
#### 10. ActionMenu
|
||
|
||
操作菜单下拉组件,用于集中展示多个操作选项。
|
||
|
||
**使用示例:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { ActionMenu, type ActionMenuItem } from '@/components/common'
|
||
import { Edit, Copy, Trash, MoreVertical } from 'lucide-vue-next'
|
||
|
||
const menuItems: ActionMenuItem[] = [
|
||
{
|
||
label: '编辑',
|
||
icon: Edit,
|
||
onClick: () => handleEdit()
|
||
},
|
||
{
|
||
label: '复制',
|
||
icon: Copy,
|
||
onClick: () => handleCopy()
|
||
},
|
||
{ separator: true },
|
||
{
|
||
label: '删除',
|
||
icon: Trash,
|
||
variant: 'destructive',
|
||
onClick: async () => {
|
||
await handleDelete()
|
||
}
|
||
}
|
||
]
|
||
</script>
|
||
|
||
<template>
|
||
<ActionMenu
|
||
:items="menuItems"
|
||
:trigger-icon="MoreVertical"
|
||
trigger-variant="ghost"
|
||
placement="bottom-end"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
**ActionMenuItem 接口:**
|
||
|
||
```ts
|
||
interface ActionMenuItem {
|
||
label?: string // 菜单项标签
|
||
icon?: Component // 图标
|
||
badge?: string | number // 徽章
|
||
variant?: 'default' | 'destructive' // 样式变体
|
||
disabled?: boolean // 是否禁用
|
||
separator?: boolean // 是否为分隔线
|
||
onClick?: () => void | Promise<void> // 点击回调
|
||
}
|
||
```
|
||
|
||
**主要 Props:**
|
||
|
||
| 属性 | 类型 | 默认值 | 说明 |
|
||
|------|------|--------|------|
|
||
| `items` | `ActionMenuItem[]` | - | 菜单项列表(必填) |
|
||
| `triggerText` | `string` | - | 触发按钮文本 |
|
||
| `triggerIcon` | `Component` | - | 触发按钮图标 |
|
||
| `triggerVariant` | `ButtonVariant` | `'outline'` | 触发按钮样式 |
|
||
| `triggerSize` | `ButtonSize` | `'sm'` | 触发按钮大小 |
|
||
| `showChevron` | `boolean` | `true` | 是否显示下拉箭头 |
|
||
| `placement` | `'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end'` | `'bottom-end'` | 菜单位置 |
|
||
|
||
---
|
||
|
||
## 工具函数 (Composables)
|
||
|
||
位于 [src/composables/](src/composables/)
|
||
|
||
### useBreakpoints
|
||
|
||
响应式断点检测,用于实现响应式布局。
|
||
|
||
```ts
|
||
import { useBreakpoints } from '@/composables/useBreakpoints'
|
||
|
||
const {
|
||
windowWidth, // 窗口宽度
|
||
isSm, // >= 640px
|
||
isMd, // >= 768px
|
||
isLg, // >= 1024px
|
||
isXl, // >= 1280px
|
||
is2Xl, // >= 1536px
|
||
current, // 当前断点 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
||
isMobile, // < 768px
|
||
isTablet, // 768px ~ 1024px
|
||
isDesktop // >= 1024px
|
||
} = useBreakpoints()
|
||
|
||
// 示例:根据屏幕大小显示不同内容
|
||
<div v-if="isMobile">移动端视图</div>
|
||
<div v-else-if="isTablet">平板视图</div>
|
||
<div v-else>桌面视图</div>
|
||
```
|
||
|
||
---
|
||
|
||
### useToast
|
||
|
||
消息提示管理,统一的 Toast 通知接口。
|
||
|
||
```ts
|
||
import { useToast } from '@/composables/useToast'
|
||
|
||
const { success, error, warning, info } = useToast()
|
||
|
||
// 成功消息(5秒后自动消失)
|
||
success('操作成功')
|
||
success('数据保存成功', '提示')
|
||
|
||
// 错误消息(8秒后自动消失)
|
||
error('操作失败')
|
||
error('保存失败,请检查网络连接', '错误')
|
||
|
||
// 警告消息(8秒后自动消失)
|
||
warning('该操作可能影响其他数据', '警告')
|
||
|
||
// 信息消息(5秒后自动消失)
|
||
info('系统将在 5 分钟后进行维护', '系统通知')
|
||
```
|
||
|
||
**接口定义:**
|
||
|
||
```ts
|
||
interface UseToast {
|
||
toasts: Ref<Toast[]>
|
||
success(message: string, title?: string): string
|
||
error(message: string, title?: string): string
|
||
warning(message: string, title?: string): string
|
||
info(message: string, title?: string): string
|
||
showToast(options: Omit<Toast, 'id'>): string
|
||
removeToast(id: string): void
|
||
clearAll(): void
|
||
}
|
||
|
||
interface Toast {
|
||
id: string
|
||
title?: string
|
||
message?: string
|
||
variant?: 'success' | 'error' | 'warning' | 'info'
|
||
duration?: number
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### useConfirm
|
||
|
||
确认对话框,用于危险操作确认。
|
||
|
||
```ts
|
||
import { useConfirm } from '@/composables/useConfirm'
|
||
|
||
const { confirm, confirmDanger, confirmWarning } = useConfirm()
|
||
|
||
// 普通确认
|
||
const ok = await confirm('确定要删除吗?', '确认删除')
|
||
if (ok) {
|
||
await deleteItem()
|
||
}
|
||
|
||
// 危险操作确认(红色按钮)
|
||
const ok = await confirmDanger(
|
||
'此操作不可撤销,确定继续吗?',
|
||
'删除确认'
|
||
)
|
||
|
||
// 警告确认(黄色主题)
|
||
const ok = await confirmWarning(
|
||
'该操作可能影响其他用户,是否继续?',
|
||
'警告'
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
### useClasses
|
||
|
||
类名工具函数,简化条件类名的生成。
|
||
|
||
```ts
|
||
import { useClasses } from '@/composables/useClasses'
|
||
|
||
const { cn, conditional, fromObject } = useClasses()
|
||
|
||
// 合并类名(过滤 falsy 值)
|
||
const className = cn(
|
||
'base-class',
|
||
isActive && 'active',
|
||
error && 'error',
|
||
'another-class'
|
||
)
|
||
// 结果: 'base-class active another-class' (假设 isActive=true, error=false)
|
||
|
||
// 条件类名
|
||
const className = conditional(isActive, 'bg-primary', 'bg-muted')
|
||
// 结果: isActive ? 'bg-primary' : 'bg-muted'
|
||
|
||
// 从对象生成类名
|
||
const className = fromObject({
|
||
'text-red-500': hasError,
|
||
'font-bold': isImportant,
|
||
'underline': isLink
|
||
})
|
||
// 结果: 只包含值为 true 的键
|
||
```
|
||
|
||
---
|
||
|
||
## 最佳实践
|
||
|
||
### 1. 组件开发规范
|
||
|
||
#### 使用 TypeScript
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
interface Props {
|
||
title: string
|
||
count?: number
|
||
items?: string[]
|
||
}
|
||
|
||
interface Emits {
|
||
(e: 'update', value: string): void
|
||
(e: 'delete', id: string): void
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
count: 0,
|
||
items: () => []
|
||
})
|
||
|
||
const emit = defineEmits<Emits>()
|
||
</script>
|
||
```
|
||
|
||
#### 遵循命名规范
|
||
|
||
- **组件文件**: PascalCase (如 `UserCard.vue`、`DataTable.vue`)
|
||
- **Composables**: camelCase + `use` 前缀 (如 `useAuth.ts`、`useBreakpoints.ts`)
|
||
- **工具函数**: camelCase (如 `formatDate.ts`、`validateEmail.ts`)
|
||
- **常量**: SCREAMING_SNAKE_CASE (如 `API_BASE_URL`、`MAX_FILE_SIZE`)
|
||
|
||
#### 合理使用插槽
|
||
|
||
```vue
|
||
<template>
|
||
<Card>
|
||
<!-- 具名插槽 + 默认内容 -->
|
||
<template #header>
|
||
<slot name="header">
|
||
<h3 class="text-lg font-semibold">默认标题</h3>
|
||
</slot>
|
||
</template>
|
||
|
||
<!-- 默认插槽 -->
|
||
<slot>默认内容</slot>
|
||
|
||
<!-- 作用域插槽 -->
|
||
<template #footer>
|
||
<slot name="footer" :count="items.length" :total="total" />
|
||
</template>
|
||
</Card>
|
||
</template>
|
||
```
|
||
|
||
#### 统一错误处理
|
||
|
||
```ts
|
||
import { useToast } from '@/composables/useToast'
|
||
import { apiClient } from '@/api/client'
|
||
|
||
const { error: showError, success: showSuccess } = useToast()
|
||
|
||
async function saveData() {
|
||
try {
|
||
await apiClient.post('/users', userData)
|
||
showSuccess('用户创建成功')
|
||
} catch (err: any) {
|
||
const message = err.response?.data?.detail || err.message || '操作失败'
|
||
showError(message, '错误')
|
||
console.error('Failed to create user:', err)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2. 样式规范
|
||
|
||
#### 优先使用 Tailwind 类
|
||
|
||
```vue
|
||
<template>
|
||
<div class="flex items-center gap-4 p-6 rounded-lg bg-card border border-border hover:shadow-md transition-shadow">
|
||
<Avatar :src="user.avatar" />
|
||
<div class="flex-1 min-w-0">
|
||
<h3 class="text-lg font-semibold truncate">{{ user.name }}</h3>
|
||
<p class="text-sm text-muted-foreground">{{ user.role }}</p>
|
||
</div>
|
||
<Badge :variant="user.active ? 'success' : 'neutral'">
|
||
{{ user.active ? '在线' : '离线' }}
|
||
</Badge>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
#### 使用主题 CSS 变量
|
||
|
||
```vue
|
||
<template>
|
||
<div class="custom-card">
|
||
<!-- 内容 -->
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.custom-card {
|
||
background-color: hsl(var(--card));
|
||
color: hsl(var(--card-foreground));
|
||
border: 1px solid hsl(var(--border));
|
||
border-radius: var(--radius);
|
||
}
|
||
|
||
.custom-card:hover {
|
||
background-color: hsl(var(--muted));
|
||
}
|
||
</style>
|
||
```
|
||
|
||
#### 响应式设计
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 移动端 1 列,平板 2 列,桌面 3 列 -->
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
<Card v-for="item in items" :key="item.id">
|
||
{{ item.name }}
|
||
</Card>
|
||
</div>
|
||
|
||
<!-- 使用 composable 实现条件渲染 -->
|
||
<div v-if="isMobile">移动端布局</div>
|
||
<div v-else>桌面端布局</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { useBreakpoints } from '@/composables/useBreakpoints'
|
||
|
||
const { isMobile } = useBreakpoints()
|
||
</script>
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 性能优化
|
||
|
||
#### 按需导入组件
|
||
|
||
```ts
|
||
import { defineAsyncComponent } from 'vue'
|
||
|
||
// 异步加载重型组件
|
||
const HeavyChart = defineAsyncComponent(() =>
|
||
import('./components/HeavyChart.vue')
|
||
)
|
||
|
||
// 带加载状态的异步组件
|
||
const HeavyTable = defineAsyncComponent({
|
||
loader: () => import('./components/HeavyTable.vue'),
|
||
loadingComponent: LoadingState,
|
||
delay: 200,
|
||
errorComponent: ErrorState,
|
||
timeout: 3000
|
||
})
|
||
```
|
||
|
||
#### 使用 v-memo 优化列表
|
||
|
||
```vue
|
||
<template>
|
||
<div
|
||
v-for="item in largeList"
|
||
:key="item.id"
|
||
v-memo="[item.updated_at, item.status]"
|
||
>
|
||
<!-- 仅当 updated_at 或 status 变化时重新渲染 -->
|
||
<ItemCard :item="item" />
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
#### 虚拟滚动
|
||
|
||
对于超过 100 条的列表,使用虚拟滚动:
|
||
|
||
```bash
|
||
npm install vue-virtual-scroller
|
||
```
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { RecycleScroller } from 'vue-virtual-scroller'
|
||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||
</script>
|
||
|
||
<template>
|
||
<RecycleScroller
|
||
:items="items"
|
||
:item-size="64"
|
||
key-field="id"
|
||
v-slot="{ item }"
|
||
>
|
||
<UserCard :user="item" />
|
||
</RecycleScroller>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 可访问性 (a11y)
|
||
|
||
#### 语义化 HTML
|
||
|
||
```vue
|
||
<template>
|
||
<nav aria-label="主导航">
|
||
<ul role="list">
|
||
<li><RouterLink to="/dashboard">仪表盘</RouterLink></li>
|
||
<li><RouterLink to="/users">用户管理</RouterLink></li>
|
||
</ul>
|
||
</nav>
|
||
|
||
<main>
|
||
<h1>页面标题</h1>
|
||
<section aria-labelledby="section-title">
|
||
<h2 id="section-title">区块标题</h2>
|
||
<!-- 内容 -->
|
||
</section>
|
||
</main>
|
||
</template>
|
||
```
|
||
|
||
#### 键盘导航
|
||
|
||
```vue
|
||
<template>
|
||
<button
|
||
@click="handleClick"
|
||
@keydown.enter="handleClick"
|
||
@keydown.space.prevent="handleClick"
|
||
:aria-label="buttonLabel"
|
||
>
|
||
<Icon :name="iconName" aria-hidden="true" />
|
||
{{ text }}
|
||
</button>
|
||
|
||
<!-- 可聚焦的非按钮元素 -->
|
||
<div
|
||
role="button"
|
||
tabindex="0"
|
||
@click="handleAction"
|
||
@keydown.enter="handleAction"
|
||
@keydown.space.prevent="handleAction"
|
||
>
|
||
自定义按钮
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
#### ARIA 属性
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 对话框 -->
|
||
<div
|
||
role="dialog"
|
||
aria-labelledby="dialog-title"
|
||
aria-describedby="dialog-description"
|
||
aria-modal="true"
|
||
>
|
||
<h2 id="dialog-title">对话框标题</h2>
|
||
<p id="dialog-description">对话框描述</p>
|
||
</div>
|
||
|
||
<!-- 表单 -->
|
||
<form>
|
||
<label for="username">用户名</label>
|
||
<input
|
||
id="username"
|
||
type="text"
|
||
aria-required="true"
|
||
aria-invalid="false"
|
||
aria-describedby="username-error"
|
||
/>
|
||
<span id="username-error" role="alert" aria-live="polite">
|
||
<!-- 错误信息 -->
|
||
</span>
|
||
</form>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
## 组件迁移检查清单
|
||
|
||
### 已完全迁移到 shadcn 的页面
|
||
|
||
- [x] Dashboard.vue
|
||
- [x] Users.vue
|
||
- [x] Settings.vue
|
||
- [x] SystemSettings.vue
|
||
- [x] Profile.vue
|
||
- [x] ActivityLogs.vue
|
||
- [x] Announcements.vue
|
||
- [x] ApiKeys.vue
|
||
- [x] AuditLogs.vue
|
||
- [x] MyApiKeys.vue
|
||
- [x] Usage.vue
|
||
- [x] ProviderList.vue
|
||
- [x] MyProviders.vue
|
||
- [x] CacheMonitoring.vue
|
||
- [x] ProviderDetailNew.vue
|
||
|
||
### 部分迁移或自定义样式
|
||
|
||
- [ ] Home.vue - 使用大量自定义动画和样式(不建议迁移)
|
||
|
||
---
|
||
|
||
## 更新日志
|
||
|
||
### v2.3.0 (2025-11-18)
|
||
|
||
**新增组件:**
|
||
- `ConfirmButton` - 带确认对话框的按钮组件
|
||
- `ActionMenu` - 操作菜单下拉组件
|
||
|
||
**优化导入系统:**
|
||
- 创建 `@/components/ui/index.ts` 统一导出所有 shadcn UI 组件
|
||
- 完善 `@/components/layout/index.ts` 和 `@/components/common/index.ts`
|
||
- 支持更简洁的组件导入方式
|
||
|
||
**导入方式优化:**
|
||
|
||
```ts
|
||
// 旧版导入 (繁琐)
|
||
import Button from '@/components/ui/button.vue'
|
||
import Input from '@/components/ui/input.vue'
|
||
import Card from '@/components/ui/card.vue'
|
||
|
||
// 新版导入 (推荐)
|
||
import { Button, Input, Card } from '@/components/ui'
|
||
import { PageHeader, Section, CardSection } from '@/components/layout'
|
||
import { DataTable, ConfirmButton, ActionMenu } from '@/components/common'
|
||
```
|
||
|
||
**文档:**
|
||
- 添加 `ConfirmButton` 和 `ActionMenu` 组件完整文档
|
||
- 更新组件导入最佳实践
|
||
|
||
### v2.2.0 (2025-11-18)
|
||
|
||
**重构:**
|
||
- 统一布局组件目录: 将 `layout-v2` 合并到 `layout`
|
||
- 所有布局组件现在从 `@/components/layout` 统一导入
|
||
- 删除冗余的 `layout-v2` 目录
|
||
|
||
**文档:**
|
||
- 添加完整的布局组件文档和使用示例
|
||
- 标记 `ShellHeader` 为待废弃组件
|
||
|
||
**迁移指南:**
|
||
```ts
|
||
// 旧版导入 (已废弃)
|
||
import { PageHeader } from '@/components/layout-v2'
|
||
|
||
// 新版导入 (推荐)
|
||
import { PageHeader } from '@/components/layout'
|
||
```
|
||
|
||
### v2.1.0 (2025-11-18)
|
||
|
||
**新增:**
|
||
- 完善所有组件文档和使用示例
|
||
- 添加完整的 TypeScript 类型定义
|
||
- 统一 Toast 工具函数接口(useToast)
|
||
- 完善响应式支持(useBreakpoints)
|
||
|
||
**修复:**
|
||
- 修复 CacheMonitoring.vue 的 toast 调用
|
||
- 统一所有页面的组件使用
|
||
|
||
**文档:**
|
||
- 更新所有组件的使用示例
|
||
- 添加最佳实践章节
|
||
- 添加性能优化指南
|
||
- 添加可访问性指南
|
||
|
||
### v2.0.0 (2025-11-17)
|
||
|
||
- 基础设计系统搭建
|
||
- 实现所有核心业务组件
|
||
- 建立主题系统
|
||
|
||
---
|
||
|
||
## 参考资源
|
||
|
||
- [shadcn/ui 官方文档](https://ui.shadcn.com/)
|
||
- [Tailwind CSS 文档](https://tailwindcss.com/docs)
|
||
- [Vue 3 文档](https://vuejs.org/)
|
||
- [Lucide Icons](https://lucide.dev/)
|
||
- [WCAG 2.1 标准](https://www.w3.org/WAI/WCAG21/quickref/)
|
||
- [主题配置文件](src/config/theme.ts)
|