初始化

This commit is contained in:
yuding
2025-12-26 17:08:02 +08:00
parent a754a5a511
commit cd1f8186d0
11 changed files with 361 additions and 53 deletions

View File

@@ -562,12 +562,314 @@ const dialog = engine.dialog.create({
| -------------- | ---------------------------- | ------------------------------ | ----------------------------- | | -------------- | ---------------------------- | ------------------------------ | ----------------------------- |
| `IconManager` | `src/utils/icon-manager.ts` | 统一管理所有 SVG 图标资源 | `docs/utils/icon-manager.md` | | `IconManager` | `src/utils/icon-manager.ts` | 统一管理所有 SVG 图标资源 | `docs/utils/icon-manager.md` |
**IconManager 说明**: ---
- 提供 `getIcon(name: string): string` 函数
- 所有组件通过该函数获取 SVG 图标字符串 ## 4.4.1 IconManager 图标管理器使用指南
- 支持默认图标回退机制
- 图标使用 `currentColor` 以支持主题颜色 ### 核心概念
- 详细说明见 `docs/utils/icon-manager.md`
**IconManager 是项目中所有 SVG 图标的统一管理工具,必须严格遵循以下规范:**
#### 强制要求
1. **禁止在代码中硬编码 SVG**: 所有图标必须通过 `getIcon()` 函数获取
2. **使用中文名称**: 图标名称使用清晰的中文描述,如 `'测量'`, `'地图'`, `'全屏'`
3. **支持主题颜色**: 所有图标使用 `fill="currentColor"` 以自动适配主题
### API 使用
#### 基本用法
```typescript
import { getIcon } from '../../utils/icon-manager';
// 获取图标 SVG 字符串
const icon = getIcon('测量');
// 在按钮配置中使用
const buttonConfig: ButtonConfig = {
id: 'measure',
icon: getIcon('测量'),
label: 'toolbar.measure'
};
// 在 DOM 中使用
button.innerHTML = getIcon('全屏');
```
#### 在组件中使用
```typescript
export class MyComponent implements IBimComponent {
private createButton(): HTMLButtonElement {
const btn = document.createElement('button');
// 直接使用 getIcon 获取图标
btn.innerHTML = getIcon('设置');
return btn;
}
}
```
### 图标分类
#### 1. 主要功能图标 (48x48)
用于 toolbar 和主要功能按钮:
- `测量`, `地图`, `框选放大`, `漫游`, `目录树`
- `剖切`, `剖切盒`, `全屏`, `设置`, `拾曲面剖切`
- `轴向剖切`, `主视角`, `文档`
#### 2. 测量工具图标 (32x32)
用于测量面板:
- `标高`, `距离`, `最小距离`, `激光边距`
- `角度`, `坡度`, `体积`, `空间体积`
#### 3. 通用 UI 图标 (24x24)
用于通用界面元素:
- `close`, `check`, `warning`, `error`, `success`
- `plus`, `minus`, `arrowUp`, `arrowDown`, `arrowLeft`, `arrowRight`
- `search`, `refresh`, `delete`, `edit`, `save`
- `expand`, `collapse`
### 图标尺寸规范
#### CSS 控制实际显示尺寸
虽然 SVG 有默认尺寸,但实际显示尺寸由 CSS 控制:
```css
/* Toolbar 按钮图标 - 底部工具栏 */
.bim-btn-group-root.is-bottom-toolbar .opt-btn-icon {
width: 32px;
height: 32px;
}
/* Walk 控制面板按钮图标 */
.walk-icon-btn svg {
width: 32px;
height: 32px;
}
/* 通用按钮图标 */
.opt-btn-icon {
width: 24px;
height: 24px;
}
```
**重要**: 确保不同组件的图标尺寸保持一致,推荐使用:
- **主按钮**: 32x32 (toolbar, walk-control-panel)
- **次要按钮**: 24x24 (menu, tree, dialog)
- **小图标**: 18x18 (下拉菜单项)
### 主题适配
#### currentColor 机制
所有图标使用 `fill="currentColor"`,会自动继承父元素的 `color` 属性:
```typescript
// icon-manager.ts 中的图标定义
测量: '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="..."/></svg>',
```
```css
/* CSS 中控制图标颜色 */
.opt-btn-icon {
color: var(--bim-icon-color, #ccc);
}
.opt-btn-icon svg {
fill: currentColor; /* 自动继承父元素的 color */
}
```
#### 主题变更自动适配
当主题切换时,只需更新 CSS 变量,所有图标自动更新颜色:
```typescript
// setTheme 方法中
element.style.setProperty('--bim-icon-color', theme.icon ?? '#ccc');
// 所有使用 currentColor 的图标自动适配新颜色
```
### 添加新图标
#### 流程步骤
1. **准备 SVG 文件**:
- 放到 `src/assets/` 目录
- 或直接获取 SVG 代码
2. **简化 SVG** (移除冗余):
```xml
<!-- 简化前 -->
<svg xmlns="..." xmlns:xlink="...">
<defs>...</defs>
<clipPath>...</clipPath>
<g clip-path="...">
<path d="..." fill="#000"/>
</g>
</svg>
<!-- 简化后 -->
<svg width="48" height="48" viewBox="0 0 48 48">
<path fill="currentColor" d="..."/>
</svg>
```
3. **添加到 icon-manager.ts**:
```typescript
const ICONS: Record<string, string> = {
// ... 现有图标
新功能: '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="..."/></svg>',
};
```
4. **使用新图标**:
```typescript
const icon = getIcon('新功能');
```
#### 命名规范
**✅ 推荐命名**:
- 使用清晰的中文描述功能
- 避免歧义和重复
- 示例: `测量`, `全屏`, `剖切盒`, `空间体积`
**❌ 不推荐命名**:
- 通用名称: `icon1`, `svg2`, `image`
- 英文缩写: `msr`, `cfg`, `btn_icon`
- 过长名称: `这是一个用于测量距离的图标`
### 错误处理
#### 自动回退机制
当请求的图标不存在时,自动返回默认图标:
```typescript
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;
}
```
**开发时的警告**:
```
[IconManager] Icon "不存在的图标" not found, using default icon
```
这帮助开发者快速发现图标名称错误。
### 最佳实践
#### ✅ 推荐做法
```typescript
// 1. 在组件顶部导入
import { getIcon } from '../../utils/icon-manager';
// 2. 在需要时调用
export const createButton = (): ButtonConfig => ({
id: 'home',
icon: getIcon('主视角'),
label: 'toolbar.home'
});
// 3. 在 DOM 中使用
const btn = document.createElement('button');
btn.innerHTML = getIcon('设置');
```
#### ❌ 避免做法
```typescript
// ❌ 不要硬编码 SVG
const icon = '<svg>...</svg>';
// ❌ 不要重复定义相同图标
const homeIcon = '<svg>...</svg>';
const anotherHomeIcon = '<svg>...</svg>';
// ❌ 不要使用未定义的图标名称
const icon = getIcon('ThisIconDoesNotExist'); // 会触发警告
```
### 性能说明
- **查找速度**: O(1) 字典查找,无性能问题
- **无需缓存**: 图标字符串很小,直接返回即可
- **按需使用**: 只在需要时调用 `getIcon()`,不需要预加载
### 维护建议
1. **定期清理**: 删除不再使用的图标
2. **统一风格**: 新增图标应与现有图标风格一致
3. **文档同步**: 添加新图标后更新 `docs/utils/icon-manager.md`
4. **测试验证**: 确保新图标在浅色/深色主题下都清晰可见
### 完整示例
#### 按钮配置中使用图标
```typescript
import { getIcon } from '../../../utils/icon-manager';
import type { ButtonConfig } from '../index.type';
import type { BimEngine } from '../../../bim-engine';
export const createMeasureButton = (engine: BimEngine): ButtonConfig => {
return {
id: 'measure',
groupId: 'group-1',
type: 'button',
label: 'toolbar.measure',
icon: getIcon('测量'), // 使用图标管理器
onClick: () => {
engine.measure?.show();
}
};
};
```
#### 组件中动态使用图标
```typescript
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] || getIcon('default');
}
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;
}
}
```
---
**相关文档**:
- 详细的图标清单和使用说明见 `docs/utils/icon-manager.md`
- SVG 简化规范和主题适配详见同一文档
### 4.5 事件总线定义 ### 4.5 事件总线定义

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -57,8 +57,8 @@
/* 图标样式 */ /* 图标样式 */
.section-axis-btn-icon { .section-axis-btn-icon {
width: 20px; width: 24px;
height: 20px; height: 24px;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@@ -4,6 +4,7 @@ import { IBimComponent } from '../../types/component';
import { localeManager, t } from '../../services/locale'; import { localeManager, t } from '../../services/locale';
import { themeManager } from '../../services/theme'; import { themeManager } from '../../services/theme';
import type { SectionAxisPanelOptions, SectionAxis } from './types'; import type { SectionAxisPanelOptions, SectionAxis } from './types';
import { getIcon } from '../../utils/icon-manager';
/** /**
* 轴向剖切面板组件 * 轴向剖切面板组件
@@ -150,13 +151,13 @@ export class SectionAxisPanel implements IBimComponent {
this.hideBtn = this.createButton( this.hideBtn = this.createButton(
'hide', 'hide',
'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.804 11.804 0 0 0 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg>', getIcon('隐藏'),
() => this.handleHideToggle() () => this.handleHideToggle()
); );
this.reverseBtn = this.createButton( this.reverseBtn = this.createButton(
'reverse', 'reverse',
'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/></svg>', getIcon('反向'),
() => this.handleReverse() () => this.handleReverse()
); );

View File

@@ -41,8 +41,8 @@
} }
.section-box-btn-icon { .section-box-btn-icon {
width: 18px; width: 24px;
height: 18px; height: 24px;
color: var(--bim-icon-color); color: var(--bim-icon-color);
} }

View File

@@ -4,6 +4,7 @@ import { IBimComponent } from '../../types/component';
import { localeManager, t } from '../../services/locale'; import { localeManager, t } from '../../services/locale';
import { themeManager } from '../../services/theme'; import { themeManager } from '../../services/theme';
import type { SectionBoxPanelOptions, SectionBoxRange } from './types'; import type { SectionBoxPanelOptions, SectionBoxRange } from './types';
import { getIcon } from '../../utils/icon-manager';
const DEFAULT_RANGE: SectionBoxRange = { const DEFAULT_RANGE: SectionBoxRange = {
x: { min: 0, max: 100 }, x: { min: 0, max: 100 },
@@ -167,9 +168,16 @@ export class SectionBoxPanel implements IBimComponent {
btn.className = 'section-box-btn'; btn.className = 'section-box-btn';
btn.title = label; btn.title = label;
const iconMap: Record<string, string> = {
hide: '隐藏',
reverse: '反向',
fit: '适应到模型',
reset: '重置'
};
const icon = document.createElement('div'); const icon = document.createElement('div');
icon.className = 'section-box-btn-icon'; icon.className = 'section-box-btn-icon';
icon.innerHTML = this.getIconSVG(type); icon.innerHTML = getIcon(iconMap[type] || type);
const labelEl = document.createElement('div'); const labelEl = document.createElement('div');
labelEl.className = 'section-box-btn-label'; labelEl.className = 'section-box-btn-label';
@@ -313,16 +321,6 @@ export class SectionBoxPanel implements IBimComponent {
if (this.reverseBtn) this.reverseBtn.classList.toggle('active', this.isReversed); if (this.reverseBtn) this.reverseBtn.classList.toggle('active', this.isReversed);
} }
private getIconSVG(type: string): string {
const icons: Record<string, string> = {
hide: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg>',
reverse: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/></svg>',
fit: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M13 3h-2v10h2V3zm4 8h2v10h-2V11zm-8 4H7v6h2v-6zm-4 3H3v3h2v-3z"/></svg>',
reset: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>'
};
return icons[type] || '';
}
public setLocales(): void { public setLocales(): void {
if (!this.hideLabelEl) return; if (!this.hideLabelEl) return;
this.hideLabelEl.textContent = t('sectionBox.actions.hide'); this.hideLabelEl.textContent = t('sectionBox.actions.hide');

View File

@@ -4,6 +4,7 @@ import { IBimComponent } from '../../types/component';
import { localeManager, t } from '../../services/locale'; import { localeManager, t } from '../../services/locale';
import { themeManager } from '../../services/theme'; import { themeManager } from '../../services/theme';
import type { SectionPlanePanelOptions } from './types'; import type { SectionPlanePanelOptions } from './types';
import { getIcon } from '../../utils/icon-manager';
/** /**
* 拾取面剖切面板组件 * 拾取面剖切面板组件
@@ -101,7 +102,7 @@ export class SectionPlanePanel implements IBimComponent {
// 隐藏按钮 // 隐藏按钮
this.hideBtn = this.createButton( this.hideBtn = this.createButton(
'hide', 'hide',
'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.804 11.804 0 0 0 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg>', getIcon('隐藏'),
() => { () => {
if (this.options.onHide) { if (this.options.onHide) {
this.options.onHide(); this.options.onHide();
@@ -112,7 +113,7 @@ export class SectionPlanePanel implements IBimComponent {
// 反向按钮 // 反向按钮
this.reverseBtn = this.createButton( this.reverseBtn = this.createButton(
'reverse', 'reverse',
'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/></svg>', getIcon('反向'),
() => { () => {
if (this.options.onReverse) { if (this.options.onReverse) {
this.options.onReverse(); this.options.onReverse();
@@ -123,7 +124,7 @@ export class SectionPlanePanel implements IBimComponent {
// 重置按钮 // 重置按钮
this.resetBtn = this.createButton( this.resetBtn = this.createButton(
'reset', 'reset',
'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>', getIcon('重置'),
() => { () => {
if (this.options.onReset) { if (this.options.onReset) {
this.options.onReset(); this.options.onReset();

View File

@@ -380,8 +380,8 @@ export class WalkControlPanel implements IBimComponent {
private getIconSVG(type: string): string { private getIconSVG(type: string): string {
const icons: Record<string, string> = { const icons: Record<string, string> = {
'plan-view': getIcon('地图'), 'plan-view': getIcon('地图'),
'path': getIcon('地图'), 'path': getIcon('路径漫游'),
'walk': getIcon('漫游') 'walk': getIcon('第一人称漫游')
}; };
return icons[type] || ''; return icons[type] || '';
} }

View File

@@ -21,6 +21,12 @@ const ICONS: Record<string, string> = {
: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><g transform="translate(-59.63 -21.333)"><path d="M.194,3.727,2.558,8.089a.842.842,0,0,0,1.091.273.914.914,0,0,0,.454-1l-1.636-3L13.919,7.817c.091,0,.091,0,.182.091a.785.785,0,1,0,.273-1.545L2.921,2.909l3-1.545h.091a.645.645,0,0,0,.182-1A.842.842,0,0,0,5.1.091L.558,2.454c-.091,0-.182.091-.273.182A.742.742,0,0,0,.194,3.727Z" transform="translate(102.244 57.563) rotate(-178)"/><path d="M88.23,54.014v2.86l-21.123,8.17V34l21.123-8.17V47.069h2.9l-.207-25.736L64,31.75V69.333L91.129,58.916v-4.9Z"/><path d="M199,255.9l-15.532,6.268v-21.23L199,234.667Z" transform="translate(-113.668 -202.979)"/></g></svg>', : '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><g transform="translate(-59.63 -21.333)"><path d="M.194,3.727,2.558,8.089a.842.842,0,0,0,1.091.273.914.914,0,0,0,.454-1l-1.636-3L13.919,7.817c.091,0,.091,0,.182.091a.785.785,0,1,0,.273-1.545L2.921,2.909l3-1.545h.091a.645.645,0,0,0,.182-1A.842.842,0,0,0,5.1.091L.558,2.454c-.091,0-.182.091-.273.182A.742.742,0,0,0,.194,3.727Z" transform="translate(102.244 57.563) rotate(-178)"/><path d="M88.23,54.014v2.86l-21.123,8.17V34l21.123-8.17V47.069h2.9l-.207-25.736L64,31.75V69.333L91.129,58.916v-4.9Z"/><path d="M199,255.9l-15.532,6.268v-21.23L199,234.667Z" transform="translate(-113.668 -202.979)"/></g></svg>',
: '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="M122.32,181.835v-13.16h-9.87V163.74h19.74v18.095Zm-19.74,0V163.74h9.87v18.095ZM96,163.74,117.385,144l21.385,19.74Z" transform="translate(-93.385 -138.917)"/></svg>', : '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="M122.32,181.835v-13.16h-9.87V163.74h19.74v18.095Zm-19.74,0V163.74h9.87v18.095ZM96,163.74,117.385,144l21.385,19.74Z" transform="translate(-93.385 -138.917)"/></svg>',
: '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="M64,128h1.714v41.143H64Zm46.286,0H112v41.143h-1.714Zm-44.571,0h44.571v3.429H65.714Zm0,6.857h44.571v3.429H65.714Zm0-3.429h3.429v3.429H65.714Zm8.571,0h3.429v3.429H74.286Zm8.571,0h27.429v3.429H82.857Zm-17.143,36h44.571v1.714H65.714Zm5.143-24H76v1.714H70.857Zm10.286,0h24v1.714h-24Zm-10.286,6.857H76V152H70.857Zm10.286,0h24V152h-24Zm-10.286,6.857H76v1.714H70.857Zm10.286,0h24v1.714h-24Z" transform="translate(-64 -124.571)"/></svg>', : '<svg width="48" height="48" viewBox="0 0 48 48"><path fill="currentColor" d="M64,128h1.714v41.143H64Zm46.286,0H112v41.143h-1.714Zm-44.571,0h44.571v3.429H65.714Zm0,6.857h44.571v3.429H65.714Zm0-3.429h3.429v3.429H65.714Zm8.571,0h3.429v3.429H74.286Zm8.571,0h27.429v3.429H82.857Zm-17.143,36h44.571v1.714H65.714Zm5.143-24H76v1.714H70.857Zm10.286,0h24v1.714h-24Zm-10.286,6.857H76V152H70.857Zm10.286,0h24V152h-24Zm-10.286,6.857H76v1.714H70.857Zm10.286,0h24v1.714h-24Z" transform="translate(-64 -124.571)"/></svg>',
: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><path d="M178.325,132.27a6.411,6.411,0,0,0,0-9.429,7.359,7.359,0,0,0-9.984,0,6.411,6.411,0,0,0,0,9.429,7.359,7.359,0,0,0,9.984,0Zm-2-7.543a3.845,3.845,0,0,1,0,5.657,4.414,4.414,0,0,1-5.99,0,3.847,3.847,0,0,1,0-5.657,4.414,4.414,0,0,1,5.99,0Zm6.618,31.639q4.975,1.006,4.975,3.189t-4.975,3.189a57.4,57.4,0,0,1-19.226,0q-4.973-1.006-4.973-3.189t4.975-3.189q.777-.156,1.613-.284v-2.521a4.488,4.488,0,0,1-1.412.217,4.761,4.761,0,0,1-.587-.036l-.038,0a29.007,29.007,0,0,0-7.657,2.332q-3.482,1.712-3.482,3.484a2.944,2.944,0,0,0,1.3,2.116,14.359,14.359,0,0,0,4.4,2.293,46.614,46.614,0,0,0,15.477,2.258,46.614,46.614,0,0,0,15.477-2.258,14.362,14.362,0,0,0,4.4-2.293,2.943,2.943,0,0,0,1.3-2.116q0-1.772-3.482-3.484a29.007,29.007,0,0,0-7.657-2.332,4.908,4.908,0,0,1-.625.039,4.488,4.488,0,0,1-1.412-.217v2.521q.836.128,1.613.284Zm-14.788-6.588v5.979q2.391-.2,5.176-.2t5.176.2v-12.2h2.824v6.222a1.25,1.25,0,0,0,.414.942v0l.081.071q.744.1,1.457.22a1.41,1.41,0,0,0,.457-.293,1.25,1.25,0,0,0,.414-.942v-8a3.75,3.75,0,0,0-1.24-2.828,4.2,4.2,0,0,0-2.995-1.172H166.745a4.2,4.2,0,0,0-2.995,1.172,3.75,3.75,0,0,0-1.24,2.828v8a1.268,1.268,0,0,0,.414.942,1.415,1.415,0,0,0,.456.293q.715-.119,1.459-.222.041-.034.081-.071a1.25,1.25,0,0,0,.414-.942v-6.222h2.824Zm-8.471-6.222v-1.778a6.253,6.253,0,0,1,2.067-4.715,7.013,7.013,0,0,1,4.992-1.952h13.176a7.013,7.013,0,0,1,4.992,1.952,6.253,6.253,0,0,1,2.067,4.715v8a3.722,3.722,0,0,1-.484,1.881l0,.005a28.526,28.526,0,0,1,5.841,2.041q5,2.462,5,5.851,0,4.238-7.531,6.9a49.5,49.5,0,0,1-16.469,2.428,49.5,49.5,0,0,1-16.469-2.428q-7.531-2.667-7.531-6.9,0-3.388,5-5.849a28.381,28.381,0,0,1,5.841-2.044,3.707,3.707,0,0,1-.488-1.884Zm24.8,16a13.145,13.145,0,0,0-2.129-.581,54.5,54.5,0,0,0-18.044,0,13.079,13.079,0,0,0-2.127.581,13.078,13.078,0,0,0,2.127.581,54.5,54.5,0,0,0,18.044,0,13.078,13.078,0,0,0,2.127-.581Z" transform="translate(-149.333 -120.889)"/></svg>',
: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><g transform="translate(0.364 4.658)"><path d="M0,287.1m0-.817v-2.042a.722.722,0,0,1,.817-.817H44.509a.722.722,0,0,1,.817.817v2.042a.722.722,0,0,1-.817.817H.817A.722.722,0,0,1,0,286.28Z" transform="translate(-0.001 -272.164)"/><path d="M829.7,13.238l-1.443,1.444a.817.817,0,0,1-1.155,0L815.263,2.844a.817.817,0,0,1,0-1.156L816.706.244a.817.817,0,0,1,1.155,0L829.7,12.083a.817.817,0,0,1,0,1.155Z" transform="translate(-782.667)"/><path d="M94.367,648m0,.817v2.042a.722.722,0,0,1-.817.817H49.858a.722.722,0,0,1-.817-.817v-2.042a.722.722,0,0,1,.817-.817H93.55A.722.722,0,0,1,94.367,648.816Z" transform="translate(-47.095 -622.268)"/><path d="M.239,649.97l1.443-1.444a.817.817,0,0,1,1.155,0l11.838,11.838a.817.817,0,0,1,0,1.156l-1.443,1.443a.817.817,0,0,1-1.155,0L.239,651.124a.817.817,0,0,1,0-1.155Z" transform="translate(0 -622.544)"/></g></svg>',
: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><g transform="translate(0 -4.9)"><path d="M43.755,35.8a23.352,23.352,0,0,1-8.074-3.095c-1.131-.912-2.263-1.819-2.263-3.458,0-.364-.164-.548-.486-.548-1.776-.364-2.263,0-1.935,2.183a5,5,0,0,0,1.776,3.095,14.337,14.337,0,0,0,4.521,2.551c2.1.912,4.521,1.276,6.461,2.551,1.935,1.091,2.1,2.551.809,4.37A12.572,12.572,0,0,1,37.3,48.187a30.594,30.594,0,0,1-17.115-1.455c-2.585-.912-4.2-.364-5.488,2,0,.184-.164.184-.323.548,2.1.728,3.876,1.455,5.811,2,5.811,1.639,11.786,2.367,17.76.548A14.453,14.453,0,0,0,46.663,45.1c2.422-4.2,1.613-8.381-2.908-9.293Zm-19.7-7.97a11.943,11.943,0,0,0-7.382-10.952A12.17,12.17,0,0,0,3.457,19.546a10.951,10.951,0,0,0-2.3,12.879c2.459,5.329,6.456,9.77,10.453,14.06.463.444.617.444,1.075,0,3.689-4.143,7.382-8.287,10-13.172A11.6,11.6,0,0,0,24.059,27.833Zm-4.151,3.255c-1.692,3.851-4.614,6.955-7.382,10.215-.463.444-.617.3-.921,0A44.891,44.891,0,0,1,4.228,31.088a7.684,7.684,0,0,1-.767-3.109A8.664,8.664,0,0,1,18.22,21.909a8.155,8.155,0,0,1,1.688,9.18ZM33.007,4.9c-5.685,0-9.785,6.416-7.274,11.76,1.454,3.34,4.1,5.745,6.746,8.15a.492.492,0,0,0,.795,0c2.908-2.537,5.554-5.211,7.008-8.821A8.117,8.117,0,0,0,33.007,4.9Zm4.366,10.422A16.825,16.825,0,0,1,33.4,20c-.4.269-.659.269-.926,0a16.342,16.342,0,0,1-3.969-4.545,5.033,5.033,0,1,1,8.864-.132Z"/><path d="M183.031,141.935a2.136,2.136,0,0,0,2.053-2.18,2.084,2.084,0,0,0-2.18-2.18,2.171,2.171,0,0,0-2.053,2.307A2.058,2.058,0,0,0,183.031,141.935Zm-20.094,7.854a4.7,4.7,0,0,0-4.6,4.881,4.913,4.913,0,0,0,4.74,4.881,4.882,4.882,0,0,0-.14-9.762Zm0,6.417a1.77,1.77,0,0,1-1.532-1.532A1.79,1.79,0,0,1,162.937,153a1.611,1.611,0,0,1,1.672,1.672A1.578,1.578,0,0,1,162.937,156.206Z" transform="translate(-150.629 -126.653)"/></g></svg>',
: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><g transform="translate(-84.667 -84.667)"><path d="M87.667,97V87.667H97V85.333H86.733a1.4,1.4,0,0,0-1.4,1.4V97Zm0,23.333H85.333V130.6a1.4,1.4,0,0,0,1.4,1.4H97v-2.333H87.667v-9.333Zm32.667,9.333V132H130.6a1.4,1.4,0,0,0,1.4-1.4V120.333h-2.333v9.333h-9.333ZM129.667,97H132V86.733a1.4,1.4,0,0,0-1.4-1.4H120.333v2.333h9.333Z"/><path d="M270.857,243.5l11.3-6.652V223.387l-11.3,6.361V243.5Zm-1.8,0v-13.75l-11.3-6.361v13.456l11.3,6.652Zm-10.278-21.621,11.177,6.284,11.177-6.284L269.958,215.3l-11.177,6.573Zm11.622-8.426,13.1,7.709a.919.919,0,0,1,.448.793v15.419a.926.926,0,0,1-.448.793l-13.1,7.709a.887.887,0,0,1-.9,0l-13.1-7.709a.905.905,0,0,1-.448-.793V221.954a.919.919,0,0,1,.448-.793l13.1-7.709A.887.887,0,0,1,270.4,213.452Z" transform="translate(-161.292 -120.997)"/></g></svg>',
: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><path d="M61.274,76.178l0-.007-.007,0a32.458,32.458,0,0,0-7.014-9.981L51.53,68.91a28.382,28.382,0,0,1,6.124,8.635c-4.472,9.25-10.809,13.636-19.472,13.636A20.615,20.615,0,0,1,30.6,89.838l-2.937,2.933a23.916,23.916,0,0,0,10.514,2.277q15.472,0,23.088-16.116a3.229,3.229,0,0,0,0-2.755ZM57.839,58.351l-2.274-2.277a.429.429,0,0,0-.608,0l-6.265,6.262a23.775,23.775,0,0,0-10.51-2.281q-15.472,0-23.088,16.116v.007a3.236,3.236,0,0,0,0,2.765,32.617,32.617,0,0,0,7.014,9.985L16.7,94.32a.429.429,0,0,0,0,.608l2.274,2.277a.429.429,0,0,0,.608,0L57.839,58.949a.426.426,0,0,0,0-.6ZM32.113,78.915a6.011,6.011,0,0,1,7.22-7.22l-7.223,7.22Zm9.9-9.9A9.452,9.452,0,0,0,29.43,81.6l-4.6,4.6a28.424,28.424,0,0,1-6.124-8.635c4.475-9.25,10.816-13.636,19.472-13.636a20.615,20.615,0,0,1,7.577,1.343Zm-4.046,14.55a5.752,5.752,0,0,1-1.01-.086l-2.744,2.741A9.446,9.446,0,0,0,46.638,73.794L43.9,76.538a6.018,6.018,0,0,1-5.932,7.024Z" transform="translate(-14.182 -52.64)"/></svg>',
: '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><path d="M32.936,32.658a15.914,15.914,0,0,1-9.6,3.064c-.451-.006-.9-.042-1.342-.077-.182-.018-.357-.047-.539-.077-.352-.047-.7-.089-1.037-.164-.211-.036-.422-.1-.621-.148-.334-.077-.668-.154-.99-.254-.153-.059-.3-.11-.457-.177-.381-.124-.755-.26-1.113-.414-.082-.036-.162-.071-.24-.1-.422-.2-.844-.408-1.248-.627-.017-.012-.035-.018-.054-.03a16.485,16.485,0,0,1-3.732-2.9c-.017-.018-.035-.042-.054-.059-.34-.355-.668-.721-.979-1.118-.065-.083-.123-.16-.194-.254A17.286,17.286,0,0,1,7.149,18.751h3.98L6.318,9.076,0,18.874H3.942a20.809,20.809,0,0,0,3.5,11.57.871.871,0,0,0,.075.14c.225.34.486.648.724.961.1.113.177.232.274.362.354.443.746.869,1.133,1.285.043.043.075.076.108.113a19.778,19.778,0,0,0,4.436,3.446c.043.027.08.043.128.076.467.259.949.5,1.432.719.124.055.242.113.359.164.419.184.847.335,1.277.493.2.076.4.14.611.21.378.113.762.21,1.153.3.257.065.509.129.772.184a2.252,2.252,0,0,0,.316.076c.37.07.735.1,1.1.151.134.027.27.049.4.065.66.065,1.314.11,1.974.11,4.006,0,8.852-1.3,11.85-4.177a1.908,1.908,0,0,0,.333-2.484,2.228,2.228,0,0,0-2.952.023ZM44.053,20.424a20.728,20.728,0,0,0-3.47-11.537c-.032-.055-.054-.113-.08-.164-.284-.405-.574-.778-.869-1.161a1.289,1.289,0,0,1-.1-.14A19.86,19.86,0,0,0,32.168,1.7c-.086-.032-.155-.076-.242-.11-.456-.195-.922-.362-1.394-.53C30.37,1,30.2.937,30.033.883,29.62.754,29.212.651,28.792.548c-.231-.055-.467-.113-.7-.164-.113-.022-.22-.055-.338-.081-.311-.055-.617-.081-.933-.124C26.6.151,26.392.119,26.18.1,25.655.042,25.135.021,24.616.016c-.1,0-.188-.016-.284-.016-.016,0-.032.005-.049.005A19.124,19.124,0,0,0,13.02,3.695a1.682,1.682,0,0,0-.466,2.515,1.777,1.777,0,0,0,2.309.3A15.251,15.251,0,0,1,24.374,3.45c.486.006.971.03,1.441.077.147.012.287.036.432.059a11.3,11.3,0,0,1,1.16.189c.162.03.334.077.493.11.381.089.744.178,1.107.3a2.577,2.577,0,0,1,.34.124c.422.142.832.29,1.23.467.047.012.082.047.123.059a16.492,16.492,0,0,1,6.182,4.8.367.367,0,0,0,.024.036A17.308,17.308,0,0,1,40.629,20.43H36.645l5.048,9.787L48,20.424Zm0,0" transform="translate(0 4.349)"/></svg>',
// ========== 测量相关图标 (32x32) ========== // ========== 测量相关图标 (32x32) ==========
: '<svg width="32" height="32" viewBox="0 0 32 32"><path fill="currentColor" d="M84.131,193.119a1.056,1.056,0,0,1,1.116.982v7.857a1.056,1.056,0,0,1-1.116.982H54.367a1.056,1.056,0,0,1-1.116-.982V194.1a1.056,1.056,0,0,1,1.116-.982Zm-1.116,1.964H55.483v5.893H83.015Zm1.116-13.749a1.064,1.064,0,0,1,1.114.935,1.032,1.032,0,0,1-1.007,1.025l-.107,0H71.2l-7.858,6.914a1.227,1.227,0,0,1-1.578,0l-8.185-7.2-.018-.016-.032-.031.049.047a1.107,1.107,0,0,1-.092-.092l-.011-.014a.869.869,0,0,1-.182-.857l0-.008L53.31,182l.012-.029.02-.045.019-.035a1.1,1.1,0,0,1,.891-.552h.007q.053,0,.107,0ZM68.043,183.3H57.06l5.492,4.831Z" transform="translate(-53.247 -176.136)"/></svg>', : '<svg width="32" height="32" viewBox="0 0 32 32"><path fill="currentColor" d="M84.131,193.119a1.056,1.056,0,0,1,1.116.982v7.857a1.056,1.056,0,0,1-1.116.982H54.367a1.056,1.056,0,0,1-1.116-.982V194.1a1.056,1.056,0,0,1,1.116-.982Zm-1.116,1.964H55.483v5.893H83.015Zm1.116-13.749a1.064,1.064,0,0,1,1.114.935,1.032,1.032,0,0,1-1.007,1.025l-.107,0H71.2l-7.858,6.914a1.227,1.227,0,0,1-1.578,0l-8.185-7.2-.018-.016-.032-.031.049.047a1.107,1.107,0,0,1-.092-.092l-.011-.014a.869.869,0,0,1-.182-.857l0-.008L53.31,182l.012-.029.02-.045.019-.035a1.1,1.1,0,0,1,.891-.552h.007q.053,0,.107,0ZM68.043,183.3H57.06l5.492,4.831Z" transform="translate(-53.247 -176.136)"/></svg>',