import { FormEvent, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { createBuild } from "../api/client"; import { getDefaultAppVersion } from "../lib/version"; export default function Upload() { const navigate = useNavigate(); const [appNameZh, setAppNameZh] = useState(""); const [appNameEn, setAppNameEn] = useState(""); const [appVersion, setAppVersion] = useState(() => getDefaultAppVersion()); const [identifier, setIdentifier] = useState(""); const [file, setFile] = useState(null); const [icon, setIcon] = useState(null); const [iconPreview, setIconPreview] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (!icon) { setIconPreview(null); return; } const url = URL.createObjectURL(icon); setIconPreview(url); return () => URL.revokeObjectURL(url); }, [icon]); async function onSubmit(event: FormEvent) { event.preventDefault(); if (!file) { setError("请选择 zip 文件"); return; } setLoading(true); setError(null); try { const formData = new FormData(); formData.append("file", file); formData.append("appNameZh", appNameZh); formData.append("appNameEn", appNameEn); formData.append("appVersion", appVersion.trim()); if (identifier.trim()) { formData.append("identifier", identifier.trim()); } if (icon) { formData.append("icon", icon); } const result = await createBuild(formData); navigate(`/jobs/${result.id}`); } catch (err) { setError(err instanceof Error ? err.message : "上传失败"); } finally { setLoading(false); } } return ( <>

上传静态站点

将包含 index.html 的 zip 打包为 Windows 与 Android 安装包。

使用说明

压缩包需包含 index.html(根目录或单层文件夹内),默认不超过 50MB。 可单独上传应用图标(PNG / JPG / ICO,优先于 zip 内图标),版本号默认取当天日期(如 2026.5.29)。中文名用于展示,英文名用于安装包与 Bundle ID。

构建参数

{iconPreview ? (
图标预览 {icon?.name}
) : null} {error ?

{error}

: null}
); }