feat: 更新应用版本和集成腾讯云 COS 上传功能

- 将应用版本更新至 1.0.2,修改相关配置文件
- 集成腾讯云 COS 上传功能,新增相关服务和钩子
- 更新 AI 体态评估页面,支持照片上传和评估结果展示
- 添加雷达图组件以展示评估结果
- 更新样式以适应新功能的展示和交互
- 修改登录页面背景效果,提升用户体验
This commit is contained in:
richarjiang
2025-08-13 15:21:54 +08:00
parent 5814044cee
commit 321947db98
20 changed files with 1664 additions and 342 deletions

59
hooks/useCosUpload.ts Normal file
View File

@@ -0,0 +1,59 @@
import { buildCosKey, buildPublicUrl } from '@/constants/Cos';
import { uploadWithRetry } from '@/services/cos';
import { useCallback, useMemo, useRef, useState } from 'react';
export type UseCosUploadOptions = {
prefix?: string;
userId?: string;
contentType?: string;
};
export function useCosUpload(defaultOptions?: UseCosUploadOptions) {
const abortRef = useRef<AbortController | null>(null);
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
const cancel = useCallback(() => {
abortRef.current?.abort();
}, []);
const upload = useCallback(
async (file: { uri?: string; name?: string; type?: string; buffer?: any; blob?: Blob } | Blob | any, options?: UseCosUploadOptions) => {
const finalOptions = { ...(defaultOptions || {}), ...(options || {}) };
const extGuess = (() => {
const name = (file && (file.name || (file as any).filename)) || '';
const match = name.match(/\.([a-zA-Z0-9]+)$/);
return match ? match[1] : undefined;
})();
const key = buildCosKey({ prefix: finalOptions.prefix, userId: finalOptions.userId, ext: extGuess });
const controller = new AbortController();
abortRef.current = controller;
setProgress(0);
setUploading(true);
try {
let body = (file as any)?.blob || (file as any)?.buffer || file;
// Expo ImagePicker 返回 { uri } 时,转换为 Blob
if (!body && (file as any)?.uri) {
const resp = await fetch((file as any).uri);
body = await resp.blob();
}
const res = await uploadWithRetry({
key,
body,
contentType: finalOptions.contentType || (file as any)?.type,
signal: controller.signal,
onProgress: ({ percent }) => setProgress(percent),
});
const url = buildPublicUrl(res.key);
return { key: res.key, url };
} finally {
setUploading(false);
}
},
[defaultOptions]
);
return useMemo(() => ({ upload, cancel, progress, uploading }), [upload, cancel, progress, uploading]);
}