feat: add settings dialog with render mode selection

- Add SettingDialogManager with radio-style render mode picker (simple/balance/advanced)
- Add getRenderMode/setRenderMode API to Engine and EngineManager layers
- Wire setting toolbar button to toggle the settings dialog
- Add i18n keys for settings dialog (zh-CN/en-US)
- Add version display at bottom-right of engine wrapper
- Bump version to 1.1.7, rebuild and sync demo libs
This commit is contained in:
yuding
2026-02-28 11:26:59 +08:00
parent a9c8317b10
commit 837177f3f2
15 changed files with 875 additions and 525 deletions

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

@@ -1,6 +1,6 @@
{ {
"name": "iflow-engine", "name": "iflow-engine",
"version": "1.1.6", "version": "1.1.7",
"description": "iFlow Engine SDK for Vue2, Vue3, React and HTML", "description": "iFlow Engine SDK for Vue2, Vue3, React and HTML",
"main": "./dist/iflow-engine.umd.js", "main": "./dist/iflow-engine.umd.js",
"module": "./dist/iflow-engine.es.js", "module": "./dist/iflow-engine.es.js",

View File

@@ -22,3 +22,17 @@
left: 20px !important; left: 20px !important;
z-index: 100; z-index: 100;
} }
.bim-engine-version {
position: absolute;
bottom: 6px;
right: 10px;
font-size: 11px;
color: rgba(0, 0, 0, 0.25);
pointer-events: none;
user-select: none;
z-index: 1;
font-family: system-ui, -apple-system, sans-serif;
letter-spacing: 0.3px;
}

View File

