fix
This commit is contained in:
@@ -169,15 +169,21 @@ export class NavigationManager {
|
||||
const hideDirection = isMovingRight ? "left" : "right";
|
||||
const showDirection = isMovingRight ? "right" : "left";
|
||||
|
||||
// 隐藏当前面板(包括子页面)
|
||||
// 清空当前主面板的子页面栈
|
||||
if (this.currentActivePanelType) {
|
||||
logger.log(
|
||||
`[NavigationManager] 切换主面板,清空当前面板的子页面栈: ${this.currentActivePanelType}`
|
||||
);
|
||||
this.clearSubPanelStack(this.currentActivePanelType, true);
|
||||
}
|
||||
|
||||
// 隐藏当前面板
|
||||
if (this.currentActivePanel && this.currentActivePanel.isValid) {
|
||||
this.hidePanelWithAnimation(this.currentActivePanel, hideDirection);
|
||||
}
|
||||
|
||||
// 加载并显示目标面板
|
||||
this.loadOrGetPanel(panelType, (panel: Node) => {
|
||||
// 清理当前面板的subPanel(在hidePanelWithAnimation中已经清理,这里不需要重复)
|
||||
|
||||
this.currentActivePanelType = panelType;
|
||||
|
||||
// 更新NavigationPanel按钮状态
|
||||
@@ -192,8 +198,11 @@ export class NavigationManager {
|
||||
});
|
||||
}
|
||||
|
||||
private subPanelDic: Map<Node, Node[]> = new Map();
|
||||
// 每个主面板的子页面栈(按打开顺序,最后一个元素是栈顶)
|
||||
private subPanelStacks: Map<PanelType, Node[]> = new Map();
|
||||
// 子面板到主面板类型的映射
|
||||
private subPanelMapping: Map<Node, PanelType> = new Map();
|
||||
// 子面板的动画配置
|
||||
private subPanelAnimationConfigs: Map<Node, SubPanelAnimationConfig> =
|
||||
new Map();
|
||||
|
||||
@@ -206,114 +215,6 @@ export class NavigationManager {
|
||||
// 动画锁,防止动画中重复操作
|
||||
private isAnimating: boolean = false;
|
||||
|
||||
/**
|
||||
* 带动画的子面板清理方法
|
||||
* @param basePanel 主面板节点
|
||||
* @param callback 所有动画完成后的回调
|
||||
*/
|
||||
private clearSubPanelsWithAnimation(
|
||||
basePanel: Node,
|
||||
callback?: Function
|
||||
): void {
|
||||
if (!basePanel || !basePanel.isValid) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
const subPanels = this.subPanelDic.get(basePanel);
|
||||
if (!subPanels || subPanels.length === 0) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
let completedCount = 0;
|
||||
const totalCount = subPanels.length;
|
||||
|
||||
// 完成动画的回调
|
||||
const onAnimationComplete = () => {
|
||||
completedCount++;
|
||||
if (completedCount >= totalCount) {
|
||||
// 清理subPanelDic
|
||||
this.subPanelDic.delete(basePanel);
|
||||
callback && callback();
|
||||
}
|
||||
};
|
||||
|
||||
// 同时播放所有子面板的关闭动画
|
||||
for (let i = subPanels.length - 1; i >= 0; i--) {
|
||||
const subPanel = subPanels[i];
|
||||
if (subPanel && subPanel.isValid) {
|
||||
// 获取动画配置
|
||||
let config = this.subPanelAnimationConfigs.get(subPanel);
|
||||
if (!config) {
|
||||
config = {
|
||||
openAnimation: PageTransitionType.SCALE,
|
||||
closeAnimation: PageTransitionType.SCALE,
|
||||
duration: 0.2,
|
||||
};
|
||||
}
|
||||
|
||||
// 播放关闭动画
|
||||
this.playSubPanelCloseAnimation(subPanel, config, () => {
|
||||
// 清理映射
|
||||
this.subPanelMapping.delete(subPanel);
|
||||
this.subPanelAnimationConfigs.delete(subPanel);
|
||||
|
||||
// 销毁节点
|
||||
const baseView = subPanel.getComponent(li_BaseView);
|
||||
if (baseView) {
|
||||
baseView.close();
|
||||
} else {
|
||||
subPanel.destroy();
|
||||
}
|
||||
|
||||
onAnimationComplete();
|
||||
});
|
||||
} else {
|
||||
onAnimationComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的subPanel清理方法
|
||||
* @param basePanel 主面板节点
|
||||
* @param destroyPanels 是否销毁面板,默认为true
|
||||
*/
|
||||
private clearSubPanels(basePanel: Node, destroyPanels: boolean = true): void {
|
||||
if (!basePanel || !basePanel.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const subPanels = this.subPanelDic.get(basePanel);
|
||||
if (!subPanels || subPanels.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清理所有子面板
|
||||
for (let i = subPanels.length - 1; i >= 0; i--) {
|
||||
const subPanel = subPanels[i];
|
||||
if (subPanel && subPanel.isValid) {
|
||||
// 清理subPanelMapping
|
||||
this.subPanelMapping.delete(subPanel);
|
||||
|
||||
// 清理动画配置
|
||||
this.subPanelAnimationConfigs.delete(subPanel);
|
||||
|
||||
if (destroyPanels) {
|
||||
const baseView = subPanel.getComponent(li_BaseView);
|
||||
if (baseView) {
|
||||
baseView.close();
|
||||
} else {
|
||||
subPanel.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清理subPanelDic
|
||||
this.subPanelDic.delete(basePanel);
|
||||
}
|
||||
public openSubPanel(
|
||||
panelName: string,
|
||||
basePanel: Node,
|
||||
@@ -330,6 +231,15 @@ export class NavigationManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// 确定主面板类型
|
||||
const basePanelType = this.getPanelType(basePanel);
|
||||
if (!basePanelType) {
|
||||
logger.error(
|
||||
`[NavigationManager] 无法确定basePanel类型: ${basePanel.name}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用默认动画配置
|
||||
const defaultConfig: SubPanelAnimationConfig = {
|
||||
openAnimation: PageTransitionType.SCALE,
|
||||
@@ -339,6 +249,17 @@ export class NavigationManager {
|
||||
};
|
||||
const config = animationConfig || defaultConfig;
|
||||
|
||||
// 获取当前栈顶的子面板
|
||||
const currentTopSubPanel = this.getTopSubPanel(basePanelType);
|
||||
|
||||
// 如果当前有栈顶子面板,先隐藏它
|
||||
if (currentTopSubPanel && currentTopSubPanel.isValid) {
|
||||
logger.log(
|
||||
`[NavigationManager] 隐藏当前栈顶子面板: ${currentTopSubPanel.name}`
|
||||
);
|
||||
this.hideSubPanel(currentTopSubPanel);
|
||||
}
|
||||
|
||||
// 如果需要隐藏basePanel,在打开动画前隐藏
|
||||
if (config.hideBasePanel && basePanel.active) {
|
||||
logger.log(`[NavigationManager] 隐藏basePanel: ${basePanel.name}`);
|
||||
@@ -353,21 +274,18 @@ export class NavigationManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subPanelDic.has(basePanel)) {
|
||||
this.subPanelDic.set(basePanel, []);
|
||||
// 初始化栈(如果不存在)
|
||||
if (!this.subPanelStacks.has(basePanelType)) {
|
||||
this.subPanelStacks.set(basePanelType, []);
|
||||
}
|
||||
|
||||
const subPanels = this.subPanelDic.get(basePanel);
|
||||
subPanels.push(n);
|
||||
// 将新子面板推入栈顶
|
||||
const stack = this.subPanelStacks.get(basePanelType);
|
||||
stack.push(n);
|
||||
|
||||
// 记录子面板到主面板的映射
|
||||
this.subPanelMapping.set(n, basePanelType);
|
||||
|
||||
const basePanelType = this.getPanelType(basePanel);
|
||||
if (basePanelType) {
|
||||
this.subPanelMapping.set(n, basePanelType);
|
||||
} else {
|
||||
logger.warn(
|
||||
`[NavigationManager] 无法确定basePanel类型: ${basePanel.name}`
|
||||
);
|
||||
}
|
||||
// 存储动画配置
|
||||
this.subPanelAnimationConfigs.set(n, config);
|
||||
|
||||
@@ -379,6 +297,10 @@ export class NavigationManager {
|
||||
);
|
||||
}
|
||||
|
||||
logger.log(
|
||||
`[NavigationManager] 子面板已推入栈顶: ${n.name}, 当前栈深度: ${stack.length}`
|
||||
);
|
||||
|
||||
// 执行打开动画
|
||||
this.playSubPanelOpenAnimation(n, config);
|
||||
};
|
||||
@@ -579,6 +501,148 @@ export class NavigationManager {
|
||||
// 确保清空集合
|
||||
this.popupPanels.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定主面板栈顶的子面板
|
||||
* @param panelType 主面板类型
|
||||
* @returns 栈顶子面板,如果栈为空则返回null
|
||||
*/
|
||||
private getTopSubPanel(panelType: PanelType): Node | null {
|
||||
const stack = this.subPanelStacks.get(panelType);
|
||||
if (!stack || stack.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return stack[stack.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空指定主面板的子页面栈
|
||||
* @param panelType 主面板类型
|
||||
* @param withAnimation 是否播放动画
|
||||
* @param callback 清空完成后的回调
|
||||
*/
|
||||
private clearSubPanelStack(
|
||||
panelType: PanelType,
|
||||
withAnimation: boolean = true,
|
||||
callback?: Function
|
||||
): void {
|
||||
const stack = this.subPanelStacks.get(panelType);
|
||||
if (!stack || stack.length === 0) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!withAnimation) {
|
||||
// 不播放动画,直接销毁所有子面板
|
||||
for (let i = stack.length - 1; i >= 0; i--) {
|
||||
const subPanel = stack[i];
|
||||
if (subPanel && subPanel.isValid) {
|
||||
// 清理映射
|
||||
this.subPanelMapping.delete(subPanel);
|
||||
this.subPanelAnimationConfigs.delete(subPanel);
|
||||
this.hiddenBasePanels.delete(subPanel);
|
||||
|
||||
// 销毁节点
|
||||
const baseView = subPanel.getComponent(li_BaseView);
|
||||
if (baseView) {
|
||||
baseView.close();
|
||||
} else {
|
||||
subPanel.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.subPanelStacks.delete(panelType);
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// 播放动画
|
||||
let completedCount = 0;
|
||||
const totalCount = stack.length;
|
||||
|
||||
const onAnimationComplete = () => {
|
||||
completedCount++;
|
||||
if (completedCount >= totalCount) {
|
||||
this.subPanelStacks.delete(panelType);
|
||||
callback && callback();
|
||||
}
|
||||
};
|
||||
|
||||
// 同时播放所有子面板的关闭动画
|
||||
for (let i = stack.length - 1; i >= 0; i--) {
|
||||
const subPanel = stack[i];
|
||||
if (subPanel && subPanel.isValid) {
|
||||
// 获取动画配置
|
||||
let config = this.subPanelAnimationConfigs.get(subPanel);
|
||||
if (!config) {
|
||||
config = {
|
||||
openAnimation: PageTransitionType.SCALE,
|
||||
closeAnimation: PageTransitionType.SCALE,
|
||||
duration: 0.2,
|
||||
};
|
||||
}
|
||||
|
||||
// 播放关闭动画
|
||||
this.playSubPanelCloseAnimation(subPanel, config, () => {
|
||||
// 清理映射
|
||||
this.subPanelMapping.delete(subPanel);
|
||||
this.subPanelAnimationConfigs.delete(subPanel);
|
||||
this.hiddenBasePanels.delete(subPanel);
|
||||
|
||||
// 销毁节点
|
||||
const baseView = subPanel.getComponent(li_BaseView);
|
||||
if (baseView) {
|
||||
baseView.close();
|
||||
} else {
|
||||
subPanel.destroy();
|
||||
}
|
||||
|
||||
onAnimationComplete();
|
||||
});
|
||||
} else {
|
||||
onAnimationComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示子面板
|
||||
* @param panel 子面板节点
|
||||
* @param config 动画配置(可选)
|
||||
* @param withAnimation 是否播放打开动画,默认为 true
|
||||
*/
|
||||
private showSubPanel(
|
||||
panel: Node,
|
||||
config?: SubPanelAnimationConfig,
|
||||
withAnimation: boolean = true
|
||||
): void {
|
||||
if (!panel || !panel.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
panel.active = true;
|
||||
|
||||
if (config && withAnimation) {
|
||||
this.playSubPanelOpenAnimation(panel, config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏子面板(不销毁)
|
||||
* @param panel 子面板节点
|
||||
* @param callback 隐藏完成后的回调
|
||||
*/
|
||||
private hideSubPanel(panel: Node, callback?: Function): void {
|
||||
if (!panel || !panel.isValid) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接隐藏,不播放动画(因为会被新面板覆盖)
|
||||
panel.active = false;
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
public closeSubPanel(
|
||||
panel: Node | li_BaseView,
|
||||
animationConfig?: SubPanelAnimationConfig
|
||||
@@ -602,6 +666,39 @@ export class NavigationManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// 确定子面板所属的主面板类型
|
||||
const basePanelType = this.subPanelMapping.get(panelNode);
|
||||
if (!basePanelType) {
|
||||
logger.warn(
|
||||
`[NavigationManager] closeSubPanel: 无法确定子面板所属的主面板类型: ${panelNode.name}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取栈
|
||||
const stack = this.subPanelStacks.get(basePanelType);
|
||||
if (!stack || stack.length === 0) {
|
||||
logger.warn(
|
||||
`[NavigationManager] closeSubPanel: 栈为空或不存在,无法关闭子面板: ${panelNode.name}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找子面板在栈中的索引
|
||||
const panelIndex = stack.indexOf(panelNode);
|
||||
if (panelIndex === -1) {
|
||||
logger.warn(
|
||||
`[NavigationManager] closeSubPanel: 子面板不在栈中: ${panelNode.name}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 从栈中移除
|
||||
stack.splice(panelIndex, 1);
|
||||
logger.log(
|
||||
`[NavigationManager] 从栈中移除子面板: ${panelNode.name}, 剩余栈深度: ${stack.length}`
|
||||
);
|
||||
|
||||
// 获取动画配置(优先使用传入的配置,然后使用存储的配置,最后使用默认配置)
|
||||
let config = animationConfig;
|
||||
if (!config && this.subPanelAnimationConfigs.has(panelNode)) {
|
||||
@@ -616,23 +713,25 @@ export class NavigationManager {
|
||||
};
|
||||
}
|
||||
|
||||
// 检查是否需要恢复显示basePanel或显示下一层子面板
|
||||
const shouldRestoreBasePanel =
|
||||
config.hideBasePanel && this.hiddenBasePanels.has(panelNode);
|
||||
const basePanel = shouldRestoreBasePanel
|
||||
? this.hiddenBasePanels.get(panelNode)
|
||||
: null;
|
||||
|
||||
// 获取新的栈顶子面板(如果有)
|
||||
const newTopSubPanel = this.getTopSubPanel(basePanelType);
|
||||
|
||||
// 执行关闭动画
|
||||
this.playSubPanelCloseAnimation(panelNode, config, () => {
|
||||
// 如果隐藏了basePanel,需要恢复显示
|
||||
// 清理映射
|
||||
this.subPanelMapping.delete(panelNode);
|
||||
this.subPanelAnimationConfigs.delete(panelNode);
|
||||
if (this.hiddenBasePanels.has(panelNode)) {
|
||||
const basePanel = this.hiddenBasePanels.get(panelNode);
|
||||
if (basePanel && basePanel.isValid) {
|
||||
logger.log(
|
||||
`[NavigationManager] 恢复显示basePanel: ${basePanel.name}`
|
||||
);
|
||||
basePanel.active = true;
|
||||
}
|
||||
this.hiddenBasePanels.delete(panelNode);
|
||||
}
|
||||
|
||||
// 从管理映射中移除
|
||||
this.removeSubPanelFromMappings(panelNode);
|
||||
|
||||
// 销毁或关闭panel
|
||||
try {
|
||||
if (panel instanceof Node) {
|
||||
@@ -643,44 +742,42 @@ export class NavigationManager {
|
||||
} catch (error) {
|
||||
logger.error("[NavigationManager] closeSubPanel error:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 从管理映射中移除子面板
|
||||
*/
|
||||
private removeSubPanelFromMappings(panelNode: Node): void {
|
||||
// 从subPanelMapping和subPanelDic中移除
|
||||
if (this.subPanelMapping.has(panelNode)) {
|
||||
const baseType = this.subPanelMapping.get(panelNode);
|
||||
const baseNode = this.panelCache.get(baseType);
|
||||
|
||||
if (baseNode && this.subPanelDic.has(baseNode)) {
|
||||
const subPanels = this.subPanelDic.get(baseNode);
|
||||
const index = subPanels.indexOf(panelNode);
|
||||
if (index !== -1) {
|
||||
subPanels.splice(index, 1);
|
||||
// 如果subPanels为空,删除整个条目
|
||||
if (subPanels.length === 0) {
|
||||
this.subPanelDic.delete(baseNode);
|
||||
}
|
||||
} else {
|
||||
logger.warn(`[NavigationManager] ${panelNode.name} 不在子页面列表中`);
|
||||
// 关闭动画完成后,决定显示什么
|
||||
if (newTopSubPanel && newTopSubPanel.isValid) {
|
||||
// 栈中还有子面板,显示新的栈顶(不播放动画,直接显示)
|
||||
logger.log(
|
||||
`[NavigationManager] 显示新的栈顶子面板(无动画): ${newTopSubPanel.name}`
|
||||
);
|
||||
const newTopConfig = this.subPanelAnimationConfigs.get(newTopSubPanel);
|
||||
this.showSubPanel(newTopSubPanel, newTopConfig, false); // 传入 false,不播放动画
|
||||
} else if (shouldRestoreBasePanel && basePanel && basePanel.isValid) {
|
||||
// 栈已空,且之前隐藏了basePanel,恢复显示basePanel
|
||||
logger.log(`[NavigationManager] 恢复显示basePanel: ${basePanel.name}`);
|
||||
basePanel.active = true;
|
||||
} else {
|
||||
// 栈已空,且没有隐藏basePanel,确保basePanel可见
|
||||
const cachedBasePanel = this.panelCache.get(basePanelType);
|
||||
if (
|
||||
cachedBasePanel &&
|
||||
cachedBasePanel.isValid &&
|
||||
!cachedBasePanel.active
|
||||
) {
|
||||
logger.log(
|
||||
`[NavigationManager] 确保basePanel可见: ${cachedBasePanel.name}`
|
||||
);
|
||||
cachedBasePanel.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.subPanelMapping.delete(panelNode);
|
||||
}
|
||||
|
||||
// 移除动画配置
|
||||
if (this.subPanelAnimationConfigs.has(panelNode)) {
|
||||
this.subPanelAnimationConfigs.delete(panelNode);
|
||||
}
|
||||
|
||||
// 移除隐藏映射
|
||||
if (this.hiddenBasePanels.has(panelNode)) {
|
||||
this.hiddenBasePanels.delete(panelNode);
|
||||
}
|
||||
// 如果栈完全清空,删除映射
|
||||
if (stack.length === 0) {
|
||||
this.subPanelStacks.delete(basePanelType);
|
||||
logger.log(
|
||||
`[NavigationManager] 子页面栈已清空,删除映射: ${basePanelType}`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -749,8 +846,8 @@ export class NavigationManager {
|
||||
if (this.panelCache.has(panelType)) {
|
||||
const cachedPanel = this.panelCache.get(panelType);
|
||||
if (cachedPanel && cachedPanel.isValid) {
|
||||
// 确保缓存panel的subPanel状态正确,清理可能残留的subPanel引用
|
||||
this.clearSubPanels(cachedPanel, false); // 不销毁panel,只清理引用
|
||||
// 注意:子面板的清理已经在 switchToPanel 中通过 clearSubPanelStack 完成了
|
||||
// 这里不需要再清理
|
||||
|
||||
// 特殊处理:主题面板刷新(避免重复调用Show)
|
||||
if (panelType === PanelType.THEME) {
|
||||
@@ -796,8 +893,8 @@ export class NavigationManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// 同时开始子面板的关闭动画
|
||||
this.clearSubPanelsWithAnimation(panel);
|
||||
// 注意:子面板的清理已经在 switchToPanel 中通过 clearSubPanelStack 完成了
|
||||
// 这里只需要播放主面板的隐藏动画
|
||||
|
||||
if (!panel.active) panel.active = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user