添加测试信息
This commit is contained in:
@@ -537,8 +537,7 @@ engine.toolbar.setButtonVisibility('my-button', false);
|
||||
| `ButtonGroupManager` | `src/managers/button-group-manager.ts` | 管理通用按钮组 | `BimComponent` |
|
||||
| `EngineManager` | `src/managers/engine-manager.ts` | 管理 3D 引擎 | `BimComponent` |
|
||||
| `RightKeyManager` | `src/managers/right-key-manager.ts` | 管理右键菜单 (Context Menu) | `BimComponent` |
|
||||
| `TreeManager` | `src/managers/tree-manager.ts` | 管理树组件实例 | `BimComponent` |
|
||||
| `ModelTreeManager` | `src/managers/model-tree-manager.ts` | 模型树业务管理器 (组合 Dialog 和 Tree) | `BimComponent` |
|
||||
| `ModelTreeManager` | `src/managers/model-tree-manager.ts` | 模型树业务管理器 (组合 Dialog 和 Tree),管理 Tree 实例 | `BimComponent` |
|
||||
|
||||
### 4.2 组件类清单
|
||||
|
||||
|
||||
@@ -185,63 +185,14 @@ const openRedDialog = () => {
|
||||
|
||||
const openTreeDialog = () => {
|
||||
if (!engine.value || !engine.value.modelTree) return;
|
||||
|
||||
// 1. 构造树数据
|
||||
const treeData = [
|
||||
{
|
||||
id: 'root',
|
||||
label: 'tree.modelStruct', // 翻译键
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
id: 'level-1',
|
||||
label: 'tree.floor1',
|
||||
expanded: true,
|
||||
children: [
|
||||
{ id: 'l1-wall', label: 'tree.wall', checked: true },
|
||||
{ id: 'l1-col', label: 'tree.column' },
|
||||
{ id: 'l1-win', label: 'tree.window' },
|
||||
{ id: 'l1-door', label: 'tree.door' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'level-2',
|
||||
label: 'tree.floor2',
|
||||
children: [
|
||||
{ id: 'l2-wall', label: 'tree.wall' },
|
||||
{ id: 'l2-col', label: 'tree.column' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 2. 使用高级 API
|
||||
engine.value.modelTree.showStructTree(treeData);
|
||||
// 使用 Manager 中的默认 Mock 数据
|
||||
engine.value.modelTree.showStructTree();
|
||||
};
|
||||
|
||||
const openSimpleTreeDialog = () => {
|
||||
if (!engine.value || !engine.value.modelTree) return;
|
||||
|
||||
const treeData = [
|
||||
{
|
||||
id: 'menu-root',
|
||||
label: 'menu.home',
|
||||
expanded: true,
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path></svg>',
|
||||
children: [
|
||||
{ id: 'm-info', label: 'menu.info' },
|
||||
{
|
||||
id: 'm-struct', label: 'tree.modelStruct', children: [
|
||||
{ id: 'm-w', label: 'tree.wall' },
|
||||
{ id: 'm-c', label: 'tree.column' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
engine.value.modelTree.showSimpleTree(treeData, 'Simple Tree');
|
||||
// 使用 Manager 中的默认 Mock 数据
|
||||
engine.value.modelTree.showSimpleTree(undefined, 'Simple Tree');
|
||||
};
|
||||
|
||||
// --- 工具栏操作 ---
|
||||
|
||||
@@ -132,8 +132,6 @@
|
||||
<button onclick="openTestDialog()">测试弹窗</button>
|
||||
<button onclick="openInfoDialog()">信息弹窗</button>
|
||||
<button onclick="openRedDialog()">警告弹窗</button>
|
||||
<button onclick="openTreeDialog()">树组件测试</button>
|
||||
<button onclick="openSimpleTreeDialog()">纯树测试</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -241,69 +239,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// --- 树组件测试 ---
|
||||
function openTreeDialog() {
|
||||
if (!engine || !engine.modelTree) return;
|
||||
|
||||
// 1. 构造树数据
|
||||
const treeData = [
|
||||
{
|
||||
id: 'root',
|
||||
label: 'tree.modelStruct',
|
||||
expanded: true,
|
||||
children: [
|
||||
{
|
||||
id: 'level-1',
|
||||
label: 'tree.floor1',
|
||||
expanded: true,
|
||||
children: [
|
||||
{ id: 'l1-wall', label: 'tree.wall', checked: true },
|
||||
{ id: 'l1-col', label: 'tree.column' },
|
||||
{ id: 'l1-win', label: 'tree.window' },
|
||||
{ id: 'l1-door', label: 'tree.door' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'level-2',
|
||||
label: 'tree.floor2',
|
||||
children: [
|
||||
{ id: 'l2-wall', label: 'tree.wall' },
|
||||
{ id: 'l2-col', label: 'tree.column' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 2. 使用高级 API 直接显示
|
||||
engine.modelTree.showStructTree(treeData);
|
||||
}
|
||||
|
||||
// --- 纯树测试 (无复选框) ---
|
||||
function openSimpleTreeDialog() {
|
||||
if (!engine || !engine.modelTree) return;
|
||||
|
||||
// 简单数据
|
||||
const treeData = [
|
||||
{
|
||||
id: 'menu-root',
|
||||
label: 'menu.home',
|
||||
expanded: true,
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path></svg>',
|
||||
children: [
|
||||
{ id: 'm-info', label: 'menu.info' },
|
||||
{ id: 'm-struct', label: 'tree.modelStruct', children: [
|
||||
{ id: 'm-w', label: 'tree.wall' },
|
||||
{ id: 'm-c', label: 'tree.column' }
|
||||
]}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 使用高级 API
|
||||
engine.modelTree.showSimpleTree(treeData, 'Simple Tree');
|
||||
}
|
||||
|
||||
// --- 工具栏操作 ---
|
||||
function toggleToolbar() {
|
||||
if (!engine || !engine.toolbar) return;
|
||||
|
||||
9580
dist/bim-engine-sdk.es.js
vendored
9580
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
386
dist/bim-engine-sdk.umd.js
vendored
386
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
135
dist/index.d.ts
vendored
135
dist/index.d.ts
vendored
@@ -18,6 +18,10 @@ export declare class BimButtonGroup implements IBimComponent {
|
||||
setEngine(engine: BimEngine): void;
|
||||
protected emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]): void;
|
||||
private initContainer;
|
||||
/**
|
||||
* 设置事件拦截,防止事件<E4BA8B><E4BBB6>泡到下层元素(如 3D 引擎)
|
||||
*/
|
||||
private setupEventInterception;
|
||||
private updatePosition;
|
||||
/**
|
||||
* 应用样式到容器
|
||||
@@ -111,6 +115,11 @@ declare class BimDialog implements IBimComponent {
|
||||
* 设置元素尺寸
|
||||
*/
|
||||
private setSize;
|
||||
/**
|
||||
* 根据内容自动调整弹窗宽度
|
||||
* @param recenter 是否重新计算定位(例如保持居中),默认 true
|
||||
*/
|
||||
fitWidth(recenter?: boolean): void;
|
||||
/**
|
||||
* 初始化弹窗位置
|
||||
*/
|
||||
@@ -142,11 +151,11 @@ export declare class BimEngine extends EventEmitter {
|
||||
private container;
|
||||
private wrapper;
|
||||
toolbar: ToolbarManager | null;
|
||||
constructTreeBtn: ConstructTreeManagerBtn | null;
|
||||
buttonGroup: ButtonGroupManager | null;
|
||||
dialog: DialogManager | null;
|
||||
engine: EngineManager | null;
|
||||
get localeManager(): LocaleManager;
|
||||
get themeManager(): ThemeManager;
|
||||
rightKey: RightKeyManager | null;
|
||||
constructor(container: HTMLElement | string, options?: {
|
||||
locale?: LocaleType;
|
||||
theme?: ThemeType;
|
||||
@@ -230,11 +239,17 @@ export declare class BimRightKey implements IBimComponent {
|
||||
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 状态变更
|
||||
@@ -343,15 +358,38 @@ export declare interface ClickPayload {
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export declare function createEngine(r) {
|
||||
const e = r.version || "v1";
|
||||
/**
|
||||
* 底部工具栏管理器 (ToolbarManager)
|
||||
* 仅负责管理底部工具栏实例。
|
||||
*/
|
||||
declare class ConstructTreeManagerBtn extends BimComponent {
|
||||
private toolbar;
|
||||
private toolbarContainer;
|
||||
private container;
|
||||
private dialog;
|
||||
constructor(engine: BimEngine, container: HTMLElement);
|
||||
private init;
|
||||
openConstructTreeDialog(): void;
|
||||
refresh(): void;
|
||||
destroy(): void;
|
||||
addGroup(groupId: string, beforeGroupId?: string): void;
|
||||
addButton(config: ButtonConfig): void;
|
||||
setButtonVisibility(id: string, v: boolean): void;
|
||||
setShowLabel(show: boolean): void;
|
||||
setVisible(visible: boolean): void;
|
||||
setBackgroundColor(color: string): void;
|
||||
setColors(colors: ButtonGroupColors): void;
|
||||
}
|
||||
|
||||
export declare function createEngine(s) {
|
||||
const e = s.version || "v1";
|
||||
switch (e) {
|
||||
case "v2":
|
||||
return new Oc(r);
|
||||
return new Fc(s);
|
||||
case "v1":
|
||||
return new U_(r);
|
||||
return new O_(s);
|
||||
:
|
||||
return console.warn(`Version '${e}' not found. Falling back to v2.`), new Oc(r);
|
||||
return console.warn(`Version '${e}' not found. Falling back to v2.`), new Fc(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,6 +504,20 @@ declare interface EngineEvents {
|
||||
z: number;
|
||||
};
|
||||
};
|
||||
'ui:tree-node-check': {
|
||||
id: string;
|
||||
checked: boolean;
|
||||
node: any;
|
||||
};
|
||||
'ui:tree-node-select': {
|
||||
id: string;
|
||||
selected: boolean;
|
||||
node: any;
|
||||
};
|
||||
'ui:tree-node-expand': {
|
||||
id: string;
|
||||
expanded: boolean;
|
||||
};
|
||||
'sys:theme-changed': {
|
||||
theme: string;
|
||||
};
|
||||
@@ -595,35 +647,6 @@ declare interface IRightKeyContent {
|
||||
|
||||
declare type Listener<T = any> = (payload: T) => void;
|
||||
|
||||
declare type LocaleChangeListener = (locale: LocaleType) => void;
|
||||
|
||||
/**
|
||||
* 语言管理器类
|
||||
*/
|
||||
declare class LocaleManager {
|
||||
private currentLocale;
|
||||
private messages;
|
||||
private listeners;
|
||||
constructor();
|
||||
/**
|
||||
* 获取当前语言
|
||||
*/
|
||||
getLocale(): LocaleType;
|
||||
/**
|
||||
* 切换语言
|
||||
*/
|
||||
setLocale(locale: LocaleType): void;
|
||||
/**
|
||||
* 翻译核心方法
|
||||
*/
|
||||
t(key: string): string;
|
||||
/**
|
||||
* 订阅变更
|
||||
*/
|
||||
subscribe(listener: LocaleChangeListener): () => void;
|
||||
private notifyListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* 语言代码类型
|
||||
*/
|
||||
@@ -693,7 +716,6 @@ declare class RightKeyManager extends BimComponent {
|
||||
private rightKeyPanel;
|
||||
private contextHandlers;
|
||||
constructor(engine: BimEngine, container: HTMLElement);
|
||||
private initEventListeners;
|
||||
destroy(): void;
|
||||
/**
|
||||
* 注册上下文菜单处理器
|
||||
@@ -715,6 +737,7 @@ declare class RightKeyManager extends BimComponent {
|
||||
hide(): void;
|
||||
/**
|
||||
* 处理右键点击事件
|
||||
* 由 BimRightKey 组件在检测到有效右键点击时调用
|
||||
*/
|
||||
private handleContextMenu;
|
||||
}
|
||||
@@ -724,10 +747,12 @@ declare interface RightKeyOptions {
|
||||
className?: string;
|
||||
/** 层级 (z-index) */
|
||||
zIndex?: number;
|
||||
/** 监听事件的容器 */
|
||||
container?: HTMLElement;
|
||||
/** 有效右键点击时的回调 */
|
||||
onContext?: (e: MouseEvent) => void;
|
||||
}
|
||||
|
||||
declare type ThemeChangeListener = (theme: ThemeConfig) => void;
|
||||
|
||||
/**
|
||||
* 全局主题配置接口
|
||||
* 定义系统通用的语义化颜色
|
||||
@@ -761,38 +786,6 @@ declare interface ThemeConfig {
|
||||
componentActive: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主题管理器 (单例)
|
||||
*/
|
||||
declare class ThemeManager {
|
||||
private currentTheme;
|
||||
private listeners;
|
||||
constructor();
|
||||
/**
|
||||
* 获取当前主题配置
|
||||
*/
|
||||
getTheme(): ThemeConfig;
|
||||
/**
|
||||
* 切换预设主题
|
||||
* @param themeName 'dark' | 'light'
|
||||
*/
|
||||
setTheme(themeName: 'dark' | 'light'): void;
|
||||
/**
|
||||
* 应用自定义主题配置
|
||||
* @param theme 配置对象
|
||||
*/
|
||||
setCustomTheme(theme: ThemeConfig): void;
|
||||
/**
|
||||
* 内部应用主题逻辑
|
||||
*/
|
||||
private applyTheme;
|
||||
/**
|
||||
* 订阅主题变更
|
||||
*/
|
||||
subscribe(listener: ThemeChangeListener): () => void;
|
||||
private notifyListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主题类型定义
|
||||
*/
|
||||
|
||||
@@ -1181,3 +1181,5 @@ type ExpandDirection = 'up' | 'down' | 'left' | 'right';
|
||||
|
||||
**重要提醒**: 本文档必须与组件代码保持同步。任何组件修改都必须更新本文档!
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -880,3 +880,5 @@ interface DialogColors {
|
||||
|
||||
**重要提醒**: 本文档必须与组件代码保持同步。任何组件修改都必须更新本文档!
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -599,3 +599,5 @@ interface ModelLoadOptions {
|
||||
|
||||
**重要提醒**: 本文档必须与组件代码保持同步。任何组件修改都必须更新本文档!
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -314,5 +314,5 @@ interface MenuOptions {
|
||||
## 13. 更新记录
|
||||
|
||||
| 日期 | 修改内容 | 修改人 |
|
||||
|------|---------|--------|
|
||||
| ---------- | ----------------------------------------- | ------------ |
|
||||
| 2024-XX-XX | 重构为纯配置对象驱动,移除 BimMenuItem 类 | AI Assistant |
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 1. 组件概述
|
||||
**BimTree** 是一个通用的树形组件,支持多级嵌套、复选框、图标和自定义内容。它通常用于展示模型结构、文件目录或层级列表。
|
||||
该组件设计为被包裹在容器(如 Dialog)中,并通过 `TreeManager` 进行创建和管理。
|
||||
该组件设计为被包裹在容器(如 Dialog)中,并通过 `ModelTreeManager` 进行创建和管理。
|
||||
|
||||
## 2. 组件类 API (BimTree)
|
||||
`src/components/tree/index.ts`
|
||||
@@ -15,11 +15,29 @@
|
||||
* `getCheckedNodes(includeHalfChecked?: boolean)`: 获取当前所有被勾选的节点配置列表。
|
||||
* `getNode(id: string)`: 获取指定 ID 的节点实例。
|
||||
|
||||
## 3. Manager API (TreeManager)
|
||||
`src/managers/tree-manager.ts`
|
||||
## 3. Manager API (ModelTreeManager)
|
||||
|
||||
* `create(options: TreeOptions)`: 创建并返回一个新的 BimTree 实例。
|
||||
* `options`: 参见 `TreeOptions` 类型定义。
|
||||
`ModelTreeManager` 负责创建和管理 `BimTree` 实例。
|
||||
|
||||
### 方法
|
||||
|
||||
#### `createTree(options: TreeOptions): BimTree`
|
||||
创建一个新的树组件实例。
|
||||
|
||||
- **参数**: `options` (TreeOptions) - 树组件配置
|
||||
- **返回**: `BimTree` - 树组件实例
|
||||
- **功能**:
|
||||
- 实例化组件
|
||||
- 自动绑定组件事件到全局事件总线 (`ui:tree-node-check`, `ui:tree-node-select` 等)
|
||||
- 初始化组件
|
||||
|
||||
#### `showStructTree(data: TreeNodeConfig[], title: string): { tree: BimTree, dialog: BimDialog }`
|
||||
显示带复选框的模型结构树弹窗。
|
||||
|
||||
#### `showSimpleTree(data: TreeNodeConfig[], title: string): { tree: BimTree, dialog: BimDialog }`
|
||||
显示简单的树形弹窗 (无复选框)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 类型定义
|
||||
`src/components/tree/types.ts`
|
||||
@@ -42,6 +60,11 @@ interface TreeOptions {
|
||||
checkStrictly?: boolean; // 是否父子联动 (默认 true)
|
||||
defaultExpandAll?: boolean;
|
||||
indent?: number;
|
||||
|
||||
// 事件回调
|
||||
onNodeCheck?: (node: BimTreeNode) => void;
|
||||
onNodeSelect?: (node: BimTreeNode) => void;
|
||||
onNodeExpand?: (node: BimTreeNode) => void;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -73,7 +96,8 @@ interface TreeOptions {
|
||||
|
||||
## 8. 使用示例
|
||||
```typescript
|
||||
const tree = engine.tree.create({
|
||||
// 通过 ModelTreeManager 创建
|
||||
const tree = engine.modelTree.createTree({
|
||||
data: [
|
||||
{ id: 'root', label: 'tree.root', children: [...] }
|
||||
],
|
||||
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.2.6",
|
||||
"vite-plugin-css-injected-by-js": "^3.5.2",
|
||||
@@ -1055,6 +1056,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-25.0.2.tgz",
|
||||
"integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@volar/language-core": {
|
||||
"version": "2.4.26",
|
||||
"resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.26.tgz",
|
||||
@@ -1992,6 +2003,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.2.6",
|
||||
"vite-plugin-css-injected-by-js": "^3.5.2",
|
||||
|
||||
@@ -21,3 +21,11 @@
|
||||
transform: translateX(-50%);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* 构造树按钮 */
|
||||
.bim-construct-tree-btn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px !important;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@ import { ToolbarManager } from './managers/toolbar-manager';
|
||||
import { ButtonGroupManager } from './managers/button-group-manager';
|
||||
import { DialogManager } from './managers/dialog-manager';
|
||||
import { EngineManager } from './managers/engine-manager';
|
||||
import { TreeManager } from './managers/tree-manager';
|
||||
import { RightKeyManager } from './managers/right-key-manager';
|
||||
import { ModelTreeManager } from './managers/model-tree-manager';
|
||||
import { ConstructTreeManagerBtn } from './managers/construct-tree-manager-btn';
|
||||
import type { EngineOptions, ModelLoadOptions } from './components/engine';
|
||||
import { localeManager } from './services/locale';
|
||||
import { themeManager } from './services/theme';
|
||||
@@ -21,15 +20,12 @@ export class BimEngine extends EventEmitter {
|
||||
private wrapper: HTMLElement | null = null;
|
||||
|
||||
public toolbar: ToolbarManager | null = null; // 底部专用
|
||||
public constructTreeBtn: ConstructTreeManagerBtn | null = null; // 底部专用
|
||||
public buttonGroup: ButtonGroupManager | null = null; // 通用
|
||||
public dialog: DialogManager | null = null;
|
||||
public engine: EngineManager | null = null; // 3D 引擎管理器
|
||||
public tree: TreeManager | null = null; // 树组件管理器
|
||||
public rightKey: RightKeyManager | null = null; // 右键菜单管理器
|
||||
public modelTree: ModelTreeManager | null = null; // 模型树业务管理器
|
||||
|
||||
public get localeManager() { return localeManager; }
|
||||
public get themeManager() { return themeManager; }
|
||||
|
||||
constructor(
|
||||
container: HTMLElement | string,
|
||||
@@ -80,9 +76,8 @@ export class BimEngine extends EventEmitter {
|
||||
this.dialog = new DialogManager(this, this.wrapper);
|
||||
this.toolbar = new ToolbarManager(this, this.wrapper);
|
||||
this.buttonGroup = new ButtonGroupManager(this, this.wrapper);
|
||||
this.tree = new TreeManager(this, this.wrapper);
|
||||
this.rightKey = new RightKeyManager(this, this.wrapper);
|
||||
this.modelTree = new ModelTreeManager(this);
|
||||
this.constructTreeBtn = new ConstructTreeManagerBtn(this, this.wrapper);
|
||||
|
||||
// 初始主题
|
||||
this.updateTheme(themeManager.getTheme());
|
||||
@@ -105,9 +100,7 @@ export class BimEngine extends EventEmitter {
|
||||
this.buttonGroup?.destroy();
|
||||
this.engine?.destroy();
|
||||
this.dialog?.destroy();
|
||||
this.tree?.destroy();
|
||||
this.rightKey?.destroy();
|
||||
this.modelTree?.destroy();
|
||||
this.container.innerHTML = '';
|
||||
this.clear();
|
||||
}
|
||||
|
||||
@@ -94,6 +94,29 @@ export class BimButtonGroup implements IBimComponent {
|
||||
}
|
||||
|
||||
this.updatePosition();
|
||||
|
||||
// 添加事件拦截,防止点击穿透到 3D 引擎
|
||||
this.setupEventInterception(this.container);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置事件拦截,防止事件<E4BA8B><E4BBB6>泡到下层元素(如 3D 引擎)
|
||||
*/
|
||||
private setupEventInterception(el: HTMLElement): void {
|
||||
const stopPropagation = (e: Event) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const events = [
|
||||
'click', 'dblclick', 'contextmenu', 'wheel',
|
||||
'mousedown', 'mouseup', 'mousemove',
|
||||
'touchstart', 'touchend', 'touchmove',
|
||||
'pointerdown', 'pointerup', 'pointermove', 'pointerenter', 'pointerleave', 'pointerover', 'pointerout'
|
||||
];
|
||||
|
||||
events.forEach(eventType => {
|
||||
el.addEventListener(eventType, stopPropagation, { passive: false });
|
||||
});
|
||||
}
|
||||
|
||||
private updatePosition() {
|
||||
@@ -406,6 +429,9 @@ export class BimButtonGroup implements IBimComponent {
|
||||
// 先添加到 DOM 以便计算尺寸
|
||||
document.body.appendChild(dropdown);
|
||||
|
||||
// 添加事件拦截
|
||||
this.setupEventInterception(dropdown);
|
||||
|
||||
// 添加菜单项
|
||||
button.children.forEach(subBtn => {
|
||||
if (this.isVisible(subBtn.id)) {
|
||||
|
||||
@@ -129,6 +129,8 @@ export class BimDialog implements IBimComponent {
|
||||
|
||||
// 设置初始尺寸
|
||||
this.setSize(el, this.options.width, this.options.height);
|
||||
// 确保最小尺寸生效
|
||||
if (this.options.minWidth) el.style.minWidth = `${this.options.minWidth}px`;
|
||||
|
||||
// 创建标题栏 (Header)
|
||||
const header = document.createElement('div');
|
||||
@@ -197,12 +199,34 @@ export class BimDialog implements IBimComponent {
|
||||
*/
|
||||
private setSize(el: HTMLElement, width?: number | string, height?: number | string) {
|
||||
if (width !== undefined) {
|
||||
if (width === 'auto' || width === 'fit-content') {
|
||||
el.style.width = width;
|
||||
} else {
|
||||
el.style.width = typeof width === 'number' ? `${width}px` : width;
|
||||
}
|
||||
}
|
||||
if (height !== undefined) {
|
||||
if (height === 'auto' || height === 'fit-content') {
|
||||
el.style.height = height;
|
||||
} else {
|
||||
el.style.height = typeof height === 'number' ? `${height}px` : height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据内容自动调整弹窗宽度
|
||||
* @param recenter 是否重新计算定位(例如保持居中),默认 true
|
||||
*/
|
||||
public fitWidth(recenter: boolean = false) {
|
||||
// 1. 设置为 fit-content 以获取自然宽度,高度保持不变
|
||||
this.element.style.width = 'fit-content';
|
||||
|
||||
// 2. 如果需要重新定位
|
||||
if (recenter) {
|
||||
this.initPosition();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化弹窗位置
|
||||
|
||||
@@ -1,10 +1,122 @@
|
||||
/* 树容器 */
|
||||
.bim-tree {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden; /* 内部滚动 */
|
||||
font-size: 14px;
|
||||
color: var(--bim-ui_text_primary, #333);
|
||||
user-select: none; /* 防止双击选中文字 */
|
||||
position: relative; /* 为下拉框定位 */
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.bim-tree-search {
|
||||
padding: 8px;
|
||||
/* border-bottom: 1px solid var(--bim-ui_border_color, #d9d9d9); */
|
||||
/* 背景透明,使用父布局背景 */
|
||||
background-color: transparent;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bim-tree-search-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bim-tree-search-icon {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--bim-ui_text_secondary, #999);
|
||||
pointer-events: none; /* 让点击穿透到 input */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bim-tree-search-icon svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bim-tree-search-input {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
/* 左侧留出图标位置: 8px left + 16px icon + 4px gap = 28px */
|
||||
padding: 4px 8px 4px 30px;
|
||||
border: 1px solid var(--bim-ui_border_color, #d9d9d9);
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
font-size: 13px;
|
||||
color: inherit;
|
||||
/* 输入框背景半透明或白色,看设计,通常输入框本身还是需要背景的,否则文字看不清 */
|
||||
background-color: var(--bim-ui_bg_color, #fff);
|
||||
transition: all 0.2s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bim-tree-search-input:focus {
|
||||
border-color: var(--bim-primary_color, #1890ff);
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 搜索结果下拉框 */
|
||||
.bim-tree-search-results {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
background-color: var(--bim-ui_bg_color, #fff);
|
||||
border: 1px solid var(--bim-ui_border_color, #eee);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bim-tree-search-results.is-visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.bim-tree-search-item {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
.bim-tree-search-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.bim-tree-search-item:hover {
|
||||
background-color: var(--bim-ui_bg_hover, #f5f5f5);
|
||||
}
|
||||
|
||||
.bim-tree-search-item-title {
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.bim-tree-search-item-path {
|
||||
font-size: 12px;
|
||||
color: var(--bim-ui_text_secondary, #999);
|
||||
margin-top: 2px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 树内容区域 */
|
||||
.bim-tree-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
/* 节点行容器 */
|
||||
@@ -146,16 +258,66 @@
|
||||
.bim-tree-title {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* 移除省略号样式,许文本撑开 */
|
||||
/* overflow: hidden; */
|
||||
/* text-overflow: ellipsis; */
|
||||
}
|
||||
|
||||
/* 子节点容器 */
|
||||
.bim-tree-children {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
overflow: visible; /* 允许子节点撑开宽度 */
|
||||
}
|
||||
|
||||
.bim-tree-children.is-visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 选中高亮状态 */
|
||||
.bim-tree-node-content.is-selected {
|
||||
background-color: var(--bim-ui_bg_selected, rgba(24, 144, 255, 0.2));
|
||||
color: var(--bim-primary_color, #1890ff);
|
||||
}
|
||||
|
||||
/* 节点操作栏 */
|
||||
.bim-tree-node-actions {
|
||||
display: none; /* 默认隐藏 */
|
||||
align-items: center;
|
||||
margin-left: 8px; /* 与文字的间距 */
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bim-tree-node-content.is-selected .bim-tree-node-actions {
|
||||
display: flex; /* 选中时显示 */
|
||||
}
|
||||
|
||||
/* 树内容区域 */
|
||||
.bim-tree-content {
|
||||
flex: 1;
|
||||
overflow: auto; /* 支持横向和纵向滚动 */
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
/* 节点行容器 */
|
||||
.bim-tree-node {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: fit-content; /* 关键:让节点容器跟随内容宽度 */
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* 节点内容行 (Flex 布局) */
|
||||
.bim-tree-node-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px; /* 标准行高 */
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
border-radius: 4px;
|
||||
padding-right: 8px;
|
||||
|
||||
/* 关键:允许宽度根据内容撑开,且至少占满容器 */
|
||||
width: fit-content;
|
||||
min-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import { ThemeConfig } from '../../themes/types';
|
||||
import { localeManager } from '../../services/locale';
|
||||
import { localeManager, t } from '../../services/locale';
|
||||
import { themeManager } from '../../services/theme';
|
||||
import { TreeOptions, TreeNodeConfig, TreeNodeCheckState } from './types';
|
||||
import { BimTreeNode } from './tree-node';
|
||||
@@ -14,15 +14,21 @@ import './index.css';
|
||||
|
||||
export class BimTree implements IBimComponent {
|
||||
public element: HTMLElement;
|
||||
private contentElement: HTMLElement; // 树内容容器
|
||||
private searchInput: HTMLInputElement | null = null;
|
||||
private searchResults: HTMLElement | null = null;
|
||||
|
||||
private options: TreeOptions;
|
||||
private nodeMap: Map<string, BimTreeNode> = new Map();
|
||||
private rootNodes: BimTreeNode[] = [];
|
||||
private selectedNode: BimTreeNode | null = null; // 当前选中的高亮节点
|
||||
|
||||
// 订阅清理函数
|
||||
private unsubscribeLocale: (() => void) | null = null;
|
||||
private unsubscribeTheme: (() => void) | null = null;
|
||||
private clickOutsideHandler: ((e: MouseEvent) => void) | null = null;
|
||||
|
||||
// 事<EFBFBD><EFBFBD><EFBFBD>回调 (由 Manager 注入)
|
||||
// 事件回调 (由 Manager 注入)
|
||||
public onNodeCheck?: (node: BimTreeNode) => void;
|
||||
public onNodeSelect?: (node: BimTreeNode) => void;
|
||||
public onNodeExpand?: (node: BimTreeNode) => void;
|
||||
@@ -32,11 +38,75 @@ export class BimTree implements IBimComponent {
|
||||
checkable: true,
|
||||
checkStrictly: true,
|
||||
indent: 24,
|
||||
defaultExpandAll: false,
|
||||
defaultExpandAll: true,
|
||||
enableSearch: false,
|
||||
searchPlaceholder: 'tree.searchPlaceholder',
|
||||
...options
|
||||
};
|
||||
|
||||
// 主容器
|
||||
this.element = document.createElement('div');
|
||||
this.element.className = 'bim-tree';
|
||||
|
||||
// 搜索区域
|
||||
if (this.options.enableSearch) {
|
||||
this.createSearchDOM();
|
||||
}
|
||||
|
||||
// 内容容器
|
||||
this.contentElement = document.createElement('div');
|
||||
this.contentElement.className = 'bim-tree-content';
|
||||
this.element.appendChild(this.contentElement);
|
||||
|
||||
// 初始化回调
|
||||
if (options.onNodeCheck) this.onNodeCheck = options.onNodeCheck;
|
||||
if (options.onNodeSelect) this.onNodeSelect = options.onNodeSelect;
|
||||
if (options.onNodeExpand) this.onNodeExpand = options.onNodeExpand;
|
||||
}
|
||||
|
||||
private createSearchDOM() {
|
||||
const searchContainer = document.createElement('div');
|
||||
searchContainer.className = 'bim-tree-search';
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'bim-tree-search-wrapper';
|
||||
|
||||
// 图标
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'bim-tree-search-icon';
|
||||
icon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m21 21l-4.343-4.343m0 0A8 8 0 1 0 5.343 5.343a8 8 0 0 0 11.314 11.314"/></svg>`;
|
||||
wrapper.appendChild(icon);
|
||||
|
||||
this.searchInput = document.createElement('input');
|
||||
this.searchInput.className = 'bim-tree-search-input';
|
||||
this.searchInput.type = 'text';
|
||||
this.searchInput.placeholder = t(this.options.searchPlaceholder || '搜索...');
|
||||
|
||||
// 绑定输入事件
|
||||
this.searchInput.addEventListener('input', (e) => {
|
||||
const query = (e.target as HTMLInputElement).value;
|
||||
this.handleSearch(query);
|
||||
});
|
||||
|
||||
wrapper.appendChild(this.searchInput);
|
||||
searchContainer.appendChild(wrapper);
|
||||
|
||||
// 搜索结果容器
|
||||
this.searchResults = document.createElement('div');
|
||||
this.searchResults.className = 'bim-tree-search-results';
|
||||
|
||||
searchContainer.appendChild(this.searchResults);
|
||||
this.element.appendChild(searchContainer);
|
||||
|
||||
// 点击外部关闭搜索结果
|
||||
this.clickOutsideHandler = (e: MouseEvent) => {
|
||||
if (this.searchResults &&
|
||||
!this.searchResults.contains(e.target as Node) &&
|
||||
!this.searchInput?.contains(e.target as Node)) {
|
||||
this.searchResults.classList.remove('is-visible');
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', this.clickOutsideHandler);
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
@@ -45,6 +115,119 @@ export class BimTree implements IBimComponent {
|
||||
// 订阅系统事件
|
||||
this.unsubscribeLocale = localeManager.subscribe(() => this.setLocales());
|
||||
this.unsubscribeTheme = themeManager.subscribe((theme) => this.setTheme(theme));
|
||||
|
||||
// 初始化主题
|
||||
this.setTheme(themeManager.getTheme());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理搜索逻辑
|
||||
*/
|
||||
private handleSearch(query: string) {
|
||||
if (!this.searchResults) return;
|
||||
|
||||
if (!query.trim()) {
|
||||
this.searchResults.classList.remove('is-visible');
|
||||
this.searchResults.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const results: { node: BimTreeNode, label: string, path: string }[] = [];
|
||||
const lowerQuery = query.toLowerCase();
|
||||
|
||||
this.nodeMap.forEach(node => {
|
||||
// 直接使用 config.label,不进行翻译
|
||||
const label = node.config.label;
|
||||
if (label.toLowerCase().includes(lowerQuery)) {
|
||||
results.push({
|
||||
node,
|
||||
label,
|
||||
path: this.getNodePath(node)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.renderSearchResults(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点路径(面包屑)
|
||||
*/
|
||||
private getNodePath(node: BimTreeNode): string {
|
||||
const path: string[] = [];
|
||||
let current: BimTreeNode | null = node.parent; // 从父级开始
|
||||
while (current) {
|
||||
// 直接使用 label
|
||||
path.unshift(current.config.label);
|
||||
current = current.parent;
|
||||
}
|
||||
return path.join(' > ');
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染搜索结果列表
|
||||
*/
|
||||
private renderSearchResults(results: { node: BimTreeNode, label: string, path: string }[]) {
|
||||
if (!this.searchResults) return;
|
||||
|
||||
this.searchResults.innerHTML = '';
|
||||
if (results.length === 0) {
|
||||
const noData = document.createElement('div');
|
||||
noData.className = 'bim-tree-search-item';
|
||||
noData.style.cursor = 'default';
|
||||
noData.style.color = '#999';
|
||||
noData.textContent = 'No results';
|
||||
this.searchResults.appendChild(noData);
|
||||
} else {
|
||||
results.forEach(res => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'bim-tree-search-item';
|
||||
|
||||
const title = document.createElement('span');
|
||||
title.className = 'bim-tree-search-item-title';
|
||||
title.textContent = res.label;
|
||||
|
||||
const path = document.createElement('span');
|
||||
path.className = 'bim-tree-search-item-path';
|
||||
path.textContent = res.path;
|
||||
|
||||
item.appendChild(title);
|
||||
if (res.path) item.appendChild(path);
|
||||
|
||||
item.addEventListener('click', () => {
|
||||
this.revealNode(res.node);
|
||||
});
|
||||
|
||||
this.searchResults!.appendChild(item);
|
||||
});
|
||||
}
|
||||
this.searchResults.classList.add('is-visible');
|
||||
}
|
||||
|
||||
/**
|
||||
* 定位到指定节点
|
||||
*/
|
||||
public revealNode(node: BimTreeNode) {
|
||||
// 1. 关闭搜索下拉
|
||||
if (this.searchResults) {
|
||||
this.searchResults.classList.remove('is-visible');
|
||||
if (this.searchInput) this.searchInput.value = ''; // 可选:清空搜索框
|
||||
}
|
||||
|
||||
// 2. 递归展开父节点
|
||||
let current = node.parent;
|
||||
while (current) {
|
||||
current.toggleExpand(true); // 强制展开
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
// 3. 选中节点 (Select)
|
||||
this.handleNodeSelect(node);
|
||||
|
||||
// 4. 滚动到可视区域
|
||||
setTimeout(() => {
|
||||
node.element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +249,9 @@ export class BimTree implements IBimComponent {
|
||||
*/
|
||||
public setLocales(): void {
|
||||
this.nodeMap.forEach(node => node.updateLabel());
|
||||
if (this.searchInput) {
|
||||
this.searchInput.placeholder = t(this.options.searchPlaceholder || 'tree.searchPlaceholder');
|
||||
}
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
@@ -77,17 +263,22 @@ export class BimTree implements IBimComponent {
|
||||
this.unsubscribeTheme();
|
||||
this.unsubscribeTheme = null;
|
||||
}
|
||||
if (this.clickOutsideHandler) {
|
||||
document.removeEventListener('click', this.clickOutsideHandler);
|
||||
this.clickOutsideHandler = null;
|
||||
}
|
||||
this.rootNodes.forEach(node => node.destroy());
|
||||
this.rootNodes = [];
|
||||
this.nodeMap.clear();
|
||||
this.element.remove();
|
||||
this.selectedNode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心渲染逻辑
|
||||
*/
|
||||
private render(): void {
|
||||
this.element.innerHTML = '';
|
||||
this.contentElement.innerHTML = ''; // 清空内容区,而不是整个 element
|
||||
this.nodeMap.clear();
|
||||
this.rootNodes = [];
|
||||
|
||||
@@ -95,20 +286,22 @@ export class BimTree implements IBimComponent {
|
||||
this.createNodeRecursively(config, null);
|
||||
});
|
||||
|
||||
// 如果设置了 defaultExpandAll,展开所有
|
||||
if (this.options.defaultExpandAll) {
|
||||
this.expandAll(true);
|
||||
}
|
||||
// 移除旧的 expandAll 调用,因为 expanded 状态已在 createNodeRecursively 中处理
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归创建节点
|
||||
*/
|
||||
private createNodeRecursively(config: TreeNodeConfig, parent: BimTreeNode | null) {
|
||||
// 处理展开状态优先级: config.expanded > defaultExpandAll
|
||||
if (config.expanded === undefined) {
|
||||
config.expanded = this.options.defaultExpandAll;
|
||||
}
|
||||
|
||||
const node = new BimTreeNode(config, this.options, {
|
||||
onExpand: (n) => { if (this.onNodeExpand) this.onNodeExpand(n); },
|
||||
onCheck: (n) => this.handleNodeCheck(n),
|
||||
onClick: (n) => { if (this.onNodeSelect) this.onNodeSelect(n); }
|
||||
onClick: (n) => this.handleNodeSelect(n)
|
||||
});
|
||||
|
||||
this.nodeMap.set(config.id, node);
|
||||
@@ -117,7 +310,7 @@ export class BimTree implements IBimComponent {
|
||||
parent.appendChild(node);
|
||||
} else {
|
||||
this.rootNodes.push(node);
|
||||
this.element.appendChild(node.element);
|
||||
this.contentElement.appendChild(node.element); // 挂载到 contentElement
|
||||
}
|
||||
|
||||
if (config.children && config.children.length > 0) {
|
||||
@@ -128,7 +321,24 @@ export class BimTree implements IBimComponent {
|
||||
|
||||
// 如果是初始化渲染,需要处理 checkStrictly 的向上联动(因为数据里可能只给了子节点 checked,父节点没给)
|
||||
// 这里做一个简单的后处理:如果 checkStrictly 开启,且当前节点 checked,则触发一次联动
|
||||
// 注意:这可能会导致性能问题,优化做法是在所有节点创建完后统一计算一次状态
|
||||
// 注意:这可能会导致性能问题,<EFBFBD><EFBFBD><EFBFBD>化做法是在所有节点创建完后统一计算一次状态
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理节点选择 (高亮)
|
||||
*/
|
||||
private handleNodeSelect(node: BimTreeNode) {
|
||||
// 如果之前有选中的,先取消选中
|
||||
if (this.selectedNode && this.selectedNode !== node) {
|
||||
this.selectedNode.setSelected(false);
|
||||
}
|
||||
|
||||
// 设置当前为选中
|
||||
node.setSelected(true);
|
||||
this.selectedNode = node;
|
||||
|
||||
// 触发外部回调
|
||||
if (this.onNodeSelect) this.onNodeSelect(node);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { TreeNodeConfig, TreeNodeCheckState, TreeOptions } from './types';
|
||||
import { t } from '../../services/locale';
|
||||
|
||||
/**
|
||||
* 树节点类
|
||||
* 负责渲染单个节点、处理交互和递归
|
||||
@@ -17,12 +15,13 @@ export class BimTreeNode {
|
||||
private switcherEl!: HTMLElement;
|
||||
private checkboxEl: HTMLElement | null = null;
|
||||
private titleEl!: HTMLElement;
|
||||
private actionsEl!: HTMLElement; // 操作栏容器
|
||||
private childrenContainer!: HTMLElement;
|
||||
|
||||
// 外部回调
|
||||
private onExpandChange: (node: BimTreeNode) => void;
|
||||
private onCheckChange: (node: BimTreeNode) => void;
|
||||
private onNodeClick: (node: BimTreeNode) => void;
|
||||
private renderActions?: (node: TreeNodeConfig) => HTMLElement | string;
|
||||
|
||||
constructor(
|
||||
config: TreeNodeConfig,
|
||||
@@ -37,13 +36,13 @@ export class BimTreeNode {
|
||||
this.onExpandChange = callbacks.onExpand;
|
||||
this.onCheckChange = callbacks.onCheck;
|
||||
this.onNodeClick = callbacks.onClick;
|
||||
this.renderActions = options.renderActions;
|
||||
|
||||
// 初始化状态
|
||||
this.checkState = config.checked ? TreeNodeCheckState.Checked : TreeNodeCheckState.Unchecked;
|
||||
|
||||
this.element = this.createDom(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建节点 DOM
|
||||
*/
|
||||
@@ -56,14 +55,6 @@ export class BimTreeNode {
|
||||
this.contentEl = document.createElement('div');
|
||||
this.contentEl.className = 'bim-tree-node-content';
|
||||
|
||||
// 计算缩进 (由父级传入或在 Tree 中统一处理样式,这里使用 padding-left 动态计算较复杂,
|
||||
// 我们改用 Flex + 占位符 或者直接在 Tree 递归时处理。
|
||||
// 实际上,递归渲染时只需把 children 放在一个有 padding-left 的容器里即可,CSS 中已经处理吗?
|
||||
// 检查 CSS: .bim-tree-children 没有 padding-left。
|
||||
// 我们采用在 CSS 中给 children 加 padding-left 的方式最简单,或者动态设置 style.
|
||||
// 为了灵活性,这里采用动态设置 contentEl 的 paddingLeft
|
||||
// 但这需要知道层级 depth。为了简单,我们在 renderChildren 时设置容器样式。
|
||||
|
||||
// 1.1 展开/折叠箭头
|
||||
this.switcherEl = document.createElement('span');
|
||||
this.switcherEl.className = 'bim-tree-switcher';
|
||||
@@ -114,11 +105,26 @@ export class BimTreeNode {
|
||||
this.updateLabel(); // 设置文本
|
||||
this.contentEl.appendChild(this.titleEl);
|
||||
|
||||
// 1.5 操作栏 (Actions)
|
||||
this.actionsEl = document.createElement('div');
|
||||
this.actionsEl.className = 'bim-tree-node-actions';
|
||||
this.actionsEl.addEventListener('click', (e) => {
|
||||
e.stopPropagation(); // 防止点击操作栏触发选中/展开
|
||||
});
|
||||
this.contentEl.appendChild(this.actionsEl);
|
||||
|
||||
// 绑定整行点击
|
||||
this.contentEl.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (this.config.disabled) return;
|
||||
|
||||
const action = this.config.clickAction || 'select';
|
||||
|
||||
if (action === 'expand') {
|
||||
this.toggleExpand();
|
||||
} else {
|
||||
this.onNodeClick(this);
|
||||
}
|
||||
});
|
||||
|
||||
nodeEl.appendChild(this.contentEl);
|
||||
@@ -128,7 +134,7 @@ export class BimTreeNode {
|
||||
this.childrenContainer.className = 'bim-tree-children';
|
||||
// 设置缩进
|
||||
const indent = options.indent || 24;
|
||||
this.childrenContainer.style.paddingLeft = `${indent}px`; // 每一级子<EFBFBD><EFBFBD><EFBFBD>器左移
|
||||
this.childrenContainer.style.paddingLeft = `${indent}px`; // 每一级子器左移
|
||||
|
||||
if (this.config.expanded && hasChildren) {
|
||||
this.childrenContainer.classList.add('is-visible');
|
||||
@@ -139,11 +145,33 @@ export class BimTreeNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新显示文本 (国际化支持)
|
||||
* 设置高亮选中状态 (Select 模式下)
|
||||
*/
|
||||
public setSelected(selected: boolean) {
|
||||
if (selected) {
|
||||
this.contentEl.classList.add('is-selected');
|
||||
// 渲染自定义操作栏
|
||||
if (this.renderActions) {
|
||||
const content = this.renderActions(this.config);
|
||||
this.actionsEl.innerHTML = '';
|
||||
if (typeof content === 'string') {
|
||||
this.actionsEl.innerHTML = content;
|
||||
} else if (content instanceof HTMLElement) {
|
||||
this.actionsEl.appendChild(content);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.contentEl.classList.remove('is-selected');
|
||||
this.actionsEl.innerHTML = ''; // 清空内容
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新显示文本 (国际化支持) -> 移除国际化,直接显示
|
||||
*/
|
||||
public updateLabel() {
|
||||
if (this.titleEl) {
|
||||
this.titleEl.textContent = t(this.config.label);
|
||||
this.titleEl.textContent = this.config.label;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +179,7 @@ export class BimTreeNode {
|
||||
* 切换展开状态
|
||||
*/
|
||||
public toggleExpand(force?: boolean) {
|
||||
if (this.config.children?.length === 0) return;
|
||||
if (!this.config.children || this.config.children.length === 0) return;
|
||||
|
||||
const newState = force !== undefined ? force : !this.config.expanded;
|
||||
this.config.expanded = newState;
|
||||
@@ -174,7 +202,7 @@ export class BimTreeNode {
|
||||
* 切换选中状态 (用户点击)
|
||||
*/
|
||||
public toggleCheck() {
|
||||
// 如果当前是半选,点击变全选;如果全选,点击<EFBFBD><EFBFBD>未选;如果未选,点击变全选
|
||||
// 如果当前是半选,点击变全选;如果全选,点击未选;如果未选,点击变全选
|
||||
// 简化逻辑:只要不是 Checked,点击都变 Checked;如果是 Checked,变 Unchecked
|
||||
const newChecked = this.checkState !== TreeNodeCheckState.Checked;
|
||||
this.setChecked(newChecked ? TreeNodeCheckState.Checked : TreeNodeCheckState.Unchecked, true);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { BimTreeNode } from './tree-node';
|
||||
|
||||
/**
|
||||
* 节点勾选状态枚举
|
||||
*/
|
||||
@@ -7,6 +9,11 @@ export enum TreeNodeCheckState {
|
||||
Indeterminate = 2 // 半选
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点点击行为类型
|
||||
*/
|
||||
export type NodeClickAction = 'select' | 'expand';
|
||||
|
||||
/**
|
||||
* 树节点配置接口
|
||||
*/
|
||||
@@ -35,8 +42,11 @@ export interface TreeNodeConfig {
|
||||
/** 自定义业务数据 */
|
||||
data?: any;
|
||||
|
||||
/** 是否是叶子节点 (用于异步加载场景,暂<EFBFBD><EFBFBD><EFBFBD>接口) */
|
||||
/** 是否是叶子节点 (用于异步加载场景,暂留接口) */
|
||||
isLeaf?: boolean;
|
||||
|
||||
/** 点击整行的行为 (默认 'select') */
|
||||
clickAction?: NodeClickAction;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,4 +71,25 @@ export interface TreeOptions {
|
||||
|
||||
/** 缩进宽度 (像素,默认 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;
|
||||
}
|
||||
@@ -26,12 +26,9 @@ export const enUS: TranslationDictionary = {
|
||||
home: 'Home',
|
||||
},
|
||||
tree: {
|
||||
modelStruct: 'Model Structure',
|
||||
floor1: 'Level 1',
|
||||
floor2: 'Level 2',
|
||||
wall: 'Walls',
|
||||
column: 'Columns',
|
||||
window: 'Windows',
|
||||
door: 'Doors',
|
||||
searchPlaceholder: 'Please enter content to search',
|
||||
},
|
||||
constructTree: {
|
||||
title: 'Construct Tree',
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,13 +28,10 @@ export interface TranslationDictionary {
|
||||
home: string;
|
||||
};
|
||||
tree: {
|
||||
modelStruct: string;
|
||||
floor1: string;
|
||||
floor2: string;
|
||||
wall: string;
|
||||
column: string;
|
||||
window: string;
|
||||
door: string;
|
||||
searchPlaceholder: string;
|
||||
};
|
||||
constructTree: {
|
||||
title: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,9 @@ export const zhCN: TranslationDictionary = {
|
||||
home: '首页',
|
||||
},
|
||||
tree: {
|
||||
modelStruct: '模型结构',
|
||||
floor1: '一楼',
|
||||
floor2: '二楼',
|
||||
wall: '墙体',
|
||||
column: '柱子',
|
||||
window: '窗户',
|
||||
door: '门',
|
||||
searchPlaceholder: '请输入要搜索的内容',
|
||||
},
|
||||
constructTree: {
|
||||
title: '目录树',
|
||||
}
|
||||
};
|
||||
|
||||
200
src/managers/construct-tree-manager-btn.ts
Normal file
200
src/managers/construct-tree-manager-btn.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import type {ButtonGroupColors, ButtonConfig} from '../components/button-group/index.type';
|
||||
import {Toolbar} from '../components/button-group/toolbar';
|
||||
import {BimComponent} from '../core/component';
|
||||
import type {BimEngine} from '../bim-engine';
|
||||
import {BimButtonGroup} from "../components/button-group";
|
||||
import {BimTree} from "../components/tree";
|
||||
import {TreeNodeConfig} from "../components/tree/types.ts";
|
||||
import {BimDialog} from "../components/dialog";
|
||||
|
||||
const MOCK_STRUCT_DATA: TreeNodeConfig[] =[
|
||||
{
|
||||
id: 'root',
|
||||
label: '全部构件',
|
||||
expanded: true,
|
||||
clickAction: 'expand',
|
||||
children: [
|
||||
{
|
||||
id: 'level-1',
|
||||
label: '一层',
|
||||
expanded: false,
|
||||
icon:'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M20.73 16.52V7.59a.7.7 0 0 0-.08-.33a.74.74 0 0 0-.36-.36l-8-3.58a.75.75 0 0 0-.62 0l-8 3.58a.8.8 0 0 0-.44.69v8.82a.83.83 0 0 0 .44.69l8 3.58a.72.72 0 0 0 .62 0l8-3.58a.77.77 0 0 0 .44-.58m-16-7.78l6.5 2.92v7.18l-6.5-2.91Zm8 2.92l6.5-2.92v7.19l-6.5 2.91ZM12 4.82l6.17 2.77L12 10.35L5.83 7.59Z"/></svg>',
|
||||
clickAction: 'expand',
|
||||
children: [
|
||||
{ id: 'l1-wall', label: '墙体(128)'},
|
||||
{ id: 'l1-column', label: '柱(46)' },
|
||||
{ id: 'l1-beam', label: '梁(82)' },
|
||||
{ id: 'l1-slab', label: '楼板(12)' },
|
||||
{ id: 'l1-door', label: '门(24)' },
|
||||
{ id: 'l1-window', label: '窗(36)' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'level-2',
|
||||
label: '二层',
|
||||
expanded: false,
|
||||
clickAction: 'expand',
|
||||
children: [
|
||||
{ id: 'l2-wall', label: '墙体(141)' },
|
||||
{ id: 'l2-column', label: '柱(52)' },
|
||||
{ id: 'l2-beam', label: '梁(90)' },
|
||||
{ id: 'l2-slab', label: '楼板(12)' },
|
||||
{ id: 'l2-door', label: '门(18)' },
|
||||
{ id: 'l2-window', label: '窗(40)' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'level-3',
|
||||
label: '三层',
|
||||
expanded: false,
|
||||
clickAction: 'expand',
|
||||
children: [
|
||||
{ id: 'l3-wall', label: '墙体(136)' },
|
||||
{ id: 'l3-column', label: '柱(48)' },
|
||||
{ id: 'l3-beam', label: '梁(88)' },
|
||||
{ id: 'l3-slab', label: '楼板(12)' },
|
||||
{ id: 'l3-door', label: '门(16)' },
|
||||
{ id: 'l3-window', label: '窗(38)' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'level-roof',
|
||||
label: '屋面层',
|
||||
expanded: false,
|
||||
clickAction: 'expand',
|
||||
children: [
|
||||
{ id: 'rf-slab', label: '屋面板(6)' },
|
||||
{ id: 'rf-beam', label: '屋面梁(24)' },
|
||||
{ id: 'rf-parapet', label: '女儿墙(18)' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* 底部工具栏管理器 (ToolbarManager)
|
||||
* 仅负责管理底部工具栏实例。
|
||||
*/
|
||||
export class ConstructTreeManagerBtn extends BimComponent {
|
||||
private toolbar: Toolbar | null = null;
|
||||
private toolbarContainer: HTMLElement | null = null;
|
||||
private container: HTMLElement;
|
||||
private dialog: BimDialog | null = null;
|
||||
|
||||
constructor(engine: BimEngine, container: HTMLElement) {
|
||||
super(engine);
|
||||
this.container = container;
|
||||
this.init();
|
||||
}
|
||||
|
||||
private init() {
|
||||
// 创建底部工具栏专用容器
|
||||
this.toolbarContainer = document.createElement('div');
|
||||
this.toolbarContainer.id = 'bim-construct-tree';
|
||||
this.container.appendChild(this.toolbarContainer);
|
||||
this.toolbar = new BimButtonGroup({
|
||||
container: this.toolbarContainer,
|
||||
showLabel: false,
|
||||
direction: 'column',
|
||||
position: 'top-left', // 底部居中
|
||||
align: 'vertical', // 图标在上
|
||||
expand: 'up' // 向上展开
|
||||
});
|
||||
this.toolbar.init();
|
||||
this.toolbar.setEngine(this.engine);
|
||||
this.toolbar.addGroup('construct-tree');
|
||||
this.toolbar.addButton({
|
||||
id: 'construct-tree-btn',
|
||||
groupId: 'construct-tree',
|
||||
type: 'button',
|
||||
label: 'construct-tree',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M15 21v-3h-4V8H9v3H2V3h7v3h6V3h7v8h-7V8h-2v8h2v-3h7v8zM4 5v4zm13 10v4zm0-10v4zm0 4h3V5h-3zm0 10h3v-4h-3zM4 9h3V5H4z"/></svg>',
|
||||
onClick: () => {
|
||||
this.openConstructTreeDialog()
|
||||
}
|
||||
});
|
||||
this.toolbar.render();
|
||||
}
|
||||
|
||||
public openConstructTreeDialog() {
|
||||
this.setVisible(false)
|
||||
const tree = new BimTree({
|
||||
data: MOCK_STRUCT_DATA,
|
||||
checkable: true,
|
||||
indent: 0,
|
||||
enableSearch: true,
|
||||
checkStrictly: true,
|
||||
defaultExpandAll: true,
|
||||
renderActions:(_node)=>{
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="1.5" d="m14.828 6.343l2.829 2.829m1.414-1.415L8.464 18.364l-3.535.707l.707-3.536L16.243 4.93a2 2 0 0 1 2.828 2.828Z"/></svg>'
|
||||
},
|
||||
onNodeCheck: (node) => {
|
||||
console.log('onNodeCheck', node);
|
||||
},
|
||||
onNodeSelect: (node) => {
|
||||
console.log('onNodeSelect', node);
|
||||
},
|
||||
onNodeExpand: (node) => {
|
||||
console.log('onNodeExpand', node);
|
||||
this.dialog?.fitWidth()
|
||||
},
|
||||
});
|
||||
tree.init();
|
||||
this.dialog = this.engine.dialog!.create({
|
||||
title: 'constructTree.title',
|
||||
minWidth: 300,
|
||||
height: 400,
|
||||
content: tree.element,
|
||||
position: {x: 20, y: 20},
|
||||
resizable: false,
|
||||
// 关键:绑定生命周期
|
||||
onClose: () => {
|
||||
this.setVisible(true)
|
||||
}
|
||||
});
|
||||
this.dialog?.fitWidth()
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.toolbar?.destroy();
|
||||
this.toolbar = null;
|
||||
}
|
||||
|
||||
// --- 转发 API ---
|
||||
public addGroup(groupId: string, beforeGroupId?: string) {
|
||||
this.toolbar?.addGroup(groupId, beforeGroupId);
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
public addButton(config: ButtonConfig) {
|
||||
this.toolbar?.addButton(config);
|
||||
this.toolbar?.render();
|
||||
}
|
||||
|
||||
public setButtonVisibility(id: string, v: boolean) {
|
||||
this.toolbar?.updateButtonVisibility(id, v);
|
||||
}
|
||||
|
||||
public setShowLabel(show: boolean) {
|
||||
this.toolbar?.setShowLabel(show);
|
||||
}
|
||||
|
||||
public setVisible(visible: boolean) {
|
||||
if (this.toolbarContainer) {
|
||||
this.toolbarContainer.style.visibility = visible ? 'visible' : 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
public setBackgroundColor(color: string) {
|
||||
this.toolbar?.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
public setColors(colors: ButtonGroupColors) {
|
||||
this.toolbar?.setColors(colors);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { BimComponent } from '../core/component';
|
||||
import { BimTree } from '../components/tree/index';
|
||||
import { BimDialog } from '../components/dialog/index';
|
||||
import { TreeNodeConfig } from '../components/tree/types';
|
||||
|
||||
/**
|
||||
* 模型树业务管理器
|
||||
* 负责组装 Tree 和 Dialog 组件,提供开箱即用的业务弹窗
|
||||
*/
|
||||
export class ModelTreeManager extends BimComponent {
|
||||
|
||||
/**
|
||||
* 显示带复选框的模型结构树弹窗
|
||||
* @param data 树节点数据
|
||||
* @param title 弹窗标题 (翻译键)
|
||||
*/
|
||||
public showStructTree(data: TreeNodeConfig[], title: string = 'tree.modelStruct'): { tree: BimTree, dialog: BimDialog } {
|
||||
// 1. 创建 Tree 实例
|
||||
const tree = this.engine.tree!.create({
|
||||
data: data,
|
||||
checkable: true,
|
||||
checkStrictly: true,
|
||||
defaultExpandAll: true
|
||||
});
|
||||
|
||||
// 2. 创建 Dialog 实例
|
||||
const dialog = this.engine.dialog!.create({
|
||||
title: title,
|
||||
width: 300,
|
||||
height: 400,
|
||||
content: tree.element,
|
||||
position: 'left-center',
|
||||
resizable: true,
|
||||
// 关键:绑定生命周期
|
||||
onClose: () => {
|
||||
tree.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
return { tree, dialog };
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示简单的树形弹窗 (无复选框)
|
||||
* @param data 树节点数据
|
||||
* @param title 弹窗标题 (翻译键)
|
||||
*/
|
||||
public showSimpleTree(data: TreeNodeConfig[], title: string = 'menu.home'): { tree: BimTree, dialog: BimDialog } {
|
||||
// 1. 创建 Tree 实例
|
||||
const tree = this.engine.tree!.create({
|
||||
data: data,
|
||||
checkable: false,
|
||||
defaultExpandAll: true
|
||||
});
|
||||
|
||||
// 2. 创建 Dialog 实例
|
||||
const dialog = this.engine.dialog!.create({
|
||||
title: title,
|
||||
width: 250,
|
||||
height: 300,
|
||||
content: tree.element,
|
||||
position: 'center',
|
||||
resizable: true,
|
||||
onClose: () => {
|
||||
tree.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
return { tree, dialog };
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
// 具体的 Tree 和 Dialog 实例由它们各自的生命周期管理
|
||||
// 这里不需要销毁它们,除非我们持有了全局引用
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
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)持有并销毁
|
||||
// 这里可以做一些全局清理工作
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import dts from 'vite-plugin-dts';
|
||||
import { resolve } from 'path';
|
||||
import cssInjectedByJs from 'vite-plugin-css-injected-by-js';
|
||||
|
||||
export default defineConfig(({ command }) => {
|
||||
export default defineConfig(() => {
|
||||
return {
|
||||
plugins: [
|
||||
// 移除 Vue 插件
|
||||
|
||||
Reference in New Issue
Block a user