Files
base-vite/src/api/utils/index.ts
2025-07-09 20:36:32 +08:00

184 lines
4.1 KiB
TypeScript

import { ApiError } from '../types'
import { API_CONFIG } from '../config'
/**
* 延迟函数
*/
export const delay = (ms: number): Promise<void> => {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* 生成唯一ID
*/
export const generateId = (): string => {
return Date.now().toString(36) + Math.random().toString(36).substr(2)
}
/**
* 格式化文件大小
*/
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 Bytes'
const k = 1024
const sizes = ['Bytes', '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]
}
/**
* 获取文件扩展名
*/
export const getFileExtension = (filename: string): string => {
return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2)
}
/**
* 验证文件类型
*/
export const validateFileType = (file: File, allowedTypes: string[]): boolean => {
const fileType = file.type
const fileExtension = getFileExtension(file.name).toLowerCase()
return allowedTypes.some(type => {
if (type.includes('/')) {
return fileType === type
}
return fileExtension === type.toLowerCase()
})
}
/**
* 创建API错误对象
*/
export const createApiError = (
code: number,
message: string,
details?: any
): ApiError => {
return {
code,
message,
details
}
}
/**
* 判断是否为API错误
*/
export const isApiError = (error: any): error is ApiError => {
return error && typeof error.code === 'number' && typeof error.message === 'string'
}
/**
* 重试函数
*/
export const retry = async <T>(
fn: () => Promise<T>,
retries: number = API_CONFIG.RETRY_COUNT,
delayMs: number = API_CONFIG.RETRY_DELAY
): Promise<T> => {
try {
return await fn()
} catch (error) {
if (retries > 0) {
await delay(delayMs)
return retry(fn, retries - 1, delayMs * 2) // 指数退避
}
throw error
}
}
/**
* 下载文件
*/
export const downloadFile = (blob: Blob, filename: string): void => {
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
}
/**
* 读取文件为Base64
*/
export const readFileAsBase64 = (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {
const result = reader.result as string
resolve(result.split(',')[1]) // 移除data:xxx;base64,前缀
}
reader.onerror = reject
reader.readAsDataURL(file)
})
}
/**
* 读取文件为ArrayBuffer
*/
export const readFileAsArrayBuffer = (file: File): Promise<ArrayBuffer> => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result as ArrayBuffer)
reader.onerror = reject
reader.readAsArrayBuffer(file)
})
}
/**
* 压缩图片
*/
export const compressImage = (
file: File,
quality: number = 0.8,
maxWidth: number = 1920,
maxHeight: number = 1080
): Promise<Blob> => {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = () => {
// 计算压缩后的尺寸
let { width, height } = img
if (width > maxWidth) {
height = (height * maxWidth) / width
width = maxWidth
}
if (height > maxHeight) {
width = (width * maxHeight) / height
height = maxHeight
}
canvas.width = width
canvas.height = height
// 绘制并压缩
ctx?.drawImage(img, 0, 0, width, height)
canvas.toBlob(
(blob) => {
if (blob) {
resolve(blob)
} else {
reject(new Error('图片压缩失败'))
}
},
file.type,
quality
)
}
img.onerror = reject
img.src = URL.createObjectURL(file)
})
}