react-native-fxview 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FXView.tsx +90 -0
- package/FXViewCategoryController.ts +380 -0
- package/FXViewController.ts +370 -0
- package/FXViewManager.ts +421 -0
- package/package.json +11 -0
- package/types.ts +39 -0
package/FXView.tsx
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React, { Component } from "react";
|
|
2
|
+
import { View, ViewProps } from "react-native";
|
|
3
|
+
import { ComponentItem } from "./types";
|
|
4
|
+
import { FXViewManager } from "./FXViewManager";
|
|
5
|
+
|
|
6
|
+
interface FXViewProps extends ViewProps {
|
|
7
|
+
fxViewId?: string; // 动态标识属性,可选
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface FXViewState {
|
|
11
|
+
components: ComponentItem[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class FXView extends Component<FXViewProps, FXViewState> {
|
|
15
|
+
state: FXViewState = {
|
|
16
|
+
components: [],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
private viewId: string;
|
|
20
|
+
|
|
21
|
+
constructor(props: FXViewProps) {
|
|
22
|
+
super(props);
|
|
23
|
+
// 使用 fxViewId,如果没有则生成一个唯一 ID
|
|
24
|
+
this.viewId =
|
|
25
|
+
props.fxViewId ||
|
|
26
|
+
`auto_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
componentDidMount() {
|
|
30
|
+
console.log("FXView mounted with viewId:", this.viewId);
|
|
31
|
+
// 注册到视图管理器
|
|
32
|
+
FXViewManager.getInstance().registerView(
|
|
33
|
+
this.viewId,
|
|
34
|
+
this.updateComponents,
|
|
35
|
+
);
|
|
36
|
+
// 初始更新
|
|
37
|
+
this.updateComponents();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
componentDidUpdate(_prevProps: FXViewProps) {
|
|
41
|
+
console.log("FXView updated", this.viewId);
|
|
42
|
+
const newViewId = this.props.fxViewId || this.viewId;
|
|
43
|
+
|
|
44
|
+
if (newViewId !== this.viewId) {
|
|
45
|
+
// ID 变化时重新注册
|
|
46
|
+
FXViewManager.getInstance().unregisterView(this.viewId);
|
|
47
|
+
|
|
48
|
+
this.viewId = newViewId;
|
|
49
|
+
|
|
50
|
+
FXViewManager.getInstance().registerView(
|
|
51
|
+
this.viewId,
|
|
52
|
+
this.updateComponents,
|
|
53
|
+
);
|
|
54
|
+
this.updateComponents();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
componentWillUnmount() {
|
|
59
|
+
console.log("FXView unmounted", this.viewId);
|
|
60
|
+
// 注销视图(会自动清理所有组件和回调)
|
|
61
|
+
FXViewManager.getInstance().unregisterView(this.viewId);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
updateComponents = () => {
|
|
65
|
+
const components = FXViewManager.getInstance().getComponents(this.viewId);
|
|
66
|
+
this.setState({ components: components });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
render() {
|
|
70
|
+
const { children } = this.props;
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
72
|
+
const { fxViewId, ...restProps } = this.props;
|
|
73
|
+
const { components } = this.state;
|
|
74
|
+
console.log("rendering fxView components", components.length);
|
|
75
|
+
return (
|
|
76
|
+
<View {...restProps}>
|
|
77
|
+
{children}
|
|
78
|
+
{components.map((item) =>
|
|
79
|
+
item.visible ? (
|
|
80
|
+
<React.Fragment key={item.componentId}>
|
|
81
|
+
{item.component}
|
|
82
|
+
</React.Fragment>
|
|
83
|
+
) : null,
|
|
84
|
+
)}
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type { FXViewProps };
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ComponentItem, ComponentController } from "./types";
|
|
3
|
+
import { HeapType, PriorityOrder, PriorityQueue } from "../queue/PriorityQueue";
|
|
4
|
+
|
|
5
|
+
export class FXViewCategoryController {
|
|
6
|
+
public fxViewId: string;
|
|
7
|
+
public categoryId: string;
|
|
8
|
+
private updateCallback?: () => void;
|
|
9
|
+
|
|
10
|
+
// 存储组件的 Map 和优先队列
|
|
11
|
+
private componentMap: Map<string, ComponentItem> = new Map();
|
|
12
|
+
private componentQueue: PriorityQueue<ComponentItem> = new PriorityQueue(
|
|
13
|
+
HeapType.MAX_HEAP,
|
|
14
|
+
PriorityOrder.FIFO,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
fxViewId: string,
|
|
19
|
+
categoryId: string,
|
|
20
|
+
triggerUpdate?: () => void,
|
|
21
|
+
) {
|
|
22
|
+
this.fxViewId = fxViewId;
|
|
23
|
+
this.categoryId = categoryId;
|
|
24
|
+
this.updateCallback = triggerUpdate;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ========== 主要 API 方法 ==========
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 创建并展示组件
|
|
31
|
+
* @param component 要显示的组件
|
|
32
|
+
* @param componentId 可选的组件 ID
|
|
33
|
+
* @returns 组件控制器
|
|
34
|
+
*/
|
|
35
|
+
add(component: React.ReactNode, componentId?: string): ComponentController {
|
|
36
|
+
console.log(
|
|
37
|
+
`FXViewCategoryController.add`,
|
|
38
|
+
`[${this.categoryId}]`,
|
|
39
|
+
componentId,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const finalComponentId = componentId || this.autoComponentId();
|
|
43
|
+
|
|
44
|
+
// 如果组件已存在,先移除旧的
|
|
45
|
+
if (this.componentMap.has(finalComponentId)) {
|
|
46
|
+
console.warn(
|
|
47
|
+
`Component ${finalComponentId} already exists, removing old one`,
|
|
48
|
+
);
|
|
49
|
+
this.remove(finalComponentId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const componentItem: ComponentItem = {
|
|
53
|
+
componentId: finalComponentId,
|
|
54
|
+
component,
|
|
55
|
+
visible: true, // add 方法默认可见
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
this.componentMap.set(finalComponentId, componentItem);
|
|
59
|
+
this.componentQueue.enqueue(componentItem);
|
|
60
|
+
this.triggerUpdate();
|
|
61
|
+
|
|
62
|
+
return this.createController(finalComponentId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 创建但不显示组件
|
|
67
|
+
* @param component 要创建的组件
|
|
68
|
+
* @param componentId 可选的组件 ID
|
|
69
|
+
* @returns 组件控制器
|
|
70
|
+
*/
|
|
71
|
+
build(component: React.ReactNode, componentId?: string): ComponentController {
|
|
72
|
+
console.log(
|
|
73
|
+
`FXViewCategoryController.build`,
|
|
74
|
+
`[${this.categoryId}]`,
|
|
75
|
+
componentId,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const finalComponentId = componentId || this.autoComponentId();
|
|
79
|
+
|
|
80
|
+
// 如果组件已存在,先移除旧的
|
|
81
|
+
if (this.componentMap.has(finalComponentId)) {
|
|
82
|
+
console.warn(
|
|
83
|
+
`Component ${finalComponentId} already exists, removing old one`,
|
|
84
|
+
);
|
|
85
|
+
this.remove(finalComponentId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const componentItem: ComponentItem = {
|
|
89
|
+
componentId: finalComponentId,
|
|
90
|
+
component,
|
|
91
|
+
visible: false, // build 方法默认不可见
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
this.componentMap.set(finalComponentId, componentItem);
|
|
95
|
+
this.componentQueue.enqueue(componentItem);
|
|
96
|
+
|
|
97
|
+
// build 不触发更新,因为组件不可见
|
|
98
|
+
// this.triggerUpdate();
|
|
99
|
+
|
|
100
|
+
return this.createController(finalComponentId);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 显示已存在的组件
|
|
105
|
+
* @param componentId 组件 ID
|
|
106
|
+
*/
|
|
107
|
+
show(componentId: string): void {
|
|
108
|
+
console.log(
|
|
109
|
+
`FXViewCategoryController.show`,
|
|
110
|
+
`[${this.categoryId}]`,
|
|
111
|
+
componentId,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const componentItem = this.componentMap.get(componentId);
|
|
115
|
+
|
|
116
|
+
if (!componentItem) {
|
|
117
|
+
console.warn(
|
|
118
|
+
`Component ${componentId} not found in category ${this.categoryId}`,
|
|
119
|
+
);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 如果已经可见,不需要重复操作
|
|
124
|
+
if (componentItem.visible) {
|
|
125
|
+
console.log(`Component ${componentId} is already visible`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
componentItem.visible = true;
|
|
130
|
+
this.triggerUpdate();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 隐藏但不删除组件
|
|
135
|
+
* @param componentId 组件 ID
|
|
136
|
+
*/
|
|
137
|
+
hide(componentId: string): void {
|
|
138
|
+
console.log(
|
|
139
|
+
`FXViewCategoryController.hide`,
|
|
140
|
+
`[${this.categoryId}]`,
|
|
141
|
+
componentId,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const componentItem = this.componentMap.get(componentId);
|
|
145
|
+
|
|
146
|
+
if (!componentItem) {
|
|
147
|
+
console.warn(
|
|
148
|
+
`Component ${componentId} not found in category ${this.categoryId}`,
|
|
149
|
+
);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 如果已经隐藏,不需要重复操作
|
|
154
|
+
if (!componentItem.visible) {
|
|
155
|
+
console.log(`Component ${componentId} is already hidden`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
componentItem.visible = false;
|
|
160
|
+
this.triggerUpdate();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 更新组件内容
|
|
165
|
+
* @param componentId 组件 ID
|
|
166
|
+
* @param component 新的组件内容
|
|
167
|
+
*/
|
|
168
|
+
update(componentId: string, component: React.ReactNode): void {
|
|
169
|
+
console.log(
|
|
170
|
+
`FXViewCategoryController.update`,
|
|
171
|
+
`[${this.categoryId}]`,
|
|
172
|
+
componentId,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const componentItem = this.componentMap.get(componentId);
|
|
176
|
+
|
|
177
|
+
if (!componentItem) {
|
|
178
|
+
console.warn(
|
|
179
|
+
`Component ${componentId} not found in category ${this.categoryId}`,
|
|
180
|
+
);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 更新组件内容
|
|
185
|
+
componentItem.component = component;
|
|
186
|
+
|
|
187
|
+
// 只有在组件可见时才触发更新
|
|
188
|
+
if (componentItem.visible) {
|
|
189
|
+
this.triggerUpdate();
|
|
190
|
+
} else {
|
|
191
|
+
console.log(
|
|
192
|
+
`Component ${componentId} is hidden, update without re-render`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 彻底删除组件
|
|
199
|
+
* @param componentId 组件 ID(必需)
|
|
200
|
+
*/
|
|
201
|
+
remove(componentId: string): void {
|
|
202
|
+
console.log(
|
|
203
|
+
`FXViewCategoryController.remove`,
|
|
204
|
+
`[${this.categoryId}]`,
|
|
205
|
+
componentId,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const componentItem = this.componentMap.get(componentId);
|
|
209
|
+
|
|
210
|
+
if (!componentItem) {
|
|
211
|
+
console.warn(
|
|
212
|
+
`Component ${componentId} not found in category ${this.categoryId}`,
|
|
213
|
+
);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 从 Map 中删除
|
|
218
|
+
this.componentMap.delete(componentId);
|
|
219
|
+
|
|
220
|
+
// 从优先队列中删除
|
|
221
|
+
this.componentQueue.remove(componentItem);
|
|
222
|
+
|
|
223
|
+
this.triggerUpdate();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 移除最后一个组件(可选:用于向后兼容)
|
|
228
|
+
*/
|
|
229
|
+
removeLast(): void {
|
|
230
|
+
console.log(`FXViewCategoryController.removeLast`, `[${this.categoryId}]`);
|
|
231
|
+
|
|
232
|
+
const lastComponent = this.componentQueue.peek();
|
|
233
|
+
|
|
234
|
+
if (!lastComponent) {
|
|
235
|
+
console.warn(`No component to remove in category ${this.categoryId}`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.remove(lastComponent.componentId);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 清空所有组件
|
|
244
|
+
*/
|
|
245
|
+
clearAll(): void {
|
|
246
|
+
console.log(`FXViewCategoryController.clearAll`, `[${this.categoryId}]`);
|
|
247
|
+
|
|
248
|
+
const hadVisibleComponents = Array.from(this.componentMap.values()).some(
|
|
249
|
+
(item) => item.visible,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
this.componentMap.clear();
|
|
253
|
+
this.componentQueue.clear();
|
|
254
|
+
|
|
255
|
+
// 只有在有可见组件时才触发更新
|
|
256
|
+
if (hadVisibleComponents) {
|
|
257
|
+
this.triggerUpdate();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 注册更新回调
|
|
263
|
+
* @param updateCallback 更新回调函数
|
|
264
|
+
*/
|
|
265
|
+
registerUpdateCallback(updateCallback?: () => void): void {
|
|
266
|
+
this.updateCallback = updateCallback;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 获取组件列表
|
|
271
|
+
* @returns 组件列表
|
|
272
|
+
*/
|
|
273
|
+
getComponents(): ComponentItem[] {
|
|
274
|
+
return this.componentQueue.getAll();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 获取组件数量
|
|
279
|
+
* @returns 组件数量
|
|
280
|
+
*/
|
|
281
|
+
getComponentCount(): number {
|
|
282
|
+
return this.componentMap.size;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 获取可见组件数量
|
|
287
|
+
* @returns 可见组件数量
|
|
288
|
+
*/
|
|
289
|
+
getVisibleComponentCount(): number {
|
|
290
|
+
return Array.from(this.componentMap.values()).filter((item) => item.visible)
|
|
291
|
+
.length;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 检查组件是否存在
|
|
296
|
+
* @param componentId 组件 ID
|
|
297
|
+
* @returns 是否存在
|
|
298
|
+
*/
|
|
299
|
+
hasComponent(componentId: string): boolean {
|
|
300
|
+
return this.componentMap.has(componentId);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 检查组件是否可见
|
|
305
|
+
* @param componentId 组件 ID
|
|
306
|
+
* @returns 是否可见
|
|
307
|
+
*/
|
|
308
|
+
isVisible(componentId: string): boolean {
|
|
309
|
+
const component = this.componentMap.get(componentId);
|
|
310
|
+
return component?.visible ?? false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ========== 私有方法 ==========
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 触发更新
|
|
317
|
+
*/
|
|
318
|
+
private triggerUpdate(): void {
|
|
319
|
+
console.log(
|
|
320
|
+
`FXViewCategoryController.triggerUpdate`,
|
|
321
|
+
`[${this.categoryId}]`,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
if (this.updateCallback) {
|
|
325
|
+
this.updateCallback();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 创建组件控制器
|
|
331
|
+
* @param componentId 组件 ID
|
|
332
|
+
* @returns 组件控制器
|
|
333
|
+
*/
|
|
334
|
+
private createController(componentId: string): ComponentController {
|
|
335
|
+
console.log(
|
|
336
|
+
`FXViewCategoryController.createController`,
|
|
337
|
+
`[${this.categoryId}]`,
|
|
338
|
+
componentId,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// ✅ 修复:不在闭包中捕获 componentItem,每次都从 Map 获取最新引用
|
|
342
|
+
return {
|
|
343
|
+
show: () => {
|
|
344
|
+
this.show(componentId);
|
|
345
|
+
},
|
|
346
|
+
hide: () => {
|
|
347
|
+
this.hide(componentId);
|
|
348
|
+
},
|
|
349
|
+
remove: () => {
|
|
350
|
+
this.remove(componentId);
|
|
351
|
+
},
|
|
352
|
+
update: (component: React.ReactNode) => {
|
|
353
|
+
this.update(componentId, component);
|
|
354
|
+
},
|
|
355
|
+
getFxViewId: () => this.fxViewId,
|
|
356
|
+
getCategoryId: () => this.categoryId,
|
|
357
|
+
getComponentId: () => componentId,
|
|
358
|
+
getComponent: () => {
|
|
359
|
+
// ✅ 每次都从 Map 获取最新的组件引用
|
|
360
|
+
return this.componentMap.get(componentId)?.component;
|
|
361
|
+
},
|
|
362
|
+
isVisible: () => {
|
|
363
|
+
return this.isVisible(componentId);
|
|
364
|
+
},
|
|
365
|
+
exists: () => {
|
|
366
|
+
return this.hasComponent(componentId);
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 自动生成 componentId
|
|
373
|
+
* @returns 生成的 componentId
|
|
374
|
+
*/
|
|
375
|
+
private autoComponentId(): string {
|
|
376
|
+
return `component_${Date.now()}_${Math.random()
|
|
377
|
+
.toString(36)
|
|
378
|
+
.substring(2, 9)}`;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ComponentController, ComponentItem } from "./types";
|
|
3
|
+
import { FXViewCategoryController } from "./FXViewCategoryController";
|
|
4
|
+
|
|
5
|
+
export class FXViewController {
|
|
6
|
+
public fxViewId: string;
|
|
7
|
+
public updateCallback?: () => void;
|
|
8
|
+
|
|
9
|
+
// 存储各分类的控制器
|
|
10
|
+
private categoryControllerMap: Map<string, FXViewCategoryController> =
|
|
11
|
+
new Map();
|
|
12
|
+
|
|
13
|
+
constructor(fxViewId: string) {
|
|
14
|
+
this.fxViewId = fxViewId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//#region ========== 主要 API 方法 ==========
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 创建并显示组件
|
|
21
|
+
* @param component 组件内容
|
|
22
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
23
|
+
* @param componentId 组件 ID(可选,自动生成)
|
|
24
|
+
* @returns 组件控制器
|
|
25
|
+
*/
|
|
26
|
+
add(
|
|
27
|
+
component: React.ReactNode,
|
|
28
|
+
categoryId?: string,
|
|
29
|
+
componentId?: string,
|
|
30
|
+
): ComponentController {
|
|
31
|
+
console.log("FXViewController.add", `[${this.fxViewId}]`, {
|
|
32
|
+
categoryId,
|
|
33
|
+
componentId,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const categoryController = this.getCategoryControllerOrCreate(categoryId);
|
|
37
|
+
return categoryController.add(component, componentId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 创建但不显示组件
|
|
42
|
+
* @param component 组件内容
|
|
43
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
44
|
+
* @param componentId 组件 ID(可选,自动生成)
|
|
45
|
+
* @returns 组件控制器
|
|
46
|
+
*/
|
|
47
|
+
build(
|
|
48
|
+
component: React.ReactNode,
|
|
49
|
+
categoryId?: string,
|
|
50
|
+
componentId?: string,
|
|
51
|
+
): ComponentController {
|
|
52
|
+
console.log("FXViewController.build", `[${this.fxViewId}]`, {
|
|
53
|
+
categoryId,
|
|
54
|
+
componentId,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const categoryController = this.getCategoryControllerOrCreate(categoryId);
|
|
58
|
+
return categoryController.build(component, componentId);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 显示已存在的组件
|
|
63
|
+
* @param componentId 组件 ID
|
|
64
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
65
|
+
*/
|
|
66
|
+
show(componentId: string, categoryId?: string): void {
|
|
67
|
+
console.log("FXViewController.show", `[${this.fxViewId}]`, {
|
|
68
|
+
categoryId,
|
|
69
|
+
componentId,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const categoryController = this.getCategoryController(categoryId);
|
|
73
|
+
if (!categoryController) {
|
|
74
|
+
console.warn(
|
|
75
|
+
`CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
|
|
76
|
+
);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
categoryController.show(componentId);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 隐藏但不删除组件
|
|
85
|
+
* @param componentId 组件 ID
|
|
86
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
87
|
+
*/
|
|
88
|
+
hide(componentId: string, categoryId?: string): void {
|
|
89
|
+
console.log("FXViewController.hide", `[${this.fxViewId}]`, {
|
|
90
|
+
categoryId,
|
|
91
|
+
componentId,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const categoryController = this.getCategoryController(categoryId);
|
|
95
|
+
if (!categoryController) {
|
|
96
|
+
console.warn(
|
|
97
|
+
`CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
|
|
98
|
+
);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
categoryController.hide(componentId);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 更新组件内容
|
|
107
|
+
* @param componentId 组件 ID
|
|
108
|
+
* @param component 新的组件内容
|
|
109
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
110
|
+
*/
|
|
111
|
+
update(
|
|
112
|
+
componentId: string,
|
|
113
|
+
component: React.ReactNode,
|
|
114
|
+
categoryId?: string,
|
|
115
|
+
): void {
|
|
116
|
+
console.log("FXViewController.update", `[${this.fxViewId}]`, {
|
|
117
|
+
categoryId,
|
|
118
|
+
componentId,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const categoryController = this.getCategoryController(categoryId);
|
|
122
|
+
if (!categoryController) {
|
|
123
|
+
console.warn(
|
|
124
|
+
`CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
|
|
125
|
+
);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
categoryController.update(componentId, component);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 彻底删除组件
|
|
134
|
+
* @param componentId 组件 ID
|
|
135
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
136
|
+
*/
|
|
137
|
+
remove(componentId: string, categoryId?: string): void {
|
|
138
|
+
console.log("FXViewController.remove", `[${this.fxViewId}]`, {
|
|
139
|
+
categoryId,
|
|
140
|
+
componentId,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const categoryController = this.getCategoryController(categoryId);
|
|
144
|
+
if (!categoryController) {
|
|
145
|
+
console.warn(
|
|
146
|
+
`CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
|
|
147
|
+
);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
categoryController.remove(componentId);
|
|
152
|
+
|
|
153
|
+
// 如果该分类没有组件了,删除整个分类控制器
|
|
154
|
+
if (categoryController.getComponentCount() === 0) {
|
|
155
|
+
this.removeCategory(categoryId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 移除最后一个组件(向后兼容)
|
|
161
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
162
|
+
*/
|
|
163
|
+
removeLast(categoryId?: string): void {
|
|
164
|
+
console.log("FXViewController.removeLast", `[${this.fxViewId}]`, {
|
|
165
|
+
categoryId,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const categoryController = this.getCategoryController(categoryId);
|
|
169
|
+
if (!categoryController) {
|
|
170
|
+
console.warn(
|
|
171
|
+
`CategoryController ${categoryId || "default"} not found in view ${this.fxViewId}`,
|
|
172
|
+
);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
categoryController.removeLast();
|
|
177
|
+
|
|
178
|
+
// 如果该分类没有组件了,删除整个分类控制器
|
|
179
|
+
if (categoryController.getComponentCount() === 0) {
|
|
180
|
+
this.removeCategory(categoryId);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 清空所有分类的所有组件
|
|
186
|
+
*/
|
|
187
|
+
clearAll(): void {
|
|
188
|
+
console.log("FXViewController.clearAll", `[${this.fxViewId}]`);
|
|
189
|
+
|
|
190
|
+
this.categoryControllerMap.forEach((controller) => {
|
|
191
|
+
controller.clearAll();
|
|
192
|
+
});
|
|
193
|
+
this.categoryControllerMap.clear();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 清空指定分类的所有组件
|
|
198
|
+
* @param categoryId 分类 ID
|
|
199
|
+
*/
|
|
200
|
+
clearCategory(categoryId?: string): void {
|
|
201
|
+
const finalCategoryId = categoryId || "default";
|
|
202
|
+
console.log("FXViewController.clearCategory", `[${this.fxViewId}]`, {
|
|
203
|
+
categoryId: finalCategoryId,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const categoryController = this.getCategoryController(categoryId);
|
|
207
|
+
if (!categoryController) {
|
|
208
|
+
console.warn(
|
|
209
|
+
`CategoryController ${finalCategoryId} not found in view ${this.fxViewId}`,
|
|
210
|
+
);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
categoryController.clearAll();
|
|
215
|
+
this.categoryControllerMap.delete(finalCategoryId);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 注册更新回调
|
|
220
|
+
* @param callback 更新回调函数
|
|
221
|
+
*/
|
|
222
|
+
registerUpdateCallback(callback?: () => void): void {
|
|
223
|
+
this.updateCallback = callback;
|
|
224
|
+
|
|
225
|
+
// 将回调传递给所有已存在的分类控制器
|
|
226
|
+
this.categoryControllerMap.forEach((controller) => {
|
|
227
|
+
controller.registerUpdateCallback(callback);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 获取组件列表
|
|
233
|
+
* @param filterOptions 过滤选项
|
|
234
|
+
* @returns 组件列表
|
|
235
|
+
*/
|
|
236
|
+
getComponents(categoryId?: string): ComponentItem[] {
|
|
237
|
+
const result: ComponentItem[] = [];
|
|
238
|
+
|
|
239
|
+
this.categoryControllerMap.forEach((categoryController, category) => {
|
|
240
|
+
// 如果指定了类别,只处理指定类别
|
|
241
|
+
if (categoryId && categoryId !== category) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const categoryComponents = categoryController.getComponents();
|
|
245
|
+
result.push(...categoryComponents);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* 获取总组件数量
|
|
253
|
+
* @returns 组件数量
|
|
254
|
+
*/
|
|
255
|
+
getComponentCount(): number {
|
|
256
|
+
let count = 0;
|
|
257
|
+
this.categoryControllerMap.forEach((controller) => {
|
|
258
|
+
count += controller.getComponentCount();
|
|
259
|
+
});
|
|
260
|
+
return count;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 获取可见组件数量
|
|
265
|
+
* @returns 可见组件数量
|
|
266
|
+
*/
|
|
267
|
+
getVisibleComponentCount(): number {
|
|
268
|
+
let count = 0;
|
|
269
|
+
this.categoryControllerMap.forEach((controller) => {
|
|
270
|
+
count += controller.getVisibleComponentCount();
|
|
271
|
+
});
|
|
272
|
+
return count;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 检查组件是否存在
|
|
277
|
+
* @param componentId 组件 ID
|
|
278
|
+
* @param categoryId 分类 ID(可选)
|
|
279
|
+
* @returns 是否存在
|
|
280
|
+
*/
|
|
281
|
+
hasComponent(componentId: string, categoryId?: string): boolean {
|
|
282
|
+
if (categoryId) {
|
|
283
|
+
const controller = this.getCategoryController(categoryId);
|
|
284
|
+
return controller?.hasComponent(componentId) ?? false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 如果没有指定分类,搜索所有分类
|
|
288
|
+
for (const controller of this.categoryControllerMap.values()) {
|
|
289
|
+
if (controller.hasComponent(componentId)) {
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 检查组件是否可见
|
|
298
|
+
* @param componentId 组件 ID
|
|
299
|
+
* @param categoryId 分类 ID(可选)
|
|
300
|
+
* @returns 是否可见
|
|
301
|
+
*/
|
|
302
|
+
isComponentVisible(componentId: string, categoryId?: string): boolean {
|
|
303
|
+
if (categoryId) {
|
|
304
|
+
const controller = this.getCategoryController(categoryId);
|
|
305
|
+
return controller?.isVisible(componentId) ?? false;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 如果没有指定分类,搜索所有分类
|
|
309
|
+
for (const controller of this.categoryControllerMap.values()) {
|
|
310
|
+
if (controller.hasComponent(componentId)) {
|
|
311
|
+
return controller.isVisible(componentId);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
//#endregion
|
|
318
|
+
|
|
319
|
+
//#region ========== 私有方法 ==========
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 获取分类控制器
|
|
323
|
+
* @param categoryId 分类 ID
|
|
324
|
+
* @returns 分类控制器或 null
|
|
325
|
+
*/
|
|
326
|
+
private getCategoryController(
|
|
327
|
+
categoryId?: string,
|
|
328
|
+
): FXViewCategoryController | null {
|
|
329
|
+
const finalCategoryId = categoryId || "default";
|
|
330
|
+
return this.categoryControllerMap.get(finalCategoryId) || null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* 获取或创建分类控制器
|
|
335
|
+
* @param categoryId 分类 ID
|
|
336
|
+
* @returns 分类控制器
|
|
337
|
+
*/
|
|
338
|
+
private getCategoryControllerOrCreate(
|
|
339
|
+
categoryId?: string,
|
|
340
|
+
): FXViewCategoryController {
|
|
341
|
+
const finalCategoryId = categoryId || "default";
|
|
342
|
+
|
|
343
|
+
let categoryController = this.categoryControllerMap.get(finalCategoryId);
|
|
344
|
+
|
|
345
|
+
if (!categoryController) {
|
|
346
|
+
categoryController = new FXViewCategoryController(
|
|
347
|
+
this.fxViewId,
|
|
348
|
+
finalCategoryId,
|
|
349
|
+
this.updateCallback,
|
|
350
|
+
);
|
|
351
|
+
this.categoryControllerMap.set(finalCategoryId, categoryController);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return categoryController;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* 删除分类控制器
|
|
359
|
+
* @param categoryId 分类 ID
|
|
360
|
+
*/
|
|
361
|
+
private removeCategory(categoryId?: string): void {
|
|
362
|
+
const finalCategoryId = categoryId || "default";
|
|
363
|
+
console.log("FXViewController.removeCategory", `[${this.fxViewId}]`, {
|
|
364
|
+
categoryId: finalCategoryId,
|
|
365
|
+
});
|
|
366
|
+
this.categoryControllerMap.delete(finalCategoryId);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
//#endregion
|
|
370
|
+
}
|
package/FXViewManager.ts
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ComponentController, ComponentItem } from "./types";
|
|
3
|
+
import { FXViewController } from "./FXViewController";
|
|
4
|
+
import { HeapType, PriorityOrder, PriorityQueue } from "../queue/PriorityQueue";
|
|
5
|
+
|
|
6
|
+
export class FXViewManager {
|
|
7
|
+
private static instance: FXViewManager;
|
|
8
|
+
|
|
9
|
+
// 存储视图控制器
|
|
10
|
+
private viewControllerMap: Map<string, FXViewController> = new Map();
|
|
11
|
+
private viewControllerQueue: PriorityQueue<FXViewController> =
|
|
12
|
+
new PriorityQueue(HeapType.MAX_HEAP, PriorityOrder.LIFO);
|
|
13
|
+
|
|
14
|
+
static getInstance(): FXViewManager {
|
|
15
|
+
if (!FXViewManager.instance) {
|
|
16
|
+
FXViewManager.instance = new FXViewManager();
|
|
17
|
+
}
|
|
18
|
+
return FXViewManager.instance;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#region ========== 主要 API 方法 ==========
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 创建并显示组件
|
|
25
|
+
* @param component 组件内容
|
|
26
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
27
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
28
|
+
* @param componentId 组件 ID(可选,自动生成)
|
|
29
|
+
* @returns 组件控制器
|
|
30
|
+
*/
|
|
31
|
+
add(
|
|
32
|
+
component: React.ReactNode,
|
|
33
|
+
fxViewId?: string,
|
|
34
|
+
categoryId?: string,
|
|
35
|
+
componentId?: string,
|
|
36
|
+
): ComponentController {
|
|
37
|
+
console.log("FXViewManager.add", {
|
|
38
|
+
fxViewId,
|
|
39
|
+
categoryId,
|
|
40
|
+
componentId,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const viewController = this.getViewControllerOrCreate(fxViewId);
|
|
44
|
+
return viewController.add(component, categoryId, componentId);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 创建但不显示组件
|
|
49
|
+
* @param component 组件内容
|
|
50
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
51
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
52
|
+
* @param componentId 组件 ID(可选,自动生成)
|
|
53
|
+
* @returns 组件控制器
|
|
54
|
+
*/
|
|
55
|
+
build(
|
|
56
|
+
component: React.ReactNode,
|
|
57
|
+
fxViewId?: string,
|
|
58
|
+
categoryId?: string,
|
|
59
|
+
componentId?: string,
|
|
60
|
+
): ComponentController {
|
|
61
|
+
console.log("FXViewManager.build", {
|
|
62
|
+
fxViewId,
|
|
63
|
+
categoryId,
|
|
64
|
+
componentId,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const viewController = this.getViewControllerOrCreate(fxViewId);
|
|
68
|
+
return viewController.build(component, categoryId, componentId);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 显示已存在的组件
|
|
73
|
+
* @param componentId 组件 ID
|
|
74
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
75
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
76
|
+
*/
|
|
77
|
+
show(componentId: string, fxViewId?: string, categoryId?: string): void {
|
|
78
|
+
console.log("FXViewManager.show", {
|
|
79
|
+
fxViewId,
|
|
80
|
+
categoryId,
|
|
81
|
+
componentId,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const viewController = this.getViewController(fxViewId);
|
|
85
|
+
if (!viewController) {
|
|
86
|
+
console.warn(`FXView ${fxViewId || "latest"} not found`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
viewController.show(componentId, categoryId);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 隐藏但不删除组件
|
|
95
|
+
* @param componentId 组件 ID
|
|
96
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
97
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
98
|
+
*/
|
|
99
|
+
hide(componentId: string, fxViewId?: string, categoryId?: string): void {
|
|
100
|
+
console.log("FXViewManager.hide", {
|
|
101
|
+
fxViewId,
|
|
102
|
+
categoryId,
|
|
103
|
+
componentId,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const viewController = this.getViewController(fxViewId);
|
|
107
|
+
if (!viewController) {
|
|
108
|
+
console.warn(`FXView ${fxViewId || "latest"} not found`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
viewController.hide(componentId, categoryId);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 更新组件内容
|
|
117
|
+
* @param componentId 组件 ID
|
|
118
|
+
* @param component 新的组件内容
|
|
119
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
120
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
121
|
+
*/
|
|
122
|
+
update(
|
|
123
|
+
componentId: string,
|
|
124
|
+
component: React.ReactNode,
|
|
125
|
+
fxViewId?: string,
|
|
126
|
+
categoryId?: string,
|
|
127
|
+
): void {
|
|
128
|
+
console.log("FXViewManager.update", {
|
|
129
|
+
fxViewId,
|
|
130
|
+
categoryId,
|
|
131
|
+
componentId,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const viewController = this.getViewController(fxViewId);
|
|
135
|
+
if (!viewController) {
|
|
136
|
+
console.warn(`FXView ${fxViewId || "latest"} not found`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
viewController.update(componentId, component, categoryId);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 彻底删除组件
|
|
145
|
+
* @param componentId 组件 ID
|
|
146
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
147
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
148
|
+
*/
|
|
149
|
+
remove(componentId: string, fxViewId?: string, categoryId?: string): void {
|
|
150
|
+
console.log("FXViewManager.remove", {
|
|
151
|
+
fxViewId,
|
|
152
|
+
categoryId,
|
|
153
|
+
componentId,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const viewController = this.getViewController(fxViewId);
|
|
157
|
+
if (!viewController) {
|
|
158
|
+
console.warn(`FXView ${fxViewId || "latest"} not found`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
viewController.remove(componentId, categoryId);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 移除最后一个组件(向后兼容)
|
|
167
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
168
|
+
* @param categoryId 分类 ID(可选,默认 "default")
|
|
169
|
+
*/
|
|
170
|
+
removeLast(fxViewId?: string, categoryId?: string): void {
|
|
171
|
+
console.log("FXViewManager.removeLast", {
|
|
172
|
+
fxViewId,
|
|
173
|
+
categoryId,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const viewController = this.getViewController(fxViewId);
|
|
177
|
+
if (!viewController) {
|
|
178
|
+
console.warn(`FXView ${fxViewId || "latest"} not found`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
viewController.removeLast(categoryId);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 清空指定视图的所有组件
|
|
187
|
+
* @param fxViewId 视图 ID(可选,清空所有视图)
|
|
188
|
+
*/
|
|
189
|
+
clearAll(fxViewId?: string): void {
|
|
190
|
+
console.log("FXViewManager.clearAll", { fxViewId });
|
|
191
|
+
|
|
192
|
+
if (fxViewId) {
|
|
193
|
+
// 清空指定视图
|
|
194
|
+
const viewController = this.getViewController(fxViewId);
|
|
195
|
+
if (viewController) {
|
|
196
|
+
viewController.clearAll();
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
// 清空所有视图
|
|
200
|
+
this.viewControllerMap.forEach((controller) => {
|
|
201
|
+
controller.clearAll();
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 清空指定分类的所有组件
|
|
208
|
+
* @param categoryId 分类 ID
|
|
209
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
210
|
+
*/
|
|
211
|
+
clearCategory(categoryId: string, fxViewId?: string): void {
|
|
212
|
+
console.log("FXViewManager.clearCategory", { fxViewId, categoryId });
|
|
213
|
+
|
|
214
|
+
const viewController = this.getViewController(fxViewId);
|
|
215
|
+
if (!viewController) {
|
|
216
|
+
console.warn(`FXView ${fxViewId || "latest"} not found`);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
viewController.clearCategory(categoryId);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//#endregion
|
|
224
|
+
|
|
225
|
+
//#region ========== 视图管理方法 ==========
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* 注册视图(FXView 挂载时调用)
|
|
229
|
+
* @param fxViewId 视图 ID
|
|
230
|
+
* @param updateCallback 更新回调函数
|
|
231
|
+
* @returns 视图控制器
|
|
232
|
+
*/
|
|
233
|
+
registerView(fxViewId: string, updateCallback: () => void): FXViewController {
|
|
234
|
+
console.log("FXViewManager.registerView", fxViewId);
|
|
235
|
+
|
|
236
|
+
let viewController = this.viewControllerMap.get(fxViewId);
|
|
237
|
+
|
|
238
|
+
if (!viewController) {
|
|
239
|
+
viewController = new FXViewController(fxViewId);
|
|
240
|
+
this.viewControllerMap.set(fxViewId, viewController);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 注册更新回调
|
|
244
|
+
viewController.registerUpdateCallback(updateCallback);
|
|
245
|
+
|
|
246
|
+
// 更新视图栈(移除旧的引用,添加新的)
|
|
247
|
+
const existingController = this.viewControllerQueue
|
|
248
|
+
.getAll()
|
|
249
|
+
.find((vc) => vc.fxViewId === fxViewId);
|
|
250
|
+
if (existingController) {
|
|
251
|
+
this.viewControllerQueue.remove(existingController);
|
|
252
|
+
}
|
|
253
|
+
this.viewControllerQueue.enqueue(viewController);
|
|
254
|
+
|
|
255
|
+
return viewController;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 注销视图(FXView 卸载时调用)
|
|
260
|
+
* @param fxViewId 视图 ID
|
|
261
|
+
* @returns 是否成功注销
|
|
262
|
+
*/
|
|
263
|
+
unregisterView(fxViewId: string): boolean {
|
|
264
|
+
console.log("FXViewManager.unregisterView", fxViewId);
|
|
265
|
+
|
|
266
|
+
const viewController = this.getViewController(fxViewId);
|
|
267
|
+
if (!viewController) {
|
|
268
|
+
console.warn(`FXView ${fxViewId} not found, cannot unregister`);
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// 清空回调
|
|
273
|
+
viewController.registerUpdateCallback(undefined);
|
|
274
|
+
|
|
275
|
+
// 清空所有组件
|
|
276
|
+
viewController.clearAll();
|
|
277
|
+
|
|
278
|
+
// 从队列中移除
|
|
279
|
+
this.viewControllerQueue.remove(viewController);
|
|
280
|
+
|
|
281
|
+
// 从 Map 中删除
|
|
282
|
+
this.viewControllerMap.delete(fxViewId);
|
|
283
|
+
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 获取最近使用的视图 ID(栈顶)
|
|
289
|
+
* @returns 最近的视图 ID 或 null
|
|
290
|
+
*/
|
|
291
|
+
getLatestFXViewId(): string | null {
|
|
292
|
+
return this.viewControllerQueue.peek()?.fxViewId || null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 获取组件列表
|
|
297
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
298
|
+
* @param categoryId 分类 ID(可选)
|
|
299
|
+
* @returns 组件列表
|
|
300
|
+
*/
|
|
301
|
+
getComponents(fxViewId?: string, categoryId?: string): ComponentItem[] {
|
|
302
|
+
console.log("FXViewManager.getComponents", { fxViewId, categoryId });
|
|
303
|
+
|
|
304
|
+
const finalFXViewId = fxViewId || this.getLatestFXViewId();
|
|
305
|
+
if (!finalFXViewId) {
|
|
306
|
+
console.warn("No FXView available");
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const viewController = this.getViewController(finalFXViewId);
|
|
311
|
+
if (!viewController) {
|
|
312
|
+
console.warn(`FXViewController ${finalFXViewId} not found`);
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return viewController.getComponents(categoryId);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 获取组件数量
|
|
321
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
322
|
+
* @returns 组件数量
|
|
323
|
+
*/
|
|
324
|
+
getComponentCount(fxViewId?: string): number {
|
|
325
|
+
const viewController = this.getViewController(fxViewId);
|
|
326
|
+
return viewController?.getComponentCount() ?? 0;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 获取可见组件数量
|
|
331
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
332
|
+
* @returns 可见组件数量
|
|
333
|
+
*/
|
|
334
|
+
getVisibleComponentCount(fxViewId?: string): number {
|
|
335
|
+
const viewController = this.getViewController(fxViewId);
|
|
336
|
+
return viewController?.getVisibleComponentCount() ?? 0;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 检查组件是否存在
|
|
341
|
+
* @param componentId 组件 ID
|
|
342
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
343
|
+
* @param categoryId 分类 ID(可选)
|
|
344
|
+
* @returns 是否存在
|
|
345
|
+
*/
|
|
346
|
+
hasComponent(
|
|
347
|
+
componentId: string,
|
|
348
|
+
fxViewId?: string,
|
|
349
|
+
categoryId?: string,
|
|
350
|
+
): boolean {
|
|
351
|
+
const viewController = this.getViewController(fxViewId);
|
|
352
|
+
return viewController?.hasComponent(componentId, categoryId) ?? false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* 检查组件是否可见
|
|
357
|
+
* @param componentId 组件 ID
|
|
358
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
359
|
+
* @param categoryId 分类 ID(可选)
|
|
360
|
+
* @returns 是否可见
|
|
361
|
+
*/
|
|
362
|
+
isComponentVisible(
|
|
363
|
+
componentId: string,
|
|
364
|
+
fxViewId?: string,
|
|
365
|
+
categoryId?: string,
|
|
366
|
+
): boolean {
|
|
367
|
+
const viewController = this.getViewController(fxViewId);
|
|
368
|
+
return viewController?.isComponentVisible(componentId, categoryId) ?? false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
//#endregion
|
|
372
|
+
|
|
373
|
+
//#region ========== 私有方法 ==========
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* 获取视图控制器
|
|
377
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图)
|
|
378
|
+
* @returns 视图控制器或 null
|
|
379
|
+
*/
|
|
380
|
+
private getViewController(fxViewId?: string): FXViewController | null {
|
|
381
|
+
const finalFXViewId = fxViewId || this.getLatestFXViewId();
|
|
382
|
+
if (!finalFXViewId) {
|
|
383
|
+
console.warn("FXViewManager.getViewController: no fxViewId available");
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
return this.viewControllerMap.get(finalFXViewId) || null;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* 获取或创建视图控制器
|
|
391
|
+
* @param fxViewId 视图 ID(可选,使用最近的视图或创建新的)
|
|
392
|
+
* @returns 视图控制器
|
|
393
|
+
*/
|
|
394
|
+
private getViewControllerOrCreate(fxViewId?: string): FXViewController {
|
|
395
|
+
const finalFXViewId =
|
|
396
|
+
fxViewId || this.getLatestFXViewId() || this.autoFXViewId();
|
|
397
|
+
|
|
398
|
+
let viewController = this.viewControllerMap.get(finalFXViewId);
|
|
399
|
+
|
|
400
|
+
if (!viewController) {
|
|
401
|
+
console.warn(
|
|
402
|
+
`FXViewController ${finalFXViewId} not registered, creating a temporary one`,
|
|
403
|
+
);
|
|
404
|
+
viewController = new FXViewController(finalFXViewId);
|
|
405
|
+
this.viewControllerMap.set(finalFXViewId, viewController);
|
|
406
|
+
this.viewControllerQueue.enqueue(viewController);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return viewController;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* 自动生成 fxViewId
|
|
414
|
+
* @returns 生成的 fxViewId
|
|
415
|
+
*/
|
|
416
|
+
private autoFXViewId(): string {
|
|
417
|
+
return `fxView_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
//#endregion
|
|
421
|
+
}
|
package/package.json
ADDED
package/types.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
|
|
3
|
+
// ========== 参数配置接口 ==========
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 组件控制器接口
|
|
7
|
+
* 用于控制动态组件的显示、隐藏、更新等操作
|
|
8
|
+
*/
|
|
9
|
+
export interface ComponentController {
|
|
10
|
+
/** 显示组件 */
|
|
11
|
+
show: () => void;
|
|
12
|
+
/** 隐藏组件 */
|
|
13
|
+
hide: () => void;
|
|
14
|
+
/** 移除组件 */
|
|
15
|
+
remove: () => void;
|
|
16
|
+
/** 更新组件内容 */
|
|
17
|
+
update: (component: React.ReactNode) => void;
|
|
18
|
+
/** 获取所属的 FXView ID */
|
|
19
|
+
getFxViewId: () => string;
|
|
20
|
+
/** 获取所属的分类 ID */
|
|
21
|
+
getCategoryId: () => string;
|
|
22
|
+
/** 获取组件 ID */
|
|
23
|
+
getComponentId: () => string;
|
|
24
|
+
/** 获取组件实例 */
|
|
25
|
+
getComponent: () => React.ReactNode;
|
|
26
|
+
/** 判断组件是否可见 */
|
|
27
|
+
isVisible: () => boolean;
|
|
28
|
+
/** 判断组件是否存在 */
|
|
29
|
+
exists: () => boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 组件项配置接口
|
|
34
|
+
*/
|
|
35
|
+
export interface ComponentItem {
|
|
36
|
+
componentId: string; // 组件唯一标识(必填)
|
|
37
|
+
component?: React.ReactNode; // 组件内容
|
|
38
|
+
visible?: boolean; // 是否可见,默认为 true
|
|
39
|
+
}
|