You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
480 lines
12 KiB
480 lines
12 KiB
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<Array<any>>
|
|
*/
|
|
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
|
|
}
|