import { LOGIN_PAGE_URL, needLoginPages } from '../config.js'; import { Router } from '@/common/utils/index.js'; /** * 节流原理:在一定时间内,只能触发一次 * * @param {Function} func 要执行的回调函数 * @param {Number} wait 延时的时间 * @param {Boolean} immediate 是否立即执行 * @return null */ let throttleTimer, throttleFlag; export function throttle(func, wait = 500, immediate = true) { if (immediate) { if (!throttleFlag) { throttleFlag = true; // 如果是立即执行,则在wait毫秒内开始时执行 typeof func === 'function' && func(); throttleTimer = setTimeout(() => { throttleFlag = false; }, wait); } } else { if (!throttleFlag) { throttleFlag = true // 如果是非立即执行,则在wait毫秒内的结束处执行 throttleTimer = setTimeout(() => { throttleFlag = false typeof func === 'function' && func(); }, wait); } } }; /** * 时间戳转为多久之前 * @param String timestamp 时间戳 * @param String | Boolean format 如果为时间格式字符串,超出一定时间范围,返回固定的时间格式; * 如果为布尔值false,无论什么时间,都返回多久以前的格式 */ export function timeFrom(timestamp = null, format = 'yyyy-mm-dd') { if (timestamp == null) timestamp = Number(new Date()); timestamp = parseInt(timestamp); // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位) if (timestamp.toString().length == 10) timestamp *= 1000; var timer = (new Date()).getTime() - timestamp; timer = parseInt(timer / 1000); // 如果小于5分钟,则返回"刚刚",其他以此类推 let tips = ''; switch (true) { case timer < 300: tips = global.i18n.t('刚刚'); break; case timer >= 300 && timer < 3600: tips = parseInt(timer / 60) + global.i18n.t('分钟前'); break; case timer >= 3600 && timer < 86400: tips = parseInt(timer / 3600) + global.i18n.t('小时前'); break; case timer >= 86400 && timer < 2592000: tips = parseInt(timer / 86400) + global.i18n.t('天前'); break; default: // 如果format为false,则无论什么时间戳,都显示xx之前 if (format === false) { if (timer >= 2592000 && timer < 365 * 86400) { tips = parseInt(timer / (86400 * 30)) + global.i18n.t('个月前'); } else { tips = parseInt(timer / (86400 * 365)) + global.i18n.t('年前'); } } else { tips = timeFormat(timestamp, format); } } return tips; } // 判断arr是否为一个数组,返回一个bool值 export function isArray(arr) { return Object.prototype.toString.call(arr) === '[object Array]'; } // 深度克隆 export function deepClone(obj) { // 对常见的“非”值,直接返回原来值 if ([null, undefined, NaN, false].includes(obj)) return obj; if (typeof obj !== "object" && typeof obj !== 'function') { //原始类型直接返回 return obj; } var o = isArray(obj) ? [] : {}; for (let i in obj) { if (obj.hasOwnProperty(i)) { o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]; } } return o; } export function timeFormat(timestamp = null, fmt = 'yyyy-mm-dd') { // padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序 // 所以这里做一个兼容polyfill的兼容处理 if (!String.prototype.padStart) { // 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解 String.prototype.padStart = function (maxLength, fillString = ' ') { if (Object.prototype.toString.call(fillString) !== "[object String]") throw new TypeError( 'fillString must be String') let str = this // 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉 if (str.length >= maxLength) return String(str) let fillLength = maxLength - str.length, times = Math.ceil(fillLength / fillString.length) while (times >>= 1) { fillString += fillString if (times === 1) { fillString += fillString } } return fillString.slice(0, fillLength) + str; } } // 其他更多是格式化有如下: // yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 timestamp = parseInt(timestamp); // 如果为null,则格式化当前时间 if (!timestamp) timestamp = Number(new Date()); // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位) if (timestamp.toString().length == 10) timestamp *= 1000; console.log('timestamp', timestamp) let date = new Date(timestamp); console.log(date); let ret; let 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; } /** * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数 * * @param {Function} func 要执行的回调函数 * @param {Number} wait 延时的时间 * @param {Boolean} immediate 是否立即执行 * @return null */ let timeout = null; export function debounce(func, wait = 500, immediate = false) { // 清除定时器 if (timeout !== null) clearTimeout(timeout); // 立即执行,此类情况一般用不到 if (immediate) { var callNow = !timeout; timeout = setTimeout(function () { timeout = null; }, wait); if (callNow) typeof func === 'function' && func(); } else { // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法 timeout = setTimeout(function () { typeof func === 'function' && func(); }, wait); } } //1都不支持 2安卓指纹 3 苹果指纹 4 苹果人脸 5 苹果人脸加指纹 export function biometrics() { return new Promise((resolve) => { //#ifdef APP-PLUS || MP-WEIXIN uni.checkIsSupportSoterAuthentication({ success(res) { // console.log('检测支持的认证方式', res); if (res.errMsg === 'checkIsSupportSoterAuthentication:ok') { let support = 1; let supportMode = res.supportMode; let platform = uni.getSystemInfoSync().platform; // "facial" 人脸 "fingerPrint" 指纹识别 console.log('supportMode', supportMode); // 如果都不支持 隐藏该选项 if (supportMode.length === 0) { support = 1; } // 如果是安卓机 只让用指纹识别 if (platform === 'android' && supportMode.findIndex(item => item === 'fingerPrint') !== -1) { support = 2; } // 如果是苹果机 看是否是支持人脸还是指纹 if (platform === 'ios') { // 指纹 if (supportMode.findIndex(item => item === 'fingerPrint') !== -1) { support = 3; } // 人脸 if (supportMode.findIndex(item => item === 'facial') !== -1) { support = 4; } // 指纹人脸同时存在 if (supportMode.findIndex(item => item === 'facial') !== -1 && supportMode.findIndex(item => item === 'fingerPrint') !== -1) { support = 5; } } resolve(support); } }, fail(err) { console.log(err); resolve(1); }, }) // #endif //#ifndef APP-PLUS || MP-WEIXIN return 1; // #endif }) } const router = new Router({ needLoginPages }); /** * @description showModal 弹窗封装 * @param {Object} options = 参数同 uni-app 官方用法 * */ export function showModal(options) { // #ifndef APP-PLUS uni.showModal(options); // #endif // #ifdef APP-PLUS if (uni.getSystemInfoSync().platform === 'android') { global.$showModal(options) .then(res => { console.log('RES', res); //确认 }).catch(err => { //取消 console.log('ERR', err); }) } else { uni.showModal(options); } // #endif } /** * @description 验证登录权限,接受一个回调函数,登录则执行回调函数,非登录状态则跳转登录页 * @param {Function} cb = 回调函数 * */ export function actionAuth(cb) { if (global.token) { cb && cb(); } else { navigateToLogin(); } } /** * @description 判断是app端还是小程序端登录 * */ let timer = null; // 登录页跳转防抖 export function navigateToLogin() { if (timer) clearTimeout(timer); timer = setTimeout(() => { const pages = getCurrentPages(); if (pages.length != 0) { const lastPage = '/' + pages.pop().route; let url = LOGIN_PAGE_URL; if (lastPage == url) return; uni.navigateTo({ url }); } else { uni.switchTab({ url: '/pages/tab/home/shopindex' }) } }, 200) } /** * @description 倒计时计算 * */ export function downTime(time) { var days = parseInt(time / (1000 * 60 * 60 * 24)); var hours = parseInt(time / (1000 * 60 * 60) % 24); var minutes = parseInt(time / (1000 * 60) % 60); var seconds = parseInt(time / 1000 % 60); return { days, hours: hours < 10 ? '0' + hours : hours, minutes: minutes < 10 ? '0' + minutes : minutes, seconds: seconds < 10 ? '0' + seconds : seconds, } } /** * @description 参数序列化 * */ export function serialize(data) { let str = ''; Object.keys(data).forEach(key => { str += key + '=' + data[key] + '&' }) str = str.substr(0, str.length - 1); return str; } /** * @description 设置状态栏颜色 * @param {String} color dark or light * @example this.$util.setNavigationBarColor('dark'); * */ export function setNavigationBarColor(color) { if (color == 'dark') { // #ifdef APP-PLUS plus.navigator.setStatusBarStyle('dark'); // #endif // #ifdef MP-WEIXIN wx.setNavigationBarColor({ frontColor: '#000000', backgroundColor: '#FFFFFF' }) // #endif } else if (color == 'light') { // #ifdef APP-PLUS plus.navigator.setStatusBarStyle('light'); // #endif // #ifdef MP-WEIXIN wx.setNavigationBarColor({ frontColor: '#FFFFFF', backgroundColor: '#000000' }) // #endif } } /** * base64图片缓存到本地,返回本地路径 * */ export function base64ToPath(base64) { return new Promise(function (resolve, reject) { if (typeof window === 'object' && 'document' in window) { base64 = base64.split(',') var type = base64[0].match(/:(.*?);/)[1] var str = atob(base64[1]) var n = str.length var array = new Uint8Array(n) while (n--) { array[n] = str.charCodeAt(n) } return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type }))) } var extName = base64.match(/data\:\S+\/(\S+);/) if (extName) { extName = extName[1] } else { reject(new Error('base64 error')) } var fileName = Date.now() + '.' + extName if (typeof plus === 'object') { var bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) bitmap.loadBase64Data(base64, function () { var filePath = '_doc/uniapp_temp/' + fileName bitmap.save(filePath, {}, function () { bitmap.clear() resolve(filePath) }, function (error) { bitmap.clear() reject(error) }) }, function (error) { bitmap.clear() reject(error) }) return } if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) { var filePath = wx.env.USER_DATA_PATH + '/' + fileName wx.getFileSystemManager().writeFile({ filePath: filePath, data: base64.replace(/^data:\S+\/\S+;base64,/, ''), encoding: 'base64', success: function () { resolve(filePath) }, fail: function (error) { reject(error) } }) return } reject(new Error('not support')) }) } export function postMsgToParent(message) { console.log('postMsgToParent', message); window.parent.postMessage(message, '*'); } /** * * @param {*} type 点击类型 navigateTo * @param {*} data */ export function handleAllFn(type, data) { // 无点击事件 if (type == 'niente') { return false } /** * 转页面类型 * navigateTo * data结构 * { * url: String 路径, * auth: Boolean (选填)是否验证登录状态true 验证 * params: Object (选填) 携带的参数 * } */ if (type == 'navigate') { router[data.type]('/' + data.url) return false } /** * * type: back * 返回 * data(选填) 不填返回一页 */ if (type == 'back') { uni.navigateBack({ delta: data.delta || 1 }); } /** * * type: backReference * 返回页面携带参数 * data{ * name:String 目标页面接受参数名 * params: 参数 * } */ if (type == "backReference") { if (!data.name) { return false } let pages = getCurrentPages(); //获取当前页面js里面的pages里的所有信息。 let prevPage = pages[pages.length - 2]; //prevPage 是获取上一个页面的js里面的pages的所有信息。 -2 是上一个页面,-3是上上个页面以此类推。 prevPage.backWithParams({ [data.name]: data.params }) uni.navigateBack({ delta: 1 // 返回上一级页面。 }) return false } /** * type showModal 弹窗封装 * data 参数同 uni-app 官方用法 * */ if (type == 'showModal') { // #ifndef APP-PLUS uni.showModal(data); // #endif // #ifdef APP-PLUS if (uni.getSystemInfoSync().platform === 'android') { global.$showModal(options) .then(res => { console.log('RES', res); //确认 }).catch(err => { //取消 console.log('ERR', err); }) } else { uni.showModal(data); } // #endif return false } // 打开指定弹窗 if (type == 'showModalDiy') { let pages = getCurrentPages(); //获取当前页面js里面的pages里的所有信息。 let prevPage = pages[pages.length - 1]; prevPage['popupShow' + data.showCancel] = true return false } /** * * type: downloadFile * 下载文件 * data 下载路径 * .then(res=>{}) 接收下载成功回调 * .catch(res=>{}) 接收下载失败回调 */ if (type == 'downloadFile') { return new Promise((resolve, reject) => { const downloadTask = uni.downloadFile({ url: data.url, success: (res) => { uni.showToast({ title: '下载成功', icon: 'success', mask: true }) // resolve(res) uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function () { console.log('save success'); } }); }, fail: (err) => { uni.showToast({ title: '下载失败', icon: 'fail', mask: true }) reject(err) }, complete: () => { } }) // 中断下载 // downloadTask.abort(); // 监听下载进度 // downloadTask.onProgressUpdate(res => { // console.log('下载进度' + res.progress); // console.log('已经下载的数据长度' + res.totalBytesWritten); // console.log('预期需要下载的数据总长度' + res.totalBytesExpectedToWrite); // }) }) } /** * * type: uploadFile * 上传文件 * data{ * url:String 上传路径 * files: 仅app与h5支持该选项 * ffileType: 仅支付宝小程序支持且必填 images/video/audio * filePath: 文件路径 * name: 后台接收key * header: 选填 * formData: 选填 携带参数 * progressCallback: 选填 监听上传载进度 * } */ if (type == 'uploadFile') { return new Promise((resolve, reject) => { const uploadTask = uni.uploadFile({ url: config.baseUrl + data.url, // #ifdef APP-PLUS || H5 files: data.files || [], // #endif // #ifdef MP-ALIPAY fileType: data.fileType, // #endif // #ifdef H5 file: data.file || null, // #endif filePath: data.filePath, name: data.name, header: data.header ? data.header : {}, formData: data.formData ? data.formData : {}, complete: (response) => { if (response.statusCode == 200) { resolve(response) } else { reject(response) } } }) if (data.progressCallback && typeof (data.progressCallback) == 'function') { uploadTask.onProgressUpdate(res => { data.progressCallback(res.progress + '%'); }) } }) } /** * * type: getLoaction * 获取定位 * **/ if (type == 'getLoaction') { console.log('我要获取定位'); return new Promise((resolve, reject) => { uni.getLocation({ type: 'wgs84', success: function (res) { console.log('当前位置的经度:' + res.longitude); console.log('当前位置的纬度:' + res.latitude); uni.showToast({ title: JSON.stringify(res), icon: "none" }) resolve(res) }, fail: function (err) { reject(err) } }); }) } /** * * type: richiesta * 点赞 收藏 * data{ * url:String 上传路径 * files: 仅app与h5支持该选项 * ffileType: 仅支付宝小程序支持且必填 images/video/audio * filePath: 文件路径 * name: 后台接收key * header: 选填 * formData: 选填 携带参数 * progressCallback: 选填 监听上传载进度 * } */ if (type == 'richiesta') { } }