**新增内容**: - 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>
7.9 KiB
7.9 KiB
组件 UI 重新设计示例
本文档展示如何将现有组件中的 emoji 替换为 SVG 图标,并应用更专业的设计风格。
示例 1: GlobalSidebar.vue
Before
<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
<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
<template>
<div class="header">
<span>📂</span>
<h2>文稿库</h2>
</div>
<button class="btn-save">
💾 保存
</button>
<button class="btn-delete">
🗑️
</button>
</template>
After
<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
<template>
<div class="toolbar">
<button @click="copyContent">
📋 复制 Markdown
</button>
<button @click="clearContent">
🗑️ 清空
</button>
</div>
</template>
After
<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
<template>
<div class="status">
⚠️ 警告信息
</div>
<div class="status">
✅ 操作成功
</div>
<div class="status">
❌ 发生错误
</div>
</template>
After
<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
<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
<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 - 测试响应式布局