This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
+262
View File
@@ -0,0 +1,262 @@
/**
* 通用工具函数
*/
/**
* 格式化时间
* @param {Date|string|number} timestamp 时间戳或日期字符串
* @param {string} format 格式,默认:yyyy-MM-dd HH:mm:ss
* @returns {string} 格式化后的时间字符串
*/
export const formatTime = (timestamp, format = 'yyyy-MM-dd HH:mm:ss') => {
if (!timestamp) return ''
const date = new Date(timestamp)
if (isNaN(date.getTime())) return ''
const map = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'H+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
'S': date.getMilliseconds()
}
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
}
for (const k in map) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1,
RegExp.$1.length === 1 ? map[k] : ('00' + map[k]).substr(('' + map[k]).length))
}
}
return format
}
/**
* 格式化相对时间(如:3分钟前)
* @param {Date|string|number} timestamp 时间戳或日期字符串
* @returns {string} 相对时间字符串
*/
export const formatRelativeTime = (timestamp) => {
if (!timestamp) return ''
const date = new Date(timestamp)
if (isNaN(date.getTime())) return ''
const now = new Date()
const diff = now.getTime() - date.getTime()
const minute = 60 * 1000
const hour = 60 * minute
const day = 24 * hour
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 < month) {
return `${Math.floor(diff / day)}天前`
} else if (diff < year) {
return `${Math.floor(diff / month)}个月前`
} else {
return `${Math.floor(diff / year)}年前`
}
}
/**
* 防抖函数
* @param {Function} func 要执行的函数
* @param {number} wait 等待时间(毫秒)
* @param {boolean} immediate 是否立即执行
* @returns {Function} 防抖后的函数
*/
export const debounce = (func, wait = 300, immediate = false) => {
let timeout
return function executedFunction(...args) {
const context = this
const later = () => {
timeout = null
if (!immediate) func.apply(context, args)
}
const callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) func.apply(context, args)
}
}
/**
* 节流函数
* @param {Function} func 要执行的函数
* @param {number} limit 限制时间(毫秒)
* @returns {Function} 节流后的函数
*/
export const throttle = (func, limit = 300) => {
let inThrottle
let lastFunc
let lastRan
return function executedFunction(...args) {
const context = this
if (!inThrottle) {
func.apply(context, args)
lastRan = Date.now()
inThrottle = true
} else {
clearTimeout(lastFunc)
lastFunc = setTimeout(() => {
if (Date.now() - lastRan >= limit) {
func.apply(context, args)
lastRan = Date.now()
}
}, limit - (Date.now() - lastRan))
}
}
}
/**
* 深度克隆对象
* @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 RegExp) return new RegExp(obj)
if (Array.isArray(obj)) {
const arrCopy = []
for (let i = 0; i < obj.length; i++) {
arrCopy[i] = deepClone(obj[i])
}
return arrCopy
}
if (typeof obj === 'object') {
const objCopy = {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = deepClone(obj[key])
}
}
return objCopy
}
return obj
}
/**
* 生成随机ID
* @param {number} length ID长度
* @returns {string} 随机ID
*/
export const generateId = (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
}
/**
* 检查是否为空(null、undefined、空字符串、空数组、空对象)
* @param {any} value 要检查的值
* @returns {boolean} 是否为空
*/
export const isEmpty = (value) => {
if (value === null || value === undefined) return true
if (typeof value === 'string' && value.trim() === '') return true
if (Array.isArray(value) && value.length === 0) return true
if (typeof value === 'object' && Object.keys(value).length === 0) return true
return false
}
/**
* 安全获取嵌套对象属性
* @param {Object} obj 对象
* @param {string} path 属性路径,如:'a.b.c'
* @param {any} defaultValue 默认值
* @returns {any} 属性值或默认值
*/
export const getSafe = (obj, path, defaultValue = null) => {
if (!obj || typeof obj !== 'object') return defaultValue
const keys = path.split('.')
let result = obj
for (const key of keys) {
if (result === null || result === undefined || typeof result !== 'object') {
return defaultValue
}
result = result[key]
}
return result === undefined ? defaultValue : result
}
/**
* 格式化文件大小
* @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 {string} text 要复制的文本
* @returns {Promise} 复制结果
*/
export const copyToClipboard = (text) => {
return new Promise((resolve, reject) => {
if (uni.setClipboardData) {
uni.setClipboardData({
data: text,
success: () => resolve(true),
fail: (err) => reject(err)
})
} else {
// 兼容H5环境
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
try {
const successful = document.execCommand('copy')
document.body.removeChild(textarea)
successful ? resolve(true) : reject(new Error('复制失败'))
} catch (err) {
document.body.removeChild(textarea)
reject(err)
}
}
})
}
+194
View File
@@ -0,0 +1,194 @@
/**
* 统一请求工具
* 封装uni.request,提供统一的错误处理和拦截器
*/
import { TIMEOUT, HEADERS } from '../config/api.config.js'
// 请求拦截器
const requestInterceptor = (config) => {
// 可以在这里添加token等认证信息
const token = uni.getStorageSync('token')
if (token) {
config.header = {
...config.header,
'Authorization': `Bearer ${token}`
}
}
// 显示加载提示
if (config.showLoading !== false) {
uni.showLoading({
title: config.loadingText || '加载中...',
mask: true
})
}
return config
}
// 响应拦截器
const responseInterceptor = (response, config) => {
// 隐藏加载提示
if (config.showLoading !== false) {
uni.hideLoading()
}
const { statusCode, data } = response
if (statusCode === 200) {
// 根据wanandroid API的返回格式处理
if (data.errorCode === 0) {
return Promise.resolve(data.data || data)
} else {
// API业务错误
uni.showToast({
title: data.errorMsg || '请求失败',
icon: 'none',
duration: 2000
})
return Promise.reject(new Error(data.errorMsg || '请求失败'))
}
} else {
// HTTP错误
uni.showToast({
title: `请求失败: ${statusCode}`,
icon: 'none',
duration: 2000
})
return Promise.reject(new Error(`HTTP错误: ${statusCode}`))
}
}
// 错误拦截器
const errorInterceptor = (error, config) => {
// 隐藏加载提示
if (config.showLoading !== false) {
uni.hideLoading()
}
console.error('请求错误:', error)
let errorMessage = '网络请求失败'
if (error.errMsg) {
if (error.errMsg.includes('timeout')) {
errorMessage = '请求超时,请检查网络'
} else if (error.errMsg.includes('fail')) {
errorMessage = '网络连接失败'
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 2000
})
return Promise.reject(error)
}
/**
* 统一请求方法
* @param {Object} config 请求配置
* @returns {Promise} 请求结果
*/
export const request = (config) => {
const defaultConfig = {
url: '',
method: 'GET',
data: {},
header: HEADERS,
timeout: TIMEOUT,
showLoading: true,
loadingText: '加载中...'
}
const mergedConfig = { ...defaultConfig, ...config }
// 执行请求拦截器
const finalConfig = requestInterceptor(mergedConfig)
return new Promise((resolve, reject) => {
uni.request({
url: finalConfig.url,
method: finalConfig.method,
data: finalConfig.data,
header: finalConfig.header,
timeout: finalConfig.timeout,
success: (response) => {
responseInterceptor(response, finalConfig)
.then(resolve)
.catch(reject)
},
fail: (error) => {
errorInterceptor(error, finalConfig)
.then(reject)
.catch(reject)
}
})
})
}
/**
* GET请求
* @param {string} url 请求地址
* @param {Object} params 请求参数
* @param {Object} config 其他配置
* @returns {Promise} 请求结果
*/
export const get = (url, params = {}, config = {}) => {
return request({
url,
method: 'GET',
data: params,
...config
})
}
/**
* POST请求
* @param {string} url 请求地址
* @param {Object} data 请求数据
* @param {Object} config 其他配置
* @returns {Promise} 请求结果
*/
export const post = (url, data = {}, config = {}) => {
return request({
url,
method: 'POST',
data,
...config
})
}
/**
* PUT请求
* @param {string} url 请求地址
* @param {Object} data 请求数据
* @param {Object} config 其他配置
* @returns {Promise} 请求结果
*/
export const put = (url, data = {}, config = {}) => {
return request({
url,
method: 'PUT',
data,
...config
})
}
/**
* DELETE请求
* @param {string} url 请求地址
* @param {Object} params 请求参数
* @param {Object} config 其他配置
* @returns {Promise} 请求结果
*/
export const del = (url, params = {}, config = {}) => {
return request({
url,
method: 'DELETE',
data: params,
...config
})
}