/**
|
* Tools
|
* @author Tevin
|
*/
|
|
import moment from 'moment';
|
import Taro from '@tarojs/taro';
|
|
export class Tools {
|
|
/**
|
* URL参数解析
|
* @param {String} name
|
* @param {String} [search]
|
* @return {String|Null}
|
*/
|
static getUrlParam(name, search = window.location.search) {
|
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
|
const r = search.substr(1).match(reg);
|
if (r !== null) {
|
return decodeURIComponent(r[2]);
|
} else {
|
return null;
|
}
|
}
|
|
/**
|
* 深度获取 url 参数
|
* @param {String} name
|
* @return {String}
|
*/
|
static getTopUrlParam(name) {
|
if (window.top !== window) {
|
return Tools.getUrlParam(name, window.top.location.search);
|
} else {
|
return Tools.getUrlParam(name);
|
}
|
}
|
|
/**
|
* 显示消息
|
* @param msg
|
*/
|
static toast(msg) {
|
Taro.showToast({
|
title: msg,
|
icon: 'none',
|
mask: true,
|
duration: 2000,
|
});
|
}
|
|
/**
|
* 读取文件 base64
|
* @param {File} file
|
* @return {Promise<string>}
|
*/
|
static getFileBase64(file) {
|
return new Promise((resolve, reject) => {
|
const reader = new FileReader();
|
reader.onload = () => resolve(reader.result);
|
reader.onerror = error => reject(error);
|
reader.readAsDataURL(file);
|
});
|
}
|
|
/**
|
* 生成 GUID
|
* @return {string}
|
*/
|
static createGUID() {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
let r = Math.random() * 16 | 0,
|
v = c === 'x' ? r : ((r & 0x3) | 0x8);
|
return v.toString(16);
|
});
|
}
|
|
/**
|
* 判断是否是数字
|
* @param {*} data
|
* @return {Boolean}
|
*/
|
static isNumber(data) {
|
return Object.prototype.toString.call(data) === '[object Number]';
|
}
|
|
/**
|
* 判断是否是字符串
|
* @param {*} data
|
* @return {Boolean}
|
*/
|
static isString(data) {
|
return Object.prototype.toString.call(data) === '[object String]';
|
}
|
|
/**
|
* 判断是否为布尔值
|
* @param {*} data
|
* @return {Boolean}
|
*/
|
static isBoolean(data) {
|
return Object.prototype.toString.call(data) === '[object Boolean]';
|
}
|
|
/**
|
* 判断是否是普通对象
|
* @param {*} data
|
* @return {Boolean}
|
*/
|
static isObject(data) {
|
return Object.prototype.toString.call(data) === '[object Object]';
|
}
|
|
/**
|
* 判断是否是数组
|
* @param {*} data
|
* @return {Boolean}
|
*/
|
static isArray(data) {
|
return Object.prototype.toString.call(data) === '[object Array]';
|
}
|
|
/**
|
* 判断是否是函数
|
* @param {*} data
|
* @return {Boolean}
|
*/
|
static isFunction(data) {
|
return Object.prototype.toString.call(data) === '[object Function]';
|
}
|
|
/**
|
* 判断是否为 null
|
* @param {*} data
|
* @return {Boolean}
|
*/
|
static isNull(data) {
|
return data === null;
|
}
|
|
/**
|
* 是否为空对象
|
* @param {*} obj
|
* @return {Boolean}
|
*/
|
static isEmptyObject(obj) {
|
if (!Tools.isObject(obj)) {
|
return true;
|
}
|
for (let p in obj) {
|
if (obj.hasOwnProperty(p)) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
/**
|
* 判断对象是否在属性上相等
|
* @param {String} a
|
* @param {String} b
|
* @return {Boolean}
|
*/
|
static isObjValEqual(a, b) {
|
const aProps = Object.getOwnPropertyNames(a);
|
const bProps = Object.getOwnPropertyNames(b);
|
if (aProps.length !== bProps.length) {
|
return false;
|
}
|
for (let i = 0, propName; i < aProps.length; i++) {
|
propName = aProps[i];
|
if (a[propName] !== b[propName]) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
static getObjDataByPath(obj, path) {
|
const arr = path.split('.');
|
const reg2 = /\[(.*)]/g;
|
const reg4 = /\w+(?=\[)|(?<=\[)(.*?)(?=])/g;
|
let stack = [];
|
let temp;
|
arr.forEach(item => {
|
if (reg2.test(item)) {
|
while ((temp = reg4.exec(item))) {
|
stack.push(temp[0]);
|
}
|
} else {
|
stack.push(item);
|
}
|
});
|
stack = stack.map(item => {
|
return item.replace(/^['"`]|['"`]$/g, '');
|
});
|
try {
|
return stack.reduce((pre, next) => {
|
return pre[next];
|
}, obj);
|
} catch (err) {
|
return undefined;
|
}
|
}
|
|
/**
|
* 统计字符串占位宽度
|
* @param {String} string
|
* @return {Number}
|
*/
|
static countStringLength(string) {
|
let realLength = 0;
|
const len = string.length;
|
let charCode = -1;
|
for (let i = 0; i < len; i++) {
|
charCode = string.charCodeAt(i);
|
// 128编号内字符占1位宽度
|
if (charCode >= 0 && charCode <= 128) {
|
realLength += 1;
|
}
|
// 128编号后字符(包括中文)占2位宽度
|
else {
|
realLength += 2;
|
}
|
}
|
return realLength;
|
}
|
|
/**
|
* 获取字符串基于字节长度的剪切点
|
* @param {String} string
|
* @param {Number} byteLength
|
* @return {Number}
|
*/
|
static getStringCutIndex(string, byteLength) {
|
if (byteLength === 0) {
|
return 0;
|
}
|
let countLength = 0;
|
let charCode = -1;
|
let cutIndex = 0;
|
const len = string.length;
|
for (let i = 0; i < len; i++) {
|
charCode = string.charCodeAt(i);
|
// 128编号内字符占1位宽度
|
if (charCode >= 0 && charCode <= 128) {
|
countLength += 1;
|
}
|
// 128编号后字符(包括中文)占2位宽度
|
else {
|
countLength += 2;
|
}
|
// 裁剪位置
|
if (countLength > byteLength) {
|
cutIndex = i;
|
break;
|
}
|
}
|
return cutIndex;
|
}
|
|
/**
|
* 限制数值范围,超出边界返回边界值
|
* @param {Number|String} num
|
* @param {[Number,Number]} range
|
* @return {Number}
|
*/
|
static limitNumberRange(num, range) {
|
num = Number(num);
|
num = num < range[0] ? range[0] : num;
|
num = num > range[1] ? range[1] : num;
|
return num;
|
}
|
|
/**
|
* 深拷贝
|
* @param {Object} source
|
* @return {Object|Array}
|
*/
|
static deepCopy(source) {
|
let result = null;
|
if (source instanceof Array) {
|
result = [];
|
} else {
|
result = {};
|
}
|
for (let key in source) {
|
if (source.hasOwnProperty(key)) {
|
if (typeof source[key] === 'object') {
|
if (Tools.isNull(source[key])) {
|
result[key] = source[key];
|
} else {
|
result[key] = this.deepCopy(source[key]);
|
}
|
} else {
|
result[key] = source[key];
|
}
|
}
|
}
|
return result;
|
}
|
|
/**
|
* 深合并
|
* @param target
|
* @param source
|
* @return {Object|Array}
|
*/
|
static deepCombine(target, source) {
|
for (let key in source) {
|
if (source.hasOwnProperty(key)) {
|
if (typeof source[key] === 'object') {
|
if (Tools.isNull(source[key])) {
|
target[key] = source[key];
|
} else {
|
if (Tools.isArray(source[key])) {
|
target[key] = [];
|
} else {
|
target[key] = {};
|
}
|
this.deepCombine(target[key], source[key]);
|
}
|
} else {
|
target[key] = source[key];
|
}
|
}
|
}
|
return target;
|
}
|
|
/**
|
* 数组元素交换位置
|
* @param {array} arr 数组
|
* @param {number} index1 要交换项目的位置
|
* @param {number} index2 被交换项目的位置
|
* @tutorial 例如:1,5就是数组中下标为1和5的两个元素交换位置
|
*/
|
static swapArray(arr, index1, index2) {
|
arr[index1] = arr.splice(index2, 1, arr[index1])[0];
|
return arr;
|
}
|
|
/**
|
* 使用 moment.js 格式化时间戳
|
* @param {Number|String} timestamp
|
* @param {String} [type='date']
|
* @return {String}
|
*/
|
static momentFormat(timestamp, type = 'date') {
|
if (!timestamp || timestamp === '0' || timestamp === 0) {
|
return '';
|
}
|
// 字符串
|
if (Tools.isString(timestamp)) {
|
// 全数值
|
if (/^\d+$/.test(timestamp)) {
|
timestamp = parseInt(timestamp);
|
}
|
// 日期
|
else {
|
try {
|
timestamp = (new Date(timestamp)).getTime();
|
} catch (e) {
|
}
|
}
|
}
|
if (timestamp < 9 * 10e8) {
|
timestamp *= 1000;
|
}
|
const curMoment = moment(timestamp);
|
if (type === 'date') {
|
return curMoment.format('YYYY-MM-DD');
|
} else if (type === 'dateTime') {
|
return curMoment.format('YYYY-MM-DD HH:mm');
|
} else if (type === 'dateTimeShort') {
|
return curMoment.format('MM-DD HH:mm');
|
} else if (type === 'dateTimeFull') {
|
return curMoment.format('YYYY-MM-DD HH:mm:ss');
|
}
|
}
|
|
/**
|
* 数值转换为时长描述
|
* @param {number} timestamp
|
* @return {string}
|
*/
|
static durationFormat(timestamp) {
|
if (typeof timestamp !== 'number') {
|
return '';
|
}
|
let words = '';
|
const duration = moment.duration(timestamp);
|
const seconds = duration.get('seconds');
|
if (seconds) {
|
words = seconds + '秒';
|
}
|
const minutes = duration.get('minutes');
|
if (minutes) {
|
words = minutes + '分' + words;
|
}
|
const hours = duration.get('hours');
|
if (hours) {
|
words = hours + '小时' + words;
|
}
|
return words;
|
}
|
|
/**
|
* 数值转换为金钱格式
|
* @param {Number|String} number
|
* @return {string}
|
*/
|
static moneyFormat(number) {
|
if (typeof number !== 'number' && !number) {
|
return '';
|
}
|
if (typeof number === 'string') {
|
number = parseInt(number) || 0;
|
}
|
const numFixed = number.toFixed(2);
|
const integer = parseInt(numFixed);
|
const decimal = numFixed.split('.')[1];
|
return integer.toLocaleString() + '.' + decimal;
|
}
|
|
/**
|
* 加法函数,用来得到精确的加法结果
|
* @param {Number|String} num1
|
* @param {Number|String} num2
|
*/
|
static accAdd(num1, num2) {
|
const r1 = Tools.getDecimalLength(num1);
|
const r2 = Tools.getDecimalLength(num2);
|
const m = Math.pow(10, Math.max(r1, r2));
|
return (Number(num1) * m + Number(num2) * m) / m;
|
}
|
|
/**
|
* 减法函数,用来得到精确的减法结果
|
* @param {Number|String} num1
|
* @param {Number|String} num2
|
*/
|
static accSub(num1, num2) {
|
return Tools.accAdd(num1, -num2);
|
}
|
|
/**
|
* 乘法函数,用来得到精确的乘法结果
|
* @param {Number|String} num1
|
* @param {Number|String} num2
|
*/
|
static accMul(num1, num2) {
|
let m = 0;
|
m += Tools.getDecimalLength(num1);
|
m += Tools.getDecimalLength(num2);
|
const r1 = Number(num1.toString().replace('.', ''));
|
const r2 = Number(num2.toString().replace('.', ''));
|
return (r1 * r2) / Math.pow(10, m);
|
}
|
|
/**
|
* 除法函数,用来得到精确的除法结果
|
* @param {Number|String} num1
|
* @param {Number|String} num2
|
*/
|
static accDiv(num1, num2) {
|
const t1 = Tools.getDecimalLength(num1);
|
const t2 = Tools.getDecimalLength(num2);
|
const r1 = Number(num1.toString().replace('.', ''));
|
const r2 = Number(num2.toString().replace('.', ''));
|
return (r1 / r2) * Math.pow(10, t2 - t1);
|
}
|
|
/**
|
* 求小数点后的数据长度
|
* @param {Number|String} num
|
* @private
|
*/
|
static getDecimalLength(num) {
|
let t = 0;
|
try {
|
t = num.toString().split('.')[1].length;
|
} catch (e) {
|
}
|
return t;
|
}
|
|
/**
|
* 判断是否为手机号码
|
* @param {Number|String} phone
|
* @returns {boolean}
|
*/
|
static isPhone(phone) {
|
return /^1[3456789]\d{9}$/.test('' + phone);
|
}
|
|
|
}
|