@@ -1,3 +1,4 @@
declare const __APP_VERSION__: string;
import './bim-engine.css'; import './bim-engine.css';
import { ToolbarManager } from './managers/toolbar-manager'; import { ToolbarManager } from './managers/toolbar-manager';
import { ButtonGroupManager } from './managers/button-group-manager'; import { ButtonGroupManager } from './managers/button-group-manager';
@@ -12,6 +13,7 @@ import { SectionAxisDialogManager } from './managers/section-axis-dialog-manager
import { SectionBoxDialogManager } from './managers/section-box-dialog-manager'; import { SectionBoxDialogManager } from './managers/section-box-dialog-manager';
import { WalkControlManager } from './managers/walk-control-manager'; import { WalkControlManager } from './managers/walk-control-manager';
import { EngineInfoDialogManager } from './managers/engine-info-dialog-manager'; import { EngineInfoDialogManager } from './managers/engine-info-dialog-manager';
import { SettingDialogManager } from './managers/setting-dialog-manager';
import { ComponentDetailManager } from './managers/component-detail-manager'; import { ComponentDetailManager } from './managers/component-detail-manager';
import { AiChatManager } from './managers/ai-chat-manager'; import { AiChatManager } from './managers/ai-chat-manager';
import type { EngineOptions, ModelLoadOptions } from './components/engine'; import type { EngineOptions, ModelLoadOptions } from './components/engine';
@@ -44,6 +46,7 @@ export class BimEngine {
public engineInfo: EngineInfoDialogManager | null = null; public engineInfo: EngineInfoDialogManager | null = null;
public componentDetail: ComponentDetailManager | null = null; public componentDetail: ComponentDetailManager | null = null;
public aiChat: AiChatManager | null = null; public aiChat: AiChatManager | null = null;
public setting: SettingDialogManager | null = null;
constructor( constructor(
container: HTMLElement | string, container: HTMLElement | string,
@@ -100,6 +103,11 @@ export class BimEngine {
this.wrapper.className = 'bim-engine-wrapper'; this.wrapper.className = 'bim-engine-wrapper';
this.container.appendChild(this.wrapper); this.container.appendChild(this.wrapper);
const versionEl = document.createElement('div');
versionEl.className = 'bim-engine-version';
versionEl.textContent = `v${__APP_VERSION__}`;
this.wrapper.appendChild(versionEl);
this.registry.container = this.container; this.registry.container = this.container;
this.registry.wrapper = this.wrapper; this.registry.wrapper = this.wrapper;
@@ -141,6 +149,10 @@ export class BimEngine {
this.registry.aiChat = this.aiChat; this.registry.aiChat = this.aiChat;
this.aiChat.init(); this.aiChat.init();
this.setting = new SettingDialogManager(this.registry);
this.registry.setting = this.setting;
this.setting.init();
this.updateTheme(themeManager.getTheme()); this.updateTheme(themeManager.getTheme());
themeManager.subscribe((theme) => { themeManager.subscribe((theme) => {
this.updateTheme(theme); this.updateTheme(theme);
@@ -166,6 +178,7 @@ export class BimEngine {
this.sectionBox?.destroy(); this.sectionBox?.destroy();
this.walkControl?.destroy(); this.walkControl?.destroy();
this.aiChat?.destroy(); this.aiChat?.destroy();
this.setting?.destroy();
this.container.innerHTML = ''; this.container.innerHTML = '';
this.registry.reset(); this.registry.reset();
} }

View File

@@ -2,7 +2,7 @@ import type { ButtonConfig } from '../../../index.type';
import { getIcon } from '../../../../../utils/icon-manager'; import { getIcon } from '../../../../../utils/icon-manager';
import type { ManagerRegistry } from '../../../../../core/manager-registry'; import type { ManagerRegistry } from '../../../../../core/manager-registry';
export const createSettingButton = (_registry?: ManagerRegistry): ButtonConfig => { export const createSettingButton = (registry?: ManagerRegistry): ButtonConfig => {
return { return {
id: 'setting', id: 'setting',
groupId: 'group-2', groupId: 'group-2',
@@ -10,8 +10,8 @@ export const createSettingButton = (_registry?: ManagerRegistry): ButtonConfig =
label: 'toolbar.setting', label: 'toolbar.setting',
icon: getIcon('设置'), icon: getIcon('设置'),
keepActive: false, keepActive: false,
onClick: (button) => { onClick: (_button) => {
console.log('设置按钮被点击:', button.id); registry?.setting?.toggle();
} }
}; };
}; };

View File

@@ -557,6 +557,33 @@ export class Engine implements IBimComponent {
// ==================== 结束:剖切功能 ==================== // ==================== 结束:剖切功能 ====================
// ==================== 渲染模式 ====================
/**
* 获取当前渲染模式
* @returns 'simple' | 'balance' | 'advanced'
*/
public getRenderMode(): string {
if (!this._isInitialized || !this.engine?.engineModelModule) {
return 'balance';
}
return this.engine.engineModelModule.getCurrentMode();
}
/**
* 设置渲染模式
* @param mode 'simple'(性能模式) | 'balance'(平衡模式) | 'advanced'(效果模式)
*/
public setRenderMode(mode: 'simple' | 'balance' | 'advanced'): void {
if (!this._isInitialized || !this.engine?.engineModelModule) {
console.warn('[Engine] Cannot set render mode: engine not initialized.');
return;
}
this.engine.engineModelModule.setMode(mode);
}
// ==================== 结束:渲染模式 ====================
// ==================== 漫游功能 ==================== // ==================== 漫游功能 ====================
/** 漫游模式是否激活 */ /** 漫游模式是否激活 */

View File

@@ -20,6 +20,7 @@ import type { SectionBoxDialogManager } from '../managers/section-box-dialog-man
import type { WalkPathDialogManager } from '../managers/walk-path-dialog-manager'; import type { WalkPathDialogManager } from '../managers/walk-path-dialog-manager';
import type { WalkPlanViewDialogManager } from '../managers/walk-plan-view-dialog-manager'; import type { WalkPlanViewDialogManager } from '../managers/walk-plan-view-dialog-manager';
import type { EngineInfoDialogManager } from '../managers/engine-info-dialog-manager'; import type { EngineInfoDialogManager } from '../managers/engine-info-dialog-manager';
import type { SettingDialogManager } from '../managers/setting-dialog-manager';
import type { ComponentDetailManager } from '../managers/component-detail-manager'; import type { ComponentDetailManager } from '../managers/component-detail-manager';
import type { AiChatManager } from '../managers/ai-chat-manager'; import type { AiChatManager } from '../managers/ai-chat-manager';
@@ -69,6 +70,8 @@ export class ManagerRegistry {
public componentDetail: ComponentDetailManager | null = null; public componentDetail: ComponentDetailManager | null = null;
/** AI 聊天管理器 */ /** AI 聊天管理器 */
public aiChat: AiChatManager | null = null; public aiChat: AiChatManager | null = null;
/** 设置对话框管理器 */
public setting: SettingDialogManager | null = null;
constructor() {} constructor() {}
@@ -93,6 +96,7 @@ export class ManagerRegistry {
this.engineInfo = null; this.engineInfo = null;
this.componentDetail = null; this.componentDetail = null;
this.aiChat = null; this.aiChat = null;
this.setting = null;
} }
/** /**

View File

@@ -209,5 +209,14 @@ export const enUS: TranslationDictionary = {
other: 'Other', other: 'Other',
otherPlaceholder: 'Enter custom answer', otherPlaceholder: 'Enter custom answer',
submit: 'Submit' submit: 'Submit'
},
setting: {
dialogTitle: 'Settings',
renderMode: 'Render Mode',
modes: {
simple: 'Performance',
balance: 'Balanced',
advanced: 'Quality',
}
} }
}; };

View File

@@ -231,6 +231,15 @@ export interface TranslationDictionary {
otherPlaceholder: string; otherPlaceholder: string;
submit: string; submit: string;
}; };
setting: {
dialogTitle: string;
renderMode: string;
modes: {
simple: string;
balance: string;
advanced: string;
};
};
} }
/** /**

View File

@@ -209,5 +209,14 @@ export const zhCN: TranslationDictionary = {
other: '其他', other: '其他',
otherPlaceholder: '请输入自定义答案', otherPlaceholder: '请输入自定义答案',
submit: '提交' submit: '提交'
},
setting: {
dialogTitle: '设置',
renderMode: '渲染模式',
modes: {
simple: '性能模式',
balance: '平衡模式',
advanced: '效果模式',
}
} }
}; };

View File

@@ -685,6 +685,26 @@ export class EngineManager extends BaseManager {
// ==================== 结束:构件操作 ==================== // ==================== 结束:构件操作 ====================
// ==================== 渲染模式 ====================
/**
* 获取当前渲染模式
* @returns 'simple' | 'balance' | 'advanced'
*/
public getRenderMode(): string {
return this.engineInstance?.getRenderMode() ?? 'balance';
}
/**
* 设置渲染模式
* @param mode 'simple' | 'balance' | 'advanced'
*/
public setRenderMode(mode: 'simple' | 'balance' | 'advanced'): void {
this.engineInstance?.setRenderMode(mode);
}
// ==================== 结束:渲染模式 ====================
/** 销毁引擎管理器 */ /** 销毁引擎管理器 */
public destroy(): void { public destroy(): void {
if (this.engineInstance) { if (this.engineInstance) {

View File

@@ -0,0 +1,107 @@
import { BaseDialogManager } from '../core/base-dialog-manager';
import { ManagerRegistry } from '../core/manager-registry';
import { t } from '../services/locale';
type RenderMode = 'simple' | 'balance' | 'advanced';
export class SettingDialogManager extends BaseDialogManager {
protected get dialogId() { return 'setting-dialog'; }
protected get dialogTitle() { return 'setting.dialogTitle'; }
protected get dialogWidth() { return 280; }
constructor(registry: ManagerRegistry) {
super(registry);
}
public init(): void {}
protected getDialogPosition() {
const container = this.registry.container;
if (!container) return { x: 100, y: 100 };
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
return {
x: (containerWidth - this.dialogWidth) / 2,
y: (containerHeight - 200) / 2
};
}
protected createContent(): HTMLElement {
const currentMode = this.registry.engine3d?.getRenderMode() ?? 'balance';
const content = document.createElement('div');
content.style.cssText = 'padding: 16px;';
// 渲染模式标题
const label = document.createElement('div');
label.style.cssText = 'font-size: 13px; color: var(--bim-text-secondary, #94a3b8); margin-bottom: 12px;';
label.textContent = t('setting.renderMode');
content.appendChild(label);
// 三个模式选项
const modes: { key: RenderMode; labelKey: string }[] = [
{ key: 'simple', labelKey: 'setting.modes.simple' },
{ key: 'balance', labelKey: 'setting.modes.balance' },
{ key: 'advanced', labelKey: 'setting.modes.advanced' },
];
const group = document.createElement('div');
group.style.cssText = 'display: flex; flex-direction: column; gap: 4px;';
for (const mode of modes) {
const item = document.createElement('div');
item.style.cssText = `
display: flex; align-items: center; gap: 10px;
padding: 10px 12px; border-radius: 6px; cursor: pointer;
transition: background 0.15s;
background: ${mode.key === currentMode ? 'var(--bim-primary, #3b82f6)' : 'transparent'};
color: ${mode.key === currentMode ? 'var(--bim-text-inverse, #fff)' : 'var(--bim-text-primary, #fff)'};
`;
// 圆点指示器
const dot = document.createElement('div');
dot.style.cssText = `
width: 16px; height: 16px; border-radius: 50%; flex-shrink: 0;
border: 2px solid ${mode.key === currentMode ? 'var(--bim-text-inverse, #fff)' : 'var(--bim-text-tertiary, #64748b)'};
display: flex; align-items: center; justify-content: center;
`;
if (mode.key === currentMode) {
const inner = document.createElement('div');
inner.style.cssText = 'width: 8px; height: 8px; border-radius: 50%; background: var(--bim-text-inverse, #fff);';
dot.appendChild(inner);
}
const text = document.createElement('span');
text.style.cssText = 'font-size: 14px; font-weight: 500;';
text.textContent = t(mode.labelKey);
item.appendChild(dot);
item.appendChild(text);
item.addEventListener('mouseenter', () => {
if (mode.key !== currentMode) {
item.style.background = 'var(--bim-floating-btn-bg, rgba(255,255,255,0.08))';
}
});
item.addEventListener('mouseleave', () => {
if (mode.key !== currentMode) {
item.style.background = 'transparent';
}
});
item.addEventListener('click', () => {
this.registry.engine3d?.setRenderMode(mode.key);
// 重新渲染弹窗内容
this.hide();
this.show();
});
group.appendChild(item);
}
content.appendChild(group);
return content;
}
}

View File

@@ -2,6 +2,7 @@ import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts'; import dts from 'vite-plugin-dts';
import { resolve } from 'path'; import { resolve } from 'path';
import cssInjectedByJs from 'vite-plugin-css-injected-by-js'; import cssInjectedByJs from 'vite-plugin-css-injected-by-js';
import pkg from './package.json';
export default defineConfig(() => { export default defineConfig(() => {
return { return {
@@ -25,6 +26,9 @@ export default defineConfig(() => {
open: '/demo/index.html', open: '/demo/index.html',
allowedHosts: true, allowedHosts: true,
}, },
define: {
__APP_VERSION__: JSON.stringify(pkg.version),
},
build: { build: {
lib: { lib: {
entry: resolve(__dirname, 'src/index.ts'), entry: resolve(__dirname, 'src/index.ts'),