refactor(measure): centralize measure type config and migrate API
- Add unified MEASURE_TYPES config in types/measure.ts - Export MEASURE_MODES_ORDERED, ENGINE_TYPE_TO_MODE, MODE_TO_ENGINE_TYPE - Refactor measure-dialog-manager to use centralized config - Refactor measure-panel to use MEASURE_TYPES for icons/order/valueType - Simplify engine/index.ts measureMap with dynamic key access - Add measure settings API (saveMeasureSetting) with cache sync - Add direct engine event listening for measure-changed and section-move - Update i18n keys for 8 measure modes
This commit is contained in:
@@ -3,6 +3,7 @@ import { IBimComponent } from '../../types/component';
|
||||
import { themeManager } from '../../services/theme';
|
||||
import type { EngineOptions, ModelLoadOptions } from './types';
|
||||
import type { MeasureMode } from '../../types/measure';
|
||||
import type { MeasureUnit, MeasurePrecision } from '../measure-panel/types';
|
||||
import type { SectionBoxRange } from '../section-box-panel/types';
|
||||
import { ManagerRegistry } from '../../core/manager-registry';
|
||||
// 导入第三方 SDK 的 createEngine 函数(从 npm 包引入)
|
||||
@@ -142,12 +143,6 @@ export class Engine implements IBimComponent {
|
||||
}
|
||||
});
|
||||
|
||||
// 监听剖切值变化事件
|
||||
this.engine.events.on('section-move', (data: any) => {
|
||||
console.log('[Engine] 剖切值变化:', data);
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.emit('section:move', data);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Engine] Failed to initialize engine:', error);
|
||||
this._isInitialized = false;
|
||||
@@ -246,189 +241,92 @@ export class Engine implements IBimComponent {
|
||||
|
||||
// ==================== 测量功能方法 ====================
|
||||
|
||||
// ==================== 测量功能(统一 API) ====================
|
||||
|
||||
/**
|
||||
* 激活具体测量类型的统一入口(私有方法)
|
||||
* @param type 测量类型
|
||||
* @param activateFunc 第三方引擎的激活函数
|
||||
* 激活测量功能
|
||||
* @param mode 测量类型
|
||||
* - 'clearHeight': 净高测量
|
||||
* - 'clearDistance': 净距测量
|
||||
* - 'distance': 距离测量 y
|
||||
* - 'elevation': 标高测量 y
|
||||
* - 'point': 坐标测量
|
||||
* - 'angle': 角度测量 y
|
||||
* - 'area': 面积测量
|
||||
* - 'slope': 坡度测量 y
|
||||
*/
|
||||
private activateMeasureType(type: MeasureMode, activateFunc: () => void): void {
|
||||
// 1. 检查引擎是否初始化
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.error('Cannot activate measure: engine not initialized.');
|
||||
public activateMeasure(mode: MeasureMode): void {
|
||||
if (!this._isInitialized || !this.engine?.measure) {
|
||||
console.error('[Engine] Cannot activate measure: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查 measure 模块是否存在
|
||||
if (!this.engine.measure) {
|
||||
console.error('Measure module not available.');
|
||||
if (this.currentMeasureType === mode) {
|
||||
console.log(`[Engine] Measure ${mode} already active, skipping.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 先激活测量模块
|
||||
if (!this.isMeasureActive) {
|
||||
console.log('激活测量功能');
|
||||
console.log('[Engine] Activating measure module');
|
||||
this.engine.measure.active();
|
||||
this.isMeasureActive = true;
|
||||
}
|
||||
|
||||
// 4. 激活具体的测量类型(直接切换,不需要停用前一个)
|
||||
activateFunc();
|
||||
this.currentMeasureType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活距离测量
|
||||
*/
|
||||
public activateDistanceMeasure(): void {
|
||||
this.activateMeasureType('distance', () => {
|
||||
console.log(`激活距离测量`);
|
||||
this.engine.measure.distanceMeasure.active();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活最小距离测量
|
||||
*/
|
||||
public activateMinDistanceMeasure(): void {
|
||||
this.activateMeasureType('minDistance', () => {
|
||||
console.log(`激活最小距离测量`);
|
||||
this.engine.measure.clearDistanceMeasure.active();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活角度测量
|
||||
*/
|
||||
public activateAngleMeasure(): void {
|
||||
this.activateMeasureType('angle', () => {
|
||||
console.log(`激活角度测量`);
|
||||
this.engine.measure.angleMeasure.active();
|
||||
console.log('[Engine] Angle measure activated (placeholder)');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活标高测量
|
||||
*/
|
||||
public activateElevationMeasure(): void {
|
||||
this.activateMeasureType('elevation', () => {
|
||||
console.log(`激活标高测量`);
|
||||
this.engine.measure.elevationMeasure.active();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活体积测量
|
||||
*/
|
||||
public activateVolumeMeasure(): void {
|
||||
this.activateMeasureType('volume', () => {
|
||||
console.log(`激活体积测量`);
|
||||
this.engine.measure.areaMeasure.active();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活激光测距
|
||||
*/
|
||||
public activateLaserDistanceMeasure(): void {
|
||||
this.activateMeasureType('laserDistance', () => {
|
||||
// TODO: 调用第三方引擎方法(当前先空着)
|
||||
// this.engine.measure.laserDistanceMeasure.active();
|
||||
console.log('[Engine] Laser distance measure activated (placeholder)');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活坡度测量
|
||||
*/
|
||||
public activateSlopeMeasure(): void {
|
||||
this.activateMeasureType('slope', () => {
|
||||
console.log(`激活坡度测量`);
|
||||
this.engine.measure.slopeMeasure.active();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活空间体积测量
|
||||
*/
|
||||
public activateSpaceVolumeMeasure(): void {
|
||||
this.activateMeasureType('spaceVolume', () => {
|
||||
// TODO: 调用第三方引擎方法(当前先空着)
|
||||
// this.engine.measure.spaceVolumeMeasure.active();
|
||||
console.log('[Engine] Space volume measure activated (placeholder)');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活测量功能(根据类型统一入口)
|
||||
* @param mode 测量类型
|
||||
*/
|
||||
public activateMeasure(mode: MeasureMode): void {
|
||||
switch (mode) {
|
||||
case 'distance':
|
||||
this.activateDistanceMeasure();
|
||||
break;
|
||||
case 'minDistance':
|
||||
this.activateMinDistanceMeasure();
|
||||
break;
|
||||
case 'angle':
|
||||
this.activateAngleMeasure();
|
||||
break;
|
||||
case 'elevation':
|
||||
this.activateElevationMeasure();
|
||||
break;
|
||||
case 'volume':
|
||||
this.activateVolumeMeasure();
|
||||
break;
|
||||
case 'laserDistance':
|
||||
this.activateLaserDistanceMeasure();
|
||||
break;
|
||||
case 'slope':
|
||||
this.activateSlopeMeasure();
|
||||
break;
|
||||
case 'spaceVolume':
|
||||
this.activateSpaceVolumeMeasure();
|
||||
break;
|
||||
console.log(`[Engine] Activating measure: ${mode}`);
|
||||
const measureKey = `${mode}Measure`;
|
||||
const measureInstance = (this.engine.measure as any)[measureKey];
|
||||
if (measureInstance && typeof measureInstance.active === 'function') {
|
||||
measureInstance.active();
|
||||
this.currentMeasureType = mode;
|
||||
} else {
|
||||
console.error(`[Engine] Measure type ${mode} not available.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用测量功能(关闭测量时调用)
|
||||
*/
|
||||
public deactivateMeasure(): void {
|
||||
if (!this._isInitialized || !this.engine?.measure) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isMeasureActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('停用测量功能');
|
||||
this.engine.measure.disActive();
|
||||
this.isMeasureActive = false;
|
||||
this.currentMeasureType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前激活的测量类型
|
||||
* @returns 当前测量类型,如果未激活则返回 null
|
||||
* @returns 当前测量类型,未激活时返回 null
|
||||
*/
|
||||
public getCurrentMeasureType(): MeasureMode | null {
|
||||
return this.currentMeasureType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有测量标注
|
||||
* 停用测量功能
|
||||
* @remarks 关闭测量时调用,会同时清除所有测量标注
|
||||
*/
|
||||
public deactivateMeasure(): void {
|
||||
if (!this._isInitialized || !this.engine?.measure) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Engine] Deactivating measure');
|
||||
this.engine.measure.disActive();
|
||||
this.engine.measure.clearAll();
|
||||
this.isMeasureActive = false;
|
||||
this.currentMeasureType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有测量标注(不停用测量模式)
|
||||
*/
|
||||
public clearAllMeasures(): void {
|
||||
if (!this._isInitialized || !this.engine?.measure) {
|
||||
return;
|
||||
}
|
||||
console.log('清除所有测量标注');
|
||||
console.log('[Engine] Clearing all measures');
|
||||
this.engine.measure.clearAll();
|
||||
}
|
||||
|
||||
// ==================== 结束:测量功能方法 ====================
|
||||
public saveMeasureSetting(setting: { unit: MeasureUnit; precision: MeasurePrecision }): void {
|
||||
if (!this._isInitialized || !this.engine?.measure) {
|
||||
return;
|
||||
}
|
||||
this.engine.measure.saveSetting?.(setting);
|
||||
}
|
||||
|
||||
// ==================== 结束:测量功能 ====================
|
||||
|
||||
// ==================== 剖切功能(统一 API) ====================
|
||||
|
||||
@@ -503,7 +401,6 @@ export class Engine implements IBimComponent {
|
||||
console.error('[Engine] Cannot set section box range: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
console.log('[Engine] Setting section box range:', range);
|
||||
this.engine.clipping.updateClippingValue(range);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,26 +3,8 @@ import type { ThemeConfig } from '../../themes/types';
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import { localeManager, t } from '../../services/locale';
|
||||
import { themeManager } from '../../services/theme';
|
||||
import type { MeasureConfig, MeasureMode, MeasurePanelOptions, MeasurePrecision, MeasureResult, MeasureUnit } from './types';
|
||||
|
||||
/**
|
||||
* 测量方式图标(SVG)
|
||||
*
|
||||
* 说明:
|
||||
* - 你上传的 SVG 原文件放在 `src/assets/icons/` 目录
|
||||
* - 原始 SVG 含 defs/clipPath/style/背景 rect,直接内联时容易出现渲染/裁剪异常(尤其多个图标同时出现)
|
||||
* - 这里把图标“瘦身”为纯 path,并统一使用 currentColor,确保稳定渲染
|
||||
*/
|
||||
const MEASURE_MODE_ICON_SVGS: Record<MeasureMode, string> = {
|
||||
distance: `<svg viewBox="0 0 32 32" aria-hidden="true"><g transform="translate(0 4.197)"><path fill="currentColor" d="M29.692,3.03,27.55.919a.529.529,0,0,1-.014-.756A.549.549,0,0,1,28.3.15l.014.013,3.067,3.023a.529.529,0,0,1,0,.756L28.317,6.966a.549.549,0,0,1-.767.013.529.529,0,0,1-.014-.756l.014-.013L29.692,4.1H2.31L4.452,6.21a.528.528,0,0,1,.013.756.547.547,0,0,1-.766.013l-.014-.013L.616,3.942a.531.531,0,0,1,0-.756L3.685.163a.548.548,0,0,1,.767.014.528.528,0,0,1,0,.742L2.31,3.03ZM24.136,15.055H23.051V18H21.966v-2.94H20.882V18H19.8v-2.94H18.712V18H17.627v-2.94H16.543v5.078H15.458V15.055H14.373V18H13.288v-2.94H12.2V18H11.119v-2.94H10.034V18H8.949v-2.94H7.865V18H6.78v-2.94H5.7v5.078H4.61V15.055H1.9a.27.27,0,0,0-.272.268v6.413A.269.269,0,0,0,1.9,22H30.1a.268.268,0,0,0,.271-.267V15.323a.269.269,0,0,0-.271-.268H27.39v5.078H26.305V15.055H25.221V18H24.136Zm5.966-1.6A1.884,1.884,0,0,1,32,15.323v6.413a1.885,1.885,0,0,1-1.9,1.871H1.9A1.885,1.885,0,0,1,0,21.736V15.323a1.885,1.885,0,0,1,1.9-1.871Z"/></g></svg>`,
|
||||
minDistance: `<svg viewBox="0 0 32 32" aria-hidden="true"><path fill="currentColor" d="M-5.839,24.8H-34.16A1.875,1.875,0,0,1-36,22.933V16.52a1.887,1.887,0,0,1,1.9-1.871H-5.9A1.887,1.887,0,0,1-4,16.52v6.412A1.875,1.875,0,0,1-5.839,24.8ZM-34.1,16.252a.27.27,0,0,0-.272.268v6.412a.27.27,0,0,0,.272.267H-5.9a.269.269,0,0,0,.271-.267V16.52a.27.27,0,0,0-.271-.268H-8.61V21.33H-9.695V16.252h-1.085v2.939h-1.085V16.252h-1.085v2.939h-1.085V16.252h-1.084v2.939H-16.2V16.252h-1.085v2.939h-1.085V16.252h-1.084V21.33h-1.084V16.252h-1.085v2.939h-1.085V16.252H-23.8v2.939h-1.085V16.252h-1.085v2.939h-1.085V16.252h-1.084v2.939H-29.22V16.252H-30.3V21.33H-31.39V16.252Z" transform="translate(36 2)"/><path fill="currentColor" d="M23.716,7.947V4.875c0-.8-.232-1.085-.765-1.085a1.573,1.573,0,0,0-1.133.585V7.947H20.4V2.75h1.163l.1.687H21.7a2.547,2.547,0,0,1,1.763-.817c1.172,0,1.676.78,1.676,2.089V7.947Zm-7.26,0V2.62h1.58V7.947Zm-3.8,0V4.875c0-.8-.243-1.085-.76-1.085a1.606,1.606,0,0,0-1.049.585V7.947H9.421V4.875c0-.8-.243-1.085-.758-1.085a1.608,1.608,0,0,0-1.05.585V7.947H6.194V2.75H7.36l.1.7H7.5A2.326,2.326,0,0,1,9.169,2.62a1.486,1.486,0,0,1,1.5.91A2.445,2.445,0,0,1,12.4,2.62c1.156,0,1.691.78,1.691,2.089V7.947Zm3.8-6.849a.79.79,0,0,1,1.58,0,.79.79,0,0,1-1.58,0Z" transform="translate(0.333 3.053)"/></svg>`,
|
||||
angle: `<svg viewBox="0 0 32 32" aria-hidden="true"><path fill="currentColor" d="M39.587,50.766h13.7a1,1,0,0,1,0,2H23.171a1,1,0,0,1,0-2h1.418l6.582-7.006v-.006a.517.517,0,0,1,.14-.357.456.456,0,0,1,.337-.144l12.1-12.876a.451.451,0,0,1,.665,0,.524.524,0,0,1,0,.708L32.883,43.355a8.3,8.3,0,0,1,6.7,7.411Zm-.949,0a7.254,7.254,0,0,0-6.611-6.5l-6.108,6.5Z" transform="translate(-22.229 -26.489)"/></svg>`,
|
||||
elevation: `<svg viewBox="0 0 32 32" aria-hidden="true"><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>`,
|
||||
volume: `<svg viewBox="0 0 32 32" aria-hidden="true"><path fill="currentColor" d="M94.74,86.658V71.189a.371.371,0,0,1,.2-.329l13.869-7.22a.371.371,0,0,1,.344,0l13.053,6.891h0l.819.431a.371.371,0,0,1,.2.328v15.3a.371.371,0,0,1-.2.328l-13.872,7.255a.371.371,0,0,1-.342,0L94.94,86.987a.371.371,0,0,1-.2-.329Zm2.119-.837,11.2,5.8a.024.024,0,0,0,.035-.022V79.483a.371.371,0,0,0-.2-.328l-11.2-5.909a.024.024,0,0,0-.035.021V85.492A.371.371,0,0,0,96.859,85.821Zm13.151-6.459v12a.12.12,0,0,0,.176.106L114,89.474l3.334-1.745,3.771-1.978a.371.371,0,0,0,.2-.328V73.5a.193.193,0,0,0-.284-.171l-10.812,5.708A.371.371,0,0,0,110.01,79.362ZM97.925,71.725l10.839,5.72a.371.371,0,0,0,.346,0L119.8,71.808a.214.214,0,0,0,0-.378l-10.649-5.621a.371.371,0,0,0-.344,0L97.925,71.47A.144.144,0,0,0,97.925,71.725Z" transform="translate(-92.982 -62.907)"/></svg>`,
|
||||
laserDistance: `<svg viewBox="0 0 32 32" aria-hidden="true"><g transform="translate(0 -1.293)"><path fill="currentColor" d="M0,1.293v31.96H32V1.293ZM30.97,32.182H1.03V2.323H30.97Z"/><path fill="currentColor" d="M160.026,291.9l1.6,1.6,7.305-7.305-7.305-7.305-1.6,1.6,4.794,4.566h-6.392v2.283h6.392Zm-5.251,0-4.566-4.566h6.164v-2.283H150.21l4.566-4.566-1.37-1.6L146.1,286.19l7.305,7.305Z" transform="translate(-141.535 -268.917)"/></g></svg>`,
|
||||
slope: `<svg viewBox="0 0 32 32" aria-hidden="true"><path fill="currentColor" d="M202.1,188.337l2.629-2.191-8.447-3.106,1.533,8.871,2.629-2.194,9.341,11.209,1.656-1.379Zm-13.726-.435a1.075,1.075,0,0,0-1.07-.341,1.057,1.057,0,0,0-.5.277l-5.11,4.08a1.08,1.08,0,0,0-.406.84l-.007,17.386a1.079,1.079,0,0,0,1.077,1.077L205.7,211.2a1.078,1.078,0,0,0,.822-1.774Zm-4.934,21.164.007-15.788,3.968-3.171,15.974,18.941Z" transform="translate(-180.36 -181.131)"/></svg>`,
|
||||
spaceVolume: `<svg viewBox="0 0 32 32" aria-hidden="true"><g transform="translate(-106.35 -97.661)"><path fill="currentColor" d="M125.977,128.829l13.076-7.363v-13.6l-13.076,6.8Zm-3.126-15.655a.565.565,0,0,1-.258-.064L109.3,106.323a.567.567,0,0,1-.011-1L122.578,98a.567.567,0,0,1,.55,0l13.288,7.325a.567.567,0,0,1-.011,1l-13.292,6.79A.63.63,0,0,1,122.851,113.174ZM110.773,105.8l12.078,6.172,12.078-6.172-12.078-6.657Z" transform="translate(-1.922)"/><path fill="currentColor" d="M120.649,322.52a.58.58,0,0,1-.262-.064l-13.08-6.8a.573.573,0,0,1-.307-.5V301a.566.566,0,0,1,.273-.486.573.573,0,0,1,.558-.019l13.076,6.8a.573.573,0,0,1,.307.5v14.161a.57.57,0,0,1-.565.569Zm-12.511-7.708,11.942,6.206V308.136l-11.942-6.206Zm15.917,9.408a.585.585,0,0,1-.288-.076.567.567,0,0,1-.281-.489V309.49a.562.562,0,0,1,.307-.5l13.076-6.8a.573.573,0,0,1,.558.019.562.562,0,0,1,.273.486v13.6a.568.568,0,0,1-.288.493l-13.076,7.359A.557.557,0,0,1,124.055,324.22Zm.569-14.385V322.68l11.942-6.722V303.629Z" transform="translate(0 -194.822)"/></g></svg>`
|
||||
};
|
||||
import type { MeasureConfig, MeasurePanelOptions, MeasurePrecision, MeasureResult, MeasureUnit } from './types';
|
||||
import { MEASURE_TYPES, MEASURE_MODES_ORDERED, type MeasureMode } from '../../types/measure';
|
||||
|
||||
/**
|
||||
* 测量面板组件(只做 UI,不实现真实测量)
|
||||
@@ -242,36 +224,23 @@ export class MeasurePanel implements IBimComponent {
|
||||
return this.activeMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换测量方式(你要求的“切换类型的方法”)
|
||||
* @param mode 目标测量方式
|
||||
*/
|
||||
public switchMode(mode: MeasureMode): void {
|
||||
this.setActiveMode(mode);
|
||||
public switchMode(mode: MeasureMode, triggerCallback: boolean = true): void {
|
||||
this.setActiveMode(mode, triggerCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前测量方式
|
||||
* @param mode 目标测量方式
|
||||
*/
|
||||
public setActiveMode(mode: MeasureMode): void {
|
||||
public setActiveMode(mode: MeasureMode, triggerCallback: boolean = true): void {
|
||||
if (this.activeMode === mode) return;
|
||||
this.activeMode = mode;
|
||||
this.applyActiveModeState();
|
||||
|
||||
// 切换方式后,主值 label 也需要更新
|
||||
this.mainValueLabelEl.textContent = t(this.getModeValueLabelI18nKey(this.activeMode));
|
||||
|
||||
// 通知外部(如果需要)
|
||||
if (this.options.onModeChange) {
|
||||
if (triggerCallback && this.options.onModeChange) {
|
||||
this.options.onModeChange(mode);
|
||||
}
|
||||
|
||||
// 模式切换后,结果展示也应刷新(例如某些字段显示为 --)
|
||||
this.renderResult();
|
||||
|
||||
// 切换模式会影响结果区高度(例如 distance 显示 xyz,其它不显示)
|
||||
// 复用 onExpandedChange 来通知外部重新计算 Dialog 高度(不额外扩展回调,保持接口简单)
|
||||
if (this.options.onExpandedChange) {
|
||||
this.options.onExpandedChange(this.isExpanded);
|
||||
}
|
||||
@@ -386,38 +355,23 @@ export class MeasurePanel implements IBimComponent {
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'bim-measure-tool-grid';
|
||||
|
||||
// 8 种测量方式(顺序严格按你给的)
|
||||
const modes: MeasureMode[] = [
|
||||
'distance',
|
||||
'minDistance',
|
||||
'angle',
|
||||
'elevation',
|
||||
'volume',
|
||||
'laserDistance',
|
||||
'slope',
|
||||
'spaceVolume'
|
||||
];
|
||||
|
||||
// 图标:优先使用你上传的 SVG 文件内容(已内联到 MEASURE_MODE_ICON_SVGS)
|
||||
// 兜底:如果某个 mode 没有配置图标,则使用圆形占位(防止页面空白)
|
||||
const fallbackCircleIconSvg = `
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="9"></circle>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
// 逐个创建按钮
|
||||
for (let i = 0; i < modes.length; i++) {
|
||||
const mode = modes[i];
|
||||
for (let i = 0; i < MEASURE_MODES_ORDERED.length; i++) {
|
||||
const mode = MEASURE_MODES_ORDERED[i];
|
||||
const config = MEASURE_TYPES[mode];
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'bim-measure-tool-btn';
|
||||
btn.dataset.mode = mode;
|
||||
|
||||
// icon
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'bim-measure-tool-icon';
|
||||
icon.innerHTML = MEASURE_MODE_ICON_SVGS[mode] || fallbackCircleIconSvg;
|
||||
icon.innerHTML = config.icon || fallbackCircleIconSvg;
|
||||
btn.appendChild(icon);
|
||||
|
||||
// 点击切换模式
|
||||
@@ -680,13 +634,10 @@ export class MeasurePanel implements IBimComponent {
|
||||
this.view = 'main';
|
||||
this.applyViewState();
|
||||
|
||||
// 配置变化会影响显示
|
||||
this.renderResult();
|
||||
|
||||
// 高度变化(设置面板 -> 主面板)也需要通知外部
|
||||
if (this.options.onExpandedChange) {
|
||||
this.options.onExpandedChange(this.isExpanded);
|
||||
}
|
||||
this.options.onConfigSave?.(next);
|
||||
this.options.onExpandedChange?.(this.isExpanded);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -822,26 +773,14 @@ export class MeasurePanel implements IBimComponent {
|
||||
// - 坡度:--%
|
||||
// - 空间体积:--mm³(单位随设置变动,即 unit³)
|
||||
|
||||
// 1.1) 主行:默认显示 label + value(数值/单位拆分)
|
||||
// 激光测距:只显示文字,因此隐藏 label/单位
|
||||
if (this.activeMode === 'laserDistance') {
|
||||
this.mainValueLabelEl.style.display = 'none';
|
||||
this.mainNumberEl.textContent = t(this.getModeI18nKey('laserDistance'));
|
||||
this.mainUnitEl.textContent = '';
|
||||
// 激光测距:你要求不使用黄色主数据
|
||||
this.mainNumberEl.classList.add('is-laser-text');
|
||||
} else {
|
||||
this.mainValueLabelEl.style.display = '';
|
||||
this.mainValueLabelEl.textContent = t(this.getModeValueLabelI18nKey(this.activeMode));
|
||||
const parts = this.formatMainValueParts(this.activeMode, this.result);
|
||||
this.mainNumberEl.textContent = parts.numberText;
|
||||
this.mainUnitEl.textContent = parts.unitText;
|
||||
// 其它模式:恢复黄色主数据
|
||||
this.mainNumberEl.classList.remove('is-laser-text');
|
||||
}
|
||||
this.mainValueLabelEl.style.display = '';
|
||||
this.mainValueLabelEl.textContent = t(this.getModeValueLabelI18nKey(this.activeMode));
|
||||
const parts = this.formatMainValueParts(this.activeMode, this.result);
|
||||
this.mainNumberEl.textContent = parts.numberText;
|
||||
this.mainUnitEl.textContent = parts.unitText;
|
||||
|
||||
// 1.2) XYZ:只有“距离”需要展示
|
||||
if (this.activeMode === 'distance') {
|
||||
const showXyz = this.activeMode === 'distance' || this.activeMode === 'point';
|
||||
if (showXyz) {
|
||||
this.xyzBoxEl.style.display = '';
|
||||
const xyz = this.result?.xyz;
|
||||
if (!xyz) {
|
||||
@@ -856,13 +795,9 @@ export class MeasurePanel implements IBimComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
// 非 distance:隐藏 xyz
|
||||
this.xyzBoxEl.style.display = 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模式名称的国际化 key
|
||||
*/
|
||||
private getModeI18nKey(mode: MeasureMode): string {
|
||||
return `measure.modes.${mode}`;
|
||||
}
|
||||
@@ -887,103 +822,47 @@ export class MeasurePanel implements IBimComponent {
|
||||
|
||||
// 注意:旧的 formatLengthWithConfig 已被 formatLengthParts 替代。
|
||||
|
||||
private convertMmToUnit(mm: number, unit: MeasureUnit): number {
|
||||
switch (unit) {
|
||||
case 'mm':
|
||||
return mm;
|
||||
case 'cm':
|
||||
return mm / 10;
|
||||
case 'm':
|
||||
return mm / 1000;
|
||||
case 'km':
|
||||
return mm / 1_000_000;
|
||||
default:
|
||||
return mm;
|
||||
}
|
||||
}
|
||||
|
||||
private getUnitI18nKey(unit: MeasureUnit): string {
|
||||
return `measure.units.${unit}`;
|
||||
}
|
||||
|
||||
// 注意:旧的 formatElevationFixedMeters / formatVolumeWithConfig 已被 formatMainValueParts 替代。
|
||||
|
||||
private convertMm3ToUnit3(mm3: number, unit: MeasureUnit): number {
|
||||
// 先把 mm³ -> 对应 unit³
|
||||
// mm -> cm: /10,因此 mm³ -> cm³: /1000
|
||||
// mm -> m : /1000,因此 mm³ -> m³ : /1e9
|
||||
// mm -> km: /1e6,因此 mm³ -> km³: /1e18
|
||||
switch (unit) {
|
||||
case 'mm':
|
||||
return mm3;
|
||||
case 'cm':
|
||||
return mm3 / 1000;
|
||||
case 'm':
|
||||
return mm3 / 1_000_000_000;
|
||||
case 'km':
|
||||
return mm3 / 1_000_000_000_000_000_000;
|
||||
default:
|
||||
return mm3;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主数据拆分:返回 { 数值文本, 单位文本 }
|
||||
* 规则:
|
||||
* - 没数据时:必须展示 `-- 单位`(而不是只展示 `--`)
|
||||
* - 单位随模式变化:
|
||||
* - 距离/最小距离:单位随设置变动
|
||||
* - 角度:°
|
||||
* - 标高:固定 m
|
||||
* - 体积/空间体积:单位³(随设置变动)
|
||||
* - 坡度:%
|
||||
*/
|
||||
private formatMainValueParts(mode: MeasureMode, result: MeasureResult | null): { numberText: string; unitText: string } {
|
||||
if (mode === 'laserDistance') return { numberText: t(this.getModeI18nKey('laserDistance')), unitText: '' };
|
||||
|
||||
// 没有数据:显示 `-- 单位`
|
||||
if (!result) {
|
||||
return this.getEmptyValuePartsByMode(mode);
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case 'distance':
|
||||
return this.formatLengthParts(result.distanceMm);
|
||||
case 'minDistance':
|
||||
return this.formatLengthParts(result.minDistanceMm);
|
||||
const config = MEASURE_TYPES[mode];
|
||||
const value = (result as any)[config.resultField];
|
||||
|
||||
switch (config.valueType) {
|
||||
case 'length':
|
||||
case 'area':
|
||||
return this.formatMeasureValue(value, config.valueType);
|
||||
case 'angle':
|
||||
return this.formatFixedUnitParts(result.angleDeg, t('measure.units.deg'));
|
||||
case 'elevation':
|
||||
// 标高固定 m(外部注入值约定为 mm)
|
||||
return this.formatFixedUnitParts(
|
||||
result.elevationMm === undefined ? undefined : result.elevationMm / 1000,
|
||||
t('measure.units.m')
|
||||
);
|
||||
case 'volume':
|
||||
return this.formatVolumeParts(result.volumeM3);
|
||||
case 'slope':
|
||||
return this.formatFixedUnitParts(result.slopePercent, t('measure.units.percent'));
|
||||
case 'spaceVolume':
|
||||
return this.formatVolumeParts(result.spaceVolumeM3);
|
||||
return this.formatFixedUnitParts(value, t('measure.units.deg'));
|
||||
case 'percent':
|
||||
return this.formatFixedUnitParts(value, t('measure.units.percent'));
|
||||
case 'point':
|
||||
return { numberText: '--', unitText: '' };
|
||||
default:
|
||||
return { numberText: '--', unitText: '' };
|
||||
}
|
||||
}
|
||||
|
||||
private getEmptyValuePartsByMode(mode: MeasureMode): { numberText: string; unitText: string } {
|
||||
switch (mode) {
|
||||
case 'distance':
|
||||
case 'minDistance':
|
||||
const config = MEASURE_TYPES[mode];
|
||||
|
||||
switch (config.valueType) {
|
||||
case 'length':
|
||||
return { numberText: '--', unitText: t(this.getUnitI18nKey(this.config.unit)) };
|
||||
case 'area':
|
||||
return { numberText: '--', unitText: `${this.config.unit}²` };
|
||||
case 'angle':
|
||||
return { numberText: '--', unitText: t('measure.units.deg') };
|
||||
case 'elevation':
|
||||
return { numberText: '--', unitText: t('measure.units.m') };
|
||||
case 'volume':
|
||||
case 'spaceVolume':
|
||||
return { numberText: '--', unitText: `${this.config.unit}³` };
|
||||
case 'slope':
|
||||
case 'percent':
|
||||
return { numberText: '--', unitText: t('measure.units.percent') };
|
||||
case 'point':
|
||||
return { numberText: '--', unitText: '' };
|
||||
default:
|
||||
return { numberText: '--', unitText: '' };
|
||||
}
|
||||
@@ -996,22 +875,32 @@ export class MeasurePanel implements IBimComponent {
|
||||
return { numberText: this.formatNumberWithPrecision(value, this.config.precision), unitText };
|
||||
}
|
||||
|
||||
private formatLengthParts(valueMm: number | undefined): { numberText: string; unitText: string } {
|
||||
const unitText = t(this.getUnitI18nKey(this.config.unit));
|
||||
if (valueMm === null || valueMm === undefined || Number.isNaN(valueMm)) {
|
||||
private formatMeasureValue(value: number | undefined, type: 'length' | 'area'): { numberText: string; unitText: string } {
|
||||
const unit = this.config.unit;
|
||||
const unitText = type === 'area' ? `${unit}²` : t(this.getUnitI18nKey(unit));
|
||||
|
||||
if (value === null || value === undefined || Number.isNaN(value)) {
|
||||
return { numberText: '--', unitText };
|
||||
}
|
||||
const converted = this.convertMmToUnit(valueMm, this.config.unit);
|
||||
return { numberText: this.formatNumberWithPrecision(converted, this.config.precision), unitText };
|
||||
}
|
||||
|
||||
private formatVolumeParts(valueMm3: number | undefined): { numberText: string; unitText: string } {
|
||||
const unitText = `${this.config.unit}³`;
|
||||
if (valueMm3 === null || valueMm3 === undefined || Number.isNaN(valueMm3)) {
|
||||
return { numberText: '--', unitText };
|
||||
let converted: number;
|
||||
if (type === 'length') {
|
||||
switch (unit) {
|
||||
case 'mm': converted = value * 1000; break;
|
||||
case 'cm': converted = value * 100; break;
|
||||
case 'km': converted = value / 1000; break;
|
||||
default: converted = value;
|
||||
}
|
||||
} else {
|
||||
switch (unit) {
|
||||
case 'mm': converted = value * 1000 * 1000; break;
|
||||
case 'cm': converted = value * 100 * 100; break;
|
||||
case 'km': converted = value / 1000 / 1000; break;
|
||||
default: converted = value;
|
||||
}
|
||||
}
|
||||
const converted = this.convertMm3ToUnit3(valueMm3, this.config.unit);
|
||||
return { numberText: this.formatNumberWithPrecision(converted, this.config.precision), unitText };
|
||||
|
||||
return { numberText: converted.toFixed(this.config.precision), unitText };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,24 +50,21 @@ export interface MeasureXYZ {
|
||||
* - 不同测量方式对应不同字段;未传入则 UI 显示 “--”。
|
||||
*/
|
||||
export interface MeasureResult {
|
||||
/** 净高(单位:mm) */
|
||||
clearHeightMm?: number;
|
||||
/** 净距(单位:mm) */
|
||||
clearDistanceMm?: number;
|
||||
/** 距离(单位:mm) */
|
||||
distanceMm?: number;
|
||||
/** 最小距离(单位:mm) */
|
||||
minDistanceMm?: number;
|
||||
/** 角度(单位:deg) */
|
||||
angleDeg?: number;
|
||||
/** 标高(单位:mm) */
|
||||
elevationMm?: number;
|
||||
/** 体积(单位:m³) */
|
||||
volumeM3?: number;
|
||||
/** 激光测距(单位:mm) */
|
||||
laserDistanceMm?: number;
|
||||
/** 角度(单位:deg) */
|
||||
angleDeg?: number;
|
||||
/** 面积(单位:m²) */
|
||||
areaM2?: number;
|
||||
/** 坡度(单位:%) */
|
||||
slopePercent?: number;
|
||||
/** 空间体积(单位:m³) */
|
||||
spaceVolumeM3?: number;
|
||||
|
||||
/** 可选:展示测量点/结果点坐标(单位由引擎侧定义,这里只负责显示) */
|
||||
/** 坐标测量点(单位由引擎侧定义) */
|
||||
xyz?: MeasureXYZ;
|
||||
}
|
||||
|
||||
@@ -102,6 +99,12 @@ export interface MeasurePanelOptions {
|
||||
* 说明:用于让外部(如 Dialog)重新计算尺寸。
|
||||
*/
|
||||
onExpandedChange?: (expanded: boolean) => void;
|
||||
|
||||
/**
|
||||
* 设置保存回调
|
||||
* 说明:用户点击"保存设置"时触发,用于同步到引擎
|
||||
*/
|
||||
onConfigSave?: (config: MeasureConfig) => void;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user