Files
richarjiang 1b76cc305a feat(ui): 实现原生标签页与玻璃效果按钮组件
引入 NativeTabs 替代默认 Tabs 以支持原生标签栏样式,并添加 GlassButton 组件实现毛玻璃效果按钮。
移除对 useBottomTabBarHeight 的依赖,统一使用固定底部间距 60。
重构头像上传逻辑,使用新的 uploadImage API 替代 COS 直传方案。
更新 expo-router 至 ~6.0.1 版本以支持不稳定特性。
2025-09-12 15:48:58 +08:00

85 lines
2.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { uploadImage } from '@/services/users';
type UploadOptions = {
key: string;
// React Native COS SDK 推荐使用本地文件路径file:// 或 content://
srcUri?: string;
// 为兼容旧实现Web/Blob。在 RN SDK 下会忽略 body
body?: any;
contentType?: string;
onProgress?: (progress: { percent: number }) => void;
signal?: AbortSignal;
};
export async function uploadToCos(options: UploadOptions): Promise<{ key: string; etag?: string; headers?: Record<string, string>; publicUrl?: string }> {
const { key, srcUri, contentType, signal } = options;
if (!srcUri || typeof srcUri !== 'string') {
throw new Error('请提供本地文件路径 srcUri形如 file:/// 或 content://');
}
if (signal?.aborted) {
throw new DOMException('Aborted', 'AbortError');
}
try {
// 创建 FormData 用于文件上传
const formData = new FormData();
// 从 srcUri 创建文件对象
let fileBlob;
if (srcUri.startsWith('file://') || srcUri.startsWith('content://')) {
// React Native 环境,使用 fetch 读取本地文件
const response = await fetch(srcUri);
fileBlob = await response.blob();
} else {
throw new Error('不支持的文件路径格式,请使用 file:// 或 content:// 格式');
}
formData.append('file', {
uri: srcUri,
type: "image/jpeg",
name: "upload.jpg",
} as any);
console.log('formData', formData)
// 使用新的上传接口
const result = await uploadImage(formData);
console.log('result', result);
return {
key: result.fileKey,
publicUrl: result.fileUrl,
};
} catch (error: any) {
if (signal?.aborted) {
throw new DOMException('Aborted', 'AbortError');
}
console.log('uploadToCos error:', error);
throw error;
}
}
export async function uploadWithRetry(options: UploadOptions & { maxRetries?: number; backoffMs?: number }): Promise<{ key: string; etag?: string }> {
const { maxRetries = 2, backoffMs = 800, ...rest } = options;
let attempt = 0;
// 简单指数退避
while (true) {
try {
return await uploadToCos(rest);
} catch (e: any) {
if (rest.signal?.aborted) throw e;
if (attempt >= maxRetries) throw e;
const wait = backoffMs * Math.pow(2, attempt);
await new Promise(r => setTimeout(r, wait));
attempt++;
}
}
}