import {sendUpdataUrl, IMG_URL} from "/http";
import moment from 'moment'
import CryptoJS from 'crypto-js';
const Storage = {
/**
* 设置缓存
* @param {string} key - 键
* @param {any} value - 值
* @param {string} expire - 过期时间 (秒),不设置表示永久
* Storage.set('storage_key', 'hello', 60)
*/
set: function (key, value, expire = '') {
let obj = {
value: value,
timestamp: new Date().getTime()
};
if (expire) {
obj.expire = expire * 1000; // 转换成毫秒
}
uni.setStorageSync(key, JSON.stringify(obj));
},
/**
* 获取缓存
* @param {string} key - 键
* @return {any} - 返回值
*/
get: function (key) {
let value = uni.getStorageSync(key);
if (!value) {
return null;
}
let obj = JSON.parse(value);
if (obj.expire) {
if (new Date().getTime() - obj.timestamp > obj.expire) {
this.remove(key);
return null;
}
}
return obj.value;
},
/**
* 删除缓存
* @param {string} key - 键
*/
remove: function (key) {
uni.removeStorageSync(key);
}
};
// 判断字符串是否包含数字
function containsNumber(str) {
return /\d/.test(str);
}
// 检查字符串中是否含有字母
function containsLetter(str) {
return /[a-zA-Z]/.test(str);
}
/**
* 跳转到指定的链接或页面。
* @param {string|Object} datas - 要跳转的URL或包含跳转信息的对象。
* - 如果是字符串,则表示直接跳转URL。
* - 如果是对象,可以包含以下属性:
* - url: 要跳转的URL。
* - type: 跳转类型(如"redirectTo"、"reLaunch"、"tab"、"back"), 无值则跳转默认配置。
* - navigateBackDelta: 如果type为"back",表示返回的页面数。
*/
// 测试 goToLinks('/pages/message/messageSystem')"
function goToLinks(datas) {
let url;
let type;
let navigateBackDelta;
if (typeof datas === 'string') {
url = datas;
} else {
url = datas.url;
type = datas.type;
navigateBackDelta = datas.navigateBackDelta || 1;
}
switch (type) {
case 'redirectTo':
// 关闭当前页面,跳转到应用内的某个页面
uni.redirectTo({ url });
break;
case 'reLaunch':
// 关闭所有页面,打开到应用内的某个页面。
uni.reLaunch({ url });
break;
case 'tab':
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
uni.switchTab({ url });
break;
case 'back':
let back = getCurrentPages();
if (back && back.length > 1) {
uni.navigateBack({
delta: navigateBackDelta || 1
});
} else {
// #ifdef H5
history.back()
// #endif
}
break;
default:
// 保留当前页面,跳转到应用内的某个页面
uni.navigateTo({ url });
}
}
/**
* 时间格式转换
* @param {string|number} timeValue - 时间字符串或时间戳
* 参数可以为 秒时间戳, 毫秒时间戳, 2023-02-23 11:23:20.123, 2023/02/23 11:23, 2023-02-23 11:23
* @param {string} format - 返回的时间格式
* @returns {string} 转换后的时间字符串
* @throws {Error} 当传入的时间格式不符合预期时抛出错误
*/
function timeConverter(timeValue, format) {
let date;
if (typeof timeValue === 'string' && timeValue.includes('T')) {
// 将 ISO 8601 格式的时间字符串转换成符合规范的时间字符串
const ISODate = new Date(timeValue);
const formattedISODate = ISODate.toISOString().split('.')[0].replace('T', ' ');
date = new Date(formattedISODate);
} else if (typeof timeValue === 'number') {
if (String(timeValue).length === 10) timeValue *= 1000; // 秒时间戳转毫秒
date = new Date(timeValue);
} else if (typeof timeValue === 'string') {
date = new Date(timeValue.replace(/-/g, '/')); // 兼容Safari
} else {
throw new Error('Invalid timeValue');
}
// 计算时区偏移并转换为北京时间(UTC+8)
const time_zone = -date.getTimezoneOffset() / 60;
let beijingTime;
if (time_zone < 0) {
beijingTime = new Date(date.getTime() + (Math.abs(time_zone) + 8) * 60 * 60 * 1000);
} else {
beijingTime = new Date(date.getTime() - (time_zone - 8) * 60 * 60 * 1000);
}
let year = beijingTime.getFullYear();
let month = (beijingTime.getMonth() + 1).toString().padStart(2, '0');
let day = beijingTime.getDate().toString().padStart(2, '0');
let hour = beijingTime.getHours().toString().padStart(2, '0');
let minute = beijingTime.getMinutes().toString().padStart(2, '0');
switch (format) {
case 'YYYY-MM-DD HH:mm':
return `${year}-${month}-${day} ${hour}:${minute}`;
case 'YYYY/MM/DD HH:mm':
return `${year}/${month}/${day} ${hour}:${minute}`;
case 'YYYY-MM-DD':
return `${year}-${month}-${day}`;
case 'YYYY-MM':
return `${year}-${month}`;
case 'MM-DD':
return `${month}-${day}`;
case 'HH:mm':
return `${hour}:${minute}`;
case 'MM-DD HH:mm':
return `${month}-${day} ${hour}:${minute}`;
default:
return `${year}-${month}-${day} ${hour}:${minute}`;
}
}
/**
* 获取当前设备平台
*/
function getPlatform() {
const platform = uni.getSystemInfoSync().platform;
switch (platform) {
case 'android':
return 'android'
case 'ios':
return 'ios'
case 'devtools':
return 'development Tool'
case 'web':
return 'h5'
case 'windows':
return 'h5'
default:
return 'Unknown Platform'
}
}
/**
* 复制
*/
function copyText(text) {
let content = text //需要复制的内容
uni.setClipboardData({
data: content,
success: function () {
uni.showToast({
title: '复制成功',
icon: 'none'
})
},
fail: function () {
uni.showToast({
title: '复制失败',
icon: 'none'
})
}
})
}
/**
* 打开外部链接
*/
function goOpenUrl(url) {
if (!url) return false
// #ifdef H5
let wind = window.open('')
wind.location.replace(url)
// #endif
// #ifdef APP
plus.runtime.openURL(url);
// #endif
}
// 长按方法
function useLongPress(callback, delay = 1000) {
let timer = null;
let isLongPress = false;
const start = (param) => {
timer = setTimeout(() => {
isLongPress = true;
callback(param); // 调用传入的回调函数
}, delay);
};
const stop = () => {
clearTimeout(timer);
if (isLongPress) {
isLongPress = false;
}
};
return { start, stop };
}
// 模仿toFixed方法, 但不四舍五入
/**
* 截断数字到指定的小数位数。
* @param {number} num - 需要被处理的数字。
* @param {number} [precision=2] - 要保留的小数位数,默认为 8。
* @param round 是否四舍五入
* @param {boolean} [alwaysFixed=false] - 是否总是固定显示小数点后 precision 位数。如果为 true,不足的部分会用零补齐。
* @returns {string} - 处理后的数字字符串。
* @throws {Error} - 如果输入的不是一个有效的数字。
*/
function truncateToFixed(num, precision = 8, round = false, alwaysFixed = false) {
if (isNaN(parseFloat(num)) || !isFinite(num)) {
throw new Error("传入的不正确.");
}
let result;
if (round) {
// 使用四舍五入
result = num.toFixed(precision);
} else {
// 不使用四舍五入,原始逻辑
result = num.toString();
let decimalPointIndex = result.indexOf('.');
if (decimalPointIndex === -1) {
// 如果没有小数点
if (alwaysFixed) {
result += '.' + '0'.repeat(precision);
}
} else {
// 有小数点
const endPosition = decimalPointIndex + 1 + precision;
result = result.slice(0, Math.min(endPosition, result.length));
}
}
if (alwaysFixed) {
let decimalPointIndex = result.indexOf('.');
while ((result.length - decimalPointIndex - 1) < precision) {
result += '0';
}
}
return result;
}
/**
* 图片压缩
* imgSrc 地址
* scale 压缩质量 0-1
* type 文件类型
*/
const compressImg = (imgSrc, scale, type, callback) => {
// uni.$u.toast('压缩中')
var img = new Image();
img.src = imgSrc;
img.onload = function() {
var that = this;
var h = (img.height * scale).toFixed(0); // 默认按质量比例压缩
var w = (img.width * scale).toFixed(0);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var width = document.createAttribute("width");
width.nodeValue = w;
var height = document.createAttribute("height");
height.nodeValue = h;
canvas.setAttributeNode(width);
canvas.setAttributeNode(height);
ctx.drawImage(that, 0, 0, w, h);
var base64 = canvas.toDataURL('image/jpeg', scale); //压缩比例
canvas = null;
if (type == 'base64') {
let data = {
size: getBase64Size(base64),
type: type,
source: base64
}
callback(base64);
} else {
let blob = base64ToBlob(base64);
console.log('压缩后的大小', blob.size);
const blobUrl = window.URL.createObjectURL(blob); //blob地址
blob.source = blobUrl
callback(blob);
}
}
};
/**base转Blob */
const base64ToBlob = (base64) => {
var arr = base64.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime
});
};
/**获取base64的文件大小 */
const getBase64Size = () => {
let size = 0;
if (base64Str) { // 获取base64图片byte大小
const equalIndex = base64Str.indexOf('='); // 获取=号下标
if (equalIndex > 0) {
const str = base64Str.substring(0, equalIndex); // 去除=号
const strLength = str.length;
const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
size = Math.floor(fileLength); // 向下取整
} else {
const strLength = base64Str.length;
const fileLength = strLength - (strLength / 8) * 2;
size = Math.floor(fileLength); // 向下取整
}
} else {
size = null;
}
return size
}
/**获取文件大小倍数,生成质量比*/
async function getCompressionRatio(fileSize) {
const fileMax = 1 * 1024 * 1024;
const multiple = (fileSize / fileMax).toFixed(2);
let compressionRatio = 1;
if (multiple > 5) {
compressionRatio = 0.5
} else if (multiple > 4) {
compressionRatio = 0.6
} else if (multiple > 3) {
compressionRatio = 0.7
} else if (multiple > 2) {
compressionRatio = 0.8
} else if (multiple > 1) {
compressionRatio = 0.9
} else {
compressionRatio = 2
}
return compressionRatio;
}
// 图片上传
// 上传文件的大小
let afterReadSize = 1024 * 1024 * 50
async function oversizeReads(event) {
if (event.file[0]?.type !== 'image') {
uni.showToast({
title: '文件类型不正确',
icon: 'none'
});
return false;
}
uni.showToast({
title: '文件大小不能超过50M',
icon: 'none'
})
return true;
}
/** 压缩图片*/
function compressFirstImg(source, compressionRatio) {
let that = this;
return new Promise((resolve, reject) => {
compressImg(source.url, compressionRatio, source.type, compressRes => {
resolve(compressRes);
})
}).then((res) => {
source.size = res.size
// window.URL.revokeObjectURL(source.url) // 删除被压缩的缓存文件,这里注意,如果是相册选择上传,可能会删除相册的图片
source.url = res.source
source.thumb = res.source
return source
}).catch(err => {
console.log('图片压缩失败', err)
})
}
async function afterReads(event, fileList, callback) {
// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
let lists = [].concat(event.file);
let fileListLen = fileList.length;
lists.map((item) => {
const fileSize = item.size
//这个图片大小取值需要定多少?
if (fileSize > 1 * 1024 * 1024) {
const compressionRatio = getCompressionRatio(fileSize);
// 目前取得是超出1M自动压缩图片
compressFirstImg(item, compressionRatio)
if (fileSize > 50 * 1024 * 1024) {
uni.showToast({
title: '文件压缩后超过50M',
icon: 'none'
})
return false
}
}
//文件最小限制
/* if (fileSize < 5 * 1024) {
uni.showToast({
title: '文件不能小于5kb',
icon: 'none'
})
return false
} */
fileList.push({...item, status: 'uploading', message: '上传中...',});
});
// console.log(lists)
for (let i = 0; i < lists.length; i++) {
const {code, data} = await sendUpdataUrl(lists[i].url);
let item = fileList[fileListLen];
if(code === 1000) {
callback(data.path)
// let item = fileList[fileListLen];
fileList.splice(fileListLen, 1, {...item, status: 'success', message: '', url: data.path});
// fileListLen++;
} else {
fileList.splice(fileListLen, 1, {...item, status: 'failed', message: '上传失败',});
}
fileListLen++;
}
}
// 精度
function numberAccuracy(num) {
let result = String(num);
if (result.indexOf('-') >= 0) {
result = '0' + String(Number(result) + 1).substr(1);
}
return result
}
/* 根据时间选项获取开始日期和结束日期(例如:本季度,本月,本周,上周等) */
function getTimelToChange(type) {
let startTime;
let endTime
if(type=='1'){ //今日
startTime = moment(moment().startOf('day').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().valueOf()).format('YYYY/MM/DD HH:mm:ss');
}else if(type=='2'){ //昨日
startTime = moment(moment().add(-1, 'days').startOf('day').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().add(-1, 'days').endOf('day').valueOf()).format('YYYY/MM/DD HH:mm:ss')
}else if(type=='3'){ //本周
startTime = moment(moment().week(moment().week()).startOf('week').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().week(moment().week()).endOf('week').valueOf()).format('YYYY/MM/DD HH:mm:ss');
}else if(type=='4'){ //上周
startTime = moment(moment().week(moment().week() - 1).startOf('week').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().week(moment().week() - 1).endOf('week').valueOf()).format('YYYY/MM/DD HH:mm:ss')
}else if(type=='5'){ //本月
startTime = moment(moment().month(moment().month()).startOf('month').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().month(moment().month()).endOf('month').valueOf()).format('YYYY/MM/DD HH:mm:ss')
}else if(type=='6'){ //上月
startTime = moment(moment().month(moment().month() - 1).startOf('month').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().month(moment().month() - 1).endOf('month').valueOf()).format('YYYY/MM/DD HH:mm:ss');
}else if(type=='7'){ //本季度
startTime = moment(moment().quarter(moment().quarter()).startOf('quarter').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().quarter(moment().quarter()).endOf('quarter').valueOf()).format('YYYY/MM/DD HH:mm:ss');
}else if(type=='8'){ //上季度
startTime = moment(moment().quarter(moment().quarter() - 1).startOf('quarter').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().quarter(moment().quarter() - 1).endOf('quarter').valueOf()).format('YYYY/MM/DD HH:mm:ss');
}else if(type=='9'){ //今年
startTime = moment(moment().year(moment().year()).startOf('year').valueOf()).format('YYYY/MM/DD HH:mm:ss');
endTime = moment(moment().year(moment().year()).endOf('year').valueOf()).format('YYYY/MM/DD HH:mm:ss');
}
return {startTime,endTime}
}
// 字符串显示前后几位,中间用 字符代替
function maskBankAccount(str, maskLength = 4, unmaskLength = 8, mask = '*') {
if (!str || str.length < unmaskLength) {
return str
}
const firstFour = str.slice(0, 4);
const lastFour = str.slice(-4);
const maskedMiddle = '*'.repeat(str.length - 8);
return `${firstFour}${maskedMiddle}${lastFour}`;
}
let timeout = null
/**
* 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function debounce(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout)
// 立即执行,此类情况一般用不到
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) typeof func === 'function' && func()
} else {
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(() => {
typeof func === 'function' && func()
}, wait)
}
}
// SHA-256 加密
export function sha256Encrypt(text) {
return CryptoJS.SHA256(text).toString(CryptoJS.enc.Hex);
}
// MD5 加密
export function md5Encrypt(text) {
return CryptoJS.MD5(text).toString(CryptoJS.enc.Hex);
}
// 将秒数转换为“几分钟前”、“几小时前”或“几天前”的格式
function timeAgo(seconds) {
seconds = parseInt(seconds)
if (isNaN(seconds)) return ''
if (seconds < 60) {
return "刚刚";
} else if (seconds < 3600) {
const minutes = Math.floor(seconds / 60);
return `${minutes} 分钟前`;
} else if (seconds < 86400) {
const hours = Math.floor(seconds / 3600);
return `${hours} 小时前`;
} else {
const days = Math.floor(seconds / 86400);
return `${days} 天前`;
}
}
export {
containsNumber,
containsLetter,
goToLinks,
timeConverter,
Storage,
getPlatform,
copyText,
goOpenUrl,
useLongPress,
truncateToFixed,
afterReadSize,
oversizeReads,
afterReads,
numberAccuracy,
getTimelToChange,
maskBankAccount,
debounce,
timeAgo,
};