first commit

This commit is contained in:
2025-11-24 16:06:44 +08:00
commit e57d497751
165 changed files with 59349 additions and 0 deletions

295
src/utils/index.js Normal file
View File

@@ -0,0 +1,295 @@
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
// 设置dayjs语言
dayjs.locale('zh-cn')
/**
* 日期格式化
* @param {Date|string} date 日期
* @param {string} format 格式
* @returns {string} 格式化后的日期字符串
*/
export const formatDate = (date, format = 'YYYY-MM-DD HH:mm:ss') => {
if (!date) return ''
return dayjs(date).format(format)
}
/**
* 相对时间
* @param {Date|string} date 日期
* @returns {string} 相对时间字符串
*/
export const fromNow = (date) => {
if (!date) return ''
return dayjs(date).fromNow()
}
/**
* 手机号格式化
* @param {string} phone 手机号
* @returns {string} 格式化后的手机号
*/
export const formatPhone = (phone) => {
if (!phone) return ''
return phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3')
}
/**
* 金额格式化
* @param {number} amount 金额
* @param {number} decimals 小数位数
* @returns {string} 格式化后的金额
*/
export const formatMoney = (amount, decimals = 2) => {
if (amount === null || amount === undefined) return '0.00'
return Number(amount).toFixed(decimals)
}
/**
* 文件大小格式化
* @param {number} bytes 字节数
* @returns {string} 格式化后的文件大小
*/
export const 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 {Function} func 要防抖的函数
* @param {number} wait 等待时间
* @returns {Function} 防抖后的函数
*/
export const debounce = (func, wait) => {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
/**
* 节流函数
* @param {Function} func 要节流的函数
* @param {number} limit 限制时间
* @returns {Function} 节流后的函数
*/
export const throttle = (func, limit) => {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
/**
* 深拷贝
* @param {any} obj 要拷贝的对象
* @returns {any} 拷贝后的对象
*/
export const 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 (Object.prototype.hasOwnProperty.call(obj, key)) {
clonedObj[key] = deepClone(obj[key])
}
}
return clonedObj
}
}
/**
* 生成UUID
* @returns {string} UUID字符串
*/
export const generateUUID = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0
const v = c == 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
})
}
/**
* 验证手机号
* @param {string} phone 手机号
* @returns {boolean} 是否有效
*/
export const validatePhone = (phone) => {
const phoneRegex = /^1[3-9]\d{9}$/
return phoneRegex.test(phone)
}
/**
* 验证邮箱
* @param {string} email 邮箱
* @returns {boolean} 是否有效
*/
export const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email)
}
/**
* 验证身份证号
* @param {string} idCard 身份证号
* @returns {boolean} 是否有效
*/
export const validateIdCard = (idCard) => {
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
return idCardRegex.test(idCard)
}
/**
* 获取URL参数
* @param {string} name 参数名
* @returns {string|null} 参数值
*/
export const getUrlParam = (name) => {
const urlParams = new URLSearchParams(window.location.search)
return urlParams.get(name)
}
/**
* 设置URL参数
* @param {string} name 参数名
* @param {string} value 参数值
*/
export const setUrlParam = (name, value) => {
const url = new URL(window.location)
url.searchParams.set(name, value)
window.history.replaceState({}, '', url)
}
/**
* 移除URL参数
* @param {string} name 参数名
*/
export const removeUrlParam = (name) => {
const url = new URL(window.location)
url.searchParams.delete(name)
window.history.replaceState({}, '', url)
}
/**
* 复制文本到剪贴板
* @param {string} text 要复制的文本
* @returns {Promise<boolean>} 是否复制成功
*/
export const copyToClipboard = async (text) => {
try {
await navigator.clipboard.writeText(text)
return true
} catch (err) {
// 降级方案
const textArea = document.createElement('textarea')
textArea.value = text
document.body.appendChild(textArea)
textArea.select()
try {
document.execCommand('copy')
document.body.removeChild(textArea)
return true
} catch (err) {
document.body.removeChild(textArea)
return false
}
}
}
/**
* 下载文件
* @param {string} url 文件URL
* @param {string} filename 文件名
*/
export const downloadFile = (url, filename) => {
const link = document.createElement('a')
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
/**
* 获取浏览器信息
* @returns {object} 浏览器信息
*/
export const getBrowserInfo = () => {
const ua = navigator.userAgent
const browser = {
name: '',
version: '',
os: ''
}
// 检测浏览器
if (ua.includes('Chrome')) {
browser.name = 'Chrome'
} else if (ua.includes('Firefox')) {
browser.name = 'Firefox'
} else if (ua.includes('Safari')) {
browser.name = 'Safari'
} else if (ua.includes('Edge')) {
browser.name = 'Edge'
} else if (ua.includes('MSIE') || ua.includes('Trident')) {
browser.name = 'IE'
}
// 检测操作系统
if (ua.includes('Windows')) {
browser.os = 'Windows'
} else if (ua.includes('Mac')) {
browser.os = 'Mac'
} else if (ua.includes('Linux')) {
browser.os = 'Linux'
} else if (ua.includes('Android')) {
browser.os = 'Android'
} else if (ua.includes('iOS')) {
browser.os = 'iOS'
}
return browser
}
/**
* 检查是否为移动设备
* @returns {boolean} 是否为移动设备
*/
export const isMobile = () => {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
}
/**
* 检查是否为微信浏览器
* @returns {boolean} 是否为微信浏览器
*/
export const isWeChat = () => {
return /MicroMessenger/i.test(navigator.userAgent)
}
/**
* 检查是否为支付宝浏览器
* @returns {boolean} 是否为支付宝浏览器
*/
export const isAlipay = () => {
return /AlipayClient/i.test(navigator.userAgent)
}