Files
bim_engine/docs/utils/icon-manager.md
2025-12-25 19:08:26 +08:00

11 KiB
Raw Blame History

Icon Manager 图标管理器文档

本文档详细描述 Icon Manager 图标管理工具的实现细节,包括 API、使用方式、图标清单等,供 AI 根据文档重现功能。


1. 工具概述

1.1 基本信息

  • 工具名称: IconManager
  • 文件路径: src/utils/icon-manager.ts
  • 导出函数: getIcon(name: string): string
  • 用途: 统一管理所有 SVG 图标资源,提供通过名称获取图标的接口
  • 特点: 单一职责,简单易用,支持默认图标回退

1.2 设计目标

  • 统一管理: 所有图标集中在一个文件中管理,避免重复和不一致
  • 简化使用: 通过简单的函数调用获取图标,无需在各处重复 SVG 代码
  • 类型安全: SVG 作为字符串存储,可直接用于 innerHTML 或图标属性
  • 容错机制: 当请求的图标不存在时,返回默认图标而不是报错

2. API 文档

2.1 getIcon 函数

function getIcon(name: string): string

参数:

  • name: string - 图标名称(中文)

返回值:

  • string - SVG 图标的完整字符串

行为:

  1. ICONS 对象中查找对应名称的图标
  2. 如果找到,返回该图标的 SVG 字符串
  3. 如果未找到,在控制台输出警告并返回默认图标

示例:

import { getIcon } from '../../utils/icon-manager';

// 获取测量图标
const measureIcon = getIcon('测量');

// 获取不存在的图标(会返回默认图标并警告)
const unknownIcon = getIcon('不存在的图标');
// 控制台输出: [IconManager] Icon "不存在的图标" not found, using default icon

3. 图标清单

3.1 来自 assets 的图标 (48x48)

这些图标从 src/assets/ 目录中的 SVG 文件简化而来,用于 toolbar 和主要功能按钮:

图标名称 用途 原始文件
测量 测量工具 测量.svg
地图 地图视图/平面图 地图.svg
框选放大 框选缩放 框选放大.svg
漫游 漫游模式 漫游.svg
目录树 构件树 目录树.svg
剖切 剖切菜单 剖切.svg
剖切盒 剖切盒 剖切盒.svg
全屏 全屏模式 全屏.svg
设置 设置面板 设置.svg
拾曲面剖切 拾取曲面剖切 拾曲面剖切.svg
轴向剖切 轴向剖切 轴向剖切.svg
主视角 主视角/首页 主视角.svg
文档 文档/属性 地图 1.svg

3.2 测量相关图标 (32x32)

用于测量面板的各种测量方式:

图标名称 用途
标高 标高测量
距离 距离测量
最小距离 最小距离测量
激光边距 激光边距测量
角度 角度测量
坡度 坡度测量
体积 体积测量
空间体积 空间体积测量

3.3 通用图标 (24x24)

常用的 UI 图标:

图标名称 用途
close 关闭
check 勾选/确认
warning 警告
error 错误
success 成功
plus 加号/新增
minus 减号/删除
arrowUp 向上箭头
arrowDown 向下箭头
arrowLeft 向左箭头
arrowRight 向右箭头
search 搜索
refresh 刷新
delete 删除
edit 编辑
save 保存
expand 展开
collapse 收起

3.4 默认图标

图标名称 用途
default 当请求的图标不存在时返回

4. 使用场景

4.1 在按钮配置中使用

import { getIcon } from '../../../utils/icon-manager';

export const createMeasureButton = (engine: BimEngine): ButtonConfig => {
    return {
        id: 'measure',
        groupId: 'group-1',
        type: 'button',
        label: 'toolbar.measure',
        icon: getIcon('测量'),  // 使用图标管理器
        onClick: () => {
            engine.measure?.show();
        }
    };
};

4.2 在组件中使用

import { getIcon } from '../../utils/icon-manager';

