feat: 添加banner页面 添加菜单

This commit is contained in:
ZhuRui
2026-07-25 09:39:56 +08:00
parent 03f1b6c220
commit 3b4dd72b11
25 changed files with 2561 additions and 157 deletions
+121
View File
@@ -0,0 +1,121 @@
import type { OssSignData } from '@/api/upload';
import { fetchOssSign } from '@/api/upload';
/**
* OSS 上传类型
* 1=证件照 2=赛事封面 3=头像 4=富文本 5=Banner
*/
export const OssUploadType = {
IdCard: '1',
EventCover: '2',
Avatar: '3',
RichText: '4',
Banner: '5',
} as const;
export type OssUploadTypeValue = (typeof OssUploadType)[keyof typeof OssUploadType];
/**
* 生成唯一文件名:16 位大小写字母+数字随机串.ext
*/
export function genFileName(filePath: string, fileName?: string): string {
let suffix = '';
if (fileName) {
const dot = fileName.lastIndexOf('.');
suffix = dot >= 0 ? fileName.slice(dot).toLowerCase() : '';
}
if (!suffix && filePath) {
const dot = filePath.lastIndexOf('.');
suffix = dot >= 0 ? filePath.slice(dot).toLowerCase() : '';
}
if (!suffix) suffix = '.jpg';
const random = Array.from({ length: 16 }, () => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return chars.charAt(Math.floor(Math.random() * chars.length));
}).join('');
return `${random}${suffix}`;
}
/** 根据签名和文件名构建 OSS 上传 FormData 字段 */
export function buildOssFormData(sign: OssSignData, fileName: string): Record<string, string> {
return {
key: `${sign.dir}${fileName}`,
policy: sign.policy,
'x-oss-signature': sign.signature,
'x-oss-signature-version': 'OSS4-HMAC-SHA256',
'x-oss-credential': sign.x_oss_credential,
'x-oss-date': sign.x_oss_date,
'x-oss-security-token': sign.security_token,
success_action_status: '200',
};
}
/**
* 使用已有签名上传单个文件到 OSSWeb 版,fetch + FormData
*/
export async function uploadFileWithSign(file: File, sign: OssSignData): Promise<string> {
const fileName = genFileName(file.name, file.name);
const key = `${sign.dir}${fileName}`;
const formData = new FormData();
Object.entries(buildOssFormData(sign, fileName)).forEach(([k, v]) => {
formData.append(k, v);
});
formData.append('file', file);
const res = await fetch(sign.host, {
method: 'POST',
body: formData,
});
if (!res.ok) {
throw new Error(`OSS 上传失败:${res.status} ${res.statusText}`);
}
return `${sign.host}/${key}`;
}
/**
* 上传单个文件到 OSS
* @returns 文件访问 URL
*/
export async function uploadFile(file: File, type: OssUploadTypeValue): Promise<{ url: string }> {
const sign = await fetchOssSign(type);
const url = await uploadFileWithSign(file, sign);
return { url };
}
/**
* 逐张上传文件到 OSS(同一批复用同一签名,单张失败不影响后续)
*/
export async function uploadFilesOneByOne(
files: File[],
type: OssUploadTypeValue,
options?: {
onSuccess?: (url: string, index: number) => void | Promise<void>;
onError?: (error: unknown, index: number) => void;
},
): Promise<{ successCount: number; failCount: number }> {
if (files.length === 0) {
return { successCount: 0, failCount: 0 };
}
const sign = await fetchOssSign(type);
let successCount = 0;
let failCount = 0;
for (let i = 0; i < files.length; i++) {
try {
const url = await uploadFileWithSign(files[i], sign);
successCount++;
await options?.onSuccess?.(url, i);
} catch (err) {
failCount++;
options?.onError?.(err, i);
}
}
return { successCount, failCount };
}