refactor: slim down EngineManager from 861 to 290 lines by removing passthrough proxy pattern
- EngineManager now only exposes public SDK API (initialize, loadModel, pause/resumeRendering, getEngineComponent, destroy) - Internal managers access Engine component directly via this.engineComponent getter on BaseManager - Non-manager components use registry.engine3d.getEngineComponent() for direct Engine access - Replaced getEngine() with onRawEvent()/offRawEvent() for raw engine event access - Migrated 62 call sites across 13 files (9 managers, 1 panel, 3 toolbar buttons) - Updated all architecture docs, API docs, and README to reflect new patterns
This commit is contained in:
@@ -5,11 +5,8 @@ import { t } from '../services/locale';
|
||||
/** 渲染模式类型 */
|
||||
type RenderMode = 'simple' | 'balance' | 'advanced';
|
||||
|
||||
/** 地面类型 */
|
||||
type GroundType = 'none' | 'concrete' | 'grass' | 'tile' | 'water' | 'wood';
|
||||
|
||||
/** 环境背景类型 */
|
||||
type EnvironmentType = 'default' | 'outdoor' | 'indoor' | 'night' | 'overcast' | 'studio';
|
||||
/** 设置项列表项(环境背景/地面类型由底层引擎动态返回) */
|
||||
type SettingListItem = { name: string; id: string };
|
||||
|
||||
// ======================== 通用样式常量 ========================
|
||||
// 全部使用项目主题 CSS 变量(--bim-*),同时提供浅色/深色皆可见的 fallback
|
||||
@@ -46,7 +43,7 @@ const HOVER_BG = 'var(--bim-component-bg-hover, rgba(0,0,0,0.04))';
|
||||
|
||||
/**
|
||||
* 设置弹窗管理器
|
||||
* 管理全局设置面板,包含渲染模式、边线、地面、对比度/饱和度、光照强度、环境背景等设置项
|
||||
* 管理全局设置面板,包含渲染模式、边线、光照/对比度/饱和度、环境背景、地面等设置项
|
||||
*/
|
||||
export class SettingDialogManager extends BaseDialogManager {
|
||||
protected get dialogId() { return 'setting-dialog'; }
|
||||
@@ -57,18 +54,26 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 边线开关状态 */
|
||||
private edgeLineEnabled: boolean = false;
|
||||
/** 当前选中的地面类型 */
|
||||
private groundType: GroundType = 'none';
|
||||
/** 地面大小 */
|
||||
private groundSize: number = 100;
|
||||
/** 对比度(0-200,100为默认值) */
|
||||
private contrastValue: number = 100;
|
||||
/** 饱和度(0-200,100为默认值) */
|
||||
private saturationValue: number = 100;
|
||||
/** 光照强度(0-200,100为默认值) */
|
||||
private lightIntensityValue: number = 100;
|
||||
/** 当前选中的环境背景 */
|
||||
private environmentType: EnvironmentType = 'default';
|
||||
/** 对比度(0-100,50为默认值) */
|
||||
private contrastValue: number = 50;
|
||||
/** 饱和度(0-100,50为默认值) */
|
||||
private saturationValue: number = 50;
|
||||
/** 光照强度(0-100,50为默认值) */
|
||||
private lightIntensityValue: number = 50;
|
||||
/** 环境背景列表(从底层引擎动态获取) */
|
||||
private environmentList: SettingListItem[] = [];
|
||||
/** 当前选中的环境背景 ID */
|
||||
private environmentId: string = '';
|
||||
/** 是否显示背景 */
|
||||
private backgroundVisible: boolean = true;
|
||||
/** 地面列表(从底层引擎动态获取) */
|
||||
private groundList: SettingListItem[] = [];
|
||||
/** 当前选中的地面 ID */
|
||||
private groundId: string = '';
|
||||
/** 地面高度(单位:m) */
|
||||
private groundElevation: number = 0;
|
||||
/** 保存的对话框位置(用于 hide/show 时保持位置不变) */
|
||||
private savedPosition: { x: number; y: number } | null = null;
|
||||
|
||||
constructor(registry: ManagerRegistry) {
|
||||
super(registry);
|
||||
@@ -76,8 +81,15 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
public init(): void { }
|
||||
|
||||
/** 对话框居中显示 */
|
||||
/** 对话框位置:若有保存的位置则恢复,否则居中 */
|
||||
protected getDialogPosition() {
|
||||
// 若上次 hide/show 保存了位置,则恢复到原位
|
||||
if (this.savedPosition) {
|
||||
const pos = this.savedPosition;
|
||||
this.savedPosition = null;
|
||||
return pos;
|
||||
}
|
||||
|
||||
const container = this.registry.container;
|
||||
if (!container) return { x: 100, y: 100 };
|
||||
|
||||
@@ -90,9 +102,39 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 从底层引擎加载当前状态(回显)
|
||||
* 在 createContent() 前调用,保证 UI 和引擎状态同步
|
||||
*/
|
||||
private loadEngineState(): void {
|
||||
const engine = this.engineComponent;
|
||||
if (!engine) return;
|
||||
|
||||
// 边线
|
||||
this.edgeLineEnabled = engine.getModelEdgeActive();
|
||||
|
||||
// 光照 / 对比度 / 饱和度
|
||||
this.lightIntensityValue = engine.getAmbientLightIntensity();
|
||||
this.contrastValue = engine.getSceneContrast();
|
||||
this.saturationValue = engine.getSceneSaturation();
|
||||
|
||||
// 环境背景
|
||||
this.environmentList = engine.getHDRBackgroundList();
|
||||
this.environmentId = engine.getHDRBackgroundId();
|
||||
this.backgroundVisible = engine.getHDRBackgroundVisibility();
|
||||
|
||||
// 地面
|
||||
this.groundList = engine.getGroundList();
|
||||
this.groundId = engine.getGroundId();
|
||||
this.groundElevation = engine.getGroundElevation();
|
||||
}
|
||||
|
||||
// ======================== 主内容构建 ========================
|
||||
|
||||
protected createContent(): HTMLElement {
|
||||
// 从引擎加载当前状态(回显)
|
||||
this.loadEngineState();
|
||||
|
||||
// 注入一次全局滑块样式
|
||||
this.ensureSliderStyle();
|
||||
|
||||
@@ -111,32 +153,41 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
content.appendChild(this.createSliderSection(
|
||||
t('setting.lightIntensity'),
|
||||
this.lightIntensityValue,
|
||||
0, 200,
|
||||
(v) => { this.lightIntensityValue = v; }
|
||||
0, 100,
|
||||
(v) => {
|
||||
this.lightIntensityValue = v;
|
||||
this.engineComponent?.setAmbientLightIntensity(v);
|
||||
}
|
||||
));
|
||||
|
||||
// 4. 对比度
|
||||
content.appendChild(this.createSliderSection(
|
||||
t('setting.contrast'),
|
||||
this.contrastValue,
|
||||
0, 200,
|
||||
(v) => { this.contrastValue = v; }
|
||||
0, 100,
|
||||
(v) => {
|
||||
this.contrastValue = v;
|
||||
this.engineComponent?.setSceneContrast(v);
|
||||
}
|
||||
));
|
||||
|
||||
// 5. 饱和度
|
||||
content.appendChild(this.createSliderSection(
|
||||
t('setting.saturation'),
|
||||
this.saturationValue,
|
||||
0, 200,
|
||||
(v) => { this.saturationValue = v; }
|
||||
0, 100,
|
||||
(v) => {
|
||||
this.saturationValue = v;
|
||||
this.engineComponent?.setSceneSaturation(v);
|
||||
}
|
||||
));
|
||||
content.appendChild(this.createDivider());
|
||||
|
||||
// 6. 环境背景(下拉)
|
||||
// 6. 环境背景(动态列表 + 显示开关)
|
||||
content.appendChild(this.createEnvironmentSection());
|
||||
content.appendChild(this.createDivider());
|
||||
|
||||
// 7. 显示地面(下拉 + 大小输入)
|
||||
// 7. 地面(动态列表 + 地面高度)
|
||||
content.appendChild(this.createGroundSection());
|
||||
|
||||
return content;
|
||||
@@ -155,7 +206,7 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 创建渲染模式按钮组 */
|
||||
private createRenderModeSection(): HTMLElement {
|
||||
const currentMode = (this.registry.engine3d?.getRenderMode() ?? 'balance') as RenderMode;
|
||||
const currentMode = (this.engineComponent?.getRenderMode() ?? 'balance') as RenderMode;
|
||||
|
||||
const section = document.createElement('div');
|
||||
|
||||
@@ -201,8 +252,12 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
});
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
this.registry.engine3d?.setRenderMode(mode.key);
|
||||
// 重建弹窗以刷新状态
|
||||
this.engineComponent?.setRenderMode(mode.key);
|
||||
// 保存当前对话框位置,重建后恢复
|
||||
const el = document.getElementById(this.dialogId);
|
||||
if (el) {
|
||||
this.savedPosition = { x: el.offsetLeft, y: el.offsetTop };
|
||||
}
|
||||
this.hide();
|
||||
this.show();
|
||||
});
|
||||
@@ -218,45 +273,18 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
/** 创建边线开关行 */
|
||||
private createEdgeLineSection(): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.style.cssText = ROW_STYLE;
|
||||
|
||||
// 标签
|
||||
const label = document.createElement('span');
|
||||
label.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
label.textContent = t('setting.edgeLine');
|
||||
row.appendChild(label);
|
||||
|
||||
// 开关容器 —— 关闭态使用 --bim-border-strong 保证浅色主题可见
|
||||
const toggleOffBg = 'var(--bim-border-strong, #cbd5e1)';
|
||||
const toggleOnBg = PRIMARY;
|
||||
|
||||
const toggle = document.createElement('div');
|
||||
toggle.style.cssText = `
|
||||
width: 40px; height: 22px; border-radius: 11px; cursor: pointer;
|
||||
position: relative; transition: background 0.2s;
|
||||
background: ${this.edgeLineEnabled ? toggleOnBg : toggleOffBg};
|
||||
`;
|
||||
|
||||
// 滑块圆点
|
||||
const knob = document.createElement('div');
|
||||
knob.style.cssText = `
|
||||
width: 18px; height: 18px; border-radius: 50%;
|
||||
background: #fff; position: absolute; top: 2px;
|
||||
transition: left 0.2s;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
||||
left: ${this.edgeLineEnabled ? '20px' : '2px'};
|
||||
`;
|
||||
toggle.appendChild(knob);
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
this.edgeLineEnabled = !this.edgeLineEnabled;
|
||||
toggle.style.background = this.edgeLineEnabled ? toggleOnBg : toggleOffBg;
|
||||
knob.style.left = this.edgeLineEnabled ? '20px' : '2px';
|
||||
});
|
||||
|
||||
row.appendChild(toggle);
|
||||
return row;
|
||||
return this.createToggleRow(
|
||||
t('setting.edgeLine'),
|
||||
this.edgeLineEnabled,
|
||||
(enabled) => {
|
||||
this.edgeLineEnabled = enabled;
|
||||
if (enabled) {
|
||||
this.engineComponent?.activeModelEdge();
|
||||
} else {
|
||||
this.engineComponent?.disActiveModelEdge();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// ======================== 3/4/5. 通用滑块 ========================
|
||||
@@ -351,98 +379,112 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// ======================== 6. 环境背景(下拉) ========================
|
||||
// ======================== 6. 环境背景(动态列表 + 显示开关) ========================
|
||||
|
||||
/** 创建环境背景下拉选择区 */
|
||||
/** 创建环境背景区 */
|
||||
private createEnvironmentSection(): HTMLElement {
|
||||
const section = document.createElement('div');
|
||||
|
||||
// 标签
|
||||
const label = document.createElement('div');
|
||||
label.style.cssText = SECTION_LABEL_STYLE;
|
||||
label.textContent = t('setting.environment');
|
||||
section.appendChild(label);
|
||||
|
||||
const envKeys: EnvironmentType[] = ['default', 'outdoor', 'indoor', 'night', 'overcast', 'studio'];
|
||||
|
||||
// 下拉列表(从底层引擎动态获取)
|
||||
const select = this.createSelect(
|
||||
envKeys.map(key => ({
|
||||
value: key,
|
||||
label: t(`setting.environments.${key}` as any)
|
||||
})),
|
||||
this.environmentType,
|
||||
(val) => { this.environmentType = val as EnvironmentType; }
|
||||
this.environmentList.map(item => ({ value: item.id, label: item.name })),
|
||||
this.environmentId,
|
||||
(val) => {
|
||||
this.environmentId = val;
|
||||
this.engineComponent?.setHDRBackgroundId(val);
|
||||
}
|
||||
);
|
||||
|
||||
section.appendChild(select);
|
||||
|
||||
// 显示背景开关
|
||||
const toggleRow = this.createToggleRow(
|
||||
t('setting.backgroundVisible'),
|
||||
this.backgroundVisible,
|
||||
(enabled) => {
|
||||
this.backgroundVisible = enabled;
|
||||
this.engineComponent?.setHDRBackgroundVisibility(enabled);
|
||||
}
|
||||
);
|
||||
toggleRow.style.marginTop = '10px';
|
||||
section.appendChild(toggleRow);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
// ======================== 7. 显示地面(下拉 + 大小) ========================
|
||||
// ======================== 7. 地面(动态列表 + 地面高度) ========================
|
||||
|
||||
/** 创建地面设置区 */
|
||||
private createGroundSection(): HTMLElement {
|
||||
const section = document.createElement('div');
|
||||
|
||||
// 地面类型标签 + 下拉
|
||||
// 标签
|
||||
const label = document.createElement('div');
|
||||
label.style.cssText = SECTION_LABEL_STYLE;
|
||||
label.textContent = t('setting.ground');
|
||||
section.appendChild(label);
|
||||
|
||||
const groundKeys: GroundType[] = ['none', 'concrete', 'grass', 'tile', 'water', 'wood'];
|
||||
|
||||
// 下拉列表(从底层引擎动态获取)
|
||||
const select = this.createSelect(
|
||||
groundKeys.map(key => ({
|
||||
value: key,
|
||||
label: t(`setting.groundTypes.${key}` as any)
|
||||
})),
|
||||
this.groundType,
|
||||
this.groundList.map(item => ({ value: item.id, label: item.name })),
|
||||
this.groundId,
|
||||
(val) => {
|
||||
this.groundType = val as GroundType;
|
||||
// 显示/隐藏大小输入
|
||||
sizeRow.style.display = val === 'none' ? 'none' : 'flex';
|
||||
this.groundId = val;
|
||||
this.engineComponent?.setGroundId(val);
|
||||
}
|
||||
);
|
||||
section.appendChild(select);
|
||||
|
||||
// 大小输入行
|
||||
const sizeRow = document.createElement('div');
|
||||
sizeRow.style.cssText = ROW_STYLE + ' margin-top: 10px;';
|
||||
sizeRow.style.display = this.groundType === 'none' ? 'none' : 'flex';
|
||||
// 地面高度输入行
|
||||
const elevationRow = document.createElement('div');
|
||||
elevationRow.style.cssText = ROW_STYLE + ' margin-top: 10px;';
|
||||
|
||||
const sizeLabel = document.createElement('span');
|
||||
sizeLabel.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
sizeLabel.textContent = t('setting.groundSize');
|
||||
sizeRow.appendChild(sizeLabel);
|
||||
const elevationLabel = document.createElement('span');
|
||||
elevationLabel.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
elevationLabel.textContent = t('setting.groundElevation');
|
||||
elevationRow.appendChild(elevationLabel);
|
||||
|
||||
const sizeInput = document.createElement('input');
|
||||
sizeInput.type = 'number';
|
||||
sizeInput.value = String(this.groundSize);
|
||||
sizeInput.min = '1';
|
||||
sizeInput.max = '10000';
|
||||
sizeInput.style.cssText = `
|
||||
width: 80px; padding: 4px 8px; border-radius: 4px;
|
||||
// 输入框 + 单位容器
|
||||
const inputWrapper = document.createElement('div');
|
||||
inputWrapper.style.cssText = 'display: flex; align-items: center; gap: 4px;';
|
||||
|
||||
const elevationInput = document.createElement('input');
|
||||
elevationInput.type = 'number';
|
||||
elevationInput.value = String(this.groundElevation);
|
||||
elevationInput.style.cssText = `
|
||||
width: 72px; padding: 4px 8px; border-radius: 4px;
|
||||
border: 1px solid ${BORDER_COLOR};
|
||||
background: ${INPUT_BG};
|
||||
color: ${TEXT_PRIMARY};
|
||||
font-size: 13px; outline: none; text-align: right;
|
||||
`;
|
||||
sizeInput.addEventListener('input', () => {
|
||||
const v = Number(sizeInput.value);
|
||||
if (!isNaN(v) && v > 0) {
|
||||
this.groundSize = v;
|
||||
elevationInput.addEventListener('input', () => {
|
||||
const v = Number(elevationInput.value);
|
||||
if (!isNaN(v)) {
|
||||
this.groundElevation = v;
|
||||
this.engineComponent?.setGroundElevation(v);
|
||||
}
|
||||
});
|
||||
// 聚焦时高亮边框
|
||||
sizeInput.addEventListener('focus', () => {
|
||||
sizeInput.style.borderColor = PRIMARY;
|
||||
elevationInput.addEventListener('focus', () => {
|
||||
elevationInput.style.borderColor = PRIMARY;
|
||||
});
|
||||
sizeInput.addEventListener('blur', () => {
|
||||
sizeInput.style.borderColor = BORDER_COLOR;
|
||||
elevationInput.addEventListener('blur', () => {
|
||||
elevationInput.style.borderColor = BORDER_COLOR;
|
||||
});
|
||||
|
||||
sizeRow.appendChild(sizeInput);
|
||||
section.appendChild(sizeRow);
|
||||
const unitLabel = document.createElement('span');
|
||||
unitLabel.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
unitLabel.textContent = t('setting.groundElevationUnit');
|
||||
|
||||
inputWrapper.appendChild(elevationInput);
|
||||
inputWrapper.appendChild(unitLabel);
|
||||
elevationRow.appendChild(inputWrapper);
|
||||
section.appendChild(elevationRow);
|
||||
|
||||
return section;
|
||||
}
|
||||
@@ -493,7 +535,7 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
onChange(select.value);
|
||||
});
|
||||
|
||||
// 聚焦高亮
|
||||
// 聚焦<EFBFBD><EFBFBD>亮
|
||||
select.addEventListener('focus', () => {
|
||||
select.style.borderColor = PRIMARY;
|
||||
});
|
||||
@@ -503,4 +545,61 @@ export class SettingDialogManager extends BaseDialogManager {
|
||||
|
||||
return select;
|
||||
}
|
||||
|
||||
// ======================== 通用开关行 ========================
|
||||
|
||||
/**
|
||||
* 创建一个标签 + 开关的行
|
||||
* @param labelText 标签文本
|
||||
* @param enabled 当前开关状态
|
||||
* @param onChange 状态变化回调
|
||||
*/
|
||||
private createToggleRow(
|
||||
labelText: string,
|
||||
enabled: boolean,
|
||||
onChange: (enabled: boolean) => void
|
||||
): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.style.cssText = ROW_STYLE;
|
||||
|
||||
// 标签
|
||||
const label = document.createElement('span');
|
||||
label.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #475569);';
|
||||
label.textContent = labelText;
|
||||
row.appendChild(label);
|
||||
|
||||
// 开关容器
|
||||
const toggleOffBg = 'var(--bim-border-strong, #cbd5e1)';
|
||||
const toggleOnBg = PRIMARY;
|
||||
|
||||
let currentState = enabled;
|
||||
|
||||
const toggle = document.createElement('div');
|
||||
toggle.style.cssText = `
|
||||
width: 40px; height: 22px; border-radius: 11px; cursor: pointer;
|
||||
position: relative; transition: background 0.2s;
|
||||
background: ${currentState ? toggleOnBg : toggleOffBg};
|
||||
`;
|
||||
|
||||
// 滑块圆点
|
||||
const knob = document.createElement('div');
|
||||
knob.style.cssText = `
|
||||
width: 18px; height: 18px; border-radius: 50%;
|
||||
background: #fff; position: absolute; top: 2px;
|
||||
transition: left 0.2s;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
||||
left: ${currentState ? '20px' : '2px'};
|
||||
`;
|
||||
toggle.appendChild(knob);
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
currentState = !currentState;
|
||||
toggle.style.background = currentState ? toggleOnBg : toggleOffBg;
|
||||
knob.style.left = currentState ? '20px' : '2px';
|
||||
onChange(currentState);
|
||||
});
|
||||
|
||||
row.appendChild(toggle);
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user