import env from '@/env/index.js' import store from '@/store/index.js' import { isDate, isString, isArray } from './is.js' /** * 日期格式化,样例 yyyy-mm-dd hh:MM:ss * @param date Date 需要转换的日期 * @param fmt string 转化的格式 yyyy-mm-dd hh:MM:ss */ export const dateTimeFormat = (date, fmt) => { if (isString(date)) { date = date.replace(/-/g, '/') } if (!isDate(date)) { date = new Date(date) } if (!date || isNaN(date.getTime())) { throw new Error('日期不正确') } let ret const opt = { '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 } /** * 日期格式化文字描述,样例 yyyy-mm-dd hh:MM:ss * @param stringTime 需要转换的日期 */ export const getTimer = (stringTime) => { let minute = 1000 * 60 let hour = minute * 60 let day = hour * 24 let week = day * 7 let month = day * 30 let time1 = new Date().getTime() //当前的时间戳 let time2 = Date.parse(new Date(stringTime)) //指定时间的时间戳 let time = time1 - time2 let result = null if (time / month >= 1) { result = parseInt(time / month) + '月前' } else if (time / week >= 1) { result = parseInt(time / week) + '周前' } else if (time / day >= 1) { result = parseInt(time / day) + '天前' } else if (time / hour >= 1) { result = parseInt(time / hour) + '小时前' } else if (time / minute >= 1) { result = parseInt(time / minute) + '分钟前' } else { result = '刚刚' } return result } /** * 创建websocket * @param {*} data * @value {string} pageInfo 页面信息 * @value {true} retry 是否重连,默认false * @returns 一个websocket实例 */ export const makeSocket = async ({ pageInfo = '', retry = false }) => { const socket = { sockTask: null, close: function () { this.sockTask.close({ code: 1000 }) closeFlag = true }, onMessage: function () { console.log('onMessage') }, onError: function () { console.log('onError') }, onRetry: function () { console.log('onRetry') } } let limitedNum = 0 let closeFlag = false let timer = null async function createSocket() { let url = '' if (env == 'production') { url = 'wss://api-client-yyt.qniao.cn/qn-websocket-service/wechatwebsock?token=' } else if (env == 'test') { url = 'wss://api-client-yyt-test.qniao.cn/qn-websocket-service/wechatwebsock?token=' } const token = store.state.qnToken const socketTask = await uni.connectSocket({ url: `${url}${token}`, header: { 'content-type': 'application/json' }, success: () => { console.log('websocket连接成功') }, fail: () => { console.log('websocket连接失败') } }) socketTask.onOpen(() => { console.log(pageInfo + ' onOpen') timer = setInterval(() => { socketTask.send({ data: 'ping' }) }, 10000) }) socketTask.onClose(() => { console.log(pageInfo + ' onClose') clearInterval(timer) timer = null if (!closeFlag && retry && limitedNum < 20) { limitedNum++ console.log('重连次数:' + limitedNum) createSocket().then(() => { installSocket() socket.onRetry() }) } }) socket.sockTask = socketTask } await createSocket() function installSocket() { if (socket.sockTask) { socket.onMessage = (fn) => { socket.sockTask.onMessage((res) => { let data = JSON.parse(res.data) console.log(pageInfo + '接收到消息:', data) if (data.type != 'heartbeat') { fn(data) } }) } socket.onError = (fn) => { socket.sockTask.onError((err) => { fn(err) }) } } } installSocket() return socket } let _boundaryCheckingState = true /** * {beyond:是否超出目标时间,day:天,hours:小时,minutes:分钟,seconds:秒钟} * @param time 计算时间 * @param target 对照时间 * @returns 时间差对象 */ export const difTime = (time, target) => { if (isString(time)) { time = time.replace(/-/g, '/') } if (isString(target)) { target = target.replace(/-/g, '/') } let begin = new Date(time).getTime() // 兼容ios时间 let end = new Date(target).getTime() let beyond = begin < end ? false : true let diff = Math.abs(begin - end) // 计算天数 let day = Math.floor(diff / (24 * 3600 * 1000)) day != day ? (day = 0) : '' diff = diff % (24 * 3600 * 1000) // 计算小时数 let hours = Math.floor(diff / (3600 * 1000)) hours != hours ? (hours = 0) : '' diff = diff % (3600 * 1000) // 计算分钟数 let minutes = Math.floor(diff / (60 * 1000)) minutes != minutes ? (minutes = 0) : '' diff = diff % (60 * 1000) // 计算秒数 let seconds = Math.floor(diff / 1000) seconds != seconds ? (seconds = 0) : '' return { beyond, day, hours, minutes, seconds } } /** * 迭代操作 */ function iteratorOperation(arr, operation) { const [num1, num2, ...others] = arr let res = operation(num1, num2) others.forEach((num) => { res = operation(res, num) }) return res } /** * Return digits length of a number * @param {*number} num Input number */ function digitLength(num) { // Get digit length of e const eSplit = num.toString().split(/[eE]/) const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0) return len > 0 ? len : 0 } /** * 把错误的数据转正 * strip(0.09999999999999998)=0.1 */ function strip(num, precision = 15) { return +parseFloat(Number(num).toPrecision(precision)) } /** * 把小数转成整数,支持科学计数法。如果是小数则放大成整数 * @param {*number} num 输入数 */ function float2Fixed(num) { if (num.toString().indexOf('e') === -1) { return Number(num.toString().replace('.', '')) } const dLen = digitLength(num) return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num) } /** * 检测数字是否越界,如果越界给出提示 * @param {*number} num 输入数 */ function checkBoundary(num) { if (_boundaryCheckingState) { if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) { console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`) } } } /** * 精确乘法 */ function times(...nums) { if (nums.length > 2) { return iteratorOperation(nums, times) } const [num1, num2] = nums const num1Changed = float2Fixed(num1) const num2Changed = float2Fixed(num2) const baseNum = digitLength(num1) + digitLength(num2) const leftValue = num1Changed * num2Changed checkBoundary(leftValue) return leftValue / Math.pow(10, baseNum) } /** * 精确除法 */ function divide(...nums) { if (nums.length > 2) { return iteratorOperation(nums, divide) } const [num1, num2] = nums const num1Changed = float2Fixed(num1) const num2Changed = float2Fixed(num2) checkBoundary(num1Changed) checkBoundary(num2Changed) // fix: 类似 10 ** -4 为 0.00009999999999999999,strip 修正 return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1)))) } /** * 精确四舍五入 */ export const round = (num, ratio) => { const base = Math.pow(10, ratio) let result = divide(Math.round(Math.abs(times(num, base))), base) if (num < 0 && result !== 0) { result = times(result, -1) } return result } /** * 根据规则校验字段 * @param {*} value 输入值 * @param {array} rules 规则集 {type,required,message} * @value {boolean} required 是否必填 * @value {string} type 字段类型校验,目前支持 phone * @returns {object} {isValid,msg} */ export const validateField = (value, rules) => { let isValid = true let msg = '' for (let rule of rules) { if (rule.required) { if (value === '' || value === undefined || value === null) { isValid = false msg = rule.message break } if (isArray(value) && value.length === 0) { isValid = false msg = rule.message break } } if (rule.type === 'phone' && value !== '' && !/^1[3456789]\d{9}$/.test(value)) { isValid = false msg = rule.message break } } return { isValid, msg } } /** * 正确数字保留几位小数显示 * @param {(number | string)} value 要修正的对象 * @param {number} precision 保留位数 * @returns {string} */ export const numToString = function (value, precision = 2) { const zeros = '00000000000000' let temp = '' try { temp = round(value, precision) + '' let dot = temp.indexOf('.') if (dot == -1) { temp = temp + '.' + zeros.substring(0, 0 + precision) } else { // 计算差几个0,精度 - 现有位数 let digits = precision - (temp.length - dot - 1) digits > 0 ? (temp = temp + zeros.substring(0, digits)) : '' } } catch (error) { console.log('数字格式错误') return value } return temp } /** * 简易版防抖函数 * @param {Function} fn 函数 * @param {number} delay 延迟时间 * @returns {Function} */ export function debounce(fn, delay) { let timer = null return function (...args) { if (timer) { clearTimeout(timer) timer = null } timer = setTimeout(() => { timer = null fn.apply(this, args) }, delay) } } /** * 简易版节流函数,可以带异步数据返回 * @param {Function} fn 函数,如果需要返回内容则需要返回Promise,并在then中return内容 * @param {number} delay 延迟时间单位毫秒 */ export function throttle(fn, delay) { let timer = null, first = true, result = null, lastInvokeTime = 0, remainingTime = null return function (...args) { if (first) { result = fn.apply(this, args) first = false lastInvokeTime = Date.now() return result } // 如果上次调用时间与当前时间差大于等于delay,则直接执行 if (Date.now() - lastInvokeTime >= delay) { result = fn.apply(this, args) lastInvokeTime = Date.now() return result } else { remainingTime = delay - (Date.now() - lastInvokeTime) if (timer) { clearTimeout(timer) createTimer.call(this, remainingTime, args) } else { createTimer.call(this, remainingTime, args) } } return result } function createTimer(time, args) { timer = null timer = setTimeout(() => { result = fn.apply(this, args) timer = null lastInvokeTime = Date.now() }, time) } } /** * 简易的解析地址的函数 * @param {string} url 需要解析的url地址 * @returns {object} {host,query} */ export function parseUrl(url) { let query = {} let host = url if (url.indexOf('?') > -1) { host = url.substring(0, url.indexOf('?')) let str = url.split('?')[1] let arr2 = str.split('&') arr2.forEach((item) => { let arr3 = item.split('=') query[arr3[0]] = arr3[1] }) } return { host, query } } /** * 获取数组的所有子集 * @param {Array} arr 数组 * @returns Array> */ export function getAllSubsets(arr) { let res = [[]] for (let item of arr) { const tempRes = res.map((subset) => { const one = subset.concat([]) one.push(item) return one }) res = res.concat(tempRes) } return res }