feat: 添加 SVG 图标系统替代 emoji
**新增内容**: - BaseIcon.vue: 基础图标组件 - IconLibrary.vue: 图标库组件,包含 40+ 常用图标 - ICON_SYSTEM.md: 图标系统使用指南 - COMPONENT_REDESIGN_EXAMPLES.md: 组件重设计示例文档 **图标覆盖范围** (40+ 图标): - 写作相关: edit, file-text, document, article - 数据素材: database, folder, chart - 设置工具: settings, gear - 操作: save, copy, trash, delete, close, check - 导航: home, menu, sidebar - 搜索筛选: search, filter - 状态通知: warning, error, info, success - 分析比较: compare, analysis, sparkles - 时间历史: history, clock - 添加移除: add, plus, minus, expand, collapse - 其他: download, upload, refresh, loading, list **设计原则**: - 使用 SVG stroke 图标,统一视觉风格 - 图标继承父元素颜色 (currentColor) - 支持 16-48px 尺寸范围 - 完全替代 emoji,提升专业性 **使用方式**: ```vue <IconLibrary name="save" :size="20" /> ``` **后续任务**: - [ ] 逐个组件迁移,移除 61 处 emoji - [ ] 应用统一的按钮样式 - [ ] 优化交互反馈 - [ ] 测试响应式布局 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
424
docs/COMPONENT_REDESIGN_EXAMPLES.md
Normal file
424
docs/COMPONENT_REDESIGN_EXAMPLES.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# 组件 UI 重新设计示例
|
||||
|
||||
本文档展示如何将现有组件中的 emoji 替换为 SVG 图标,并应用更专业的设计风格。
|
||||
|
||||
## 示例 1: GlobalSidebar.vue
|
||||
|
||||
### Before
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="sidebar-item" :class="{ active: isActive }">
|
||||
<span class="icon">✍️</span>
|
||||
<span class="label">AI 写作</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidebar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-3);
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import IconLibrary from './icons/IconLibrary.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-item" :class="{ active: isActive }">
|
||||
<IconLibrary name="edit" :size="18" class="icon" />
|
||||
<span class="label">AI 写作</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidebar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-radius: var(--radius-md);
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.sidebar-item:hover {
|
||||
background: var(--bg-elevated);
|
||||
}
|
||||
|
||||
.sidebar-item.active {
|
||||
background: var(--info-bg);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 示例 2: DocumentsPanel.vue
|
||||
|
||||
### Before
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="header">
|
||||
<span>📂</span>
|
||||
<h2>文稿库</h2>
|
||||
</div>
|
||||
|
||||
<button class="btn-save">
|
||||
💾 保存
|
||||
</button>
|
||||
|
||||
<button class="btn-delete">
|
||||
🗑️
|
||||
</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import IconLibrary from './icons/IconLibrary.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="header">
|
||||
<IconLibrary name="folder" :size="20" />
|
||||
<h2>文稿库</h2>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">
|
||||
<IconLibrary name="save" :size="16" />
|
||||
<span>保存</span>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-icon btn-danger" aria-label="删除文档">
|
||||
<IconLibrary name="trash" :size="18" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
font-size: var(--text-xl);
|
||||
font-weight: var(--font-semibold);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-medium);
|
||||
transition: all var(--transition-fast);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent-primary);
|
||||
color: var(--text-inverse);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-primary-hover);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
padding: var(--space-2);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: var(--accent-danger);
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: var(--danger-bg);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 示例 3: MainContent.vue 工具栏
|
||||
|
||||
### Before
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="toolbar">
|
||||
<button @click="copyContent">
|
||||
📋 复制 Markdown
|
||||
</button>
|
||||
<button @click="clearContent">
|
||||
🗑️ 清空
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import IconLibrary from './icons/IconLibrary.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="toolbar">
|
||||
<button class="toolbar-btn" @click="copyContent">
|
||||
<IconLibrary name="copy" :size="16" />
|
||||
<span>复制 Markdown</span>
|
||||
</button>
|
||||
<button class="toolbar-btn toolbar-btn-danger" @click="clearContent">
|
||||
<IconLibrary name="trash" :size="16" />
|
||||
<span>清空</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.toolbar {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-secondary);
|
||||
font-size: var(--text-xs);
|
||||
transition: all var(--transition-fast);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toolbar-btn:hover {
|
||||
border-color: var(--border-strong);
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-elevated);
|
||||
}
|
||||
|
||||
.toolbar-btn-danger:hover {
|
||||
border-color: var(--accent-danger);
|
||||
color: var(--accent-danger);
|
||||
background: var(--danger-bg);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 示例 4: 状态指示器
|
||||
|
||||
### Before
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="status">
|
||||
⚠️ 警告信息
|
||||
</div>
|
||||
<div class="status">
|
||||
✅ 操作成功
|
||||
</div>
|
||||
<div class="status">
|
||||
❌ 发生错误
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import IconLibrary from './icons/IconLibrary.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="status status-warning">
|
||||
<IconLibrary name="warning" :size="16" />
|
||||
<span>警告信息</span>
|
||||
</div>
|
||||
|
||||
<div class="status status-success">
|
||||
<IconLibrary name="check" :size="16" />
|
||||
<span>操作成功</span>
|
||||
</div>
|
||||
|
||||
<div class="status status-error">
|
||||
<IconLibrary name="error" :size="16" />
|
||||
<span>发生错误</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
background: var(--warning-bg);
|
||||
color: var(--accent-warning);
|
||||
border: 1px solid rgba(251, 191, 36, 0.2);
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background: var(--success-bg);
|
||||
color: var(--accent-success);
|
||||
border: 1px solid rgba(52, 211, 153, 0.2);
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background: var(--danger-bg);
|
||||
color: var(--accent-danger);
|
||||
border: 1px solid rgba(248, 113, 113, 0.2);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 示例 5: 空状态提示
|
||||
|
||||
### Before
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📄</div>
|
||||
<p>暂无文稿</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: var(--space-4);
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import IconLibrary from './icons/IconLibrary.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon-wrapper">
|
||||
<IconLibrary name="document" :size="48" />
|
||||
</div>
|
||||
<p class="empty-title">暂无文稿</p>
|
||||
<p class="empty-desc">在左侧创建或选择文稿开始写作</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--space-20) var(--space-6);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon-wrapper {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px dashed var(--border-default);
|
||||
border-radius: var(--radius-xl);
|
||||
margin-bottom: var(--space-6);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
font-size: var(--text-base);
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 设计原则总结
|
||||
|
||||
### 1. 一致的间距系统
|
||||
- 使用 `gap` 属性设置图标和文字的间距
|
||||
- 推荐间距:`var(--space-2)` (8px)
|
||||
|
||||
### 2. 语义化颜色
|
||||
- 主要操作:`var(--accent-primary)`
|
||||
- 成功状态:`var(--accent-success)`
|
||||
- 警告状态:`var(--accent-warning)`
|
||||
- 危险操作:`var(--accent-danger)`
|
||||
|
||||
### 3. 交互反馈
|
||||
- 所有可点击元素添加 `transition`
|
||||
- hover 状态使用 `var(--bg-elevated)` 背景变化
|
||||
- 添加适当的 `border-radius`
|
||||
|
||||
### 4. 图标尺寸规范
|
||||
- 按钮内图标:16px
|
||||
- 标题/标签图标:18-20px
|
||||
- 大型展示图标:24-48px
|
||||
|
||||
### 5. 对齐方式
|
||||
- 使用 `display: flex`
|
||||
- `align-items: center` 确保垂直居中
|
||||
- 图标添加 `flex-shrink: 0` 防止被压缩
|
||||
|
||||
## 迁移检查清单
|
||||
|
||||
在完成每个组件的迁移后,请确认:
|
||||
|
||||
- [ ] 移除所有 emoji 字符
|
||||
- [ ] 导入 `IconLibrary` 组件
|
||||
- [ ] 图标名称符合语义
|
||||
- [ ] 图标尺寸合适
|
||||
- [ ] 图标和文字正确对齐
|
||||
- [ ] hover 状态有视觉反馈
|
||||
- [ ] 颜色使用设计令牌
|
||||
- [ ] 间距符合设计系统
|
||||
- [ ] 添加必要的 `aria-label`
|
||||
- [ ] 测试响应式布局
|
||||
230
docs/ICON_SYSTEM.md
Normal file
230
docs/ICON_SYSTEM.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# 图标系统使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
本项目使用 SVG 图标替代 emoji,提供更专业、统一的视觉体验。
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 方式一:使用 IconLibrary 组件(推荐)
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import IconLibrary from '@/components/icons/IconLibrary.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 基本使用 -->
|
||||
<IconLibrary name="edit" :size="20" />
|
||||
|
||||
<!-- 不同尺寸 -->
|
||||
<IconLibrary name="save" :size="16" />
|
||||
<IconLibrary name="delete" :size="24" />
|
||||
|
||||
<!-- 配合文本 -->
|
||||
<button class="btn">
|
||||
<IconLibrary name="save" :size="16" />
|
||||
<span>保存</span>
|
||||
</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 方式二:直接使用 SVG
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</svg>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 可用图标列表
|
||||
|
||||
### 写作相关
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `edit` | 编辑/写作 | ✏️ |
|
||||
| `file-text` | 文档 | 📄 |
|
||||
| `document` | 文件 | 📝 |
|
||||
| `article` | 文章 | 📰 |
|
||||
| `format-text` | 文本格式 | 📃 |
|
||||
|
||||
### 数据和素材
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `database` | 数据库 | 🗄️ |
|
||||
| `folder` | 文件夹 | 📁 |
|
||||
| `chart` | 图表 | 📊 |
|
||||
|
||||
### 设置和工具
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `settings` | 设置 | ⚙️ |
|
||||
| `gear` | 齿轮 | 🔧 |
|
||||
|
||||
### 操作
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `save` | 保存 | 💾 |
|
||||
| `copy` | 复制 | 📋 |
|
||||
| `trash` | 删除 | 🗑️ |
|
||||
| `delete` | 删除 | ❌ |
|
||||
| `close` | 关闭 | ✕ |
|
||||
| `check` | 确认 | ✅ |
|
||||
|
||||
### 导航
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `home` | 首页 | 🏠 |
|
||||
| `menu` | 菜单 | 📋 |
|
||||
| `sidebar` | 侧边栏 | 📂 |
|
||||
|
||||
### 搜索和筛选
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `search` | 搜索 | 🔍 |
|
||||
| `filter` | 筛选 | 🔎 |
|
||||
|
||||
### 状态和通知
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `warning` | 警告 | ⚠️ |
|
||||
| `error` | 错误 | ❌ |
|
||||
| `info` | 信息 | ℹ️ |
|
||||
| `success` | 成功 | ✅ |
|
||||
|
||||
### 分析和比较
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `compare` | 对比 | ⚖️ |
|
||||
| `analysis` | 分析 | 📈 |
|
||||
| `sparkles` | AI/魔法 | ✨ |
|
||||
|
||||
### 时间和历史
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `history` | 历史 | 🕐 |
|
||||
| `clock` | 时间 | ⏰ |
|
||||
|
||||
### 添加和移除
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `add` / `plus` | 添加 | ➕ |
|
||||
| `minus` | 减少 | ➖ |
|
||||
| `expand` | 展开 | ⬇️ |
|
||||
| `collapse` | 折叠 | ⬆️ |
|
||||
|
||||
### 其他
|
||||
| 图标名称 | 说明 | 替代的 emoji |
|
||||
|---------|------|-------------|
|
||||
| `download` | 下载 | 📥 |
|
||||
| `upload` | 上传 | 📤 |
|
||||
| `refresh` | 刷新 | 🔄 |
|
||||
| `loading` | 加载中 | ⏳ |
|
||||
| `list` | 列表 | 📋 |
|
||||
|
||||
## 迁移示例
|
||||
|
||||
### Before (使用 emoji)
|
||||
|
||||
```vue
|
||||
<button class="btn">
|
||||
💾 保存
|
||||
</button>
|
||||
|
||||
<div class="panel">
|
||||
⚙️ 设置
|
||||
</div>
|
||||
```
|
||||
|
||||
### After (使用 SVG 图标)
|
||||
|
||||
```vue
|
||||
<button class="btn">
|
||||
<IconLibrary name="save" :size="16" />
|
||||
<span>保存</span>
|
||||
</button>
|
||||
|
||||
<div class="panel">
|
||||
<IconLibrary name="settings" :size="20" />
|
||||
<span>设置</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 样式建议
|
||||
|
||||
### 图标尺寸
|
||||
|
||||
```css
|
||||
/* 按钮内图标 */
|
||||
.btn-icon { size: 16px; }
|
||||
|
||||
/* 标题图标 */
|
||||
.title-icon { size: 20px; }
|
||||
|
||||
/* 大图标 */
|
||||
.large-icon { size: 24px; }
|
||||
```
|
||||
|
||||
### 图标间距
|
||||
|
||||
```vue
|
||||
<!-- 图标与文字之间留有空隙 -->
|
||||
<button class="btn">
|
||||
<IconLibrary name="save" :size="16" />
|
||||
<span class="ml-2">保存</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
### 图标颜色
|
||||
|
||||
图标默认使用 `currentColor`,会继承父元素的 `color` 属性:
|
||||
|
||||
```css
|
||||
/* 主要操作图标 */
|
||||
.btn-primary .icon {
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
/* 危险操作图标 */
|
||||
.btn-danger .icon {
|
||||
color: var(--accent-danger);
|
||||
}
|
||||
```
|
||||
|
||||
## 添加新图标
|
||||
|
||||
如果需要添加新图标,请按以下步骤:
|
||||
|
||||
1. 在 `IconLibrary.vue` 的 `icons` 对象中添加新图标
|
||||
2. 提供 SVG path 数据
|
||||
3. 更新此文档
|
||||
|
||||
```javascript
|
||||
'your-icon': {
|
||||
paths: ['M12 2L2 22h20L12 2z'], // 示例 path
|
||||
viewBox: '0 0 24 24'
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **一致性**:在整个应用中使用相同的图标系统
|
||||
2. **语义化**:选择符合用户预期的图标
|
||||
3. **尺寸**:保持相近场景的图标尺寸一致
|
||||
4. **对齐**:使用 `flex` 布局确保图标与文本正确对齐
|
||||
5. **可访问性**:为图标提供 `aria-label` 或配合文字使用
|
||||
|
||||
```vue
|
||||
<!-- 推荐:图标 + 文字 -->
|
||||
<button>
|
||||
<IconLibrary name="save" :size="16" aria-hidden="true" />
|
||||
<span>保存</span>
|
||||
</button>
|
||||
|
||||
<!-- 如需单独使用,添加 aria-label -->
|
||||
<button aria-label="保存文档">
|
||||
<IconLibrary name="save" :size="20" />
|
||||
</button>
|
||||
```
|
||||
Reference in New Issue
Block a user