// 工具函数库
|
|
/**
|
* 格式化日期
|
* @param {Date|string|number} date 日期
|
* @param {string} format 格式化字符串
|
* @returns {string} 格式化后的日期字符串
|
*/
|
function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
|
if (!date) return ''
|
|
const d = new Date(date)
|
if (isNaN(d.getTime())) return ''
|
|
const year = d.getFullYear()
|
const month = String(d.getMonth() + 1).padStart(2, '0')
|
const day = String(d.getDate()).padStart(2, '0')
|
const hours = String(d.getHours()).padStart(2, '0')
|
const minutes = String(d.getMinutes()).padStart(2, '0')
|
const seconds = String(d.getSeconds()).padStart(2, '0')
|
|
return format
|
.replace('YYYY', year)
|
.replace('MM', month)
|
.replace('DD', day)
|
.replace('HH', hours)
|
.replace('mm', minutes)
|
.replace('ss', seconds)
|
}
|
|
/**
|
* 格式化相对时间
|
* @param {Date|string|number} date 日期
|
* @returns {string} 相对时间字符串
|
*/
|
function formatRelativeTime(date) {
|
if (!date) return ''
|
|
const d = new Date(date)
|
if (isNaN(d.getTime())) return ''
|
|
const now = new Date()
|
const diff = now.getTime() - d.getTime()
|
|
const minute = 60 * 1000
|
const hour = 60 * minute
|
const day = 24 * hour
|
const week = 7 * day
|
const month = 30 * day
|
const year = 365 * day
|
|
if (diff < minute) {
|
return '刚刚'
|
} else if (diff < hour) {
|
return Math.floor(diff / minute) + '分钟前'
|
} else if (diff < day) {
|
return Math.floor(diff / hour) + '小时前'
|
} else if (diff < week) {
|
return Math.floor(diff / day) + '天前'
|
} else if (diff < month) {
|
return Math.floor(diff / week) + '周前'
|
} else if (diff < year) {
|
return Math.floor(diff / month) + '个月前'
|
} else {
|
return Math.floor(diff / year) + '年前'
|
}
|
}
|
|
/**
|
* 防抖函数
|
* @param {function} func 要防抖的函数
|
* @param {number} wait 等待时间
|
* @returns {function} 防抖后的函数
|
*/
|
function debounce(func, wait = 300) {
|
let timeout
|
return function executedFunction(...args) {
|
const later = () => {
|
clearTimeout(timeout)
|
func.apply(this, args)
|
}
|
clearTimeout(timeout)
|
timeout = setTimeout(later, wait)
|
}
|
}
|
|
/**
|
* 节流函数
|
* @param {function} func 要节流的函数
|
* @param {number} limit 时间间隔
|
* @returns {function} 节流后的函数
|
*/
|
function throttle(func, limit = 300) {
|
let inThrottle
|
return function executedFunction(...args) {
|
if (!inThrottle) {
|
func.apply(this, args)
|
inThrottle = true
|
setTimeout(() => inThrottle = false, limit)
|
}
|
}
|
}
|
|
/**
|
* 深拷贝
|
* @param {any} obj 要拷贝的对象
|
* @returns {any} 拷贝后的对象
|
*/
|
function deepClone(obj) {
|
if (obj === null || typeof obj !== 'object') return obj
|
if (obj instanceof Date) return new Date(obj.getTime())
|
if (obj instanceof Array) return obj.map(item => deepClone(item))
|
if (typeof obj === 'object') {
|
const clonedObj = {}
|
for (const key in obj) {
|
if (obj.hasOwnProperty(key)) {
|
clonedObj[key] = deepClone(obj[key])
|
}
|
}
|
return clonedObj
|
}
|
}
|
|
/**
|
* 生成随机字符串
|
* @param {number} length 字符串长度
|
* @returns {string} 随机字符串
|
*/
|
function randomString(length = 8) {
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
let result = ''
|
for (let i = 0; i < length; i++) {
|
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
}
|
return result
|
}
|
|
/**
|
* 验证手机号
|
* @param {string} phone 手机号
|
* @returns {boolean} 是否有效
|
*/
|
function validatePhone(phone) {
|
const phoneRegex = /^1[3-9]\d{9}$/
|
return phoneRegex.test(phone)
|
}
|
|
/**
|
* 验证邮箱
|
* @param {string} email 邮箱
|
* @returns {boolean} 是否有效
|
*/
|
function validateEmail(email) {
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
return emailRegex.test(email)
|
}
|
|
/**
|
* 验证身份证号
|
* @param {string} idCard 身份证号
|
* @returns {boolean} 是否有效
|
*/
|
function validateIdCard(idCard) {
|
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
|
return idCardRegex.test(idCard)
|
}
|
|
/**
|
* 格式化文件大小
|
* @param {number} bytes 字节数
|
* @returns {string} 格式化后的文件大小
|
*/
|
function formatFileSize(bytes) {
|
if (bytes === 0) return '0 B'
|
|
const k = 1024
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
}
|
|
/**
|
* 获取文件扩展名
|
* @param {string} filename 文件名
|
* @returns {string} 扩展名
|
*/
|
function getFileExtension(filename) {
|
return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2)
|
}
|
|
/**
|
* 判断是否为图片文件
|
* @param {string} filename 文件名
|
* @returns {boolean} 是否为图片
|
*/
|
function isImageFile(filename) {
|
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg']
|
const extension = getFileExtension(filename).toLowerCase()
|
return imageExtensions.includes(extension)
|
}
|
|
/**
|
* 判断是否为视频文件
|
* @param {string} filename 文件名
|
* @returns {boolean} 是否为视频
|
*/
|
function isVideoFile(filename) {
|
const videoExtensions = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv']
|
const extension = getFileExtension(filename).toLowerCase()
|
return videoExtensions.includes(extension)
|
}
|
|
/**
|
* 显示加载提示
|
* @param {string} title 提示文字
|
* @param {boolean} mask 是否显示透明蒙层
|
*/
|
function showLoading(title = '加载中...', mask = true) {
|
wx.showLoading({
|
title: title,
|
mask: mask
|
})
|
}
|
|
/**
|
* 隐藏加载提示
|
*/
|
function hideLoading() {
|
wx.hideLoading()
|
}
|
|
/**
|
* 显示成功提示
|
* @param {string} title 提示文字
|
* @param {number} duration 显示时长
|
*/
|
function showSuccess(title, duration = 2000) {
|
wx.showToast({
|
title: title,
|
icon: 'success',
|
duration: duration
|
})
|
}
|
|
/**
|
* 显示错误提示
|
* @param {string} title 提示文字
|
* @param {number} duration 显示时长
|
*/
|
function showError(title, duration = 2000) {
|
wx.showToast({
|
title: title,
|
icon: 'error',
|
duration: duration
|
})
|
}
|
|
/**
|
* 显示普通提示
|
* @param {string} title 提示文字
|
* @param {number} duration 显示时长
|
*/
|
function showToast(title, duration = 2000) {
|
wx.showToast({
|
title: title,
|
icon: 'none',
|
duration: duration
|
})
|
}
|
|
/**
|
* 确认对话框
|
* @param {string} content 内容
|
* @param {string} title 标题
|
* @returns {Promise<boolean>} 用户选择结果
|
*/
|
function confirm(content, title = '提示') {
|
return new Promise((resolve) => {
|
wx.showModal({
|
title: title,
|
content: content,
|
success: (res) => {
|
resolve(res.confirm)
|
}
|
})
|
})
|
}
|
|
/**
|
* 页面跳转
|
* @param {string} url 页面路径
|
* @param {object} params 参数
|
*/
|
function navigateTo(url, params = {}) {
|
let fullUrl = url
|
|
if (Object.keys(params).length > 0) {
|
const queryString = Object.keys(params)
|
.map(key => `${key}=${encodeURIComponent(params[key])}`)
|
.join('&')
|
fullUrl += (url.includes('?') ? '&' : '?') + queryString
|
}
|
|
wx.navigateTo({
|
url: fullUrl
|
})
|
}
|
|
/**
|
* 页面重定向
|
* @param {string} url 页面路径
|
* @param {object} params 参数
|
*/
|
function redirectTo(url, params = {}) {
|
let fullUrl = url
|
|
if (Object.keys(params).length > 0) {
|
const queryString = Object.keys(params)
|
.map(key => `${key}=${encodeURIComponent(params[key])}`)
|
.join('&')
|
fullUrl += (url.includes('?') ? '&' : '?') + queryString
|
}
|
|
wx.redirectTo({
|
url: fullUrl
|
})
|
}
|
|
/**
|
* 返回上一页
|
* @param {number} delta 返回的页面数
|
*/
|
function navigateBack(delta = 1) {
|
wx.navigateBack({
|
delta: delta
|
})
|
}
|
|
/**
|
* GraphQL请求封装
|
* @param {string} query GraphQL查询语句
|
* @param {object} variables 查询变量
|
* @returns {Promise} 请求结果
|
*/
|
function graphqlRequest(query, variables = {}) {
|
const app = getApp()
|
return app.graphqlRequest(query, variables)
|
}
|
|
module.exports = {
|
formatDate,
|
formatRelativeTime,
|
debounce,
|
throttle,
|
deepClone,
|
randomString,
|
validatePhone,
|
validateEmail,
|
validateIdCard,
|
formatFileSize,
|
getFileExtension,
|
isImageFile,
|
isVideoFile,
|
showLoading,
|
hideLoading,
|
showSuccess,
|
showError,
|
showToast,
|
confirm,
|
navigateTo,
|
redirectTo,
|
navigateBack,
|
graphqlRequest
|
}
|