/** * 數據工具類 * @author BrightLi * @since 2020/5/3 */ import Report from "../net/Report"; import SKLogger from "./SKLogger"; export default class SKDataUtil { // 生成UUID static uuid(): string { if (typeof (window) !== "undefined" && typeof (window.crypto) !== "undefined" && typeof (window.crypto.getRandomValues) !== "undefined") { let buf: Uint16Array = new Uint16Array(8); window.crypto.getRandomValues(buf); return (this.pad4(buf[0]) + this.pad4(buf[1]) + "-" + this.pad4(buf[2]) + "-" + this.pad4(buf[3]) + "-" + this.pad4(buf[4]) + "-" + this.pad4(buf[5]) + this.pad4(buf[6]) + this.pad4(buf[7])); } else { return this.random4() + this.random4() + "-" + this.random4() + "-" + this.random4() + "-" + this.random4() + "-" + this.random4() + this.random4() + this.random4(); } } private static pad4(num: number): string { let ret: string = num.toString(16); while (ret.length < 4) { ret = "0" + ret; } return ret; } private static random4(): string { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } // 隨機數 static random(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min; } // 從數組中隨機抽取一項 static randomList(value: any[]): any { if (value == null || value.length < 1) { return null; } let index = this.random(0, value.length - 1); return value[index]; } // 替換指定位置的字符 static replaceChar(value: string, index: number, char: string): string { return value.substr(0, index) + char + value.substr(index + char.length); } // 從前查找相同字符出現的最後一個位置 static sameIndex(value: string, char: string): number { var result: number = -1; for (var i = 0; i < value.length; i++) { if (value[i] == char) { if (result == -1) { result = i; } else { result++; } } else if (result != -1) { break; } } return result; } // 從後查找相同字符出現的最後一個位置 static sameLastIndex(value: string, char: string): number { var result: number = -1; for (var i = value.length - 1; i > 0; i--) { if (value[i] == char) { if (result == -1) { result = i; } else { result--; } } else if (result != -1) { break; } } return result; } // 格式化數字,不足位補0 static prefixInteger(num: number, length: number): string { let result = (Array(length).join('0') + num).slice(-length); return result; } // "YYYY-mm-dd HH:MM" static formatDate(format: string, value: string | number): string { let now = new Date(value); let opt: any = { "Y+": now.getFullYear().toString(), "m+": (now.getMonth() + 1).toString(), "d+": now.getDate().toString(), "H+": now.getHours().toString(), "M+": now.getMinutes().toString(), "S+": now.getSeconds().toString(), } let result: RegExpExecArray; for (let key in opt) { result = new RegExp(`(${key})`).exec(format); if (result) { format = format.replace(result[1], (result[1].length == 1) ? (opt[key]) : (opt[key].padStart(result[1].length, "0"))); } } return format; } // 根據ID生成5位邀請碼 static encodeInvite(value: number) { let key = 'E50CDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ'; let result = ""; while (value > 0) { let mod = value % 35; value = (value - mod) / 35; result = key[mod] + result; } if (result.length < 5) { result = "F" + result; let len = result.length; let min = 0; let max = key.length - 1; for (let i = len; i < 5; i++) { let index = Math.floor(Math.random() * (max - min + 1)) + min; result = key[index] + result; } } return result; } // 解碼邀請碼 static decodeInvite(value: string): number { let key = 'E50CDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ'; let index = value.indexOf("F"); if (index != -1) { value = value.slice(index + 1, value.length); } let result = 0; let p = 0; for (let i = value.length - 1; i >= 0; i--) { let char = value[i]; index = key.indexOf(char); if (index != -1) { result += index * Math.pow(35, p); p++; } } return result; } static numberOfList(list: number[], index: number, valid: number): number { if (list == null || !SKDataUtil.isArray(list) || list.length < index) { return valid; } return list[index]; } // 從數組中獲得匹配 static numberByList(list: any[], key: string, value: number, targetKey: string): any { for (let item of list) { let temp = item[key]; if (!temp) { continue; } if (temp == value) { let result = item[targetKey]; return result; } } return null; } // 是否存在KV static hasKVByList(list: any[], key: string, value: any): boolean { for (let item of list) { if (item[key] == value) { return true; } } return false; } // 是否有互斥 static hasMutex(target: any[], mutex: any[], mutexKey: string, key: string): boolean { for (let item of target) { let temp = item[key]; if (!temp) { continue; } // 如果是互斥數組 if (Array.isArray(temp)) { for (let b of mutex) { for (let a of temp) { if (item[key] == b) { return true; } } } } else { for (let b of mutex) { if (temp == b) { return true; } } } } return false; } // 獲得二維數組中的數值 static intOf2Array(list: number[][], one: number, two: number, valid: number): number { if (!list) { return valid; } if (one < 0 || one >= list.length) { return valid; } let temp = list[one] as number[]; if (!temp) { return valid; } if (two < 0 || two >= temp.length) { return valid; } return temp[two]; } // 根據概率返回真假 static probability(min: number, max: number): boolean { let result = SKDataUtil.random(min, max); if (result <= min) { return true; } else { return false; } } static textAssetByList(value: any[], index: number): cc.TextAsset { if (value == null || index >= value.length) { return null; } let result: cc.TextAsset = value[index]; return result; } static prefabByList(value: any[], index: number): cc.Prefab { if (value == null || index >= value.length) { return null; } let result: cc.Prefab = value[index]; return result; } static atlasByList(value: any[], index: number): cc.SpriteAtlas { if (value == null || index >= value.length) { return null; } let result: cc.SpriteAtlas = value[index]; return result; } static jsonAssetBy(value: any): cc.JsonAsset { let result: cc.JsonAsset = value; return result; } static jsonAssetByList(value: any[], index: number): cc.JsonAsset { if (value == null || index >= value.length) { return null; } let result: cc.JsonAsset = value[index]; return result; } static dictByDict(target: any, sheet: any, key: any): any { if (!target) { return null; } let dict = target[sheet]; if (!dict) { return null; } let result = dict[key]; return result; } static findByDict(target: any, targetKey: any, value: any, resultKey: any, valid: any): any { if (!target) { return valid; } for (let key in target) { let item = target[key]; if (item[targetKey] == value) { return item[resultKey]; } } return valid; } static numberByString(target: string, splitter: string, index: number, valid: number = 0): number { if (!target) { return valid; } let temp = target.split(splitter); if (temp.length < index) { return valid; } let result = parseInt(temp[index]); return result; } // JSON解析 static jsonBy(value: any): any { if (value == null) { return null; } if (!this.isString(value)) { return value; } if (this.isEmptyString(value)) { return null; } let result: any = null; try { result = JSON.parse(value); } catch (error) { SKLogger.warn(`JSON解析:${value},失敗:${error}`); } finally { return result; } } // 轉換成JSON字符串 static toJson(value: any): string { if (value == null) { return ""; } if (this.isString(value)) { return value; } let result: string = ""; try { result = JSON.stringify(value); } catch (error) { SKLogger.warn(`JSON轉換:${value},失敗:${error}`); } finally { return result; } } // 是否為數字 static isNumber(value: any): boolean { if (value == null) { return false; } let type = typeof value; if (type !== 'number') { return false; } return true; } // 轉換成數字 static toNumber(value: any): number { if (this.isNumber(value)) { return value; } if (value == "undefined") { return NaN; } if (this.isString(value)) { let result = parseFloat(value); return result; } return NaN; } // 版本號比較 static checkVersion(versionA: string, versionB: string): number { let vA = versionA.split('.'); let vB = versionB.split('.'); for (let i = 0; i < vA.length; ++i) { let a = parseInt(vA[i]); let b = parseInt(vB[i] || '0'); if (a === b) { continue; } else { return a - b; } } if (vB.length > vA.length) { return -1; } return 0; } // 是否為數組 static isArray(value: any): boolean { if (value == null) { return false; } let result = Array.isArray(value); return result; } // 是否為對象 static isObject(value: any): boolean { if (value == null) { return false; } let type = typeof value; if (type === "object") { return true; } return false; } // 是否為字符串 static isString(value: any): boolean { if (value == null) { return false; } let result = typeof (value); if (result == "string") { return true; } return false; } // 是否為空字符串 static isEmptyString(value: any): boolean { if (value == null) { return true; } if (!this.isString(value)) { return true; } let result = value; if (result.length < 1) { return true; } return false; } // 正則測試 static testRegExp(value: string, regexp: RegExp, min: number, max: number, tip: string) { if (value.length < min) { return `長度不能少於${min}個`; } if (value.length > max) { return `最多只能${max}個`; } let temp = regexp.test(value); if (!temp) { return tip; } return ""; } // 檢查合法性 static checkSymbol(value: string, min: number, max: number, tip: string): string { if (value.length < min) { return `長度不能少於${min}個`; } if (value.length > max) { return `最多只能${max}個`; } let regexp = new RegExp("[\r\n\t]"); let check = regexp.test(value); if (check) { return `不能有回車或製表符!`; } regexp = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]"); check = regexp.test(value); if (check) { return tip; } return ""; } // 正則測試 static testNotRegExp(value: string, regexp: RegExp, min: number, max: number, tip: string) { if (value.length < min) { return `長度不能少於${min}個`; } if (value.length > max) { return `最多只能${max}個`; } let temp = regexp.test(value); if (temp) { return tip; } return ""; } // 獲得鍵值 static valueForKey(target: any, key: any): any { if (target == null) { cc.warn(`$警告:獲得鍵值,對像不存在`); return null; } if (key == null) { cc.warn(`$警告:獲得鍵值,對象${target}KEY值不存在`); return null; } if (this.isArray(target)) { let result = this.getItemByList(target, key); return result; } if (!this.isObject(target)) { cc.warn(`$警告:獲得鍵值,${target}[${key}]不是一個對象`); return null; } let temp: Object = target; if (!temp.hasOwnProperty(key)) { cc.warn(`$警告:獲得鍵值,${target.toString()}[${key}]不存在`); return null; } return temp[key]; } // 檢查是否合法 static checkValid(value: string): string { if (value == null || value.length < 1) { return "不能為空!"; } let temp = value.trim(); if (value.length != temp.length) { return "兩側不能有空格"; } let list = [{ char: "\"", text: "雙引號" }, { char: "\'", text: "單引號" }]; for (let item of list) { if (value.indexOf(item.char) != -1) { return `不能包含${item.text}`; } } return ""; } static trim(value: string): string { if (value == null || value.length < 1) { return ""; } value = value.trim(); return value; } // 獲得最大值經驗值 static maxExp(config: any, exp: number): any { let result = exp; for (let item of config) { if (exp < item.exp) { result = item.exp; break; } } return result; } // 獲得當前階段的經驗值與最大值 static currentExp(config: any, exp: number): any { let result: any = { value: 0, max: 0 }; for (let item of config) { let level = item.level; let max_exp = item.exp; if (exp < max_exp) { if (level > 1) { let prev = config[`${level - 1}`]; result.value = exp - prev.exp; result.max = max_exp - prev.exp; } else { result.value = exp; result.max = max_exp; } break; } } return result; } // 是否有屬性 static hasProperty(target: any, key: string | number | symbol): boolean { if (target == null) { return false; } if (!this.isObject(target)) { return false; } let result = target; if (result.hasOwnProperty(key)) { return true; } return false; } // 獲得字典長度 static getLength(value: any): number { if (value == null) { return 0; } if (Array.isArray(value)) { return value.length; } let length = 0; for (let _key in value) { length++; } return length; } // 對鍵求和 static getKeyTotal(value: any): number { let total: number = 0; for (let key in value) { let count = parseInt(key); total += count; } return total; } /** * 深度拷貝 * @param value * @returns */ static clone(value: any): any { if (value == null) { return null; } let result: any = Array.isArray(value) ? [] : {}; try { result = JSON.parse(JSON.stringify(value)); } catch (error) { let info = `警告:深拷貝對象:${value},失敗:${error}` Report.report(info); } finally { return result; } } // 克隆類實例 static cloneClass(value: any): any { if (value == null) { return null; } let result = Object.create( Object.getPrototypeOf(value), Object.getOwnPropertyDescriptors(value) ); return result; } // 是否在範圍內 static atRange(target: number, range: number[]): boolean { for (let item of range) { if (target == item) { return true; } } return false; } static clamp(value: number, min: number, max: number): number { if (value < min) { return min; } if (value > max) { return max; } return value; } // 將大數字單位轉化成 千、萬、千萬、億 static transform(value: number): string { if (value == null) { return ""; } value = SKDataUtil.toNumber(value); if (isNaN(value)) { return ""; } let newValue = ['', '', '']; let fr = 1000; const ad = 1; let num = 3; const fm = 1; while (value / fr >= 1) { fr *= 10; num += 1; } if (num <= 4) { // 千 newValue[1] = '千'; newValue[0] = `${this.toDecimal1(value / 1000)}`; } else if (num <= 8) { // 萬 const text1 = `${(num - 4) / 3 > 1 ? '千萬' : '萬'}`; // tslint:disable-next-line:no-shadowed-variable const fm = '萬' === text1 ? 10000 : 10000000; newValue[1] = text1; newValue[0] = `${this.toDecimal1(value / fm)}`; } else if (num <= 16) {// 億 let text1 = (num - 8) / 3 > 1 ? '千億' : '億'; text1 = (num - 8) / 4 > 1 ? '萬億' : text1; text1 = (num - 8) / 7 > 1 ? '千萬億' : text1; // tslint:disable-next-line:no-shadowed-variable let fm = 1; if ('億' === text1) { fm = 100000000; } else if ('千億' === text1) { fm = 100000000000; } else if ('萬億' === text1) { fm = 1000000000000; } else if ('千萬億' === text1) { fm = 1000000000000000; } newValue[1] = text1; newValue[0] = `${this.toDecimal1(value / fm)}`; } if (value < 1000) { newValue[1] = ''; newValue[0] = `${value}`; } let result = newValue.join(''); return result; } // 獲得數組元素 static getItemByList(list: any, index: number): any { if (!this.isArray(list)) { return null; } if (index < 0 || index >= list.length) { return null; } let result = list[index]; return result; } // 獲得對像元素 static getItemByMap(obj: any, index: number): any { if (index < 0) { return null; } if (!this.isObject(obj)) { return null; } let i = 0; for (let key in obj) { if (i == index) { return obj[key]; } i++; } } // 保留1位小數 static toDecimal1(value: number): number { let temp = this.toNumber(value); if (isNaN(temp)) { return 0; } temp = Math.floor(value * 10) / 10; return temp; } // 保留2位小數 static toDecimal2(value: number): number { let temp = this.toNumber(value); if (isNaN(temp)) { return 0; } temp = Math.floor(value * 100) / 100; return temp; } // 是否為數字字符串 static isNumberString(value: string): boolean { var regPos = /^\d+$/; //非負浮點數 if (regPos.test(value)) { return true; } else { return false; } } // 獲得距離 static distance(source: any, target: any): number { if (source == null || target == null) { return 0; } let sx = (source.x != null ? source.x : 0); let sy = (source.y != null ? source.y : 0); let tx = (target.x != null ? target.x : 0); let ty = (target.y != null ? target.y : 0); let xd = Math.abs(sx - tx); let yd = Math.abs(sy - ty); let result = Math.pow(xd * xd + yd * yd, 0.5); return result; } }