Merge branch 'main' of 47.107.44.202:xionglijia/18xchat
This commit is contained in:
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -43,6 +43,7 @@ export class ChatAIService {
|
||||
private ai: GoogleGenAI;
|
||||
private chatInstances: Map<number, any> = 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 = "";
|
||||
|
||||
@@ -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", {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 进行置顶显示
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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] 无法获取当前激活的主页面");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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] 无法获取当前激活的主页面");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GirlData>(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,实现全屏显示
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user