feat: upgrade to v1.3.2 with settings panel overhaul, clear height enhancements and bug fixes

- Overhaul settings dialog: add edge line toggle, contrast/saturation/light intensity sliders, environment and ground type selectors
- Add clear height measurement options: direction (up/down) and select type (point/element) with radio button UI
- Fix right-click context menu triggering during model drag rotation (add move threshold)
- Fix measure dialog event listener leak (on → off for cleanup)
- Update mini map API to use engine.minMap.toggle()
- Replace text-based measure icons with proper SVG assets (净高/净距/坐标/面积)
- Add i18n keys for all new settings and clear height options (zh-CN / en-US)
- Bump iflow-engine-base dependency to ^2.0.5
- Rebuild dist and sync demo libs
This commit is contained in:
yuding
2026-03-04 16:40:35 +08:00
parent 0ccc891d7c
commit c3bd82c03a
22 changed files with 15047 additions and 11698 deletions

View File

@@ -2,13 +2,13 @@ import type { ThemeConfig } from '../../themes/types';
import { IBimComponent } from '../../types/component';
import { themeManager } from '../../services/theme';
import type { EngineOptions, ModelLoadOptions, EngineInfo } from './types';
import { type MeasureMode } from '../../types/measure';
import { type MeasureMode, type ClearHeightDirection, type ClearHeightSelectType } from '../../types/measure';
import type { MeasureUnit, MeasurePrecision } from '../measure-panel/types';
import type { SectionBoxRange } from '../section-box-panel/types';
import type { ManagerRegistry } from '../../core/manager-registry';
// 导入第三方 SDK 的 createEngine 函数(从 npm 包引入)
import { createEngine as createEngineSDK } from 'iflow-engine-base';
//import { createEngine as createEngineSDK } from '../../../../bim_engine_base/dist/bim-engine-sdk.es';
//import { createEngine as createEngineSDK } from 'iflow-engine-base';
import { createEngine as createEngineSDK } from '../../../../bim_engine_base/dist/bim-engine-sdk.es';
import "../../../../bim_engine_base/dist/iflow-engine-base.css"
export type { EngineOptions, ModelLoadOptions, EngineInfo };
@@ -369,6 +369,30 @@ export class Engine implements IBimComponent {
this.engine.measure.saveSetting?.(setting);
}
/**
* 设置净高测量朝向
* @param direction 0=朝下1=朝上
*/
public setClearHeightDirection(direction: ClearHeightDirection): void {
if (!this._isInitialized || !this.engine?.measure) {
return;
}
console.log(`[Engine] Setting clearHeight direction: ${direction}`);
this.engine.measure.clearHeightMeasure.setDirection(direction);
}
/**
* 设置净高测量选择对象类型
* @param selectType 'point'=选择点,'element'=选择构件
*/
public setClearHeightSelectType(selectType: ClearHeightSelectType): void {
if (!this._isInitialized || !this.engine?.measure) {
return;
}
console.log(`[Engine] Setting clearHeight selectType: ${selectType}`);
this.engine.measure.clearHeightMeasure.setSelectType(selectType);
}
// ==================== 结束:测量功能 ====================
// ==================== 剖切功能(统一 API ====================
@@ -696,11 +720,22 @@ export class Engine implements IBimComponent {
* 切换小地图显示状态
*/
public toggleMiniMap(): void {
if (!this._isInitialized || !this.engine?.controlModule) {
if (!this._isInitialized || !this.engine) {
console.error('[Engine] Cannot toggle mini map: engine not initialized.');
return;
}
this.engine.controlModule.toggleMinMap();
this.engine.minMap?.toggle();
}
/**
* 获取小地图显示状态
* @returns true=显示false=隐藏
*/
public getMiniMapState(): boolean {
if (!this._isInitialized || !this.engine) {
return false;
}
return this.engine.minMap?.getState() ?? false;
}
/**
@@ -1004,7 +1039,7 @@ export class Engine implements IBimComponent {
const normalizeUrl = (url: string): string => url.replace(/\/+$/, '');
const loadedModels = Array.isArray((this.engine as any)?.models)
? ((this.engine as any).models as Array<{ url?: string; nodesMap?: Map<number, { indexes?: unknown[] }> }> )
? ((this.engine as any).models as Array<{ url?: string; nodesMap?: Map<number, { indexes?: unknown[] }> }>)
: [];
const modelMap = new Map<string, { nodesMap?: Map<number, { indexes?: unknown[] }> }>();
for (const loadedModel of loadedModels) {

View File

@@ -208,6 +208,66 @@
transform: rotate(180deg);
}
/* ========== 模式选项区(净高朝向/选择对象) ========== */
.bim-measure-mode-options {
display: none;
flex-direction: column;
gap: 8px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid var(--bim-divider);
}
.bim-measure-mode-options.is-visible {
display: flex;
}
.bim-measure-radio-group {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
line-height: 1.4;
}
.bim-measure-radio-group .bim-measure-radio-label {
color: var(--bim-text-secondary);
min-width: 58px;
flex: 0 0 auto;
}
.bim-measure-radio-group .bim-measure-radio-items {
display: flex;
gap: 4px;
}
.bim-measure-radio-item {
display: inline-flex;
align-items: center;
justify-content: center;
height: 26px;
padding: 0 10px;
border-radius: 4px;
border: 1px solid var(--bim-border-default);
background: var(--bim-bg-inset);
color: var(--bim-text-secondary);
font-size: 12px;
cursor: pointer;
transition: all 0.15s ease;
user-select: none;
}
.bim-measure-radio-item:hover {
border-color: var(--bim-border-strong);
color: var(--bim-text-primary);
}
.bim-measure-radio-item.is-active {
background: var(--bim-primary-subtle);
border-color: var(--bim-primary);
color: var(--bim-primary);
}
.bim-measure-result {
margin-top: 12px;
padding-top: 12px;

View File

@@ -3,7 +3,7 @@ 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, MeasurePanelOptions, MeasurePrecision, MeasureResult, MeasureUnit } from './types';
import type { MeasureConfig, MeasurePanelOptions, MeasurePrecision, MeasureResult, MeasureUnit, ClearHeightDirection, ClearHeightSelectType } from './types';
import { MEASURE_TYPES, MEASURE_MODES_ORDERED, getValueType, type MeasureMode, type MeasureValueType } from '../../types/measure';
/**
@@ -27,6 +27,10 @@ export class MeasurePanel implements IBimComponent {
private isExpanded: boolean;
private result: MeasureResult | null = null;
// 净高模式专属状态
private clearHeightDirection: ClearHeightDirection = 0; // 默认朝下
private clearHeightSelectType: ClearHeightSelectType = 'point'; // 默认选择点
/**
* 测量配置(单位/精度)
* 说明:
@@ -67,6 +71,11 @@ export class MeasurePanel implements IBimComponent {
private clearBtn!: HTMLButtonElement;
private settingsBtn!: HTMLButtonElement;
// 净高模式选项 DOM
private modeOptionsEl!: HTMLElement;
private directionBtns: Map<ClearHeightDirection, HTMLButtonElement> = new Map();
private selectTypeBtns: Map<ClearHeightSelectType, HTMLButtonElement> = new Map();
// Settings DOM
private mainViewEl!: HTMLElement;
private settingsViewEl!: HTMLElement;
@@ -115,6 +124,7 @@ export class MeasurePanel implements IBimComponent {
// 初始渲染状态(按钮显隐、选中态、结果区)
this.applyExpandedState();
this.applyActiveModeState();
this.applyClearHeightOptionsState();
this.applyViewState();
this.renderResult();
@@ -233,6 +243,7 @@ export class MeasurePanel implements IBimComponent {
if (this.activeMode === mode) return;
this.activeMode = mode;
this.applyActiveModeState();
this.applyClearHeightOptionsState();
this.mainValueLabelEl.textContent = t(this.getModeValueLabelI18nKey(this.activeMode));
@@ -422,6 +433,10 @@ export class MeasurePanel implements IBimComponent {
toolsBox.appendChild(toggleBox);
this.mainViewEl.appendChild(toolsBox);
// 净高模式选项区(朝向 + 选择对象)
this.modeOptionsEl = this.createModeOptionsDom();
this.mainViewEl.appendChild(this.modeOptionsEl);
// 中部:结果区
const resultBox = document.createElement('div');
resultBox.className = 'bim-measure-result';
@@ -593,6 +608,118 @@ export class MeasurePanel implements IBimComponent {
return box;
}
/**
* 创建净高模式选项区 DOM朝向 + 选择对象)
*/
private createModeOptionsDom(): HTMLElement {
const box = document.createElement('div');
box.className = 'bim-measure-mode-options';
// 朝向单选组
const dirGroup = document.createElement('div');
dirGroup.className = 'bim-measure-radio-group';
const dirLabel = document.createElement('span');
dirLabel.className = 'bim-measure-radio-label';
dirLabel.dataset.i18nKey = 'measure.clearHeight.direction';
const dirItems = document.createElement('div');
dirItems.className = 'bim-measure-radio-items';
const dirDown = this.createRadioItem('measure.clearHeight.directionDown', () => {
this.setClearHeightDirection(0);
});
const dirUp = this.createRadioItem('measure.clearHeight.directionUp', () => {
this.setClearHeightDirection(1);
});
this.directionBtns.set(0, dirDown);
this.directionBtns.set(1, dirUp);
dirItems.appendChild(dirDown);
dirItems.appendChild(dirUp);
dirGroup.appendChild(dirLabel);
dirGroup.appendChild(dirItems);
box.appendChild(dirGroup);
// 选择对象单选组
const selGroup = document.createElement('div');
selGroup.className = 'bim-measure-radio-group';
const selLabel = document.createElement('span');
selLabel.className = 'bim-measure-radio-label';
selLabel.dataset.i18nKey = 'measure.clearHeight.selectType';
const selItems = document.createElement('div');
selItems.className = 'bim-measure-radio-items';
const selPoint = this.createRadioItem('measure.clearHeight.selectPoint', () => {
this.setClearHeightSelectType('point');
});
const selElement = this.createRadioItem('measure.clearHeight.selectElement', () => {
this.setClearHeightSelectType('element');
});
this.selectTypeBtns.set('point', selPoint);
this.selectTypeBtns.set('element', selElement);
selItems.appendChild(selPoint);
selItems.appendChild(selElement);
selGroup.appendChild(selLabel);
selGroup.appendChild(selItems);
box.appendChild(selGroup);
return box;
}
private createRadioItem(i18nKey: string, onClick: () => void): HTMLButtonElement {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'bim-measure-radio-item';
btn.dataset.i18nKey = i18nKey;
btn.textContent = t(i18nKey);
btn.addEventListener('click', onClick);
return btn;
}
private setClearHeightDirection(direction: ClearHeightDirection): void {
if (this.clearHeightDirection === direction) return;
this.clearHeightDirection = direction;
this.applyClearHeightOptionsState();
this.options.onClearHeightDirectionChange?.(direction);
}
private setClearHeightSelectType(selectType: ClearHeightSelectType): void {
if (this.clearHeightSelectType === selectType) return;
this.clearHeightSelectType = selectType;
this.applyClearHeightOptionsState();
this.options.onClearHeightSelectTypeChange?.(selectType);
}
/**
* 应用净高选项状态(显隐 + 选中态)
*/
private applyClearHeightOptionsState(): void {
// 显隐
if (this.activeMode === 'clearHeight') {
this.modeOptionsEl.classList.add('is-visible');
} else {
this.modeOptionsEl.classList.remove('is-visible');
}
// 朝向按钮选中态
for (const [dir, btn] of this.directionBtns.entries()) {
if (dir === this.clearHeightDirection) {
btn.classList.add('is-active');
} else {
btn.classList.remove('is-active');
}
}
// 选择对象按钮选中态
for (const [type, btn] of this.selectTypeBtns.entries()) {
if (type === this.clearHeightSelectType) {
btn.classList.add('is-active');
} else {
btn.classList.remove('is-active');
}
}
}
private makeOption(unit: MeasureUnit): HTMLOptionElement {
const opt = document.createElement('option');
opt.value = unit;

View File

@@ -7,9 +7,9 @@
*/
// 从通用 types 目录导入 MeasureMode避免组件间耦合
import type { MeasureMode } from '../../types/measure';
import type { MeasureMode, ClearHeightDirection, ClearHeightSelectType } from '../../types/measure';
// 同时重新导出,保持向后兼容
export type { MeasureMode };
export type { MeasureMode, ClearHeightDirection, ClearHeightSelectType };
/**
* 距离/标高等“长度类”单位
@@ -105,6 +105,18 @@ export interface MeasurePanelOptions {
* 说明:用户点击"保存设置"时触发,用于同步到引擎
*/
onConfigSave?: (config: MeasureConfig) => void;
/**
* 净高朝向变更回调
* @param direction 0=朝下1=朝上
*/
onClearHeightDirectionChange?: (direction: ClearHeightDirection) => void;
/**
* 净高选择对象变更回调
* @param selectType 'point'=选择点,'element'=选择构件
*/
onClearHeightSelectTypeChange?: (selectType: ClearHeightSelectType) => void;
}

View File

@@ -16,7 +16,10 @@ export class BimRightKey implements IBimComponent {
private options?: RightKeyOptions;
private mouseDownTime: number = 0;
private mouseDownX: number = 0;
private mouseDownY: number = 0;
private readonly CLICK_THRESHOLD: number = 200; // ms
private readonly MOVE_THRESHOLD: number = 5; // px
constructor(options?: RightKeyOptions) {
this.options = options;
@@ -75,9 +78,11 @@ export class BimRightKey implements IBimComponent {
}
private handleContainerMouseDown = (e: MouseEvent): void => {
// 记录右键按下时间 (button 2 是右键)
// 记录右键按下时间和位置 (button 2 是右键)
if (e.button === 2) {
this.mouseDownTime = Date.now();
this.mouseDownX = e.clientX;
this.mouseDownY = e.clientY;
}
};
@@ -91,6 +96,13 @@ export class BimRightKey implements IBimComponent {
return;
}
// 检查移动距离,如果鼠标移动超过阈值(用户在拖拽旋转模型),则不触发回调
const dx = e.clientX - this.mouseDownX;
const dy = e.clientY - this.mouseDownY;
if (dx * dx + dy * dy > this.MOVE_THRESHOLD * this.MOVE_THRESHOLD) {
return;
}
// 触发有效右键回调
if (this.options?.onContext) {
this.options.onContext(e);