export class WalkControlPanel implements IBimComponent {
    private getIconSVG(type: string): string {
        const icons: Record<string, string> = {
            'plan-view': getIcon('地图'),
            'path': getIcon('地图'),
            'walk': getIcon('漫游')
        };
        return icons[type] || '';
    }

    private createIconButton(type: string, onClick: () => void): HTMLButtonElement {
        const btn = document.createElement('button');
        btn.className = `walk-icon-btn walk-icon-btn-${type}`;
        btn.innerHTML = this.getIconSVG(type);
        btn.addEventListener('click', onClick);
        return btn;
    }
}

4.3 在树节点中使用

import { getIcon } from '../../utils/icon-manager';

const treeData: TreeNodeConfig[] = [
    {
        id: 'level-1',
        label: '一层',
        icon: getIcon('目录树'),
        children: [...]
    }
];

5. SVG 简化规范

5.1 简化原则

从 assets 目录中的 SVG 文件简化时,遵循以下原则:

  1. 保留核心路径:只保留 <path> 元素及其 d 属性
  2. 移除冗余属性:删除 <defs>, <clipPath>, <style>, <g> 等非必要元素
  3. 保持颜色属性:保留 fill="currentColor" 以支持主题颜色
  4. 统一尺寸:根据用途设置合适的 viewBoxwidth/height
  5. 简化变换:如果有 transform,尽量简化或合并到路径中

5.2 简化示例

原始 SVG (从 assets/测量.svg):

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" viewBox="0 0 48 48">
  <defs>
    <clipPath id="clip-path">
      <rect width="48" height="48" fill="none"/>
    </clipPath>
  </defs>
  <g id="测量" clip-path="url(#clip-path)">
    <path d="M0,28.207H3.429v3.526H44.571V28.207H48v7.052H0ZM0,3.526v14.1a3.478,3.478,0,0,0,3.429,3.526H6.857V17.629h3.429v3.526h3.429V17.629h3.429v3.526h3.429V10.578H24V21.155h3.429V17.629h3.429v3.526h3.429V17.629h3.429v3.526h3.429V10.578h3.429V21.155A3.478,3.478,0,0,0,48,17.629V3.526A3.478,3.478,0,0,0,44.571,0H3.429A3.478,3.478,0,0,0,0,3.526Z" transform="translate(0 5.456)" fill="#000"/>
  </g>
</svg>

简化后 (在 icon-manager.ts 中):

测量: '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="M0,28.207H3.429v3.526H44.571V28.207H48v7.052H0ZM0,3.526v14.1a3.478,3.478,0,0,0,3.429,3.526H6.857V17.629h3.429v3.526h3.429V17.629h3.429v3.526h3.429V10.578H24V21.155h3.429V17.629h3.429v3.526h3.429V17.629h3.429v3.526h3.429V10.578h3.429V21.155A3.478,3.478,0,0,0,48,17.629V3.526A3.478,3.478,0,0,0,44.571,0H3.429A3.478,3.478,0,0,0,0,3.526Z" transform="translate(0 5.456)"/></svg>',

关键变化:

  • 移除了 xmlns, xmlns:xlink, <defs>, <clipPath>, <g>
  • fill="#000" 改为 fill="currentColor" 以支持主题
  • 保留了核心的 <path>viewBox

6. 图标尺寸规范

6.1 尺寸分类

根据使用场景,图标分为三种尺寸:

  1. 48x48: 主要功能按钮(toolbar、工具栏)
  2. 32x32: 测量工具图标
  3. 24x24: 通用 UI 图标(关闭、展开等)

6.2 与 CSS 的配合

图标尺寸在 CSS 中可以被覆盖:

/* Toolbar 按钮图标 */
.opt-btn-icon svg {
    width: 100%;
    height: 100%;
}

/* 漫游控制面板按钮图标 */
.walk-icon-btn svg {
    width: 32px;
    height: 32px;
}

