feat: WC2026 赛事 seed、生产上线初始化脚本与目录归档

重构 seed 为 WC2026 72 场小组赛与 48 强优胜盘;新增 production 模式仅保留 admin 与赛事示例;提供 prod-init-db 全量重置脚本;管理端 i18n 分包与赛事归档能力。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-12 18:17:00 +08:00
parent 8f14e85ebd
commit e7e938f261
94 changed files with 12332 additions and 976 deletions

View File

@@ -59,6 +59,33 @@ function selectMethod(m: PaymentMethod) {
selectedMethod.value = m;
}
const MAX_ORIGINAL_BYTES = 10 * 1024 * 1024;
const MAX_SCREENSHOT_BYTES = 1024 * 1024;
async function compressScreenshot(file: File): Promise<File> {
const baseOptions = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true,
maxIteration: 15,
} as const;
const attempts = [
{ ...baseOptions, initialQuality: 0.85 },
{ ...baseOptions, initialQuality: 0.65, maxWidthOrHeight: 1600 },
{ ...baseOptions, initialQuality: 0.5, maxWidthOrHeight: 1280 },
];
for (const options of attempts) {
const compressed = (await imageCompression(file, options)) as File;
if (compressed.size <= MAX_SCREENSHOT_BYTES) {
return compressed;
}
}
throw new Error('COMPRESS_TOO_LARGE');
}
async function handleFileChange(event: Event) {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
@@ -70,27 +97,22 @@ async function handleFileChange(event: Event) {
return;
}
// Max 10MB before compression
if (file.size > 10 * 1024 * 1024) {
if (file.size > MAX_ORIGINAL_BYTES) {
alert(t('recharge.file_too_large'));
input.value = '';
return;
}
// Compress image
compressing.value = true;
try {
const compressed = await imageCompression(file, {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true,
});
screenshotFile.value = compressed as File;
const compressed = await compressScreenshot(file);
screenshotFile.value = compressed;
screenshotPreview.value = URL.createObjectURL(compressed);
} catch {
// Fallback: use original if compression fails
screenshotFile.value = file;
screenshotPreview.value = URL.createObjectURL(file);
alert(t('recharge.compress_failed'));
screenshotFile.value = null;
screenshotPreview.value = '';
input.value = '';
} finally {
compressing.value = false;
}
@@ -115,6 +137,10 @@ async function handleSubmit() {
alert(t('recharge.upload_screenshot'));
return;
}
if (screenshotFile.value.size > MAX_SCREENSHOT_BYTES) {
alert(t('recharge.compress_failed'));
return;
}
submitting.value = true;
try {