hy-app
Version:
fix: 修复text和tag换掉
522 lines (495 loc) • 16 kB
text/typescript
import Base64 from "./base64";
import type { CSSProperties } from "vue";
import { error, isNumber } from "./index";
let base64: any = new Base64();
/**
* 加密函数
* @param {any} data 对象
* @return { string } 加密字符串
* */
function encryptData(data: Record<string, any> | string): string {
return base64.encode(JSON.stringify(data));
}
/**
* 解密函数
* @param {string} encryptedVal 加密字符串
* @returns { any | any[] } 解码的数据
* */
function decryptData(encryptedVal: string): Record<string, any> {
return JSON.parse(base64.decode(encryptedVal.toString()));
}
/**
*用于解密使用 AES 加密算法加密的数据
*/
// const decrypt = (sessionKey: string, encryptedData: string, iv: string) => {
// console.log(CryptoJS)
// let key = CryptoJS.enc.Base64.parse(sessionKey)
// let ivv = CryptoJS.enc.Base64.parse(iv)
// let decrypt = CryptoJS.AES.decrypt(encryptedData, key, {
// iv: ivv,
// mode: CryptoJS.mode.CBC,
// padding: CryptoJS.pad.Pkcs7
// })
// return JSON.parse(base64.decode(CryptoJS.enc.Base64.stringify(decrypt)))
// }
/**
* @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾
* @param {String|Number} value 需要添加单位的值
* @param {String} unit 添加的单位名 比如px
* @returns {String}
*/
const addUnit = (
value: string | number = "auto",
unit: string = "",
): string => {
if (!unit) {
unit = "px";
}
if (unit == "rpx" && isNumber(String(value))) {
value = Number(value) * 2;
}
value = String(value);
// 用内置验证规则中的number判断是否为数值
return isNumber(value) ? `${value}${unit}` : value;
};
/**
* @description 日期的月或日补零操作
* @param {String | Number} value 需要补零的值
* @returns {String}
*/
const padZero = (value: string | number): string => {
return `00${value}`.slice(-2);
};
/**
* @description 后面补零
* @param {String | Number} value 需要补零的值
* @param {Number} length 多少位
* @returns {String}
*/
const addZero = (value: string | number, length: number): string => {
if (value === undefined || value === null) return "";
let val = value.toString();
if (length > val.length) {
val += "0".repeat(length - val.length);
} else {
val = val.slice(0, length);
}
return val;
};
/**
* @description 清空对象里面的值
* @param val 任意类型的值
* */
const clearVal = (val: any) => {
const type = typeof val;
const isArray = val instanceof Array;
switch (type) {
case "string":
return "";
case "number":
return 0;
case "boolean":
return false;
case "undefined":
return null;
case "object":
if (!val) return null;
if (isArray) {
val.map((item) => {
clearVal(item);
});
return val;
} else {
Object.keys(val).map((k) => {
val[k] = clearVal(val[k]);
});
return val;
}
default:
return "";
}
};
/**
* 时间戳格式化
* @param timestamp 时间戳或者时间格式,例:1702051200000、Sat Apr 06 2024 11:35:56 GMT+0800 (中国标准时间)
* @param fmt 例:yyyy-MM-dd HH:mm:ss / yyyy-MM-dd
* @return date 例:2023-12-09
*/
const formatTime = (
timestamp: number | string,
fmt: string = "yyyy-MM-dd HH:mm:ss",
): string => {
let date: any;
if (timestamp) {
date = new Date(timestamp) ? new Date(timestamp) : timestamp;
let ret;
const opt: any = {
"y+": date.getFullYear().toString(), //年
"M+": (date.getMonth() + 1).toString(), //月
"d+": date.getDate().toString(), //日
"H+": date.getHours().toString(), //时
"m+": date.getMinutes().toString(), //分
"s+": date.getSeconds().toString(), //秒
//如果有其他格式字符需求可以继续添加,必须转化为字符串
};
for (let k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {
fmt = fmt.replace(
ret[1],
ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"),
);
}
}
return fmt;
}
return date;
};
/**
* @description 时间戳或年月日格式转为多久之前
* @param {String|Number} timestamp 时间戳/年月日格式
* @param {String|Boolean} format
* 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式;
* 如果为布尔值false,无论什么时间,都返回多久以前的格式
* @returns {string} 转化后的内容
*/
const formatTimeToString = (
timestamp: string | number,
format: string | boolean = "yyyy-mm-dd",
): string => {
const now = new Date();
const oneYear = new Date(now.getFullYear(), 0, 1).getTime(); // 当年一月一号时间戳
if (timestamp == null) timestamp = Number(now);
timestamp =
typeof timestamp === "string"
? parseInt(timestamp)
: new Date(timestamp).getTime();
// 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
if (timestamp.toString().length == 10) timestamp *= 1000;
let timer = now.getTime() - timestamp;
timer = parseInt(String(timer / 1000));
// 如果小于5分钟,则返回"刚刚",其他以此类推
let tips = "";
switch (true) {
case timer < 300:
tips = "刚刚";
break;
case timer >= 300 && timer < 3600:
tips = `${parseInt(String(timer / 60))}分钟前`;
break;
case timer >= 3600 && timer < 86400:
tips = `${parseInt(String(timer / 3600))}小时前`;
break;
case timer >= 86400 && timer < 2592000:
tips = `${parseInt(String(timer / 86400))}天前`;
break;
default:
// 如果format为false,则无论什么时间戳,都显示xx之前
if (format === false) {
if (timer >= 2592000 && timer < 365 * 86400) {
tips = `${parseInt(String(timer / (86400 * 30)))}个月前`;
} else {
tips = `${parseInt(String(timer / (86400 * 365)))}年前`;
}
} else {
if (timestamp > oneYear) {
formatTime(timestamp, "MM-dd");
} else {
tips =
format === true
? formatTime(timestamp, "yyyy-MM-dd")
: formatTime(timestamp, format);
}
}
}
return tips;
};
/**
* @description 本地图片转base64方法(兼容APP、H5、小程序)
* @param {string} path 图片本地路径
* @returns Promise对象
*/
const imageToBase64 = (path: string) => {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
plus.io.resolveLocalFileSystemURL(path, (entry: any) => {
entry.file((file: any) => {
let fileReader = new plus.io.FileReader();
fileReader.readAsDataURL(file);
fileReader.onloadend = (evt: any) => {
let base64 = evt.target.result.split(",")[1];
resolve(base64);
};
});
});
// #endif
// #ifdef H5
uni.request({
url: path,
responseType: "arraybuffer",
success: (res: UniApp.RequestSuccessCallbackResult) => {
resolve(uni.arrayBufferToBase64(res.data as ArrayBuffer));
},
});
// #endif
// #ifdef MP-WEIXIN
uni.getFileSystemManager().readFile({
filePath: path,
encoding: "base64",
success: (res) => {
resolve(res.data);
},
});
// #endif
});
};
/**
* 函数防抖:一段实现执行多次,只执行最后一次
* @param {void} fn 回调函数
* @param {number} wait 节流时间
* @returns {void}
* @constructor
*/
let timeout: ReturnType<typeof setTimeout> | null = null;
function debounce<T extends (...args: any[]) => void>(
fn: T,
wait: number = 500,
immediate: boolean = false,
) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout);
// 立即执行,此类情况一般用不到
if (immediate) {
const callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait);
if (callNow) typeof fn === "function" && fn();
} else {
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(() => {
typeof fn === "function" && fn();
}, wait);
}
}
let timer: ReturnType<typeof setTimeout> | null = null;
let flag: boolean | undefined;
/**
* 函数节流: 一段时间执行一次
* @param {void} fn 回调函数
* @param {number} wait 节流等待时间
* @param {boolean} immediate 是否立马执行
* @returns {void}
* @constructor
*/
const throttle = (
fn: Function,
wait: number = 500,
immediate: boolean = true,
): void => {
if (immediate) {
if (!flag) {
flag = true;
// 如果是立即执行,则在wait毫秒内开始时执行
typeof fn === "function" && fn();
timer = setTimeout(() => {
flag = false;
}, wait);
}
} else if (!flag) {
flag = true;
// 如果是非立即执行,则在wait毫秒内的结束处执行
timer = setTimeout(() => {
flag = false;
typeof fn === "function" && fn();
}, wait);
}
};
/**
* 递归拷贝对象
* @param {object} source 对象或数组
* @returns 深拷贝的数组和对象
* */
const deepClone = (source: any) => {
if (!source && typeof source !== "object") {
throw new Error("该值不存在或者不是个对象");
}
const targetObj: any = source.constructor === Array ? [] : {};
Object.keys(source).forEach((keys) => {
if (source[keys] && typeof source[keys] === "object") {
targetObj[keys] = deepClone(source[keys]);
} else {
targetObj[keys] = source[keys];
}
});
return targetObj;
};
/**
* 字节转化(b/KB/MB/GB)单位
* @param {number} bytes 字节
* @returns {string} 返回单位大小
* */
const bytesToSize = (bytes: number) => {
const sizes = ["b", "KB", "MB", "GB", "TB"];
if (bytes === 0) {
return "0b";
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
if (i === 0) {
return `${bytes}${sizes[i]}`;
}
return `${(bytes / 1024 ** i).toFixed(1)}${sizes[i]}`;
};
/**
* @description 将对象转换为 URL 查询参数字符串
* @param params - 要转换的对象
* @returns 转换后的查询参数字符串
*/
const objectToUrlParams = (params: Record<string, any>): string => {
return Object.entries(params)
.filter(([key, value]) => value !== undefined && value !== null) // 过滤掉值为 undefined 或 null 的项
.map(([key, value]) => {
// 对值进行编码以确保 URL 安全
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
})
.join("&"); // 使用 & 拼接所有参数
};
/**
* @description 地址栏参数转换对象
* @param paramStr - 字符串参数
* @returns 返回转换对象
*/
const urlParamsToObject = (paramStr: string): AnyObject => {
const params: AnyObject = {};
// 去掉字符串两端的可能的空格
paramStr = paramStr.trim();
// 如果字符串以?开头,去掉它
if (paramStr.startsWith("?")) {
paramStr = paramStr.substring(1);
}
// 按&分割字符串,得到键值对数组
const pairs = paramStr.split("&");
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i];
// 按=分割键值对
const [key, ...valueParts] = pair.split("=");
const value = valueParts.join("=");
// 将键值对存入对象
params[decodeURIComponent(key)] = decodeURIComponent(value);
}
return params;
};
/**
* 获取 [min,max]的随机数
* Math.floor(Math.random()*10) 可均衡获取 0 到 9 的随机整数
* @param min 最小值
* @param max 最大值
* @returns {Number} string 随机数
*/
const random = (min: number | string, max: number | string): number => {
min = Number(min);
max = Number(max);
return Math.floor(Math.random() * (max - min + 1) + min) || 0;
};
const range = (min = 0, max = 0, value = 0) => {
return Math.max(min, Math.min(max, Number(value)));
};
export type RectResultType<T extends boolean> = T extends true
? UniApp.NodeInfo[]
: UniApp.NodeInfo;
/**
* 查询节点信息
* 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
* 解决办法为在组件根部再套一个没有任何作用的view元素
* @param selector 元素类名或id
* @param all 是否获取多个相同元素数值
* @param ins 在微信小程序里,因为utils文件里面获取不到instance值所以必须通过ins这个传过来【注意,(支付宝小程序无效)】
* @param useFields 是否使用 fields 方法获取节点信息
*/
const getRect = <T extends boolean>(
selector: string,
all?: T,
ins?: any,
useFields?: boolean,
): Promise<RectResultType<T>> => {
return new Promise<RectResultType<T>>((resolve, reject) => {
let query: UniNamespace.SelectorQuery | null = null;
if (ins) {
// TODO: 在微信小程序里,因为utils文件里面获取不到instance值所以必须通过ins这个传过来
query = uni.createSelectorQuery().in(ins);
} else {
query = uni.createSelectorQuery();
}
const method = all ? "selectAll" : "select";
const callback = (rect: UniApp.NodeInfo | UniApp.NodeInfo[]) => {
if (all && Array.isArray(rect) && rect.length > 0) {
resolve(rect as RectResultType<T>);
} else if (!all && rect) {
resolve(rect as RectResultType<T>);
} else {
error(`调用getRect方法,没有找到${selector}对应的元素内容`);
reject(new Error("No nodes found"));
}
};
if (useFields) {
query[method](selector)
.fields({ size: true, node: true }, callback)
.exec();
} else {
query[method](selector).boundingClientRect(callback).exec();
}
});
};
/**
* @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.rpx2px进行转换
* @param {number|string} value 用户传递值的px值
* @param {boolean} unit
* @returns {number|string}
*/
function getPx(value: string | number, unit: true): string;
function getPx(value: string | number, unit?: false): number;
function getPx(value: string | number, unit: boolean = false): string | number {
if (isNumber(value) || typeof value === "number") {
return unit ? `${value}px` : Number(value);
}
// 如果带有rpx,先取出其数值部分,再转为px值
if (/(rpx|upx)$/.test(value)) {
return unit
? `${uni.rpx2px(parseInt(value))}px`
: Number(uni.rpx2px(parseInt(value)));
} else if (/(px)$/.test(value)) {
return unit ? value : Number(value.replace("px", ""));
} else {
return unit ? `${parseInt(value)}px` : Number(value);
}
}
/**
* @description 对象转换字符串,用在于style样式上
* */
const formatObject = (obj: CSSProperties) => {
return Object.entries(obj)
.map(([key, value]) => `${key.toUpperCase()}: ${value}`)
.join("; ");
};
export {
encryptData,
decryptData,
addUnit,
padZero,
addZero,
clearVal,
formatTime,
formatTimeToString,
imageToBase64,
debounce,
throttle,
deepClone,
bytesToSize,
objectToUrlParams,
urlParamsToObject,
random,
range,
getRect,
getPx,
formatObject,
};