From 347a7292ccc8cb926edfbd85dceb8aaf681ff3b7 Mon Sep 17 00:00:00 2001 From: xlj <543978819@qq.com> Date: Mon, 13 Oct 2025 16:20:37 +0800 Subject: [PATCH 1/2] fix bug --- assets/Scripts/Main/Manager/ViewManager.ts | 6 +++++- assets/Scripts/Sub/UI/PreloadUI.ts | 19 ++++++++++++++----- .../chat18x/ui/panels/ChargePopPanel.ts | 10 +--------- .../chat18x/ui/panels/PersonalPanel.ts | 12 ++---------- .../chat18x/ui/panels/PurchasePanel.ts | 8 ++------ .../Scripts/chat18x/ui/panels/ThemePanel.ts | 10 +--------- .../Scripts/chat18x/ui/panels/Web3PopPanel.ts | 2 -- assets/bundles/Chat18x/ThemePanel.prefab | 8 +++++--- 8 files changed, 30 insertions(+), 45 deletions(-) diff --git a/assets/Scripts/Main/Manager/ViewManager.ts b/assets/Scripts/Main/Manager/ViewManager.ts index 7c03663e..9ef0285b 100644 --- a/assets/Scripts/Main/Manager/ViewManager.ts +++ b/assets/Scripts/Main/Manager/ViewManager.ts @@ -101,7 +101,11 @@ export class ViewManager extends Component { // t_node.sType = "top" GameRootUI.I.AddUIToLayer(t_node, CommonConfig.UILayerGroup.Layer_top); } else if (type == "tips") { - GameRootUI.I.AddUIToLayer(t_node, CommonConfig.UILayerGroup.Layer_tips); + GameRootUI.I.AddUIToLayer( + t_node, + CommonConfig.UILayerGroup.Layer_tips, + -1 + ); } } diff --git a/assets/Scripts/Sub/UI/PreloadUI.ts b/assets/Scripts/Sub/UI/PreloadUI.ts index c46933fd..b5a65a63 100644 --- a/assets/Scripts/Sub/UI/PreloadUI.ts +++ b/assets/Scripts/Sub/UI/PreloadUI.ts @@ -96,7 +96,7 @@ export class PreloadUI extends Component { this.desc = this._nodeTab.Desc.getComponent(Label); // 设置视频为静音,提高移动端自动播放成功率 - this.VideoPlayer.muted = true; + this.VideoPlayer.mute = true; this.VideoPlayer.loop = true; this.VideoPlayer.stayOnBottom = true; @@ -205,16 +205,25 @@ export class PreloadUI extends Component { // 尝试播放视频 try { this.VideoPlayer.play(); - logger.log(`PreloadUI: Video play attempted (retry: ${this.videoPlayRetryCount}/${this.maxVideoPlayRetry})`); + logger.log( + `PreloadUI: Video play attempted (retry: ${this.videoPlayRetryCount}/${this.maxVideoPlayRetry})` + ); // 延迟检查播放状态,如果失败则重试 this.scheduleOnce(() => { - if (!this.VideoPlayer.isPlaying && this.videoPlayRetryCount < this.maxVideoPlayRetry) { + if ( + !this.VideoPlayer.isPlaying && + this.videoPlayRetryCount < this.maxVideoPlayRetry + ) { this.videoPlayRetryCount++; - logger.warn(`PreloadUI: Video play failed, retrying... (${this.videoPlayRetryCount}/${this.maxVideoPlayRetry})`); + logger.warn( + `PreloadUI: Video play failed, retrying... (${this.videoPlayRetryCount}/${this.maxVideoPlayRetry})` + ); this.tryPlayVideo(); } else if (!this.VideoPlayer.isPlaying) { - logger.warn("PreloadUI: Video autoplay failed after max retries. Will retry on user interaction."); + logger.warn( + "PreloadUI: Video autoplay failed after max retries. Will retry on user interaction." + ); } }, 0.5); } catch (error) { diff --git a/assets/Scripts/chat18x/ui/panels/ChargePopPanel.ts b/assets/Scripts/chat18x/ui/panels/ChargePopPanel.ts index e89ebf65..e4f02681 100644 --- a/assets/Scripts/chat18x/ui/panels/ChargePopPanel.ts +++ b/assets/Scripts/chat18x/ui/panels/ChargePopPanel.ts @@ -20,15 +20,7 @@ export class ChargePopPanel extends li_BaseView { } gotoCharge() { - NavigationManager.Instance.openSubPanel( - "PurchasePanel", - NavigationManager.Instance.getCurrentActivePanelNode(), - { base: NavigationManager.Instance.getCurrentActivePanelNode() }, - { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, - } - ); + NavigationManager.Instance.openPopupPanel("PurchasePanel"); this.close(); } diff --git a/assets/Scripts/chat18x/ui/panels/PersonalPanel.ts b/assets/Scripts/chat18x/ui/panels/PersonalPanel.ts index 2c17d086..5eac4139 100644 --- a/assets/Scripts/chat18x/ui/panels/PersonalPanel.ts +++ b/assets/Scripts/chat18x/ui/panels/PersonalPanel.ts @@ -155,18 +155,10 @@ export class PersonalPanel extends li_BaseView { private openSettings(): void { // TODO: 打开设置面板 - NavigationManager.Instance.openSubPanel("SettingPanel", this.node); + NavigationManager.Instance.openPopupPanel("SettingPanel", this.node); } private openPurchase() { - NavigationManager.Instance.openSubPanel( - "PurchasePanel", - this.node, - { base: this.node }, - { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, - } - ); + NavigationManager.Instance.openPopupPanel("PurchasePanel"); } private openFavorites() { diff --git a/assets/Scripts/chat18x/ui/panels/PurchasePanel.ts b/assets/Scripts/chat18x/ui/panels/PurchasePanel.ts index e228f412..bc982c02 100644 --- a/assets/Scripts/chat18x/ui/panels/PurchasePanel.ts +++ b/assets/Scripts/chat18x/ui/panels/PurchasePanel.ts @@ -44,10 +44,7 @@ export class PurchasePanel extends li_BaseView { cashBtnSelecting; web3BtnSelecting; - base: Node; - openUIDataCT(data: any): void { - this.base = data.base; - } + openUIDataCT(data: any): void {} onLoadCT(): void { this.node.active = false; this.scheduleOnce(() => { @@ -208,12 +205,11 @@ export class PurchasePanel extends li_BaseView { payType: param.payType, goodId: param.goodId, networkType: this.getDefaultNetworkType(), - basePanel: this.base, payPanel: this, }; if (this.web3PopPanel == null) - NavigationManager.Instance.openSubPanel("Web3PopPanel", this.base, data); + NavigationManager.Instance.openPopupPanel("Web3PopPanel", data); else { data.networkType = undefined; this.web3PopPanel.refreshData(data); diff --git a/assets/Scripts/chat18x/ui/panels/ThemePanel.ts b/assets/Scripts/chat18x/ui/panels/ThemePanel.ts index bb90c103..9d6f2904 100644 --- a/assets/Scripts/chat18x/ui/panels/ThemePanel.ts +++ b/assets/Scripts/chat18x/ui/panels/ThemePanel.ts @@ -470,15 +470,7 @@ export class ThemePanel extends li_BaseView { } enterShop() { - NavigationManager.Instance.openSubPanel( - "PurchasePanel", - this.node, - { base: this.node }, - { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, - } - ); + NavigationManager.Instance.openPopupPanel("PurchasePanel"); } openRecomandList() { diff --git a/assets/Scripts/chat18x/ui/panels/Web3PopPanel.ts b/assets/Scripts/chat18x/ui/panels/Web3PopPanel.ts index d50ef412..f14a10bb 100644 --- a/assets/Scripts/chat18x/ui/panels/Web3PopPanel.ts +++ b/assets/Scripts/chat18x/ui/panels/Web3PopPanel.ts @@ -36,7 +36,6 @@ export class Web3PopPanel extends li_BaseView { private networkType: proto.cs.EnmNetworkType; private goodId: number; - private basePanel: PersonalPanel; private payPanel: PurchasePanel; private countdownTimer: any; @@ -112,7 +111,6 @@ export class Web3PopPanel extends li_BaseView { this.goodId = data.goodId; this.curType = data.networkType != undefined ? data.networkType : this.curType; - this.basePanel = data.basePanel; this.payPanel = data.payPanel; } refreshView() { diff --git a/assets/bundles/Chat18x/ThemePanel.prefab b/assets/bundles/Chat18x/ThemePanel.prefab index 4a039bc1..31c7afae 100644 --- a/assets/bundles/Chat18x/ThemePanel.prefab +++ b/assets/bundles/Chat18x/ThemePanel.prefab @@ -612,6 +612,8 @@ "__id__": 0 }, "fileId": "eaz1XV32NMAYZ1f8eNu4bc", + "instance": null, + "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { @@ -717,13 +719,13 @@ "b": 108, "a": 255 }, - "_string": "已过期", + "_string": "Expired", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 20, + "_actualFontSize": 17, "_fontSize": 25.6, "_fontFamily": "Arial", - "_lineHeight": 32.4, + "_lineHeight": 48.6, "_overflow": 2, "_enableWrapText": true, "_font": { From 104e7f566ccbe02e43d22b1fcaff3a5856ed20b1 Mon Sep 17 00:00:00 2001 From: xlj <543978819@qq.com> Date: Mon, 13 Oct 2025 17:09:39 +0800 Subject: [PATCH 2/2] fix --- assets/Scripts/chat18x/core/ChatAIService.ts | 19 +- assets/Scripts/chat18x/core/ChatController.ts | 15 +- .../chat18x/manager/NavigationManager.ts | 443 +++++++++++------- assets/Scripts/chat18x/ui/panels/ChatPanel.ts | 14 + .../chat18x/ui/panels/GirlDetailPanel.ts | 24 +- assets/Scripts/chat18x/ui/panels/ShowPanel.ts | 9 +- .../Scripts/chat18x/uiitems/GirlListItem.ts | 28 +- .../chat18x/uiitems/PastGirlListItem.ts | 27 +- .../Scripts/chat18x/uiitems/RecomandItem.ts | 27 +- 9 files changed, 376 insertions(+), 230 deletions(-) diff --git a/assets/Scripts/chat18x/core/ChatAIService.ts b/assets/Scripts/chat18x/core/ChatAIService.ts index 7143b615..1bb86037 100644 --- a/assets/Scripts/chat18x/core/ChatAIService.ts +++ b/assets/Scripts/chat18x/core/ChatAIService.ts @@ -43,6 +43,7 @@ export class ChatAIService { private ai: GoogleGenAI; private chatInstances: Map = new Map(); private currentRoleId: number | null = null; + private shouldShowErrorTips: boolean = true; // 控制是否显示错误提示 private constructor() { const config = ApiConfig.Instance.getAIConfig(); @@ -98,6 +99,14 @@ export class ChatAIService { return this._instance; } + /** + * 设置是否显示错误提示 + * @param show 是否显示 + */ + public setShouldShowErrorTips(show: boolean): void { + this.shouldShowErrorTips = show; + } + /** * 创建或获取指定角色的聊天实例 * @param roleId 角色ID @@ -306,7 +315,10 @@ export class ChatAIService { return response.text; } else { - TipsPanel.show(response.promptFeedback.blockReason); + // 只在允许显示错误提示时才显示 + if (this.shouldShowErrorTips) { + TipsPanel.show(response.promptFeedback.blockReason); + } const warningMsg = `AI返回了空响应 (角色ID: ${roleId})`; ErrorHandler.Instance.handleError( new Error(warningMsg), @@ -321,7 +333,10 @@ export class ChatAIService { roleId, message: message, }); - TipsPanel.show("api调用失败,请使用vpn并重新启动"); + // 只在允许显示错误提示时才显示 + if (this.shouldShowErrorTips) { + TipsPanel.show("api调用失败,请使用vpn并重新启动"); + } return null; } } diff --git a/assets/Scripts/chat18x/core/ChatController.ts b/assets/Scripts/chat18x/core/ChatController.ts index 04d440dd..35a6a771 100644 --- a/assets/Scripts/chat18x/core/ChatController.ts +++ b/assets/Scripts/chat18x/core/ChatController.ts @@ -208,6 +208,12 @@ export class ChatController { message ); + // 检查View是否仍然绑定,如果已解绑则不处理响应(用户可能已退出Panel) + if (!this.callback) { + logger.log("ChatController: View已解绑,忽略AI响应"); + return false; + } + if (response) { // 添加用户消息到模型 this.chatModel.addDialog(true, message); @@ -232,7 +238,7 @@ export class ChatController { return false; } } catch (error) { - + ErrorHandler.Instance.handleApiError( error, "ChatController.sendMessage", @@ -241,7 +247,12 @@ export class ChatController { message: message.substring(0, 100) + "...", } ); - this.handleError(error as Error); + // 只有当View仍然绑定时才调用错误处理回调 + if (this.callback) { + this.handleError(error as Error); + } else { + logger.log("ChatController: View已解绑,不显示错误提示"); + } return false; } } diff --git a/assets/Scripts/chat18x/manager/NavigationManager.ts b/assets/Scripts/chat18x/manager/NavigationManager.ts index 98286bed..e64d952e 100644 --- a/assets/Scripts/chat18x/manager/NavigationManager.ts +++ b/assets/Scripts/chat18x/manager/NavigationManager.ts @@ -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 = new Map(); + // 每个主面板的子页面栈(按打开顺序,最后一个元素是栈顶) + private subPanelStacks: Map = new Map(); + // 子面板到主面板类型的映射 private subPanelMapping: Map = new Map(); + // 子面板的动画配置 private subPanelAnimationConfigs: Map = 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; diff --git a/assets/Scripts/chat18x/ui/panels/ChatPanel.ts b/assets/Scripts/chat18x/ui/panels/ChatPanel.ts index 3ab7486a..dff00d6a 100644 --- a/assets/Scripts/chat18x/ui/panels/ChatPanel.ts +++ b/assets/Scripts/chat18x/ui/panels/ChatPanel.ts @@ -14,6 +14,7 @@ import { import "../../utils/polyfills"; import { DialogManager } from "../../manager/DialogManager"; import { ChatController, IChatPanelCallback } from "../../core/ChatController"; +import { ChatAIService } from "../../core/ChatAIService"; import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView"; import Utils from "db://assets/Scripts/Main/Common/Utils"; import { InnerMsgCode } from "db://assets/Scripts/Main/Config/InnerMsgCode"; @@ -73,6 +74,7 @@ export class ChatPanel extends li_BaseView implements IChatPanelCallback { private waitingAI: Node; nameKey: string; private currentEmotion: VideoEmotion | null = null; + private isActive: boolean = false; // 标记Panel是否处于活跃状态 private onLanguageChangeCallback = () => { if (!this.girlName) return; @@ -388,10 +390,14 @@ export class ChatPanel extends li_BaseView implements IChatPanelCallback { protected onEnable(): void { if (!this.manager) this.manager = DialogManager.getInstance(); + this.isActive = true; // 设置为活跃状态 + ChatAIService.Instance.setShouldShowErrorTips(true); // 启用错误提示 this.base.active = false; this.refresh(); } protected onDisable(): void { + this.isActive = false; // 设置为非活跃状态,停止响应异步操作 + ChatAIService.Instance.setShouldShowErrorTips(false); // 禁用错误提示,避免在其他页面显示 this.base.active = true; } @@ -404,12 +410,20 @@ export class ChatPanel extends li_BaseView implements IChatPanelCallback { } if (this.isWaiting) { TipsPanel.show(LanguageUtils.getText("chat_error_code_1003")); + return; // 修复:添加return,避免重复发送 } // 通过ChatController发送消息 this.isWaiting = true; this.waitingAI.active = true; const succeed = await this.chatController.sendMessage(str); + + // 检查Panel是否仍然活跃,如果已经关闭则不处理结果 + if (!this.isActive) { + logger.log("ChatPanel已关闭,忽略AI响应结果"); + return; + } + if (succeed) { // 清空输入框 this.editBox.string = ""; diff --git a/assets/Scripts/chat18x/ui/panels/GirlDetailPanel.ts b/assets/Scripts/chat18x/ui/panels/GirlDetailPanel.ts index 193e2f56..6c2b9c13 100644 --- a/assets/Scripts/chat18x/ui/panels/GirlDetailPanel.ts +++ b/assets/Scripts/chat18x/ui/panels/GirlDetailPanel.ts @@ -329,18 +329,18 @@ export class GirlDetailPanel extends li_BaseView { const isRelease = girlData.getIsRelease(this.category.toString(), this.id); const isFree = girlData.isGirlFreeType(this.category.toString(), this.id); //if (isRelease || isFree) { - // 使用带过渡动画的导航方法 - NavigationManager.Instance.switchToPanel( - PanelType.PAST_GIRL_LIST, - false, - (base) => { - const chatData = { girlId: this.id, base: base }; - NavigationManager.Instance.openSubPanel("ChatPanel", base, chatData, { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, - }); - } - ); + // 直接在当前页面打开ChatPanel作为子页面 + const currentPanel = NavigationManager.Instance.getCurrentActivePanelNode(); + if (currentPanel) { + const chatData = { girlId: this.id, base: currentPanel }; + NavigationManager.Instance.openSubPanel("ChatPanel", currentPanel, chatData, { + openAnimation: PageTransitionType.SLIDE_LEFT, + closeAnimation: PageTransitionType.SLIDE_RIGHT, + hideBasePanel: true, + }); + } else { + logger.warn("[GirlDetailPanel] 无法获取当前激活的主页面"); + } // } else { // //未解锁 // NavigationManager.Instance.openPopupPanel("GirlListPopupPanel", { diff --git a/assets/Scripts/chat18x/ui/panels/ShowPanel.ts b/assets/Scripts/chat18x/ui/panels/ShowPanel.ts index 3aa8f9fa..f481e3bc 100644 --- a/assets/Scripts/chat18x/ui/panels/ShowPanel.ts +++ b/assets/Scripts/chat18x/ui/panels/ShowPanel.ts @@ -76,7 +76,14 @@ export class ShowPanel extends li_BaseView { // 加载远程资源 let newPath = envData.cdn + "/" + "Girls/" + path + ".png"; ResManager.I.changeRemoteSpriteFrame(this.image, newPath, () => { - Utils.adjustBgPixelRatio(this.image.node, 3); + // 固定高度为 1334,等比缩放宽度 + const targetHeight = 1334; + const uiTransform = this.image.node.getComponent(UITransform); + if (uiTransform && uiTransform.height > 0) { + const scale = targetHeight / uiTransform.height; + uiTransform.height = targetHeight; + uiTransform.width = uiTransform.width * scale; + } }); } else { // 使用自己的 videoPlayer 进行置顶显示 diff --git a/assets/Scripts/chat18x/uiitems/GirlListItem.ts b/assets/Scripts/chat18x/uiitems/GirlListItem.ts index 3a9a13b9..58c05bd4 100644 --- a/assets/Scripts/chat18x/uiitems/GirlListItem.ts +++ b/assets/Scripts/chat18x/uiitems/GirlListItem.ts @@ -186,15 +186,14 @@ export class GirlListItem extends Component { currentPanel, this.id, { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, + openAnimation: PageTransitionType.NONE, + closeAnimation: PageTransitionType.NONE, hideBasePanel: true, // 隐藏底层panel,实现全屏显示 } ); } else { logger.warn("[GirlListItem] 无法获取当前激活的主页面"); } - this.baseNode.close(); } onClickChat() { @@ -205,16 +204,17 @@ export class GirlListItem extends Component { girlData.getGrilCategoryById(this.id) ); - NavigationManager.Instance.switchToPanel( - PanelType.PAST_GIRL_LIST, - false, - (base) => { - const chatData = { girlId: this.id, base: base }; - NavigationManager.Instance.openSubPanel("ChatPanel", base, chatData, { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, - }); - } - ); + // 直接在当前页面打开ChatPanel作为子页面 + const currentPanel = NavigationManager.Instance.getCurrentActivePanelNode(); + if (currentPanel) { + const chatData = { girlId: this.id, base: currentPanel }; + NavigationManager.Instance.openSubPanel("ChatPanel", currentPanel, chatData, { + openAnimation: PageTransitionType.SLIDE_LEFT, + closeAnimation: PageTransitionType.SLIDE_RIGHT, + hideBasePanel: true, + }); + } else { + logger.warn("[GirlListItem] 无法获取当前激活的主页面"); + } } } diff --git a/assets/Scripts/chat18x/uiitems/PastGirlListItem.ts b/assets/Scripts/chat18x/uiitems/PastGirlListItem.ts index 86384df8..66a44e35 100644 --- a/assets/Scripts/chat18x/uiitems/PastGirlListItem.ts +++ b/assets/Scripts/chat18x/uiitems/PastGirlListItem.ts @@ -220,8 +220,8 @@ export class PastGirlListItem extends Component { currentPanel, this.id, { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, + openAnimation: PageTransitionType.NONE, + closeAnimation: PageTransitionType.NONE, hideBasePanel: true, // 隐藏底层panel,实现全屏显示 } ); @@ -238,16 +238,17 @@ export class PastGirlListItem extends Component { girlData.getGrilCategoryById(this.id) ); - NavigationManager.Instance.switchToPanel( - PanelType.PAST_GIRL_LIST, - false, - (base) => { - const chatData = { girlId: this.id, base: base }; - NavigationManager.Instance.openSubPanel("ChatPanel", base, chatData, { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, - }); - } - ); + // 直接在当前页面打开ChatPanel作为子页面 + const currentPanel = NavigationManager.Instance.getCurrentActivePanelNode(); + if (currentPanel) { + const chatData = { girlId: this.id, base: currentPanel }; + NavigationManager.Instance.openSubPanel("ChatPanel", currentPanel, chatData, { + openAnimation: PageTransitionType.SLIDE_LEFT, + closeAnimation: PageTransitionType.SLIDE_RIGHT, + hideBasePanel: true, + }); + } else { + logger.warn("[PastGirlListItem] 无法获取当前激活的主页面"); + } } } diff --git a/assets/Scripts/chat18x/uiitems/RecomandItem.ts b/assets/Scripts/chat18x/uiitems/RecomandItem.ts index 871907a4..57e0dd21 100644 --- a/assets/Scripts/chat18x/uiitems/RecomandItem.ts +++ b/assets/Scripts/chat18x/uiitems/RecomandItem.ts @@ -96,17 +96,18 @@ export class RecomandItem extends Component { girlData.getGrilCategoryById(this.id) ); - NavigationManager.Instance.switchToPanel( - PanelType.PAST_GIRL_LIST, - false, - (base) => { - const chatData = { girlId: this.id, base: base }; - NavigationManager.Instance.openSubPanel("ChatPanel", base, chatData, { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, - }); - } - ); + // 直接在当前页面打开ChatPanel作为子页面 + const currentPanel = NavigationManager.Instance.getCurrentActivePanelNode(); + if (currentPanel) { + const chatData = { girlId: this.id, base: currentPanel }; + NavigationManager.Instance.openSubPanel("ChatPanel", currentPanel, chatData, { + openAnimation: PageTransitionType.SLIDE_LEFT, + closeAnimation: PageTransitionType.SLIDE_RIGHT, + hideBasePanel: true, + }); + } else { + logger.warn("[RecomandItem] 无法获取当前激活的主页面"); + } } gotoDetail() { const girlData = DataManager.I.getDataById(DataId.Girl); @@ -124,8 +125,8 @@ export class RecomandItem extends Component { currentPanel, this.id, { - openAnimation: PageTransitionType.SLIDE_LEFT, - closeAnimation: PageTransitionType.SLIDE_RIGHT, + openAnimation: PageTransitionType.NONE, + closeAnimation: PageTransitionType.NONE, hideBasePanel: true, // 隐藏底层panel,实现全屏显示 } );