| 1 | /** |
| 2 | * 浏览器插件相关工具函数 |
| 3 | */ |
| 4 | |
| 5 | import type { ProgressEvent } from '@/store' |
| 6 | import { PluginStatus } from '@/store' |
| 7 | import { PLUGIN_STATUS_I18N_KEY, PUBLISH_STAGE_I18N_KEY } from './constants' |
| 8 | |
| 9 | /** |
| 10 | * 获取插件状态的国际化 key |
| 11 | * @param status 插件状态 |
| 12 | * @returns i18n key |
| 13 | */ |
| 14 | export function getPluginStatusI18nKey(status: PluginStatus): string { |
| 15 | return PLUGIN_STATUS_I18N_KEY[status] || 'plugin:status.unknown' |
| 16 | } |
| 17 | |
| 18 | /** |
| 19 | * 获取发布阶段的国际化 key |
| 20 | * @param stage 发布阶段 |
| 21 | * @returns i18n key |
| 22 | */ |
| 23 | export function getPublishStageI18nKey(stage: ProgressEvent['stage']): string { |
| 24 | return PUBLISH_STAGE_I18N_KEY[stage] || stage |
| 25 | } |
| 26 | |
| 27 | /** |
| 28 | * 判断插件是否已就绪(已安装且已授权) |
| 29 | * @param status 插件状态 |
| 30 | * @returns 是否已就绪 |
| 31 | */ |
| 32 | export function isPluginReady(status: PluginStatus): boolean { |
| 33 | return status === PluginStatus.READY |
| 34 | } |
| 35 | |
| 36 | /** |
| 37 | * 判断插件是否已连接(兼容旧代码,等同于 isPluginReady) |
| 38 | * @param status 插件状态 |
| 39 | * @returns 是否已连接 |
| 40 | */ |
| 41 | export function isPluginConnected(status: PluginStatus): boolean { |
| 42 | return status === PluginStatus.READY |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * 判断插件是否已安装但未授权 |
| 47 | * @param status 插件状态 |
| 48 | * @returns 是否已安装未授权 |
| 49 | */ |
| 50 | export function isPluginInstalledNoPermission(status: PluginStatus): boolean { |
| 51 | return status === PluginStatus.INSTALLED_NO_PERMISSION |
| 52 | } |
| 53 | |
| 54 | /** |
| 55 | * 判断插件是否未安装 |
| 56 | * @param status 插件状态 |
| 57 | * @returns 是否未安装 |
| 58 | */ |
| 59 | export function isPluginNotInstalled(status: PluginStatus): boolean { |
| 60 | return status === PluginStatus.NOT_INSTALLED || status === PluginStatus.UNKNOWN |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * 格式化进度百分比 |
| 65 | * @param progress 进度值(0-100) |
| 66 | * @returns 格式化的百分比文本 |
| 67 | */ |
| 68 | export function formatProgress(progress: number): string { |
| 69 | return `${Math.round(progress)}%` |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * 格式化文件大小 |
| 74 | * @param bytes 字节数 |
| 75 | * @returns 格式化的文件大小 |
| 76 | */ |
| 77 | export function formatFileSize(bytes: number): string { |
| 78 | if (bytes === 0) |
| 79 | return '0 B' |
| 80 | |
| 81 | const k = 1024 |
| 82 | const sizes = ['B', 'KB', 'MB', 'GB'] |
| 83 | const i = Math.floor(Math.log(bytes) / Math.log(k)) |
| 84 | |
| 85 | return `${(bytes / k ** i).toFixed(2)} ${sizes[i]}` |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * 验证文件类型 |
| 90 | * @param file 文件对象 |
| 91 | * @param acceptTypes 接受的文件类型数组 |
| 92 | * @returns 是否有效 |
| 93 | */ |
| 94 | export function validateFileType(file: File, acceptTypes: string[]): boolean { |
| 95 | return acceptTypes.some((type) => { |
| 96 | if (type.endsWith('/*')) { |
| 97 | const prefix = type.slice(0, -2) |
| 98 | return file.type.startsWith(prefix) |
| 99 | } |
| 100 | return file.type === type |
| 101 | }) |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * 验证视频文件 |
| 106 | * @param file 文件对象 |
| 107 | * @returns 是否是有效的视频文件 |
| 108 | */ |
| 109 | export function isValidVideoFile(file: File): boolean { |
| 110 | return validateFileType(file, [ |
| 111 | 'video/mp4', |
| 112 | 'video/mpeg', |
| 113 | 'video/quicktime', |
| 114 | 'video/x-msvideo', |
| 115 | 'video/x-ms-wmv', |
| 116 | ]) |
| 117 | } |
| 118 | |
| 119 | /** |
| 120 | * 验证图片文件 |
| 121 | * @param file 文件对象 |
| 122 | * @returns 是否是有效的图片文件 |
| 123 | */ |
| 124 | export function isValidImageFile(file: File): boolean { |
| 125 | return validateFileType(file, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/jpg']) |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * 验证文件大小 |
| 130 | * @param file 文件对象 |
| 131 | * @param maxSize 最大大小(字节) |
| 132 | * @returns 是否在限制内 |
| 133 | */ |
| 134 | export function validateFileSize(file: File, maxSize: number): boolean { |
| 135 | return file.size <= maxSize |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * 创建带重试的异步函数 |
| 140 | * @param fn 异步函数 |
| 141 | * @param maxRetries 最大重试次数 |
| 142 | * @param delay 重试延迟(毫秒) |
| 143 | * @returns 包装后的函数 |
| 144 | */ |
| 145 | export function withRetry<T>(fn: () => Promise<T>, maxRetries = 3, delay = 1000): () => Promise<T> { |
| 146 | return async () => { |
| 147 | let lastError: any |
| 148 | |
| 149 | for (let i = 0; i < maxRetries; i++) { |
| 150 | try { |
| 151 | return await fn() |
| 152 | } |
| 153 | catch (error) { |
| 154 | lastError = error |
| 155 | |
| 156 | if (i < maxRetries - 1) { |
| 157 | await new Promise(resolve => setTimeout(resolve, delay)) |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | throw lastError |
| 163 | } |
| 164 | } |
| 165 |