添加折叠面板
This commit is contained in:
4
.cursor/rules/bim/RULE.md
Normal file
4
.cursor/rules/bim/RULE.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
你是一个资深的前端工程师,我这个项目需要你每次都看下AI_COLLABORATION.md,文件里面有项目的所有信息
|
||||||
57
.recycle/2025-12-15/src/managers/tree-manager.ts
Normal file
57
.recycle/2025-12-15/src/managers/tree-manager.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { BimComponent } from '../core/component';
|
||||||
|
import { BimTree } from '../components/tree/index';
|
||||||
|
import { TreeOptions } from '../components/tree/types';
|
||||||
|
import type { BimEngine } from '../bim-engine';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树组件管理器
|
||||||
|
* 负责创建和管理 BimTree 实例
|
||||||
|
*/
|
||||||
|
export class TreeManager extends BimComponent {
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
constructor(engine: BimEngine, _container: HTMLElement) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个新的树组件实例
|
||||||
|
* @param options 配置选项
|
||||||
|
*/
|
||||||
|
public create(options: TreeOptions): BimTree {
|
||||||
|
const tree = new BimTree(options);
|
||||||
|
|
||||||
|
// 绑定事件桥接
|
||||||
|
tree.onNodeCheck = (node) => {
|
||||||
|
this.emit('ui:tree-node-check', {
|
||||||
|
id: node.config.id,
|
||||||
|
checked: node.config.checked || false,
|
||||||
|
node: node.config
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.onNodeSelect = (node) => {
|
||||||
|
this.emit('ui:tree-node-select', {
|
||||||
|
id: node.config.id,
|
||||||
|
selected: true,
|
||||||
|
node: node.config
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.onNodeExpand = (node) => {
|
||||||
|
this.emit('ui:tree-node-expand', {
|
||||||
|
id: node.config.id,
|
||||||
|
expanded: node.config.expanded || false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.init();
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
// TreeManager 本身不持有 Tree 实例的强引用列表
|
||||||
|
// 实例通常由调用者(如 Dialog)持有并销毁
|
||||||
|
// 这里可以做一些全局清理工作
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -461,28 +461,19 @@ interface IBimComponent {
|
|||||||
- **BimEngine**: 总控制器,通过 Manager 统一管理所有组件
|
- **BimEngine**: 总控制器,通过 Manager 统一管理所有组件
|
||||||
|
|
||||||
#### 为什么必须通过 Manager?
|
#### 为什么必须通过 Manager?
|
||||||
1. **统一管理**: Manager 负责组件的生命周期管理,确保资源正确释放
|
1. **强制统一管理**: SDK 入口不再导出组件类(如 `BimDialog`),物理上切断了直接实例化的可能。
|
||||||
2. **主题和语言**: Manager 统一应用主题和国际化,保证一致性
|
2. **主题和语言**: Manager 统一应用主题和国际化,保证一致性
|
||||||
3. **事件总线**: Manager 可以监听和发送事件,实现组件间解耦通信
|
3. **事件总线**: Manager 可以监听和发送事件,实现组件间解耦通信
|
||||||
- 简单场景:直接调用 Manager 方法
|
|
||||||
- 复杂场景:通过事件总线进行发布订阅
|
|
||||||
4. **容器管理**: Manager 管理组件的挂载容器,避免冲突
|
4. **容器管理**: Manager 管理组件的挂载容器,避免冲突
|
||||||
5. **API 封装**: Manager 提供统一的公共 API,隐藏组件实现细节
|
5. **API 封装**: Manager 提供统一的公共 API,隐藏组件实现细节
|
||||||
|
|
||||||
#### 使用示例
|
#### 使用示例
|
||||||
|
|
||||||
**❌ 错误方式 - 直接使用组件:**
|
**❌ 错误方式 - 尝试直接导入组件:**
|
||||||
```typescript
|
```typescript
|
||||||
// 错误:直接创建和使用组件
|
// 错误:BimDialog 类未导出,会导致编译错误
|
||||||
import { BimDialog } from 'bim-engine-sdk';
|
import { BimDialog } from 'bim-engine-sdk';
|
||||||
|
// Error: Module 'bim-engine-sdk' has no exported member 'BimDialog'.
|
||||||
const dialog = new BimDialog({
|
|
||||||
container: document.getElementById('container'),
|
|
||||||
title: '测试弹窗',
|
|
||||||
content: '这是内容'
|
|
||||||
});
|
|
||||||
dialog.init();
|
|
||||||
// 问题:没有通过 Manager 管理,无法统一应用主题、语言等
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**✅ 正确方式 - 通过 Manager 使用:**
|
**✅ 正确方式 - 通过 Manager 使用:**
|
||||||
@@ -500,41 +491,15 @@ const dialog = engine.dialog.create({
|
|||||||
title: '测试弹窗',
|
title: '测试弹窗',
|
||||||
content: '这是内容'
|
content: '这是内容'
|
||||||
});
|
});
|
||||||
// 优势:
|
|
||||||
// 1. 自动应用当前主题
|
|
||||||
// 2. 自动应用当前语言
|
|
||||||
// 3. 统一管理弹窗实例
|
|
||||||
// 4. 可以监听事件总线
|
|
||||||
```
|
|
||||||
|
|
||||||
**✅ 另一个正确示例 - 工具栏按钮:**
|
|
||||||
```typescript
|
|
||||||
// 正确:通过 ToolbarManager 操作工具栏
|
|
||||||
import { BimEngine } from 'bim-engine-sdk';
|
|
||||||
|
|
||||||
const engine = new BimEngine('container');
|
|
||||||
|
|
||||||
// 通过 ToolbarManager 添加按钮
|
|
||||||
engine.toolbar.addButton({
|
|
||||||
id: 'my-button',
|
|
||||||
groupId: 'group-1',
|
|
||||||
type: 'button',
|
|
||||||
label: 'toolbar.myButton',
|
|
||||||
icon: '<svg>...</svg>',
|
|
||||||
onClick: (button) => {
|
|
||||||
console.log('按钮被点击');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 通过 ToolbarManager 控制可见性
|
|
||||||
engine.toolbar.setButtonVisibility('my-button', false);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 组件导出说明
|
#### 组件导出说明
|
||||||
虽然 `src/index.ts` 中导出了 `BimButtonGroup` 和 `Toolbar` 组件,但这是为了:
|
`src/index.ts` **仅导出** `BimEngine` 主类和必要的类型定义(如 `DialogOptions`)。
|
||||||
- 高级用户需要完全自定义的场景
|
所有具体的组件类(如 `BimDialog`、`Toolbar`)均视为**内部实现细节**,不对外暴露。这意味着:
|
||||||
- 内部 Manager 的实现需要
|
- 用户不能继承这些组件类进行扩展。
|
||||||
- **不推荐** 外部用户直接使用,应该通过 Manager
|
- 用户必须依赖 SDK 提供的 Manager API。
|
||||||
|
- 这保证了 SDK 内部架构的封闭性和稳定性。
|
||||||
|
|
||||||
|
|
||||||
### 4.1 Manager 类清单
|
### 4.1 Manager 类清单
|
||||||
|
|
||||||
@@ -545,7 +510,8 @@ engine.toolbar.setButtonVisibility('my-button', false);
|
|||||||
| `ButtonGroupManager` | `src/managers/button-group-manager.ts` | 管理通用按钮组 | `BimComponent` |
|
| `ButtonGroupManager` | `src/managers/button-group-manager.ts` | 管理通用按钮组 | `BimComponent` |
|
||||||
| `EngineManager` | `src/managers/engine-manager.ts` | 管理 3D 引擎 | `BimComponent` |
|
| `EngineManager` | `src/managers/engine-manager.ts` | 管理 3D 引擎 | `BimComponent` |
|
||||||
| `RightKeyManager` | `src/managers/right-key-manager.ts` | 管理右键菜单 (Context Menu) | `BimComponent` |
|
| `RightKeyManager` | `src/managers/right-key-manager.ts` | 管理右键菜单 (Context Menu) | `BimComponent` |
|
||||||
| `ModelTreeManager` | `src/managers/model-tree-manager.ts` | 模型树业务管理器 (组合 Dialog 和 Tree),管理 Tree 实例 | `BimComponent` |
|
| `ModelTreeManager` | `src/managers/model-tree-manager.ts` | 模型树业务管理器 | `BimComponent` |
|
||||||
|
| `PropertyPanelManager` | `src/managers/property-panel-manager.ts` | 属性面板业务管理器 (演示 Collapse) | `BimComponent` |
|
||||||
|
|
||||||
### 4.2 组件类清单
|
### 4.2 组件类清单
|
||||||
|
|
||||||
@@ -559,7 +525,8 @@ engine.toolbar.setButtonVisibility('my-button', false);
|
|||||||
| `BimRightKey` | `src/components/right-key/index.ts` | 右键浮层容器 | `IBimComponent` |
|
| `BimRightKey` | `src/components/right-key/index.ts` | 右键浮层容器 | `IBimComponent` |
|
||||||
| `BimMenu` | `src/components/menu/index.ts` | 通用菜单列表 | `IBimComponent` |
|
| `BimMenu` | `src/components/menu/index.ts` | 通用菜单列表 | `IBimComponent` |
|
||||||
| `BimTree` | `src/components/tree/index.ts` | 通用树形组件 | `IBimComponent` |
|
| `BimTree` | `src/components/tree/index.ts` | 通用树形组件 | `IBimComponent` |
|
||||||
| `BimTab` | `src/components/tab/index.ts` | 固定标签页组件(无运行时增删,当前在 ConstructTreeManagerBtn 内直接使用) | `IBimComponent` |
|
| `BimTab` | `src/components/tab/index.ts` | 固定标签页组件 | `IBimComponent` |
|
||||||
|
| `BimCollapse` | `src/components/collapse/index.ts` | 折叠面板组件 | `IBimComponent` |
|
||||||
|
|
||||||
### 4.3 服务类清单
|
### 4.3 服务类清单
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 6. 功能面板 -->
|
||||||
|
<div class="control-group">
|
||||||
|
<h2>📑 功能面板 (Panels)</h2>
|
||||||
|
<div class="btn-container">
|
||||||
|
<button onclick="openPropertyPanel()">属性面板</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 5. 3D 引擎 -->
|
<!-- 5. 3D 引擎 -->
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<h2>🎮 3D 引擎 (Engine3D)</h2>
|
<h2>🎮 3D 引擎 (Engine3D)</h2>
|
||||||
@@ -386,6 +394,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开属性面板
|
||||||
|
*/
|
||||||
|
function openPropertyPanel() {
|
||||||
|
if (!engine || !engine.propertyPanel) {
|
||||||
|
console.error('Property panel not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
engine.propertyPanel.show();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新引擎状态显示
|
* 更新引擎状态显示
|
||||||
*/
|
*/
|
||||||
|
|||||||
2035
dist/bim-engine-sdk.es.js
vendored
2035
dist/bim-engine-sdk.es.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bim-engine-sdk.es.js.map
vendored
2
dist/bim-engine-sdk.es.js.map
vendored
File diff suppressed because one or more lines are too long
388
dist/bim-engine-sdk.umd.js
vendored
388
dist/bim-engine-sdk.umd.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bim-engine-sdk.umd.js.map
vendored
2
dist/bim-engine-sdk.umd.js.map
vendored
File diff suppressed because one or more lines are too long
362
dist/index.d.ts
vendored
362
dist/index.d.ts
vendored
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 通用按钮组组件 (BimButtonGroup)
|
* 通用按钮组组件 (BimButtonGroup)
|
||||||
*/
|
*/
|
||||||
export declare class BimButtonGroup implements IBimComponent {
|
declare class BimButtonGroup implements IBimComponent {
|
||||||
private container;
|
private container;
|
||||||
private options;
|
private options;
|
||||||
private groups;
|
private groups;
|
||||||
@@ -156,6 +156,7 @@ export declare class BimEngine extends EventEmitter {
|
|||||||
dialog: DialogManager | null;
|
dialog: DialogManager | null;
|
||||||
engine: EngineManager | null;
|
engine: EngineManager | null;
|
||||||
rightKey: RightKeyManager | null;
|
rightKey: RightKeyManager | null;
|
||||||
|
propertyPanel: PropertyPanelManager | null;
|
||||||
constructor(container: HTMLElement | string, options?: {
|
constructor(container: HTMLElement | string, options?: {
|
||||||
locale?: LocaleType;
|
locale?: LocaleType;
|
||||||
theme?: ThemeType;
|
theme?: ThemeType;
|
||||||
@@ -172,121 +173,75 @@ export declare class BimEngine extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用菜单列表组件
|
* 树节点类
|
||||||
* 负责渲染一组菜单项,支持分组、排序、图标、快捷键提示和递归多级子菜单。
|
* 负责渲染单个节点、处理交互和递归
|
||||||
* 它不包含定位逻辑,仅负责内容渲染。
|
|
||||||
*/
|
*/
|
||||||
export declare class BimMenu implements IBimComponent {
|
declare class BimTreeNode {
|
||||||
|
config: TreeNodeConfig;
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
private options;
|
children: BimTreeNode[];
|
||||||
private unsubscribeLocale;
|
parent: BimTreeNode | null;
|
||||||
private unsubscribeTheme;
|
checkState: TreeNodeCheckState;
|
||||||
private activeSubMenu;
|
private contentEl;
|
||||||
constructor(options: MenuOptions);
|
private switcherEl;
|
||||||
|
private checkboxEl;
|
||||||
|
private titleEl;
|
||||||
|
private actionsEl;
|
||||||
|
private childrenContainer;
|
||||||
|
private onExpandChange;
|
||||||
|
private onCheckChange;
|
||||||
|
private onNodeClick;
|
||||||
|
private renderActions?;
|
||||||
|
constructor(config: TreeNodeConfig, options: TreeOptions, callbacks: {
|
||||||
|
onExpand: (n: BimTreeNode) => void;
|
||||||
|
onCheck: (n: BimTreeNode) => void;
|
||||||
|
onClick: (n: BimTreeNode) => void;
|
||||||
|
});
|
||||||
/**
|
/**
|
||||||
* 初始化组件
|
* 创建节点 DOM
|
||||||
* 渲染 DOM 结构并订阅语言变更
|
|
||||||
*/
|
*/
|
||||||
init(): void;
|
private createDom;
|
||||||
/**
|
/**
|
||||||
* 设置主题
|
* 设置高亮选中状态 (Select 模式下)
|
||||||
* @param theme 全局主题配置
|
|
||||||
*/
|
*/
|
||||||
setTheme(theme: ThemeConfig): void;
|
setSelected(selected: boolean): void;
|
||||||
/**
|
/**
|
||||||
* 响应语言变更
|
* 更新显示文本 (国际化支持) -> 移除国际化,直接显示
|
||||||
* 重新渲染整个菜单以更新文本
|
|
||||||
*/
|
*/
|
||||||
setLocales(): void;
|
updateLabel(): void;
|
||||||
/**
|
/**
|
||||||
* 销毁组件
|
* 切换展开状态
|
||||||
* 清理事件监听、子菜单和 DOM 元素
|
*/
|
||||||
|
toggleExpand(force?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 切换选中状态 (用户点击)
|
||||||
|
*/
|
||||||
|
toggleCheck(): void;
|
||||||
|
/**
|
||||||
|
* 设置选中状态 (API调用或联动)
|
||||||
|
* @param state 新状态
|
||||||
|
* @param fireEvent 是否触发事件
|
||||||
|
*/
|
||||||
|
setChecked(state: TreeNodeCheckState, fireEvent?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 更新复选框 UI 样式
|
||||||
|
*/
|
||||||
|
updateCheckboxUI(): void;
|
||||||
|
/**
|
||||||
|
* 添加子节点实例
|
||||||
|
*/
|
||||||
|
appendChild(childNode: BimTreeNode): void;
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
*/
|
*/
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
/**
|
|
||||||
* 获取组件根元素
|
|
||||||
* 实现 IRightKeyContent 接口,允许被 RightKey 容器挂载
|
|
||||||
*/
|
|
||||||
getElement(): HTMLElement;
|
|
||||||
/**
|
|
||||||
* 核心渲染逻辑
|
|
||||||
* 处理分组、排序和 DOM 生成
|
|
||||||
*/
|
|
||||||
private render;
|
|
||||||
/**
|
|
||||||
* 创建单个菜单项的 DOM 元素
|
|
||||||
*/
|
|
||||||
private createItemElement;
|
|
||||||
/**
|
|
||||||
* 打开子菜单
|
|
||||||
* @param item 当前菜单项
|
|
||||||
* @param parentLi 触发的 DOM 元素(用于定位)
|
|
||||||
*/
|
|
||||||
private openSubMenu;
|
|
||||||
/**
|
|
||||||
* 关闭当前激活的子菜单
|
|
||||||
*/
|
|
||||||
private closeSubMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 右键浮层容器组件 (RightKey)
|
|
||||||
* 这是一个纯粹的定位容器,负责在屏幕指定位置显示内容。
|
|
||||||
* 它不关心具体内容是什么,只处理定位、边界检测和关闭逻辑。
|
|
||||||
*/
|
|
||||||
export declare class BimRightKey implements IBimComponent {
|
|
||||||
private element;
|
|
||||||
private content;
|
|
||||||
private isVisible;
|
|
||||||
private onCloseCallback?;
|
|
||||||
private options?;
|
|
||||||
private mouseDownTime;
|
|
||||||
private readonly CLICK_THRESHOLD;
|
|
||||||
constructor(options?: RightKeyOptions);
|
|
||||||
init(): void;
|
|
||||||
setTheme(_theme: ThemeConfig): void;
|
|
||||||
setLocales(): void;
|
|
||||||
destroy(): void;
|
|
||||||
private handleContainerMouseDown;
|
|
||||||
private handleContainerMouseUp;
|
|
||||||
private handleContainerContextMenu;
|
|
||||||
/**
|
|
||||||
* 设置关闭时的回调函数
|
|
||||||
* 通常用于通知 Manager 状态变更
|
|
||||||
*/
|
|
||||||
setOnClose(callback: () => void): void;
|
|
||||||
/**
|
|
||||||
* 挂载内容组件
|
|
||||||
* @param content 实现了 IRightKeyContent 接口的组件实例
|
|
||||||
*/
|
|
||||||
mount(content: IRightKeyContent): void;
|
|
||||||
/**
|
|
||||||
* 卸载当前内容
|
|
||||||
*/
|
|
||||||
unmountContent(): void;
|
|
||||||
/**
|
|
||||||
* 在指定位置显示容器
|
|
||||||
* 包含智能边界检测逻辑,防止溢出屏幕
|
|
||||||
* @param x 目标 X 坐标 (通常是鼠标点击位置)
|
|
||||||
* @param y 目标 Y 坐标
|
|
||||||
*/
|
|
||||||
show(x: number, y: number): void;
|
|
||||||
/**
|
|
||||||
* 隐藏容器
|
|
||||||
*/
|
|
||||||
hide(): void;
|
|
||||||
/**
|
|
||||||
* 处理全局点击事件
|
|
||||||
* 用于检测是否点击了容器外部
|
|
||||||
*/
|
|
||||||
private handleGlobalClick;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 按钮内部文字图标排列 */
|
/** 按钮内部文字图标排列 */
|
||||||
declare type ButtonAlign = 'vertical' | 'horizontal';
|
declare type ButtonAlign = 'vertical' | 'horizontal';
|
||||||
|
|
||||||
/** 按钮配置 */
|
/** 按钮配置 */
|
||||||
declare interface ButtonConfig {
|
export declare interface ButtonConfig {
|
||||||
id: string;
|
id: string;
|
||||||
type: ButtonType;
|
type: ButtonType;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -305,11 +260,6 @@ declare interface ButtonConfig {
|
|||||||
minWidth?: number;
|
minWidth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare interface ButtonGroup {
|
|
||||||
id: string;
|
|
||||||
buttons: OptButton[];
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface ButtonGroupColors {
|
declare interface ButtonGroupColors {
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
btnBackgroundColor?: string;
|
btnBackgroundColor?: string;
|
||||||
@@ -352,10 +302,40 @@ export declare interface ButtonGroupOptions extends ButtonGroupColors {
|
|||||||
|
|
||||||
declare type ButtonType = 'button' | 'menu';
|
declare type ButtonType = 'button' | 'menu';
|
||||||
|
|
||||||
export declare interface ClickPayload {
|
export declare interface CollapseItemConfig {
|
||||||
button: OptButton;
|
/** 唯一标识符 */
|
||||||
action: 'activate' | 'deactivate' | 'trigger';
|
id: string;
|
||||||
isActive?: boolean;
|
/** 标题文本的翻译键 (例如 'panel.attributes') */
|
||||||
|
title: string;
|
||||||
|
/** 内容: HTML字符串 或 HTMLElement */
|
||||||
|
content: string | HTMLElement;
|
||||||
|
/** 标题栏左侧图标 (SVG 字符串, 可选) */
|
||||||
|
icon?: string;
|
||||||
|
/** 标题栏右侧额外内容 (可选) */
|
||||||
|
extra?: string | HTMLElement;
|
||||||
|
/** 是否禁用 */
|
||||||
|
disabled?: boolean;
|
||||||
|
/** 自定义类名 */
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare interface CollapseOptions {
|
||||||
|
/** 挂载容器 */
|
||||||
|
container: HTMLElement | string;
|
||||||
|
/** 面板项列表 */
|
||||||
|
items: CollapseItemConfig[];
|
||||||
|
/** 是否开启手风琴模式 (默认 false) */
|
||||||
|
accordion?: boolean;
|
||||||
|
/** 初始展开的面板 ID 列表 */
|
||||||
|
activeIds?: string[];
|
||||||
|
/** 是否显示边框 (默认 true) */
|
||||||
|
bordered?: boolean;
|
||||||
|
/** 是否幽灵模式 (默认 false) */
|
||||||
|
ghost?: boolean;
|
||||||
|
/** 自定义类名 */
|
||||||
|
className?: string;
|
||||||
|
/** 切换面板时的回调 */
|
||||||
|
onChange?: (activeIds: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -381,18 +361,6 @@ declare class ConstructTreeManagerBtn extends BimComponent {
|
|||||||
setColors(colors: ButtonGroupColors): void;
|
setColors(colors: ButtonGroupColors): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare function createEngine(s) {
|
|
||||||
const e = s.version || "v1";
|
|
||||||
switch (e) {
|
|
||||||
case "v2":
|
|
||||||
return new Fc(s);
|
|
||||||
case "v1":
|
|
||||||
return new O_(s);
|
|
||||||
:
|
|
||||||
return console.warn(`Version '${e}' not found. Falling back to v2.`), new Fc(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹窗颜色配置
|
* 弹窗颜色配置
|
||||||
*/
|
*/
|
||||||
@@ -446,7 +414,7 @@ declare class DialogManager extends BimComponent {
|
|||||||
/**
|
/**
|
||||||
* 弹窗配置选项接口
|
* 弹窗配置选项接口
|
||||||
*/
|
*/
|
||||||
declare interface DialogOptions extends DialogColors {
|
export declare interface DialogOptions extends DialogColors {
|
||||||
/** 弹窗挂载的父容器 */
|
/** 弹窗挂载的父容器 */
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
/** 弹窗标题 */
|
/** 弹窗标题 */
|
||||||
@@ -480,12 +448,12 @@ declare interface DialogOptions extends DialogColors {
|
|||||||
* 可以是预设的字符串位置(如 'center', 'top-left' 等),
|
* 可以是预设的字符串位置(如 'center', 'top-left' 等),
|
||||||
* 也可以是具体的坐标对象 { x, y }
|
* 也可以是具体的坐标对象 { x, y }
|
||||||
*/
|
*/
|
||||||
declare type DialogPosition = 'center' | 'top-left' | 'top-center' | 'top-right' | 'left-center' | 'right-center' | 'bottom-left' | 'bottom-center' | 'bottom-right' | {
|
export declare type DialogPosition = 'center' | 'top-left' | 'top-center' | 'top-right' | 'left-center' | 'right-center' | 'bottom-left' | 'bottom-center' | 'bottom-right' | {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare interface EngineEvents {
|
export declare interface EngineEvents {
|
||||||
'ui:open-dialog': {
|
'ui:open-dialog': {
|
||||||
id: string;
|
id: string;
|
||||||
data?: any;
|
data?: any;
|
||||||
@@ -518,6 +486,9 @@ declare interface EngineEvents {
|
|||||||
id: string;
|
id: string;
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
};
|
};
|
||||||
|
'ui:collapse-change': {
|
||||||
|
activeIds: string[];
|
||||||
|
};
|
||||||
'sys:theme-changed': {
|
'sys:theme-changed': {
|
||||||
theme: string;
|
theme: string;
|
||||||
};
|
};
|
||||||
@@ -611,7 +582,7 @@ declare type GroupPosition = 'center' | 'top-left' | 'top-center' | 'top-right'
|
|||||||
* BIM 引擎组件通用接口
|
* BIM 引擎组件通用接口
|
||||||
* 所有受引擎管理的 UI 组件都必须实现此接口
|
* 所有受引擎管理的 UI 组件都必须实现此接口
|
||||||
*/
|
*/
|
||||||
declare interface IBimComponent {
|
export declare interface IBimComponent {
|
||||||
/**
|
/**
|
||||||
* 初始化组件
|
* 初始化组件
|
||||||
* 用于创建 DOM、绑定事件、加载资源等
|
* 用于创建 DOM、绑定事件、加载资源等
|
||||||
@@ -634,17 +605,6 @@ declare interface IBimComponent {
|
|||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface IRightKeyContent {
|
|
||||||
/**
|
|
||||||
* 获取组件的根 DOM 元素
|
|
||||||
*/
|
|
||||||
getElement(): HTMLElement;
|
|
||||||
/**
|
|
||||||
* 销毁组件
|
|
||||||
*/
|
|
||||||
destroy(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type Listener<T = any> = (payload: T) => void;
|
declare type Listener<T = any> = (payload: T) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -655,7 +615,7 @@ declare type LocaleType = 'zh-CN' | 'en-US';
|
|||||||
/**
|
/**
|
||||||
* 菜单项配置接口 (用于简化的对象配置)
|
* 菜单项配置接口 (用于简化的对象配置)
|
||||||
*/
|
*/
|
||||||
export declare interface MenuItemConfig {
|
declare interface MenuItemConfig {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
@@ -667,24 +627,6 @@ export declare interface MenuItemConfig {
|
|||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <20><><EFBFBD>单组件配置选项
|
|
||||||
*/
|
|
||||||
declare interface MenuOptions {
|
|
||||||
/**
|
|
||||||
* 菜单项列表
|
|
||||||
* 可以是扁平数组,组件会根据 group 字段自动分组
|
|
||||||
*/
|
|
||||||
items: MenuItemConfig[];
|
|
||||||
/**
|
|
||||||
* 分组显示顺序
|
|
||||||
* 包含组 ID 的字符串数组。
|
|
||||||
* 例如: ['view', 'edit', 'tools']
|
|
||||||
* 未在此数组中定义的组将按默认顺序排在最后。
|
|
||||||
*/
|
|
||||||
groupOrder?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模型加载选项
|
* 模型加载选项
|
||||||
* 用于配置模型的位置、旋转和缩放
|
* 用于配置模型的位置、旋转和缩放
|
||||||
@@ -700,10 +642,24 @@ export declare interface ModelLoadOptions {
|
|||||||
id?: string;
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare interface OptButton extends ButtonConfig {
|
/**
|
||||||
|
* 节点点击行为类型
|
||||||
|
*/
|
||||||
|
export declare type NodeClickAction = 'select' | 'expand';
|
||||||
|
|
||||||
|
declare interface OptButton extends ButtonConfig {
|
||||||
children?: OptButton[];
|
children?: OptButton[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare class PropertyPanelManager extends BimComponent {
|
||||||
|
constructor(engine: BimEngine);
|
||||||
|
init(): void;
|
||||||
|
show(): void;
|
||||||
|
private createBaseInfoContent;
|
||||||
|
private createMaterialContent;
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 右键菜单管理器 (RightKeyManager)
|
* 右键菜单管理器 (RightKeyManager)
|
||||||
* 负责协调右键交互流程:
|
* 负责协调右键交互流程:
|
||||||
@@ -742,22 +698,11 @@ declare class RightKeyManager extends BimComponent {
|
|||||||
private handleContextMenu;
|
private handleContextMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface RightKeyOptions {
|
|
||||||
/** 自定义 CSS 类名 */
|
|
||||||
className?: string;
|
|
||||||
/** 层级 (z-index) */
|
|
||||||
zIndex?: number;
|
|
||||||
/** 监听事件的容器 */
|
|
||||||
container?: HTMLElement;
|
|
||||||
/** 有效右键点击时的回调 */
|
|
||||||
onContext?: (e: MouseEvent) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局主题配置接口
|
* 全局主题配置接口
|
||||||
* 定义系统通用的语义化颜色
|
* 定义系统通用的语义化颜色
|
||||||
*/
|
*/
|
||||||
declare interface ThemeConfig {
|
export declare interface ThemeConfig {
|
||||||
/** 主题名称 */
|
/** 主题名称 */
|
||||||
name: string;
|
name: string;
|
||||||
/** 品牌色/主色 */
|
/** 品牌色/主色 */
|
||||||
@@ -789,18 +734,7 @@ declare interface ThemeConfig {
|
|||||||
/**
|
/**
|
||||||
* 主题类型定义
|
* 主题类型定义
|
||||||
*/
|
*/
|
||||||
declare type ThemeType = 'dark' | 'light' | 'custom';
|
export declare type ThemeType = 'dark' | 'light' | 'custom';
|
||||||
|
|
||||||
/**
|
|
||||||
* 底部工具栏 (Toolbar)
|
|
||||||
* BimButtonGroup 的子类,专门用于加载工具栏默认按钮。
|
|
||||||
*/
|
|
||||||
export declare class Toolbar extends BimButtonGroup {
|
|
||||||
/**
|
|
||||||
* 重写初始化,加载默认按钮
|
|
||||||
*/
|
|
||||||
init(): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 底部工具栏管理器 (ToolbarManager)
|
* 底部工具栏管理器 (ToolbarManager)
|
||||||
@@ -824,4 +758,74 @@ declare class ToolbarManager extends BimComponent {
|
|||||||
setColors(colors: ButtonGroupColors): void;
|
setColors(colors: ButtonGroupColors): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点勾选状态枚举
|
||||||
|
*/
|
||||||
|
export declare enum TreeNodeCheckState {
|
||||||
|
Unchecked = 0,
|
||||||
|
Checked = 1,
|
||||||
|
Indeterminate = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树节点配置接口
|
||||||
|
*/
|
||||||
|
export declare interface TreeNodeConfig {
|
||||||
|
/** 唯一标识符 */
|
||||||
|
id: string;
|
||||||
|
/** 显示文本的翻译键 */
|
||||||
|
label: string;
|
||||||
|
/** 节点图标 (SVG string 或 URL) */
|
||||||
|
icon?: string;
|
||||||
|
/** 子节点列表 */
|
||||||
|
children?: TreeNodeConfig[];
|
||||||
|
/** 初始展开状态 (默认 false) */
|
||||||
|
expanded?: boolean;
|
||||||
|
/** 初始选中状态 (默认 false) */
|
||||||
|
checked?: boolean;
|
||||||
|
/** 是否禁用 (默认 false) */
|
||||||
|
disabled?: boolean;
|
||||||
|
/** 自定义业务数据 */
|
||||||
|
data?: any;
|
||||||
|
/** 是否是叶子节点 (用于异步加载场景,暂留接口) */
|
||||||
|
isLeaf?: boolean;
|
||||||
|
/** 点击整行的行为 (默认 'select') */
|
||||||
|
clickAction?: NodeClickAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树组件配置选项
|
||||||
|
*/
|
||||||
|
export declare interface TreeOptions {
|
||||||
|
/** 树的数据源 */
|
||||||
|
data: TreeNodeConfig[];
|
||||||
|
/** 是否显示复选框 (默认 true) */
|
||||||
|
checkable?: boolean;
|
||||||
|
/**
|
||||||
|
* 父子节点选中状态是否关联 (默认 true)
|
||||||
|
* true: 选中父选子,子全选自动选父
|
||||||
|
* false: 独立选中
|
||||||
|
*/
|
||||||
|
checkStrictly?: boolean;
|
||||||
|
/** 默认展开所有节点 (默认 false) */
|
||||||
|
defaultExpandAll?: boolean;
|
||||||
|
/** 缩进宽度 (像素,默认 24) */
|
||||||
|
indent?: number;
|
||||||
|
/** 是否启用搜索功能 (默认 false) */
|
||||||
|
enableSearch?: boolean;
|
||||||
|
/** 搜索框占位符 */
|
||||||
|
searchPlaceholder?: string;
|
||||||
|
/** 节点勾选回调 */
|
||||||
|
onNodeCheck?: (node: BimTreeNode) => void;
|
||||||
|
/** 节点选择回调 */
|
||||||
|
onNodeSelect?: (node: BimTreeNode) => void;
|
||||||
|
/** 节点展开/折叠回调 */
|
||||||
|
onNodeExpand?: (node: BimTreeNode) => void;
|
||||||
|
/**
|
||||||
|
* 选中时显示的自定义操作栏渲染函数
|
||||||
|
* 返回 HTML 字符串或 HTMLElement
|
||||||
|
*/
|
||||||
|
renderActions?: (node: TreeNodeConfig) => HTMLElement | string;
|
||||||
|
}
|
||||||
|
|
||||||
export { }
|
export { }
|
||||||
|
|||||||
69
docs/components/collapse.md
Normal file
69
docs/components/collapse.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Collapse 组件文档
|
||||||
|
|
||||||
|
> **注意**: 本组件为 UI 组件,必须通过 Manager(如 `PropertyPanelManager`)封装后使用,不可直接在业务代码中实例化。
|
||||||
|
|
||||||
|
## 1. 组件概述
|
||||||
|
|
||||||
|
`BimCollapse` 是一个通用的折叠面板组件,支持手风琴模式、自定义内容和标题。常用于属性面板、设置菜单等场景。
|
||||||
|
|
||||||
|
## 2. API 参考
|
||||||
|
|
||||||
|
### 2.1 配置项 `CollapseOptions`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface CollapseOptions {
|
||||||
|
container: HTMLElement | string; // 挂载容器
|
||||||
|
items: CollapseItemConfig[]; // 面板项列表
|
||||||
|
accordion?: boolean; // 是否开启手风琴模式 (默认 false)
|
||||||
|
activeIds?: string[]; // 初始展开的 ID 列表
|
||||||
|
bordered?: boolean; // 是否显示边框 (默认 true)
|
||||||
|
ghost?: boolean; // 是否幽灵模式 (默认 false)
|
||||||
|
className?: string; // 自定义类名
|
||||||
|
onChange?: (activeIds: string[]) => void; // 切换回调
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 面板项配置 `CollapseItemConfig`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface CollapseItemConfig {
|
||||||
|
id: string; // 唯一标识
|
||||||
|
title: string; // 标题翻译键 (例如 'panel.base')
|
||||||
|
content: string | HTMLElement; // 内容
|
||||||
|
icon?: string; // 标题图标 (SVG)
|
||||||
|
extra?: string | HTMLElement; // 标题右侧额外内容
|
||||||
|
disabled?: boolean; // 是否禁用
|
||||||
|
className?: string; // 自定义类名
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 方法
|
||||||
|
|
||||||
|
* `toggleItem(id: string)`: 切换指定面板的展开/折叠状态。
|
||||||
|
* `setLocales()`: 更新组件文本(通常自动调用)。
|
||||||
|
* `destroy()`: 销毁组件。
|
||||||
|
|
||||||
|
## 3. 使用示例 (在 Manager 中)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { BimCollapse } from '../components/collapse/index';
|
||||||
|
|
||||||
|
// 在 Manager 的方法中
|
||||||
|
const collapse = new BimCollapse({
|
||||||
|
container: this.containerElement,
|
||||||
|
accordion: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 'item1',
|
||||||
|
title: 'my.title.key', // 翻译键
|
||||||
|
content: '<div>Content Here</div>'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 国际化
|
||||||
|
|
||||||
|
组件会自动订阅 `localeManager`。
|
||||||
|
* **标题**: `config.title` 必须是翻译键。
|
||||||
|
* **内容**: 如果内容包含文本,请确保内容生成时已翻译,或内容本身具有响应国际化的能力。
|
||||||
92
docs/components/tab.md
Normal file
92
docs/components/tab.md
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
## 1. 组件概述
|
||||||
|
- **名称**:BimTab
|
||||||
|
- **位置**:`src/components/tab/`
|
||||||
|
- **功能**:渲染固定标签页(不支持运行期增删),支持点击切换、禁用、可选内容托管。主题由全局 `ThemeManager` 提供,文案通过 `t()` 翻译。
|
||||||
|
- **使用方式**:直接通过构造函数 + `init()` 创建;本项目内目前仅在 `ConstructTreeManagerBtn` 弹窗中使用(无专门 Manager)。
|
||||||
|
|
||||||
|
## 2. 组件类 API(BimTab)
|
||||||
|
- `constructor(options: TabOptions)`:创建实例并挂载到 `options.container`。
|
||||||
|
- `init(): void`:渲染头部与内容,订阅主题/语言。
|
||||||
|
- `activateTab(tabId: string): void`:切换激活标签,触发 `onChange`。
|
||||||
|
- `setTheme(theme: ThemeConfig): void`:应用主题变量。
|
||||||
|
- `setLocales(): void`:刷新标题文案。
|
||||||
|
- `destroy(): void`:解绑事件、取消订阅、移除 DOM。
|
||||||
|
|
||||||
|
## 3. 分化组件
|
||||||
|
- 当前无子类或变体,后续可扩展滚动、溢出折叠等能力。
|
||||||
|
|
||||||
|
## 4. 使用场景说明
|
||||||
|
- **ConstructTreeManagerBtn 弹窗**:顶部三段标签(构件/系统/空间),构件标签承载原有 Tree,系统/空间暂为空容器。
|
||||||
|
- 适用于固定分区切换,内容简单或由外部回调控制。
|
||||||
|
|
||||||
|
## 5. UI 结构
|
||||||
|
```
|
||||||
|
div.bim-tab
|
||||||
|
div.bim-tab__nav [role=tablist]
|
||||||
|
button.bim-tab__item[role=tab][aria-selected][aria-disabled?]
|
||||||
|
span.bim-tab__icon? (可选)
|
||||||
|
span.bim-tab__title
|
||||||
|
div.bim-tab__content
|
||||||
|
div.bim-tab__panel[role=tabpanel][aria-labelledby=tab-xxx]
|
||||||
|
...
|
||||||
|
```
|
||||||
|
- 状态类:`.is-active`、`.is-disabled`。
|
||||||
|
- 内容区:仅切换显示,不强制填充(可为空)。
|
||||||
|
|
||||||
|
## 6. 逻辑流程
|
||||||
|
1) `constructor`:创建根节点、头部、内容容器;缓存 tab 数据;挂载到传入容器。
|
||||||
|
2) `init`:渲染头部按钮与内容面板,设置初始激活;应用当前主题/语言;订阅主题、语言变更。
|
||||||
|
3) 交互:点击非禁用按钮 → `activateTab` 更新头部/面板状态 → 回调 `onChange`。
|
||||||
|
4) 销毁:解绑点击监听、取消订阅、清空映射并移除 DOM。
|
||||||
|
|
||||||
|
## 7. 国际化
|
||||||
|
- 标题:`t(tab.title)`,若翻译键不存在则回退原文。
|
||||||
|
- 新增翻译键:`tab.component` / `tab.system` / `tab.space`(已在 `zh-CN.ts`、`en-US.ts` 注册)。
|
||||||
|
- `setLocales()`:遍历头部按钮刷新标题文案;订阅 `localeManager` 自动响应语言切换。
|
||||||
|
|
||||||
|
## 8. 主题支持
|
||||||
|
- 来自 `themeManager`,不从配置传入。
|
||||||
|
- 使用的变量(设置在根节点上):
|
||||||
|
- `--bim-tab-bg`、`--bim-tab-nav-bg`
|
||||||
|
- `--bim-tab-text`、`--bim-tab-text-secondary`、`--bim-tab-text-active`
|
||||||
|
- `--bim-tab-border`、`--bim-tab-hover-bg`、`--bim-tab-active-bg`
|
||||||
|
- `--bim-tab-icon`
|
||||||
|
- `setTheme(theme)`:根据 `ThemeConfig` 写入上述 CSS 变量。
|
||||||
|
|
||||||
|
## 9. 使用示例
|
||||||
|
```ts
|
||||||
|
const tabMount = document.createElement('div');
|
||||||
|
const tab = new BimTab({
|
||||||
|
container: tabMount,
|
||||||
|
tabs: [
|
||||||
|
{ id: 'component', title: 'tab.component', content: componentEl },
|
||||||
|
{ id: 'system', title: 'tab.system', content: systemEl },
|
||||||
|
{ id: 'space', title: 'tab.space', content: spaceEl },
|
||||||
|
],
|
||||||
|
activeId: 'component',
|
||||||
|
onChange: (id) => {
|
||||||
|
// 根据 id 做额外逻辑(如埋点、动态加载)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
tab.init();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 实现细节
|
||||||
|
- 仅支持固定 tabs:`TabOptions.tabs` 为初始化列表,不提供新增/删除接口。
|
||||||
|
- 内容托管两种方式:`content: HTMLElement`(append)或 `content: string`(innerHTML)。未提供内容时面板为空。
|
||||||
|
- 事件绑定:头部使用事件委托绑定 click,销毁时移除。
|
||||||
|
- 访问性:头部 `role=tab`、`aria-selected`、`aria-disabled`;面板 `role=tabpanel` 且 `aria-labelledby` 对应头部 id。
|
||||||
|
- 主题/语言订阅:`localeManager.subscribe`、`themeManager.subscribe`,销毁时必须取消。
|
||||||
|
|
||||||
|
## 11. 类型定义
|
||||||
|
- 位置:`src/components/tab/index.type.ts`
|
||||||
|
- 主要类型:
|
||||||
|
- `TabItem`: `{ id; title; disabled?; icon?; content?; meta? }`
|
||||||
|
- `TabOptions`: `{ container; tabs; activeId?; onChange? }`
|
||||||
|
|
||||||
|
## 12. 文件清单
|
||||||
|
- `src/components/tab/index.ts`:组件实现
|
||||||
|
- `src/components/tab/index.type.ts`:类型定义
|
||||||
|
- `src/components/tab/index.css`:样式
|
||||||
|
- (无 Manager)当前仅在 `ConstructTreeManagerBtn` 中直接使用
|
||||||
|
|
||||||
@@ -5,6 +5,7 @@ import { DialogManager } from './managers/dialog-manager';
|
|||||||
import { EngineManager } from './managers/engine-manager';
|
import { EngineManager } from './managers/engine-manager';
|
||||||
import { RightKeyManager } from './managers/right-key-manager';
|
import { RightKeyManager } from './managers/right-key-manager';
|
||||||
import { ConstructTreeManagerBtn } from './managers/construct-tree-manager-btn';
|
import { ConstructTreeManagerBtn } from './managers/construct-tree-manager-btn';
|
||||||
|
import { PropertyPanelManager } from './managers/property-panel-manager';
|
||||||
import type { EngineOptions, ModelLoadOptions } from './components/engine';
|
import type { EngineOptions, ModelLoadOptions } from './components/engine';
|
||||||
import { localeManager } from './services/locale';
|
import { localeManager } from './services/locale';
|
||||||
import { themeManager } from './services/theme';
|
import { themeManager } from './services/theme';
|
||||||
@@ -25,6 +26,7 @@ export class BimEngine extends EventEmitter {
|
|||||||
public dialog: DialogManager | null = null;
|
public dialog: DialogManager | null = null;
|
||||||
public engine: EngineManager | null = null; // 3D 引擎管理器
|
public engine: EngineManager | null = null; // 3D 引擎管理器
|
||||||
public rightKey: RightKeyManager | null = null; // 右键菜单管理器
|
public rightKey: RightKeyManager | null = null; // 右键菜单管理器
|
||||||
|
public propertyPanel: PropertyPanelManager | null = null; // 属性面板 (演示 Collapse)
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -78,6 +80,7 @@ export class BimEngine extends EventEmitter {
|
|||||||
this.buttonGroup = new ButtonGroupManager(this, this.wrapper);
|
this.buttonGroup = new ButtonGroupManager(this, this.wrapper);
|
||||||
this.rightKey = new RightKeyManager(this, this.wrapper);
|
this.rightKey = new RightKeyManager(this, this.wrapper);
|
||||||
this.constructTreeBtn = new ConstructTreeManagerBtn(this, this.wrapper);
|
this.constructTreeBtn = new ConstructTreeManagerBtn(this, this.wrapper);
|
||||||
|
this.propertyPanel = new PropertyPanelManager(this);
|
||||||
|
|
||||||
// 初始主题
|
// 初始主题
|
||||||
this.updateTheme(themeManager.getTheme());
|
this.updateTheme(themeManager.getTheme());
|
||||||
@@ -101,6 +104,7 @@ export class BimEngine extends EventEmitter {
|
|||||||
this.engine?.destroy();
|
this.engine?.destroy();
|
||||||
this.dialog?.destroy();
|
this.dialog?.destroy();
|
||||||
this.rightKey?.destroy();
|
this.rightKey?.destroy();
|
||||||
|
this.propertyPanel?.destroy();
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/components/button-group/toolbar/buttons/info/icon.ts
Normal file
1
src/components/button-group/toolbar/buttons/info/icon.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const infoIcon = '<svg viewBox="0 0 1024 1024"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="currentColor"/><path d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z" fill="currentColor"/></svg>';
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
import type { ButtonConfig } from '../../../index.type';
|
import { ButtonConfig } from '../../../index.type';
|
||||||
|
import { infoIcon } from './icon';
|
||||||
|
|
||||||
/**
|
|
||||||
* 定位按钮配置
|
|
||||||
*/
|
|
||||||
export const infoButton: ButtonConfig = {
|
export const infoButton: ButtonConfig = {
|
||||||
id: 'info',
|
id: 'toolbar-info',
|
||||||
groupId: 'group-2',
|
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'toolbar.info',
|
label: 'toolbar.info',
|
||||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 7q.425 0 .713-.288T13 6t-.288-.712T12 5t-.712.288T11 6t.288.713T12 7m0 8q.425 0 .713-.288T13 14v-4q0-.425-.288-.712T12 9t-.712.288T11 10v4q0 .425.288.713T12 15m-6 3l-2.3 2.3q-.475.475-1.088.213T2 19.575V4q0-.825.588-1.412T4 2h16q.825 0 1.413.588T22 4v12q0 .825-.587 1.413T20 18z"/></svg>',
|
icon: infoIcon,
|
||||||
keepActive: false,
|
onClick: () => {
|
||||||
onClick: (button) => {
|
// WORKAROUND: Dispatch a standard custom event on document
|
||||||
console.log('信息按钮被点击:', button.id);
|
document.dispatchEvent(new CustomEvent('bim-demo:open-property-panel'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
121
src/components/collapse/index.css
Normal file
121
src/components/collapse/index.css
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/* Root Container */
|
||||||
|
.bim-collapse {
|
||||||
|
background-color: var(--bim-bg-color, #ffffff);
|
||||||
|
border: 1px solid var(--bim-border-color, #d9d9d9);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--bim-text-color, rgba(0, 0, 0, 0.88));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse.is-ghost {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse.is-ghost .bim-collapse-item {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse.is-ghost .bim-collapse-header {
|
||||||
|
background-color: transparent;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse.is-ghost .bim-collapse-content {
|
||||||
|
background-color: transparent;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Item */
|
||||||
|
.bim-collapse-item {
|
||||||
|
border-bottom: 1px solid var(--bim-border-color, #d9d9d9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-item.is-disabled .bim-collapse-header {
|
||||||
|
color: var(--bim-disabled-color, rgba(0, 0, 0, 0.25));
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.bim-collapse-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background-color: var(--bim-header-bg-color, rgba(0, 0, 0, 0.02));
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-header:hover {
|
||||||
|
background-color: var(--bim-header-hover-bg-color, rgba(0, 0, 0, 0.05));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Arrow Icon */
|
||||||
|
.bim-collapse-arrow {
|
||||||
|
margin-right: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
transition: transform 0.24s;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-arrow svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-item.is-active .bim-collapse-arrow {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon (User provided) */
|
||||||
|
.bim-collapse-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-icon svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Title */
|
||||||
|
.bim-collapse-title {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extra */
|
||||||
|
.bim-collapse-extra {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content */
|
||||||
|
.bim-collapse-content {
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: var(--bim-content-bg-color, #ffffff);
|
||||||
|
border-top: 1px solid var(--bim-border-color, #d9d9d9);
|
||||||
|
transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-content.is-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-collapse-content-box {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
244
src/components/collapse/index.ts
Normal file
244
src/components/collapse/index.ts
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import './index.css';
|
||||||
|
import { CollapseOptions, CollapseItemConfig } from './types';
|
||||||
|
import { IBimComponent } from '../../types/component';
|
||||||
|
import { t, localeManager } from '../../services/locale';
|
||||||
|
import { themeManager } from '../../services/theme';
|
||||||
|
import type { ThemeConfig } from '../../themes/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个折叠面板项
|
||||||
|
*/
|
||||||
|
class BimCollapseItem {
|
||||||
|
public element: HTMLElement;
|
||||||
|
public headerEl!: HTMLElement;
|
||||||
|
public contentEl!: HTMLElement;
|
||||||
|
public contentBoxEl!: HTMLElement;
|
||||||
|
public arrowEl!: HTMLElement;
|
||||||
|
public titleEl!: HTMLElement;
|
||||||
|
|
||||||
|
private config: CollapseItemConfig;
|
||||||
|
private parent: BimCollapse;
|
||||||
|
|
||||||
|
constructor(config: CollapseItemConfig, parent: BimCollapse) {
|
||||||
|
this.config = config;
|
||||||
|
this.parent = parent;
|
||||||
|
this.element = this.createDom();
|
||||||
|
}
|
||||||
|
|
||||||
|
private createDom(): HTMLElement {
|
||||||
|
const itemEl = document.createElement('div');
|
||||||
|
itemEl.className = `bim-collapse-item ${this.config.className || ''}`;
|
||||||
|
if (this.config.disabled) itemEl.classList.add('is-disabled');
|
||||||
|
itemEl.dataset.id = this.config.id;
|
||||||
|
|
||||||
|
// <20><>部区域
|
||||||
|
this.headerEl = document.createElement('div');
|
||||||
|
this.headerEl.className = 'bim-collapse-header';
|
||||||
|
|
||||||
|
// 箭头图标
|
||||||
|
this.arrowEl = document.createElement('span');
|
||||||
|
this.arrowEl.className = 'bim-collapse-arrow';
|
||||||
|
this.arrowEl.innerHTML = `<svg viewBox="0 0 1024 1024"><path d="M288 192l448 320-448 320z"></path></svg>`;
|
||||||
|
this.headerEl.appendChild(this.arrowEl);
|
||||||
|
|
||||||
|
// 自定义图标 (可选)
|
||||||
|
if (this.config.icon) {
|
||||||
|
const iconEl = document.createElement('span');
|
||||||
|
iconEl.className = 'bim-collapse-icon';
|
||||||
|
iconEl.innerHTML = this.config.icon;
|
||||||
|
this.headerEl.appendChild(iconEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标题文本
|
||||||
|
this.titleEl = document.createElement('span');
|
||||||
|
this.titleEl.className = 'bim-collapse-title';
|
||||||
|
this.titleEl.textContent = t(this.config.title); // 初始翻译
|
||||||
|
this.headerEl.appendChild(this.titleEl);
|
||||||
|
|
||||||
|
// 额外内容 (可选,如右侧标签)
|
||||||
|
if (this.config.extra) {
|
||||||
|
const extraEl = document.createElement('div');
|
||||||
|
extraEl.className = 'bim-collapse-extra';
|
||||||
|
if (typeof this.config.extra === 'string') {
|
||||||
|
extraEl.innerHTML = this.config.extra;
|
||||||
|
} else {
|
||||||
|
extraEl.appendChild(this.config.extra);
|
||||||
|
}
|
||||||
|
this.headerEl.appendChild(extraEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击事件
|
||||||
|
this.headerEl.addEventListener('click', () => {
|
||||||
|
if (this.config.disabled) return;
|
||||||
|
this.parent.toggleItem(this.config.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
itemEl.appendChild(this.headerEl);
|
||||||
|
|
||||||
|
// 内容区域
|
||||||
|
this.contentEl = document.createElement('div');
|
||||||
|
this.contentEl.className = 'bim-collapse-content is-hidden';
|
||||||
|
|
||||||
|
this.contentBoxEl = document.createElement('div');
|
||||||
|
this.contentBoxEl.className = 'bim-collapse-content-box';
|
||||||
|
|
||||||
|
if (typeof this.config.content === 'string') {
|
||||||
|
this.contentBoxEl.innerHTML = this.config.content;
|
||||||
|
} else {
|
||||||
|
this.contentBoxEl.appendChild(this.config.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.contentEl.appendChild(this.contentBoxEl);
|
||||||
|
itemEl.appendChild(this.contentEl);
|
||||||
|
|
||||||
|
return itemEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateLocale() {
|
||||||
|
if (this.titleEl) {
|
||||||
|
this.titleEl.textContent = t(this.config.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setActive(isActive: boolean) {
|
||||||
|
if (isActive) {
|
||||||
|
this.element.classList.add('is-active');
|
||||||
|
this.contentEl.classList.remove('is-hidden');
|
||||||
|
// 简单的动画处理:设置 height
|
||||||
|
// 实际生产中可能需要更复杂的 JS 动画库或 transitionend 事件处理
|
||||||
|
// 这里依赖 CSS transition
|
||||||
|
} else {
|
||||||
|
this.element.classList.remove('is-active');
|
||||||
|
this.contentEl.classList.add('is-hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 折叠面板组件
|
||||||
|
*/
|
||||||
|
export class BimCollapse implements IBimComponent {
|
||||||
|
private element: HTMLElement;
|
||||||
|
private options: CollapseOptions;
|
||||||
|
private items: Map<string, BimCollapseItem> = new Map();
|
||||||
|
private activeIds: Set<string> = new Set();
|
||||||
|
private unsubscribeLocale: (() => void) | null = null;
|
||||||
|
private unsubscribeTheme: (() => void) | null = null;
|
||||||
|
|
||||||
|
constructor(options: CollapseOptions) {
|
||||||
|
this.options = {
|
||||||
|
bordered: true,
|
||||||
|
accordion: false,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
this.element.className = `bim-collapse ${this.options.className || ''}`;
|
||||||
|
if (!this.options.bordered) this.element.style.border = 'none';
|
||||||
|
if (this.options.ghost) this.element.classList.add('is-ghost');
|
||||||
|
|
||||||
|
const container = typeof this.options.container === 'string'
|
||||||
|
? document.getElementById(this.options.container)
|
||||||
|
: this.options.container;
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
container.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化激活的 ID
|
||||||
|
if (this.options.activeIds) {
|
||||||
|
this.options.activeIds.forEach(id => this.activeIds.add(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
// 创建子项
|
||||||
|
this.options.items.forEach(itemConfig => {
|
||||||
|
const item = new BimCollapseItem(itemConfig, this);
|
||||||
|
this.items.set(itemConfig.id, item);
|
||||||
|
this.element.appendChild(item.element);
|
||||||
|
|
||||||
|
// 设置初始状态
|
||||||
|
if (this.activeIds.has(itemConfig.id)) {
|
||||||
|
item.setActive(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 订阅语言变更
|
||||||
|
this.unsubscribeLocale = localeManager.subscribe(() => {
|
||||||
|
this.setLocales();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 订阅主题变更
|
||||||
|
this.unsubscribeTheme = themeManager.subscribe((theme) => {
|
||||||
|
this.setTheme(theme);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始应用主题
|
||||||
|
this.setTheme(themeManager.getTheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleItem(id: string) {
|
||||||
|
const isActive = this.activeIds.has(id);
|
||||||
|
|
||||||
|
if (this.options.accordion) {
|
||||||
|
// 手风琴模式:关闭其他所有,只展开目标
|
||||||
|
this.activeIds.clear();
|
||||||
|
if (!isActive) {
|
||||||
|
this.activeIds.add(id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 普通模式:切换目标状态
|
||||||
|
if (isActive) {
|
||||||
|
this.activeIds.delete(id);
|
||||||
|
} else {
|
||||||
|
this.activeIds.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refreshState();
|
||||||
|
|
||||||
|
if (this.options.onChange) {
|
||||||
|
this.options.onChange(Array.from(this.activeIds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private refreshState() {
|
||||||
|
this.items.forEach((item, id) => {
|
||||||
|
item.setActive(this.activeIds.has(id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public setTheme(theme: ThemeConfig): void {
|
||||||
|
const style = this.element.style;
|
||||||
|
style.setProperty('--bim-bg-color', theme.panelBackground);
|
||||||
|
style.setProperty('--bim-border-color', theme.border);
|
||||||
|
style.setProperty('--bim-text-color', theme.textPrimary);
|
||||||
|
|
||||||
|
// 头部默认背景色使用 componentBackground
|
||||||
|
style.setProperty('--bim-header-bg-color', theme.componentHover);
|
||||||
|
style.setProperty('--bim-header-hover-bg-color', theme.componentHover);
|
||||||
|
|
||||||
|
style.setProperty('--bim-content-bg-color', theme.panelBackground);
|
||||||
|
style.setProperty('--bim-disabled-color', theme.textSecondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLocales(): void {
|
||||||
|
this.items.forEach(item => item.updateLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
if (this.unsubscribeLocale) {
|
||||||
|
this.unsubscribeLocale();
|
||||||
|
this.unsubscribeLocale = null;
|
||||||
|
}
|
||||||
|
if (this.unsubscribeTheme) {
|
||||||
|
this.unsubscribeTheme();
|
||||||
|
this.unsubscribeTheme = null;
|
||||||
|
}
|
||||||
|
this.element.remove();
|
||||||
|
this.items.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/components/collapse/types.ts
Normal file
49
src/components/collapse/types.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
export interface CollapseItemConfig {
|
||||||
|
/** 唯一标识符 */
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/** 标题文本的翻译键 (例如 'panel.attributes') */
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
/** 内容: HTML字符串 或 HTMLElement */
|
||||||
|
content: string | HTMLElement;
|
||||||
|
|
||||||
|
/** 标题栏左侧图标 (SVG 字符串, 可选) */
|
||||||
|
icon?: string;
|
||||||
|
|
||||||
|
/** 标题栏右侧额外内容 (可选) */
|
||||||
|
extra?: string | HTMLElement;
|
||||||
|
|
||||||
|
/** 是否禁用 */
|
||||||
|
disabled?: boolean;
|
||||||
|
|
||||||
|
/** 自定义类名 */
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CollapseOptions {
|
||||||
|
/** 挂载容器 */
|
||||||
|
container: HTMLElement | string;
|
||||||
|
|
||||||
|
/** 面板项列表 */
|
||||||
|
items: CollapseItemConfig[];
|
||||||
|
|
||||||
|
/** 是否开启手风琴模式 (默认 false) */
|
||||||
|
accordion?: boolean;
|
||||||
|
|
||||||
|
/** 初始展开的面板 ID 列表 */
|
||||||
|
activeIds?: string[];
|
||||||
|
|
||||||
|
/** 是否显示边框 (默认 true) */
|
||||||
|
bordered?: boolean;
|
||||||
|
|
||||||
|
/** 是否幽灵模式 (默认 false) */
|
||||||
|
ghost?: boolean;
|
||||||
|
|
||||||
|
/** 自定义类名 */
|
||||||
|
className?: string;
|
||||||
|
|
||||||
|
/** 切换面板时的回调 */
|
||||||
|
onChange?: (activeIds: string[]) => void;
|
||||||
|
}
|
||||||
@@ -62,7 +62,6 @@
|
|||||||
|
|
||||||
.bim-dialog-content {
|
.bim-dialog-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 10px;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--bim-dialog-text-color);
|
color: var(--bim-dialog-text-color);
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ import { createEngine as createEngineSDK } from '../../bim-engine-sdk.es.js';
|
|||||||
// 重新导出类型,方便外部引用
|
// 重新导出类型,方便外部引用
|
||||||
export type { EngineOptions, ModelLoadOptions };
|
export type { EngineOptions, ModelLoadOptions };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Engine 实例的工厂函数
|
||||||
|
* 兼容旧代码直接 import { createEngine } 的方式
|
||||||
|
*/
|
||||||
|
export const createEngine = (options: EngineOptions) => {
|
||||||
|
return new Engine(options);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 3D 引擎组件
|
* 3D 引擎组件
|
||||||
* 负责创建和管理第三方 3D 引擎实例
|
* 负责创建和管理第三方 3D 引擎实例
|
||||||
|
|||||||
108
src/components/tab/index.css
Normal file
108
src/components/tab/index.css
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
.bim-tab {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--bim-tab-text, #e6e6e6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 4px 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--bim-tab-text, #e6e6e6);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease, border-color 0.2s ease;
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__item:hover:not(.is-disabled):not(.is-active) {
|
||||||
|
color: var(--bim-tab-text, #e6e6e6);
|
||||||
|
border-bottom-color: var(--bim-tab-border, rgba(255, 255, 255, 0.15));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__item.is-active {
|
||||||
|
color: var(--bim-tab-text-active, #4da3ff);
|
||||||
|
border-bottom-color: var(--bim-tab-text-active, #4da3ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__item.is-disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--bim-tab-icon, currentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__icon svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__title {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
min-height: 0; /* 防止撑开导致外层滚动 */
|
||||||
|
overflow: hidden; /* 限制滚动只在内部面板 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__panel {
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bim-tab__panel.is-active {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 构件树弹窗内的内容布局:tab+搜索固定,树区域滚动 */
|
||||||
|
.construct-tab__container {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construct-tab__panel-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0; /* 允许内部滚动 */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construct-tab__panel-content .bim-tree {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
254
src/components/tab/index.ts
Normal file
254
src/components/tab/index.ts
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
import { IBimComponent } from '../../types/component';
|
||||||
|
import { localeManager, t } from '../../services/locale';
|
||||||
|
import { themeManager } from '../../services/theme';
|
||||||
|
import type { ThemeConfig } from '../../themes/types';
|
||||||
|
import type { TabItem, TabOptions } from './index.type';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单标签页组件(固定标签,不支持运行时增删)
|
||||||
|
* - 仅处理标签头部与内容切换
|
||||||
|
* - 主题从 ThemeManager 获取,不在配置中传入
|
||||||
|
* - 文案通过 t() 翻译,支持传原文直接展示
|
||||||
|
*/
|
||||||
|
export class BimTab implements IBimComponent {
|
||||||
|
/** 组件根节点 */
|
||||||
|
public element: HTMLElement;
|
||||||
|
/** 头部容器 */
|
||||||
|
private navElement: HTMLElement;
|
||||||
|
/** 内容容器 */
|
||||||
|
private contentElement: HTMLElement;
|
||||||
|
/** 业务配置 */
|
||||||
|
private options: TabOptions;
|
||||||
|
/** 当前激活的标签 id */
|
||||||
|
private activeId: string | null;
|
||||||
|
/** id -> TabItem */
|
||||||
|
private tabMap: Map<string, TabItem> = new Map();
|
||||||
|
/** id -> 内容容器 */
|
||||||
|
private panelMap: Map<string, HTMLElement> = new Map();
|
||||||
|
/** 主题/语言订阅解除函数 */
|
||||||
|
private unsubscribeLocale: (() => void) | null = null;
|
||||||
|
private unsubscribeTheme: (() => void) | null = null;
|
||||||
|
/** 头部点击事件处理引用(便于销毁时解绑) */
|
||||||
|
private navClickHandler: ((e: MouseEvent) => void) | null = null;
|
||||||
|
|
||||||
|
constructor(options: TabOptions) {
|
||||||
|
this.options = options;
|
||||||
|
this.activeId = options.activeId || (options.tabs[0]?.id ?? null);
|
||||||
|
|
||||||
|
// 预置 tabMap,方便后续查找
|
||||||
|
options.tabs.forEach((tab) => this.tabMap.set(tab.id, tab));
|
||||||
|
|
||||||
|
// 构建基础 DOM 结构
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
this.element.className = 'bim-tab';
|
||||||
|
|
||||||
|
this.navElement = document.createElement('div');
|
||||||
|
this.navElement.className = 'bim-tab__nav';
|
||||||
|
this.navElement.setAttribute('role', 'tablist');
|
||||||
|
this.element.appendChild(this.navElement);
|
||||||
|
|
||||||
|
this.contentElement = document.createElement('div');
|
||||||
|
this.contentElement.className = 'bim-tab__content';
|
||||||
|
this.element.appendChild(this.contentElement);
|
||||||
|
|
||||||
|
// 挂载到容器
|
||||||
|
this.options.container.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化组件
|
||||||
|
*/
|
||||||
|
public init(): void {
|
||||||
|
this.renderNav();
|
||||||
|
this.renderPanels();
|
||||||
|
// 初始化文案与主题
|
||||||
|
this.setLocales();
|
||||||
|
this.setTheme(themeManager.getTheme());
|
||||||
|
|
||||||
|
// 订阅语言、主题变化
|
||||||
|
this.unsubscribeLocale = localeManager.subscribe(() => this.setLocales());
|
||||||
|
this.unsubscribeTheme = themeManager.subscribe((theme) => this.setTheme(theme));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染头部标签
|
||||||
|
*/
|
||||||
|
private renderNav(): void {
|
||||||
|
this.navElement.innerHTML = '';
|
||||||
|
|
||||||
|
this.navClickHandler = (event: MouseEvent) => {
|
||||||
|
const target = (event.target as HTMLElement).closest<HTMLButtonElement>('.bim-tab__item');
|
||||||
|
if (!target) return;
|
||||||
|
const tabId = target.dataset.id;
|
||||||
|
if (!tabId) return;
|
||||||
|
const tab = this.tabMap.get(tabId);
|
||||||
|
if (tab?.disabled) return;
|
||||||
|
this.activateTab(tabId);
|
||||||
|
};
|
||||||
|
this.navElement.addEventListener('click', this.navClickHandler);
|
||||||
|
|
||||||
|
this.options.tabs.forEach((tab) => {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.type = 'button';
|
||||||
|
btn.className = 'bim-tab__item';
|
||||||
|
btn.dataset.id = tab.id;
|
||||||
|
btn.setAttribute('role', 'tab');
|
||||||
|
btn.id = `tab-${tab.id}`;
|
||||||
|
btn.setAttribute('aria-selected', `${tab.id === this.activeId}`);
|
||||||
|
if (tab.disabled) {
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.setAttribute('aria-disabled', 'true');
|
||||||
|
btn.classList.add('is-disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图标
|
||||||
|
if (tab.icon) {
|
||||||
|
const iconEl = document.createElement('span');
|
||||||
|
iconEl.className = 'bim-tab__icon';
|
||||||
|
iconEl.innerHTML = tab.icon;
|
||||||
|
btn.appendChild(iconEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleEl = document.createElement('span');
|
||||||
|
titleEl.className = 'bim-tab__title';
|
||||||
|
titleEl.textContent = this.resolveTitle(tab.title);
|
||||||
|
btn.appendChild(titleEl);
|
||||||
|
|
||||||
|
if (tab.id === this.activeId) {
|
||||||
|
btn.classList.add('is-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.navElement.appendChild(btn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染内容面板
|
||||||
|
*/
|
||||||
|
private renderPanels(): void {
|
||||||
|
this.contentElement.innerHTML = '';
|
||||||
|
this.panelMap.clear();
|
||||||
|
|
||||||
|
this.options.tabs.forEach((tab) => {
|
||||||
|
const panel = document.createElement('div');
|
||||||
|
panel.className = 'bim-tab__panel';
|
||||||
|
panel.dataset.id = tab.id;
|
||||||
|
panel.setAttribute('role', 'tabpanel');
|
||||||
|
panel.setAttribute('aria-labelledby', `tab-${tab.id}`);
|
||||||
|
|
||||||
|
if (tab.content instanceof HTMLElement) {
|
||||||
|
panel.appendChild(tab.content);
|
||||||
|
} else if (typeof tab.content === 'string') {
|
||||||
|
panel.innerHTML = tab.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tab.id === this.activeId) {
|
||||||
|
panel.classList.add('is-active');
|
||||||
|
} else {
|
||||||
|
panel.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.panelMap.set(tab.id, panel);
|
||||||
|
this.contentElement.appendChild(panel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 激活指定标签
|
||||||
|
* @param tabId 目标标签 id
|
||||||
|
*/
|
||||||
|
public activateTab(tabId: string): void {
|
||||||
|
if (this.activeId === tabId) return;
|
||||||
|
const targetTab = this.tabMap.get(tabId);
|
||||||
|
if (!targetTab || targetTab.disabled) return;
|
||||||
|
|
||||||
|
this.activeId = tabId;
|
||||||
|
// 更新头部状态
|
||||||
|
const buttons = this.navElement.querySelectorAll<HTMLButtonElement>('.bim-tab__item');
|
||||||
|
buttons.forEach((btn) => {
|
||||||
|
const isActive = btn.dataset.id === tabId;
|
||||||
|
btn.classList.toggle('is-active', isActive);
|
||||||
|
btn.setAttribute('aria-selected', `${isActive}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新面板显示
|
||||||
|
this.panelMap.forEach((panel, id) => {
|
||||||
|
const isActive = id === tabId;
|
||||||
|
panel.classList.toggle('is-active', isActive);
|
||||||
|
panel.style.display = isActive ? 'block' : 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.options.onChange) {
|
||||||
|
this.options.onChange(tabId, targetTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用主题
|
||||||
|
*/
|
||||||
|
public setTheme(theme: ThemeConfig): void {
|
||||||
|
const style = this.element.style;
|
||||||
|
style.setProperty('--bim-tab-bg', theme.panelBackground);
|
||||||
|
style.setProperty('--bim-tab-nav-bg', theme.panelBackground);
|
||||||
|
style.setProperty('--bim-tab-text', theme.textPrimary);
|
||||||
|
style.setProperty('--bim-tab-text-secondary', theme.textSecondary);
|
||||||
|
style.setProperty('--bim-tab-text-active', theme.primary);
|
||||||
|
style.setProperty('--bim-tab-border', theme.border);
|
||||||
|
style.setProperty('--bim-tab-hover-bg', theme.componentHover);
|
||||||
|
style.setProperty('--bim-tab-active-bg', theme.componentActive);
|
||||||
|
style.setProperty('--bim-tab-icon', theme.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用当前语言文案
|
||||||
|
*/
|
||||||
|
public setLocales(): void {
|
||||||
|
const buttons = this.navElement.querySelectorAll<HTMLButtonElement>('.bim-tab__item');
|
||||||
|
buttons.forEach((btn) => {
|
||||||
|
const id = btn.dataset.id;
|
||||||
|
if (!id) return;
|
||||||
|
const tab = this.tabMap.get(id);
|
||||||
|
if (!tab) return;
|
||||||
|
const titleEl = btn.querySelector<HTMLElement>('.bim-tab__title');
|
||||||
|
if (titleEl) {
|
||||||
|
titleEl.textContent = this.resolveTitle(tab.title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理资源
|
||||||
|
*/
|
||||||
|
public destroy(): void {
|
||||||
|
if (this.navClickHandler) {
|
||||||
|
this.navElement.removeEventListener('click', this.navClickHandler);
|
||||||
|
this.navClickHandler = null;
|
||||||
|
}
|
||||||
|
if (this.unsubscribeLocale) {
|
||||||
|
this.unsubscribeLocale();
|
||||||
|
this.unsubscribeLocale = null;
|
||||||
|
}
|
||||||
|
if (this.unsubscribeTheme) {
|
||||||
|
this.unsubscribeTheme();
|
||||||
|
this.unsubscribeTheme = null;
|
||||||
|
}
|
||||||
|
this.panelMap.clear();
|
||||||
|
this.tabMap.clear();
|
||||||
|
this.element.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具:解析标题(优先翻译,不存在则回退原值)
|
||||||
|
*/
|
||||||
|
private resolveTitle(title: string): string {
|
||||||
|
try {
|
||||||
|
const translated = t(title);
|
||||||
|
return translated || title;
|
||||||
|
} catch (err) {
|
||||||
|
// 翻译失败时使用原值
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
37
src/components/tab/index.type.ts
Normal file
37
src/components/tab/index.type.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 标签项定义(TabItem)
|
||||||
|
* 用于描述单个标签页的基础信息和内容。
|
||||||
|
*/
|
||||||
|
export interface TabItem {
|
||||||
|
/** 唯一标识 */
|
||||||
|
id: string;
|
||||||
|
/** 标题文案或翻译键,渲染时统一走 t() */
|
||||||
|
title: string;
|
||||||
|
/** 是否禁用 */
|
||||||
|
disabled?: boolean;
|
||||||
|
/** 可选图标,支持内联 SVG / HTML 字符串 */
|
||||||
|
icon?: string;
|
||||||
|
/** 可选的内容区域,支持 HTMLElement 或 HTML 字符串 */
|
||||||
|
content?: HTMLElement | string;
|
||||||
|
/** 业务侧自定义附加数据 */
|
||||||
|
meta?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab 组件初始化参数
|
||||||
|
*/
|
||||||
|
export interface TabOptions {
|
||||||
|
/** 挂载容器 */
|
||||||
|
container: HTMLElement;
|
||||||
|
/** 预设的固定标签列表(不支持运行期增删) */
|
||||||
|
tabs: TabItem[];
|
||||||
|
/** 初始激活的标签 id,默认使用首个标签 */
|
||||||
|
activeId?: string;
|
||||||
|
/**
|
||||||
|
* 切换回调
|
||||||
|
* @param tabId 当前激活的标签 id
|
||||||
|
* @param tab 当前激活的标签对象
|
||||||
|
*/
|
||||||
|
onChange?: (tabId: string, tab: TabItem | undefined) => void;
|
||||||
|
}
|
||||||
|
|
||||||
31
src/index.ts
31
src/index.ts
@@ -1,21 +1,16 @@
|
|||||||
import { BimEngine } from './bim-engine';
|
// Main Entry
|
||||||
|
export * from './bim-engine';
|
||||||
|
|
||||||
// 导出通用组件
|
// Types - Core
|
||||||
export { BimButtonGroup } from './components/button-group';
|
export * from './types/component';
|
||||||
export { Toolbar } from './components/button-group/toolbar';
|
export * from './types/events';
|
||||||
|
export type { ThemeConfig, ThemeType } from './themes/types';
|
||||||
|
|
||||||
// 导出相关类型定义
|
// Types - Components
|
||||||
export type { OptButton, ButtonGroup, ButtonGroupOptions, ClickPayload } from './components/button-group/index.type';
|
export type { EngineOptions, ModelLoadOptions } from './components/engine/types';
|
||||||
|
export type { DialogOptions, DialogPosition } from './components/dialog/index.type';
|
||||||
|
export type { ButtonConfig, ButtonGroupOptions } from './components/button-group/index.type';
|
||||||
|
export type { TreeOptions, TreeNodeConfig, TreeNodeCheckState, NodeClickAction } from './components/tree/types';
|
||||||
|
export type { CollapseOptions, CollapseItemConfig } from './components/collapse/types';
|
||||||
|
|
||||||
// 导出 RightKey/Menu 组件
|
// Note: Component classes are intentionally NOT exported to enforce Manager pattern usage.
|
||||||
export type { MenuItemConfig } from './components/menu/item';
|
|
||||||
export { BimMenu } from './components/menu';
|
|
||||||
export { BimRightKey } from './components/right-key';
|
|
||||||
|
|
||||||
// 导出主引擎类
|
|
||||||
export { BimEngine };
|
|
||||||
|
|
||||||
// 导出 3D 引擎相关类型
|
|
||||||
export type { EngineOptions, ModelLoadOptions } from './components/engine';
|
|
||||||
|
|
||||||
export { createEngine } from './bim-engine-sdk.es.js';
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const enUS: TranslationDictionary = {
|
|||||||
walkPerson: 'Person',
|
walkPerson: 'Person',
|
||||||
walkBird: 'Bird Eye',
|
walkBird: 'Bird Eye',
|
||||||
walkMenu: 'Menu',
|
walkMenu: 'Menu',
|
||||||
|
tree: 'Tree',
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
testTitle: 'Test Dialog',
|
testTitle: 'Test Dialog',
|
||||||
@@ -35,5 +36,14 @@ export const enUS: TranslationDictionary = {
|
|||||||
component: 'Component',
|
component: 'Component',
|
||||||
system: 'System',
|
system: 'System',
|
||||||
space: 'Space',
|
space: 'Space',
|
||||||
|
},
|
||||||
|
panel: {
|
||||||
|
property: {
|
||||||
|
title: 'Property Panel',
|
||||||
|
base: 'Basic Info',
|
||||||
|
material: 'Material',
|
||||||
|
advanced: 'Advanced'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,18 @@ export interface TranslationDictionary {
|
|||||||
location: string;
|
location: string;
|
||||||
setting: string;
|
setting: string;
|
||||||
walk: string;
|
walk: string;
|
||||||
|
walkMenu: string;
|
||||||
walkPerson: string;
|
walkPerson: string;
|
||||||
walkBird: string;
|
walkBird: string;
|
||||||
walkMenu: string;
|
tree: string;
|
||||||
|
};
|
||||||
|
panel: {
|
||||||
|
property: {
|
||||||
|
title: string;
|
||||||
|
base: string;
|
||||||
|
material: string;
|
||||||
|
advanced: string;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
dialog: {
|
dialog: {
|
||||||
testTitle: string;
|
testTitle: string;
|
||||||
@@ -32,12 +41,12 @@ export interface TranslationDictionary {
|
|||||||
};
|
};
|
||||||
constructTree: {
|
constructTree: {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
};
|
||||||
tab: {
|
tab: {
|
||||||
component: string;
|
component: string;
|
||||||
system: string;
|
system: string;
|
||||||
space: string;
|
space: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ export const zhCN: TranslationDictionary = {
|
|||||||
location: '定位',
|
location: '定位',
|
||||||
setting: '设置',
|
setting: '设置',
|
||||||
walk: '漫游',
|
walk: '漫游',
|
||||||
walkPerson: '人视',
|
walkMenu: '漫游菜单',
|
||||||
walkBird: '鸟瞰',
|
walkPerson: '第一人称',
|
||||||
walkMenu: '菜单',
|
walkBird: '第三人称',
|
||||||
|
tree: '模型树'
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
testTitle: '测试弹窗',
|
testTitle: '测试弹窗',
|
||||||
@@ -35,5 +36,13 @@ export const zhCN: TranslationDictionary = {
|
|||||||
component: '构件',
|
component: '构件',
|
||||||
system: '系统',
|
system: '系统',
|
||||||
space: '空间',
|
space: '空间',
|
||||||
|
},
|
||||||
|
panel: {
|
||||||
|
property: {
|
||||||
|
title: '属性面板',
|
||||||
|
base: '基本属性',
|
||||||
|
material: '材质信息',
|
||||||
|
advanced: '高级设置'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
92
src/managers/property-panel-manager.ts
Normal file
92
src/managers/property-panel-manager.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { BimComponent } from '../core/component';
|
||||||
|
import { BimEngine } from '../bim-engine';
|
||||||
|
import { BimCollapse } from '../components/collapse/index';
|
||||||
|
|
||||||
|
export class PropertyPanelManager extends BimComponent {
|
||||||
|
|
||||||
|
constructor(engine: BimEngine) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(): void {
|
||||||
|
document.addEventListener('bim-demo:open-property-panel', () => {
|
||||||
|
this.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public show() {
|
||||||
|
if (!this.engine.dialog) {
|
||||||
|
console.warn('Dialog manager is not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dialog = this.engine.dialog.create({
|
||||||
|
title: 'panel.property.title', // '属性面板'
|
||||||
|
minWidth: 320,
|
||||||
|
height: 420,
|
||||||
|
position: 'top-right',
|
||||||
|
resizable: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Create Content Container
|
||||||
|
const contentContainer = document.createElement('div');
|
||||||
|
contentContainer.style.height = '100%';
|
||||||
|
contentContainer.style.overflowY = 'auto';
|
||||||
|
|
||||||
|
// Use public API to set content
|
||||||
|
dialog.setContent(contentContainer);
|
||||||
|
|
||||||
|
// 3. Create Collapse inside the Dialog
|
||||||
|
new BimCollapse({
|
||||||
|
container: contentContainer,
|
||||||
|
accordion: true,
|
||||||
|
activeIds: ['base'],
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 'base',
|
||||||
|
title: 'panel.property.base', // '基本属性'
|
||||||
|
content: this.createBaseInfoContent(),
|
||||||
|
icon: '<svg viewBox="0 0 1024 1024"><path d="M512 64q190.4 0 326.4 136T974.4 526.4 838.4 852.8 512 988.8 185.6 852.8 49.6 526.4 185.6 200 512 64m0-64C229.6 0 0 229.6 0 512s229.6 512 512 512 512-229.6 512-512S794.4 0 512 0z" fill="currentColor"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'material',
|
||||||
|
title: 'panel.property.material', // '材质信息'
|
||||||
|
content: this.createMaterialContent(),
|
||||||
|
icon: '<svg viewBox="0 0 1024 1024"><path d="M128 128h768v768H128z" fill="none" stroke="currentColor" stroke-width="64"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'advanced',
|
||||||
|
title: 'panel.property.advanced', // '高级设置'
|
||||||
|
content: '<div>Loading...</div>', // Placeholder
|
||||||
|
disabled: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private createBaseInfoContent(): HTMLElement {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.style.padding = '8px 0';
|
||||||
|
div.innerHTML = `
|
||||||
|
<div style="margin-bottom:8px"><b>ID:</b> <span style="color:#666">E-2023001</span></div>
|
||||||
|
<div style="margin-bottom:8px"><b>Name:</b> <span style="color:#666">Wall-01</span></div>
|
||||||
|
<div style="margin-bottom:8px"><b>Level:</b> <span style="color:#666">F1</span></div>
|
||||||
|
`;
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createMaterialContent(): HTMLElement {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = `
|
||||||
|
<div style="display:flex;align-items:center;margin-bottom:8px">
|
||||||
|
<div style="width:20px;height:20px;background:#ccc;margin-right:8px;border:1px solid #999"></div>
|
||||||
|
<span>Concrete</span>
|
||||||
|
</div>
|
||||||
|
<div>Density: 2400 kg/m³</div>
|
||||||
|
`;
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
// Cleanup if needed
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,11 +8,27 @@ export interface EngineEvents {
|
|||||||
'engine:object-clicked': { objectId: string; position: { x: number, y: number, z: number } };
|
'engine:object-clicked': { objectId: string; position: { x: number, y: number, z: number } };
|
||||||
|
|
||||||
// 树组件事件
|
// 树组件事件
|
||||||
|
|
||||||
'ui:tree-node-check': { id: string; checked: boolean; node: any };
|
'ui:tree-node-check': { id: string; checked: boolean; node: any };
|
||||||
|
|
||||||
'ui:tree-node-select': { id: string; selected: boolean; node: any };
|
'ui:tree-node-select': { id: string; selected: boolean; node: any };
|
||||||
|
|
||||||
'ui:tree-node-expand': { id: string; expanded: boolean };
|
'ui:tree-node-expand': { id: string; expanded: boolean };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 折叠面板事件
|
||||||
|
|
||||||
|
'ui:collapse-change': { activeIds: string[] };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 系统事件
|
// 系统事件
|
||||||
|
|
||||||
'sys:theme-changed': { theme: string };
|
'sys:theme-changed': { theme: string };
|
||||||
|
|
||||||
'sys:locale-changed': { locale: string };
|
'sys:locale-changed': { locale: string };
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user