761 lines
22 KiB
TypeScript
761 lines
22 KiB
TypeScript
/**
|
||
* 數據工具類
|
||
* @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 = <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 = <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 = <cc.SpriteAtlas>value[index];
|
||
return result;
|
||
}
|
||
|
||
|
||
static jsonAssetBy(value: any): cc.JsonAsset {
|
||
let result: cc.JsonAsset = <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 = <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 = <string>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 = <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 = <Object>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;
|
||
}
|
||
} |