Files
Max-Cocos-Demo/assets/scripts/DialogBox.ts
2025-10-28 21:55:41 +08:00

291 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { _decorator, Button, Component, director, Label, Node, tween, Vec3 } from "cc";
/**
* DialogBox配置选项接口
*/
export interface IDialogBoxOptions {
/** 标题,默认为"提示" */
title?: string;
/** 内容,默认为空 */
content?: string;
/** 确定回调,可选 */
onConfirm?: () => void;
/** 取消回调,可选 */
onCancel?: () => void;
/** 确定按钮文本,默认为"确定" */
confirmText?: string;
/** 取消按钮文本,默认为"取消" */
cancelText?: string;
/** 是否显示取消按钮默认为true */
showCancel?: boolean;
/** 场景中DialogBox节点名称默认为"DialogBox" */
dialogNodeName?: string;
hideClose?: boolean;
}
const { ccclass, property } = _decorator;
/**
* 基础对话框组件
*/
@ccclass("DialogBox")
export class DialogBox extends Component {
// 静态缓存场景中的DialogBox节点
private static sceneDialogBox: DialogBox | null = null;
private static dialogNode: Node | null = null;
@property(Label)
titleLabel: Label = null!;
@property(Label)
contentLabel: Label = null!;
@property(Button)
confirmBtn: Button = null!;
@property(Label)
confirmBtnLabel: Label = null!;
@property(Button)
cancelBtn: Button = null!;
@property(Label)
cancelBtnLabel: Label = null!;
// 回调函数
private onConfirmCallback: (() => void) | null = null;
private onCancelCallback: (() => void) | null = null;
start() {
// 绑定按钮事件
if (this.confirmBtn) {
this.confirmBtn.node.on(Button.EventType.CLICK, this.onConfirmClick, this);
}
if (this.cancelBtn) {
this.cancelBtn.node.on(Button.EventType.CLICK, this.onCancelClick, this);
}
}
/**
* 设置对话框内容
* @param options 配置选项
*/
public setContent(options: IDialogBoxOptions = {}): void {
const {
title = "提示",
content = "",
onConfirm,
onCancel,
confirmText = "确定",
cancelText = "取消",
showCancel = true,
} = options;
// 设置标题和内容
if (this.titleLabel) {
this.titleLabel.string = title;
}
if (this.contentLabel) {
this.contentLabel.string = content;
}
// 设置按钮文本
if (this.confirmBtnLabel) {
this.confirmBtnLabel.string = confirmText;
}
if (this.cancelBtnLabel) {
this.cancelBtnLabel.string = cancelText;
}
// 控制取消按钮显示状态
if (this.cancelBtn) {
this.cancelBtn.node.active = showCancel;
}
// 保存回调函数
this.onConfirmCallback = onConfirm || null;
this.onCancelCallback = onCancel || null;
}
/**
* 显示对话框(带弹出动画)
*/
public show(): void {
this.node.active = true;
// 初始化缩放和透明度
this.node.setScale(Vec3.ZERO);
// 弹出动画缩放从0到1带回弹效果
tween(this.node)
.to(
0.3,
{ scale: Vec3.ONE },
{
easing: "backOut", // 回弹缓动效果
},
)
.start();
}
/**
* 隐藏对话框(带关闭动画)
*/
public hide(): void {
// 停止所有动画
tween(this.node).stop();
// 关闭动画缩放到0
tween(this.node)
.to(
0.2,
{ scale: Vec3.ZERO },
{
easing: "backIn", // 回缩缓动效果
},
)
.call(() => {
// 动画结束后隐藏节点
this.node.active = false;
// 恢复缩放值,为下次显示做准备
this.node.setScale(Vec3.ONE);
})
.start();
// 清理回调
this.onConfirmCallback = null;
this.onCancelCallback = null;
}
/**
* 确定按钮点击事件
*/
private onConfirmClick(): void {
// 执行回调
if (this.onConfirmCallback) {
this.onConfirmCallback();
}
// 隐藏对话框
this.hide();
}
/**
* 取消按钮点击事件
*/
private onCancelClick(): void {
// 执行回调
if (this.onCancelCallback) {
this.onCancelCallback();
}
// 隐藏对话框
this.hide();
}
onDestroy() {
// 清理事件监听
if (this.confirmBtn) {
this.confirmBtn.node.off(Button.EventType.CLICK, this.onConfirmClick, this);
}
if (this.cancelBtn) {
this.cancelBtn.node.off(Button.EventType.CLICK, this.onCancelClick, this);
}
// 停止所有动画
tween(this.node).stop();
// 清理静态引用
if (DialogBox.sceneDialogBox === this) {
DialogBox.sceneDialogBox = null;
DialogBox.dialogNode = null;
}
}
/**
* 静态方法:显示对话框
* @param options 配置选项
*/
public static showDialog(options: IDialogBoxOptions = {}): void {
const { dialogNodeName = "DialogBox" } = options;
// 获取场景中的DialogBox节点
if (!DialogBox.sceneDialogBox || !DialogBox.dialogNode?.isValid) {
DialogBox.findDialogBoxInScene(dialogNodeName);
}
if (!DialogBox.sceneDialogBox) {
return; // 场景中未找到DialogBox节点
}
// 设置内容并显示
DialogBox.sceneDialogBox.setContent(options);
DialogBox.sceneDialogBox.show();
}
/**
* 静态方法:隐藏当前对话框(带动画)
*/
public static hideCurrentDialog(): void {
if (DialogBox.sceneDialogBox) {
DialogBox.sceneDialogBox.hide();
}
}
/**
* 静态方法:立即隐藏当前对话框(无动画)
*/
public static hideCurrentDialogImmediately(): void {
if (DialogBox.sceneDialogBox) {
// 停止所有动画
tween(DialogBox.sceneDialogBox.node).stop();
// 立即隐藏
DialogBox.sceneDialogBox.node.active = false;
DialogBox.sceneDialogBox.node.setScale(Vec3.ONE);
// 清理回调
DialogBox.sceneDialogBox.onConfirmCallback = null;
DialogBox.sceneDialogBox.onCancelCallback = null;
}
}
/**
* 在场景中查找DialogBox节点
* @param nodeName 节点名称
*/
private static findDialogBoxInScene(nodeName: string): void {
const scene = director.getScene();
if (!scene) {
return;
}
// 递归查找DialogBox节点
const findNodeRecursively = (node: Node, targetName: string): Node | null => {
if (node.name === targetName) {
return node;
}
for (const child of node.children) {
const found = findNodeRecursively(child, targetName);
if (found) {
return found;
}
}
return null;
};
const dialogNode = findNodeRecursively(scene, nodeName);
if (dialogNode) {
const dialogComponent = dialogNode.getComponent(DialogBox);
if (dialogComponent) {
DialogBox.sceneDialogBox = dialogComponent;
DialogBox.dialogNode = dialogNode;
// 初始状态为隐藏
dialogNode.active = false;
}
}
}
}