实际显示尺寸由 CSS 控制,SVG 的 widthheight 属性主要用于:

  1. 定义默认尺寸
  2. 计算宽高比
  3. 作为 viewBox 的参考

7. 添加新图标

7.1 添加流程

  1. 准备 SVG 文件:

    • 将新图标文件放到 src/assets/ 目录
    • 或直接获取 SVG 代码
  2. 简化 SVG:

    • 按照 5.1 节的简化原则处理 SVG
    • 确保 fill="currentColor" 以支持主题
    • 设置合适的尺寸
  3. 添加到 ICONS 对象:

    const ICONS: Record<string, string> = {
        // ... 现有图标
        新图标名称: '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="..."/></svg>',
    };
    
  4. 使用新图标:

    const icon = getIcon('新图标名称');
    

7.2 命名规范

  • 使用中文名称:便于理解和维护
  • 描述功能:名称应清晰描述图标用途
  • 避免重复:检查是否已存在类似图标

推荐命名:

  • 好的命名: 测量, 地图, 全屏, 剖切盒
  • 不好的命名: icon1, svg2, btn_icon

8. 实现细节(供 AI 重现)

8.1 核心数据结构

const ICONS: Record<string, string> = {
    // key: 图标名称(中文)
    // value: SVG 字符串(完整的 <svg>...</svg>)
};

8.2 获取逻辑

export function getIcon(name: string): string {
    const icon = ICONS[name];

    if (!icon) {
        console.warn(`[IconManager] Icon "${name}" not found, using default icon`);
        return ICONS.default;
    }

    return icon;
}

关键点:

  1. 简单的字典查找
  2. 未找到时返回默认图标而不是 undefined
  3. 在控制台输出警告信息,便于调试

8.3 为什么不使用枚举?

设计选择:使用字符串而不是 TypeScript 枚举

原因:

  1. 简化使用:直接传中文字符串,更直观
  2. 灵活性:可以动态添加图标而不需要修改类型定义
  3. 国际化友好:中文名称与 UI 文案保持一致
  4. 降低耦合:使用方不需要导入枚举类型

9. 与主题系统的配合

9.1 currentColor 属性

所有图标使用 fill="currentColor",这样图标颜色会继承父元素的 color 属性:

测量: '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="..."/></svg>',

9.2 在 CSS 中控制颜色

.opt-btn-icon {
    color: var(--bim-icon-color, #ccc);
}

.opt-btn-icon svg {
    fill: currentColor;  /* 继承父元素的 color */
}

9.3 主题变更时的自动适配

当主题颜色变更时,只需更新 CSS 变量,所有图标会自动适配新颜色:

// 在 setTheme 中
element.style.setProperty('--bim-icon-color', theme.icon ?? '#ccc');
// 所有使用 currentColor 的图标自动更新颜色

10. 最佳实践

10.1 使用建议

推荐做法:

// 在组件顶部导入
import { getIcon } from '../../utils/icon-manager';

// 在需要时调用
const icon = getIcon('测量');
button.innerHTML = icon;

避免做法:

// 不要在组件中硬编码 SVG
const icon = '<svg>...</svg>';  // 不推荐

// 不要重复定义相同的图标
const myIcon = '<svg>...</svg>';  // 应该使用 getIcon()

10.2 性能考虑

  • 字符串查找很快:ICONS 对象的查找是 O(1) 操作
  • 无需缓存:图标字符串很小,不需要额外的缓存机制
  • 按需使用:只在需要时调用 getIcon(),不需要预加载所有图标

10.3 维护建议

  1. 定期整理:删除不再使用的图标
  2. 统一风格:新增图标应与现有图标风格一致
  3. 文档同步:添加新图标时更新本文档的图标清单
  4. 命名规范:使用清晰、一致的中文命名

11. 更新记录

日期 修改内容 修改人
2025-12-25 创建图标管理器,整合所有 SVG 图标资源 AI Assistant
2025-12-25 添加"文档"图标,替换所有 toolbar 按钮图标 AI Assistant