Files
bim_engine/demo/index.html
2026-03-16 16:13:36 +08:00

794 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>iFlow Engine Demo</title>
<!-- 从本地 lib 目录加载 SDK 文件 -->
<script src="./lib/iflow-engine.umd.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f5f5;
height: 100vh;
display: flex;
overflow: hidden;
/* 防止整个页面滚动 */
}
/* 左侧侧边栏:控制面板 */
.sidebar {
width: 320px;
background: white;
border-right: 1px solid #e0e0e0;
padding: 20px;
overflow-y: auto;
/* 内容过多时可滚动 */
display: flex;
flex-direction: column;
gap: 20px;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05);
z-index: 10;
}
.sidebar h1 {
font-size: 1.4rem;
color: #333;
margin-bottom: 10px;
}
.control-group {
background: #f9f9f9;
padding: 15px;
border-radius: 8px;
border: 1px solid #eee;
}
.control-group h2 {
font-size: 1rem;
color: #555;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 2px solid #e0e0e0;
}
.btn-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
button {
padding: 6px 12px;
font-size: 0.9rem;
border: 1px solid #ddd;
background: #fff;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
flex: 1 1 auto;
/* 按钮自动填充 */
}
button:hover {
background: #f0f0f0;
border-color: #ccc;
}
button.primary {
background: #0078d4;
color: white;
border-color: #0063b1;
}
button.primary:hover {
background: #0063b1;
}
/* 右侧主内容区:引擎展示 */
.main-content {
flex: 1;
position: relative;
background: #eef2f5;
display: flex;
justify-content: center;
align-items: center;
}
#app {
width: 100%;
height: 100%;
background: #fff;
position: relative;
}
</style>
</head>
<body>
<!-- 左侧控制面板 -->
<aside class="sidebar">
<h1>BIM SDK Demo</h1>
<!-- 1. 语言设置 -->
<div class="control-group">
<h2>🌍 语言 (Language)</h2>
<div class="btn-container">
<button class="primary" onclick="setLang('zh-CN')">中文</button>
<button class="primary" onclick="setLang('en-US')">English</button>
</div>
</div>
<!-- 2. 弹窗测试 -->
<div class="control-group">
<h2>🪟 弹窗 (Dialog)</h2>
<div class="btn-container">
<button onclick="openTestDialog()">测试弹窗</button>
<button onclick="openInfoDialog()">信息弹窗</button>
<button onclick="openRedDialog()">警告弹窗</button>
</div>
</div>
<!-- 3. 工具栏操作 -->
<div class="control-group">
<h2>🛠️ 工具栏 (Toolbar)</h2>
<div class="btn-container">
<button onclick="toggleToolbar()">显隐工具栏</button>
<button onclick="toggleLabel()">显隐标签</button>
<button onclick="toggleLocationBtn()">显隐定位按钮</button>
</div>
<div class="btn-container" style="margin-top: 8px;">
<button onclick="addCustomGroup()">加组</button>
<button onclick="addCustomButton()">加按钮</button>
</div>
<div class="btn-container" style="margin-top: 8px;">
<button onclick="setToolbarType('default')">默认样式</button>
<button onclick="setToolbarType('glass-pill')">胶囊样式</button>
</div>
</div>
<!-- 4. 样式主题 -->
<div class="control-group">
<h2>🎨 样式 (Theme)</h2>
<div class="btn-container">
<button onclick="setTheme('dark')">深色 (Dark)</button>
<button onclick="setTheme('light')">浅色 (Light)</button>
<button onclick="setCustomTheme()">自定义 (Red)</button>
</div>
</div>
<!-- 6. 功能面板 -->
<div class="control-group">
<h2>📑 功能面板 (Panels)</h2>
<div class="btn-container">
<button onclick="openPropertyPanel()">属性面板</button>
</div>
</div>
<!-- 7. 多Tab加载 -->
<div class="control-group">
<h2>📺 多Tab加载 (Multi-Tab)</h2>
<div class="btn-container">
<button class="primary" onclick="window.open('./multi-tab.html', '_blank')">打开多Tab视图</button>
</div>
<div style="margin-top: 8px; font-size: 0.8rem; color: #888;">
两个独立面板,各自加载模型,可拖拽分割线调整宽度
</div>
</div>
<!-- 8. 构件树数据 -->
<div class="control-group">
<h2>🌳 构件树 (Tree Data)</h2>
<div class="btn-container">
<button onclick="getLevelTree()">楼层树</button>
<button onclick="getTypeTree()">类型树</button>
<button onclick="getMajorTree()">专业树</button>
</div>
</div>
<!-- 5. 3D 引擎 -->
<div class="control-group">
<h2>🎮 3D 引擎 (Engine3D)</h2>
<div class="btn-container">
<button class="primary" onclick="initEngine3D()">初始化引擎</button>
<button class="primary" onclick="loadModel()">加载模型</button>
<button onclick="switchModel()">切换模型</button>
<button onclick="loadCombinedModel()">组合模型</button>
<button onclick="openCombineDialog()">自动组合</button>
</div>
<div class="btn-container" style="margin-top: 8px;">
<button onclick="pauseRendering()">暂停渲染</button>
<button onclick="resumeRendering()">恢复渲染</button>
</div>
<div style="margin-top: 10px; font-size: 0.85rem; color: #666;">
<div>状态: <span id="engine-status">未初始化</span></div>
</div>
</div>
<!-- 2D 图纸 -->
<div class="control-group">
<h2>📐 2D 图纸 (Engine2D)</h2>
<div class="btn-container">
<button class="primary" id="btn-init2d" onclick="initEngine2d()">初始化 2D 引擎</button>
<button id="btn-load2d" onclick="loadDrawing()" disabled>加载图纸</button>
</div>
<div style="margin-top: 8px;">
<input type="text" id="url-input-2d"
style="width:100%;padding:6px 8px;font-size:0.85rem;border:1px solid #ddd;border-radius:4px;font-family:Consolas,Monaco,monospace;outline:none;"
placeholder="输入新的图纸 URL" />
</div>
<div class="btn-container" style="margin-top: 6px;">
<button class="primary" id="btn-switch2d" onclick="switchDrawing()" disabled>切换图纸</button>
</div>
<div style="margin-top: 6px; font-size: 0.85rem; color: #666;">
<div>状态: <span id="engine2d-status">未初始化</span></div>
</div>
</div>
<!-- 720 全景 -->
<div class="control-group">
<h2>🎥 720 全景 (Engine720)</h2>
<div class="btn-container">
<button class="primary" id="btn-init720" onclick="initEngine720()">初始化 720 引擎</button>
<button id="btn-load720" onclick="loadPanorama()" disabled>加载全景图</button>
</div>
<div style="margin-top: 6px; font-size: 0.85rem; color: #666;">
<div>状态: <span id="engine720-status">未初始化</span></div>
</div>
</div>
</aside>
<!-- 右侧主区域 -->
<main class="main-content">
<div id="app"></div>
</main>
<!-- 自动组合弹窗 -->
<div id="combine-overlay"
style="display:none; position:fixed; inset:0; background:rgba(0,0,0,.45); z-index:9999; display:none; justify-content:center; align-items:center;">
<div
style="background:#fff; border-radius:10px; padding:24px; width:520px; max-width:90vw; box-shadow:0 8px 32px rgba(0,0,0,.18);">
<h3 style="margin:0 0 12px; font-size:1.1rem; color:#333;">自动组合加载</h3>
<p style="margin:0 0 10px; font-size:.85rem; color:#888;">请输入模型 URL每行一个或用英文逗号分隔</p>
<textarea id="combine-urls" rows="6"
style="width:100%; padding:10px; font-size:.85rem; border:1px solid #ddd; border-radius:6px; resize:vertical; font-family:monospace;"
placeholder="https://example.com/model1/&#10;https://example.com/model2/"></textarea>
<div style="display:flex; justify-content:flex-end; gap:8px; margin-top:14px;">
<button onclick="closeCombineDialog()" style="min-width:72px;">取消</button>
<button class="primary" onclick="confirmCombineLoad()" style="min-width:72px;">确定加载</button>
</div>
</div>
</div>
<script>
let engine = null; // BimEngine (3D) 实例
let engine2d = null; // BimEngine2d 实例
let engine720 = null; // BimEngine720 实例
let isToolbarVisible = true;
let isLabelVisible = true;
let isLocationVisible = true;
let customGroupAdded = false;
/**
* 销毁所有引擎实例
*/
function destroyAllEngines() {
if (engine) { engine.destroy(); engine = null; }
if (engine2d) { engine2d.destroy(); engine2d = null; }
if (engine720) { engine720.destroy(); engine720 = null; }
}
// 页面加载完成后默认初始化 3D 引擎
window.onload = () => {
if (window.IflowEngine) {
try {
initEngine3D();
} catch (err) {
console.error('Init failed:', err);
}
} else {
console.error('SDK not found');
}
};
// --- 语言设置 ---
function setLang(lang) {
if (engine) engine.setLocale(lang);
}
// --- 弹窗测试 ---
function openTestDialog() {
if (!engine || !engine.dialog) return;
engine.dialog.create({
title: 'dialog.testTitle',
width: 350,
height: 200,
position: 'center',
draggable: true,
resizable: true
});
}
function openInfoDialog() {
if (!engine || !engine.dialog) return;
engine.dialog.showInfoDialog();
}
function openRedDialog() {
if (!engine || !engine.dialog) return;
engine.dialog.create({
title: 'Alert',
content: '<div style="color: #ffcccc;">Critical Warning!</div>',
width: 300,
height: 150,
backgroundColor: 'rgba(100, 0, 0, 0.95)',
headerBackgroundColor: '#cc0000',
titleColor: '#ffffff',
borderColor: '#ff6666',
position: { x: 50, y: 50 }
});
}
// --- 工具栏操作 ---
function toggleToolbar() {
if (!engine || !engine.toolbar) return;
isToolbarVisible = !isToolbarVisible;
engine.toolbar.setVisible(isToolbarVisible);
}
function toggleLabel() {
if (!engine || !engine.toolbar) return;
isLabelVisible = !isLabelVisible;
engine.toolbar.setShowLabel(isLabelVisible);
}
function toggleLocationBtn() {
if (!engine || !engine.toolbar) return;
isLocationVisible = !isLocationVisible;
engine.toolbar.setButtonVisibility('location', isLocationVisible);
}
function addCustomGroup() {
if (!engine || !engine.toolbar) return;
if (customGroupAdded) {
alert('已添加过');
return;
}
engine.toolbar.addGroup('custom-group', 'group-1');
customGroupAdded = true;
}
function addCustomButton() {
if (!engine || !engine.toolbar) return;
if (!customGroupAdded) {
alert('Please add custom group first / 请先添加自定义组');
return;
}
var btnId = 'custom-btn-' + Date.now();
engine.toolbar.addButton({
id: btnId,
groupId: 'custom-group',
type: 'button',
label: 'New',
icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><path d="M8 14s1.5 2 4 2 4-2 4-2"></path><line x1="9" y1="9" x2="9.01" y2="9"></line><line x1="15" y1="9" x2="15.01" y2="9"></line></svg>',
onClick: function (btn) {
alert('Clicked: ' + btn.label);
}
});
}
function setToolbarType(type) {
if (!engine || !engine.toolbar) return;
engine.toolbar.setType(type);
console.log('Toolbar type changed to:', type);
}
// --- 主题操作 ---
function setTheme(themeName) {
if (engine) engine.setTheme(themeName);
if (engine2d) engine2d.setTheme(themeName);
if (engine720) engine720.setTheme(themeName);
}
function setCustomTheme() {
if (!engine) return;
engine.setCustomTheme({
name: 'red-alert',
primary: '#d32f2f',
primaryHover: '#b71c1c',
background: '#ffebee',
panelBackground: 'rgba(255, 255, 255, 0.9)',
textPrimary: '#b71c1c',
textSecondary: '#e57373',
border: '#ffcdd2',
icon: '#d32f2f',
iconActive: '#b71c1c',
componentBackground: 'rgba(255, 205, 210, 0.3)',
componentHover: 'rgba(255, 205, 210, 0.8)',
componentActive: '#e57373'
});
}
// ====== 3D 引擎操作 ======
/**
* 初始化 3D 引擎(独立实例,销毁其他引擎类型)
*/
function initEngine3D() {
destroyAllEngines();
updateEngineStatus('初始化中...');
update2dStatus('未初始化');
update720Status('未初始化');
document.getElementById('btn-load2d').disabled = true;
document.getElementById('btn-load720').disabled = true;
try {
engine = new IflowEngine.BimEngine('app', { locale: 'zh-CN' });
var success = engine.engine.initialize({
backgroundColor: 0x333333,
version: 'v2',
showStats: false,
showViewCube: true
});
if (success) {
updateEngineStatus('已初始化');
console.log('✅ 3D 引擎初始化成功');
loadModel();
} else {
updateEngineStatus('初始化失败');
console.error('❌ 3D 引擎初始化失败');
}
} catch (error) {
updateEngineStatus('初始化错误');
console.error('❌ 3D 引擎初始化错误:', error);
}
}
function loadModel() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
try {
var modelUrl = 'https://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/iflow/models/417664a3-76c8-4d94-9344-1337246a5d4e/';
engine.engine.loadModel([modelUrl], {
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1]
});
console.log('✅ 模型加载请求已发送:', modelUrl);
} catch (error) {
console.error('❌ 模型加载错误:', error);
}
}
function loadCombinedModel() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
try {
var modelUrls = [
'https://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/iflow/models/974feae2-4be3-4e85-9d5e-ea655b0c890d/',
'https://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/iflow/models/6129815d-9ae4-4414-a9ab-e7c3ee1b2584/',
'https://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/iflow/models/8ddf95f4-28cc-4218-94c3-165e69fa51b1/'
];
engine.engine.loadModel(modelUrls, {
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1]
});
console.log('✅ 组合模型加载请求已发送:', modelUrls);
} catch (error) {
console.error('❌ 组合模型加载错误:', error);
}
}
function pauseRendering() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
engine.engine.pauseRendering();
console.log('✅ 渲染已暂停');
}
function resumeRendering() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
engine.engine.resumeRendering();
console.log('✅ 渲染已恢复');
}
function switchModel() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
var newUrl = prompt('请输入新的模型 URL:', 'https://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/iflow/models/8634e556-a94e-4ba7-be3e-2ea1507cced5/');
if (!newUrl || newUrl.trim() === '') return;
try {
engine.engine.loadModel([newUrl.trim()], {
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1]
});
console.log('✅ 切换模型请求已发送:', newUrl);
} catch (error) {
console.error('❌ 切换模型错误:', error);
}
}
function openPropertyPanel() {
if (!engine || !engine.propertyPanel) {
console.error('Property panel not available');
return;
}
engine.propertyPanel.show();
}
function getLevelTree() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎并加载模型!');
return;
}
var data = engine.engine.getLevelTreeData();
console.log('🌳 楼层树数据:', data);
}
function getTypeTree() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎并加载模型!');
return;
}
var data = engine.engine.getTypeTreeData();
console.log('🌳 类型树数据:', data);
}
function getMajorTree() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎并加载模型!');
return;
}
var data = engine.engine.getMajorTreeData();
console.log('🌳 专业树数据:', data);
}
function updateEngineStatus(status) {
var statusEl = document.getElementById('engine-status');
if (!statusEl) return;
statusEl.textContent = status;
if (status === '已初始化') {
statusEl.style.color = '#28a745';
} else if (status.indexOf('失败') !== -1 || status.indexOf('错误') !== -1) {
statusEl.style.color = '#dc3545';
} else {
statusEl.style.color = '#666';
}
}
// --- 自动组合加载 ---
function openCombineDialog() {
if (!engine || !engine.engine || !engine.engine.isInitialized()) {
alert('请先初始化 3D 引擎!');
return;
}
document.getElementById('combine-overlay').style.display = 'flex';
}
function closeCombineDialog() {
document.getElementById('combine-overlay').style.display = 'none';
}
function confirmCombineLoad() {
var raw = document.getElementById('combine-urls').value.trim();
if (!raw) { alert('请输入至少一个模型 URL'); return; }
raw = raw
.replace(/[\u2018\u2019]/g, "'")
.replace(/[\u201C\u201D]/g, '"')
.replace(/[\u200B\uFEFF\u00A0]/g, '')
.trim();
var urls = [];
try {
var jsonStr = raw.replace(/'/g, '"');
var parsed = JSON.parse(jsonStr);
if (Array.isArray(parsed)) {
urls = parsed.map(function (s) { return String(s).trim(); }).filter(Boolean);
}
} catch (e) {
urls = raw.split(/[,;\n\r]+/)
.map(function (s) { return s.trim().replace(/^['"|\[\]\s]+|['"|\[\]\s]+$/g, ''); })
.filter(Boolean);
}
urls = urls.filter(function (s) { return /^https?:\/\//i.test(s); });
if (urls.length === 0) { alert('未解析到有效的 URL'); return; }
for (var i = 0; i < urls.length; i++) {
try { new URL(urls[i]); } catch (e) {
alert('第 ' + (i + 1) + ' 个 URL 无效:\n' + urls[i]);
return;
}
}
try {
engine.engine.loadModel(urls, { position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] });
console.log('✅ 自动组合加载已发送:', urls);
closeCombineDialog();
document.getElementById('combine-urls').value = '';
} catch (error) {
console.error('❌ 自动组合加载错误:', error);
}
}
// ====== 2D 图纸操作 ======
/**
* 初始化 2D 引擎(独立实例,销毁其他引擎类型)
*/
function initEngine2d() {
destroyAllEngines();
updateEngineStatus('未初始化');
update2dStatus('初始化中...');
update720Status('未初始化');
document.getElementById('btn-load720').disabled = true;
try {
engine2d = new IflowEngine.BimEngine2d('app', {
backgroundColor: 0x1a1a1a,
gridEnabled: true,
axesEnabled: true,
enablePerformanceMonitoring: true
});
update2dStatus('已初始化');
document.getElementById('btn-load2d').disabled = false;
document.getElementById('btn-switch2d').disabled = false;
console.log('✅ 2D 引擎初始化成功');
loadDrawing();
} catch (error) {
update2dStatus('初始化错误');
console.error('❌ 2D 引擎初始化错误:', error);
}
}
function loadDrawing() {
if (!engine2d) {
alert('请先初始化 2D 引擎!');
return;
}
var url = 'https://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/iflow/models/cc389dc8-c6d5-43db-81f1-6998d4eee8f6/312';
update2dStatus('正在加载...');
try {
engine2d.loadDrawing(url).then(function () {
update2dStatus('图纸已加载');
console.log('✅ 2D 图纸加载完成');
}).catch(function (err) {
update2dStatus('加载失败');
console.error('❌ 2D 图纸加载失败:', err);
});
} catch (error) {
update2dStatus('加载错误');
console.error('❌ 2D 图纸加载错误:', error);
}
}
function switchDrawing() {
var urlInput = document.getElementById('url-input-2d');
var newUrl = urlInput.value.trim();
if (!newUrl) {
alert('请输入图纸 URL');
return;
}
if (!engine2d) {
alert('请先初始化 2D 引擎!');
return;
}
update2dStatus('正在切换...');
try {
engine2d.loadDrawing(newUrl).then(function () {
update2dStatus('图纸已加载');
console.log('✅ 2D 图纸切换完成: ' + newUrl);
}).catch(function (err) {
update2dStatus('切换失败');
console.error('❌ 2D 图纸切换失败:', err);
});
} catch (error) {
update2dStatus('切换错误');
console.error('❌ 2D 图纸切换错误:', error);
}
}
function update2dStatus(status) {
var el = document.getElementById('engine2d-status');
if (!el) return;
el.textContent = status;
if (status === '已初始化' || status === '图纸已加载') {
el.style.color = '#28a745';
} else if (status === '正在加载...' || status === '初始化中...') {
el.style.color = '#1565c0';
} else if (status.indexOf('失败') !== -1 || status.indexOf('错误') !== -1) {
el.style.color = '#dc3545';
} else {
el.style.color = '#666';
}
}
// ====== 720 全景操作 ======
/**
* 初始化 720 引擎(独立实例,销毁其他引擎类型)
*/
function initEngine720() {
destroyAllEngines();
updateEngineStatus('未初始化');
update2dStatus('未初始化');
update720Status('初始化中...');
document.getElementById('btn-load2d').disabled = true;
try {
engine720 = new IflowEngine.BimEngine720('app', {
fov: 75,
enableZoom: true,
enableRotate: true,
sphereRadius: 500
});
update720Status('已初始化');
document.getElementById('btn-load720').disabled = false;
console.log('✅ 720 引擎初始化成功');
loadPanorama();
} catch (error) {
update720Status('初始化错误');
console.error('❌ 720 引擎初始化错误:', error);
}
}
function loadPanorama() {
if (!engine720) {
alert('请先初始化 720 引擎!');
return;
}
var url = 'http://lyz-1259524260.cos.ap-guangzhou.myqcloud.com/qcloud/2026/03/10/8ed54edd7e2740c7b6a3f534dd672d12.png';
update720Status('正在加载...');
try {
engine720.loadPanorama(url).then(function () {
update720Status('全景图已加载');
console.log('✅ 720 全景图加载完成');
}).catch(function (err) {
update720Status('加载失败');
console.error('❌ 720 全景图加载失败:', err);
});
} catch (error) {
update720Status('加载错误');
console.error('❌ 720 全景图加载错误:', error);
}
}
function update720Status(status) {
var el = document.getElementById('engine720-status');
if (!el) return;
el.textContent = status;
if (status === '已初始化' || status === '全景图已加载') {
el.style.color = '#28a745';
} else if (status === '正在加载...' || status === '初始化中...') {
el.style.color = '#1565c0';
} else if (status.indexOf('失败') !== -1 || status.indexOf('错误') !== -1) {
el.style.color = '#dc3545';
} else {
el.style.color = '#666';
}
}
</script>
</body>
</html>