Files
18xchat/assets/Scripts/Main/Common/Utils.ts
T
2025-10-14 15:19:12 +08:00

429 lines
13 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,
Color,
isValid,
Label,
Node,
RichText,
Size,
Sprite,
UITransform,
Vec3,
view,
} from "cc";
import li_EventManager from "./li_EventManager";
import LanguageUtils from "./LanguageUtils";
import { logger } from "db://assets/Scripts/Main/Common/Logger";
const { ccclass, property } = _decorator;
@ccclass
export default class Utils {
//log日志
public static Log(msg: string | any, ...subst: any[]) {
let IsLocalCSChanel = true;
if (IsLocalCSChanel) {
logger.log(msg, subst);
}
}
//警告日志
public static Warning(msg: string | any, ...subst: any[]) {
let IsLocalCSChanel = true;
if (IsLocalCSChanel) {
logger.warn(msg, subst);
}
}
//错误日志
public static Error(msg: string | any, ...subst: any[]) {
let IsLocalCSChanel = true;
if (IsLocalCSChanel) {
logger.error(msg, subst);
}
}
//解析节点
public static parseNode = function (node: Node, oriObj?: any) {
if (!node || !node.children) return;
oriObj = oriObj || node;
let chs = node.children;
for (let i = 0; i < chs.length; i++) {
let ch = chs[i];
let chName = ch.name;
if (chName && chName.length > 0) {
try {
oriObj[chName] = ch;
} catch (error) {
//hg_utils.hgLog("chName err:", chName);
}
}
Utils.parseNode(ch, oriObj);
}
};
//更换父节点,保留原有的坐标和旋转
public static changeNodeParent(node: Node, parentNode: Node) {
let world_Q = node.getWorldRotation();
let world_pos = node.getWorldPosition(); //原来的世界坐标
node.setParent(parentNode);
node.setRotation(world_Q);
node.setWorldPosition(world_pos);
}
/**
* 封装setString方法
*/
static setString(node: Node, str: string | number) {
if (!isValid(node)) return;
let lb: any = node.getComponent(Label);
if (!lb) return;
if (str == null) {
str = "";
}
str += "";
lb.string = str;
}
/**
* 添加富文本
* @param node
* @param str
* @param outLine
* @param width
* @returns
*/
static addRichText(
node: Node,
str: string,
outLine: string | null = null,
width: number | null = 2,
max: number = 0
) {
if (!node) return;
let comp = node.getComponent(RichText);
if (!comp) return;
str = Utils.replaceAll(str, "</>", "</color>");
str = str.replace(/\<\/font\>/g, "</color>");
str = Utils.replaceAll(str, "'>", ">");
str = Utils.replaceAll(str, "<font", "<");
str = Utils.replaceAll(str, "color='", "color=");
str = Utils.replaceAll(str, "<br>", "\n");
if (outLine) {
str = `<outline color=${outLine} width=${width}>${str}</outline>`;
}
comp.string = str;
// let csize:UITransform = comp.node.getComponent(UITransform)!
// comp.maxWidth = Math.min(csize.width,max) ;
}
static replaceAll(str: string, ch1: string, ch2: string) {
while (str.indexOf(ch1) >= 0) {
str = str.replace(ch1, ch2);
}
return str;
}
/**
* 字符串左侧填充到指定长度
* @param str 要填充的字符串
* @param length 目标长度
* @param padChar 填充字符,默认为 "0"
* @returns 填充后的字符串
*/
static padStart(str: string, length: number, padChar: string = "0"): string {
while (str.length < length) {
str = padChar + str;
}
return str;
}
/**截取字符串,超过长度的用...代替
* @param str 要处理的字符串
* @param labelLimit 字符串长度限制
* @param repStr 超过长度后代替的字符串
*/
static clampAiAnswer(
ansStr: string,
labelLimit: number,
repStr: string = "..."
) {
// let labelLimit = 20 //字符串长度限制
let tempStr: string = "";
let tempLen: number = 0;
for (let i = 0; i < ansStr.length; i++) {
tempLen += ansStr.charCodeAt(i) > 255 ? 2 : 1;
if (tempLen > labelLimit) {
return tempStr + repStr;
}
tempStr += ansStr.charAt(i);
}
return ansStr;
}
/**设置节点置灰 */
static setNodeGray(node: Node, isGray: boolean, extra: any = {}) {
if (!isValid(node)) return;
//图片
let sp: Sprite = node.getComponent(Sprite);
if (sp) {
sp.grayscale = isGray;
}
// //文字
// let lb:Label = node.getComponent(Label);
// if (lb) {
// if (isGray) {
// lb.color = extra.labClrGray ? extra.labClrGray : Color.GRAY;
// } else {
// lb.color = extra.labClrWhite ? extra.labClrWhite : Color.WHITE;
// }
// }
//对子节点执行同样操作
let children = node.children;
for (let i = 0; i < children.length; i++) {
Utils.setNodeGray(children[i], isGray, extra);
}
}
/**适配背景图
* @method node 背景图节点
* @param mod 适配模式 1=按高度适配 2=按宽度适配
*/
public static adjustBgPixelRatioToSize(
windowSize: Size,
node: Node,
mod: number = 1
) {
//let windowSize = view.getVisibleSize();
let bgTf = node.getComponent(UITransform);
if (bgTf) {
if (mod == 3) {
if (bgTf.height < bgTf.width) {
mod = 1;
} else {
mod = 2;
}
}
if (mod == 1) {
let scale = windowSize.height / bgTf.height;
bgTf.height = bgTf.height * scale;
bgTf.width = bgTf.width * scale;
} else if (mod == 2) {
let scale = windowSize.width / bgTf.width;
bgTf.height = bgTf.height * scale;
bgTf.width = bgTf.width * scale;
}
}
}
/**适配背景图
* @method node 背景图节点
* @param mod 适配模式 1=按高度适配 2=按宽度适配
*/
public static adjustBgPixelRatio(node: Node, mod: number = 1) {
let windowSize = view.getVisibleSize();
let bgTf = node.getComponent(UITransform);
if (bgTf) {
if (mod == 3) {
if (windowSize.height - bgTf.height < windowSize.width - bgTf.width) {
mod = 1;
} else {
mod = 2;
}
}
if (mod == 1 && bgTf.height < windowSize.height) {
let scale = windowSize.height / bgTf.height;
bgTf.height = bgTf.height * scale;
bgTf.width = bgTf.width * scale;
} else if (mod == 2 && bgTf.width < windowSize.width) {
let scale = windowSize.width / bgTf.width;
bgTf.height = bgTf.height * scale;
bgTf.width = bgTf.width * scale;
}
}
}
/**适配背景图 按比例缩放 屏幕比设计分辨率小时缩小,反之则不缩放
* @param node 背景图节点
* @param mod 适配模式 1=按高度适配 2=按宽度适配
*/
public static adjustBgScaleRatio(node: Node, mod: number = 1) {
let windowSize = view.getVisibleSize();
let designSize = view.getDesignResolutionSize();
if (mod == 1 && windowSize.height < designSize.height) {
let scale = windowSize.height / designSize.height;
node.scale = new Vec3(scale, scale, 1);
} else if (mod == 2 && windowSize.width < designSize.width) {
let scale = windowSize.width / designSize.width;
node.scale = new Vec3(scale, scale, 1);
}
}
/**获取屏幕分辨率和设计分辨率的比例
* @method mod 适配模式 1=按高度适配 2=按宽度适配
*/
public static getScaleRatio(mod: number = 1) {
let windowSize = view.getVisibleSize();
let designSize = view.getDesignResolutionSize();
// logger.log("游戏分辨率:", windowSize, designSize);
if (mod == 1) {
return windowSize.height / designSize.height;
} else {
return windowSize.width / designSize.width;
}
}
/**
* @method 浅复制一个对象
* @param source 需要浅复制的对象
* 合并对象,如果excludes中没有指明需要排除的字段,target也含有相同的字段,则会被object同名字段值覆盖掉
* @param object
* @param excludes 需要排除的字段
*/
static applyIf(
object: any,
target: any = {},
excludes: Array<string> = []
): Object {
if (!target) target = {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
if (excludes.indexOf(key) < 0) {
target[key] = object[key];
}
}
}
return target;
}
/**
* @method 浅复制一个对象
* @param source 需要浅复制的对象
* @return 返回一个新的对象
* @log 1. vincent,2018-12-18, func、date、reg 和 err 类型不能正常拷贝
*/
static easyCopy(source: Object): Object {
const newObject = [];
return Utils.applyIf(source);
}
/**复制一个数据表 */
static clone(data: any) {
return Utils.easyCopy(data);
}
/**发送内部事件 */
public static sendInnerMsg(innerId: number, param?: any) {
li_EventManager.I.onInnerEL(innerId, param);
}
/**注册内部事件 */
public static addInnerEL(innerId: number, i_target, i_callback: Function) {
li_EventManager.I.addInnerEL(innerId, i_callback, i_target);
}
/**移除内部事件 */
public static removeInnerEL(innerId: number, i_target, i_callback: Function) {
li_EventManager.I.removeInnerEL(innerId, i_callback, i_target);
}
//获取随机数
public static getRandomInt(i_start: number, i_end: number): number {
i_start = Math.ceil(i_start);
i_end = Math.floor(i_end);
return Math.floor(Math.random() * (i_end - i_start + 1)) + i_start;
}
//数字转字符串,指定位数,位数不足前面补0
public static PrefixInt(num, length) {
return (Array(length).join("0") + num).slice(-length);
}
//数字保留几位小数
public static DecimalPlaces(num: number, weishu: number) {
return num.toFixed(weishu);
}
/**获取屏幕分辨率 */
public static GetWinSize(): Size {
return view.getVisibleSize();
}
/**获取当前日期,格式YYYY-MM-DD */
public static GetNowFormatDay(
nowDate: Date | null = null,
char: string = "-"
) {
if (nowDate == null) {
nowDate = new Date();
}
let day = nowDate.getDate();
let month = nowDate.getMonth() + 1; //注意月份需要+1
let year = nowDate.getFullYear();
//补全0,并拼接
return (
year + char + Utils.completeDate(month) + char + Utils.completeDate(day)
);
}
//补全0
private static completeDate(value: number) {
return value < 10 ? "0" + value : value;
}
//打印消耗时间===================================================================================================
private static _eplTime = 0;
/**记录当前时间 */
public static ProfilerTimeRecord() {
Utils._eplTime = new Date().getTime();
}
/**打印消耗时间 */
public static ProfilerTimePrint(key: string = "") {
let time = new Date().getTime();
let timecha = time - Utils._eplTime;
logger.log(`${key}消耗时间:${timecha}毫秒`);
Utils._eplTime = time;
}
// 获取格式化字符串: vip失效时间
public static formatVipLeftTime(expiryTimestamp: number): string {
// 当前时间戳,单位:秒
const nowSeconds = Math.floor(Date.now() / 1000);
// 差值(秒)
const diffSeconds = Math.max(0, expiryTimestamp - nowSeconds);
if (diffSeconds == 0) return null;
// 转换为天、小时、分钟
const days = Math.floor(diffSeconds / (60 * 60 * 24));
const hours = Math.floor((diffSeconds % (60 * 60 * 24)) / 3600);
const minutes = Math.floor((diffSeconds % 3600) / 60);
logger.log(
"vip失效剩余时间差(秒):",
diffSeconds,
"天:",
days,
"小时:",
hours,
"分钟:",
minutes
);
if (days > 0) {
return `${days} ${LanguageUtils.getText("purchasepanel.day")}`;
}
if (hours > 0) {
return `${hours} ${LanguageUtils.getText(
"purchasepanel.hour"
)}:${minutes} ${LanguageUtils.getText("purchasepanel.minute")}`;
}
return `${minutes} ${LanguageUtils.getText("purchasepanel.minute")}`;
}
/**
* 格式化聊天时间显示
* @param timestamp 时间戳(毫秒),如果为null则显示"暂无聊天记录"
* @returns 格式化的时间字符串
* 格式规则:显示完整的年月日时分秒(例如:2025-9-17 20:40:50
*/
public static formatChatTime(timestamp: number | null): string {
if (!timestamp) {
return LanguageUtils.getText("pastgirllistpanel.nomemory");
}
const chatDate = new Date(timestamp);
const year = chatDate.getFullYear();
const month = chatDate.getMonth() + 1;
const day = chatDate.getDate();
const hours = Utils.padStart(chatDate.getHours().toString(), 2);
const minutes = Utils.padStart(chatDate.getMinutes().toString(), 2);
const seconds = Utils.padStart(chatDate.getSeconds().toString(), 2);
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
}