Separate dataset upload queues
This commit is contained in:
@@ -637,7 +637,7 @@ function App() {
|
||||
const [selectedGpuIds, setSelectedGpuIds] = useState<number[]>([]);
|
||||
const [selectedCondaEnv, setSelectedCondaEnv] = useState("auto");
|
||||
const [uploadKind, setUploadKind] = useState<"images" | "masks">("images");
|
||||
const [uploadFiles, setUploadFiles] = useState<FileList | null>(null);
|
||||
const [uploadFilesByKind, setUploadFilesByKind] = useState<Record<"images" | "masks", FileList | null>>({ images: null, masks: null });
|
||||
const [agentValidation, setAgentValidation] = useState<ValidationAgentPayload | null>(null);
|
||||
const [weightVerification, setWeightVerification] = useState<WeightVerifyPayload | null>(null);
|
||||
const [agentBusy, setAgentBusy] = useState(false);
|
||||
@@ -761,6 +761,7 @@ function App() {
|
||||
const uploadAccept = uploadKind === "images"
|
||||
? "image/*,video/*,.zip,.tar,.tar.gz,.tgz"
|
||||
: "image/*,.txt,.json,.yaml,.yml,.zip,.tar,.tar.gz,.tgz";
|
||||
const activeUploadFiles = uploadFilesByKind[uploadKind];
|
||||
const inferenceOutputs = useMemo(() => {
|
||||
const predictions = results.filter((item) => item.role === "segmentation" && item.previewable).slice(0, 8);
|
||||
const heatmaps = results.filter((item) => item.role === "heatmap" && item.previewable).slice(0, 8);
|
||||
@@ -979,12 +980,13 @@ function App() {
|
||||
}
|
||||
|
||||
async function uploadDatasetFiles() {
|
||||
if (!uploadFiles || uploadFiles.length === 0) return;
|
||||
const files = uploadFilesByKind[uploadKind];
|
||||
if (!files || files.length === 0) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
setActionError("");
|
||||
const body = new FormData();
|
||||
Array.from(uploadFiles).forEach((file) => body.append("files", file));
|
||||
Array.from(files).forEach((file) => body.append("files", file));
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetch(`${API_BASE}/api/datasets/${encodeURIComponent(datasetName)}/upload/${uploadKind}`, {
|
||||
@@ -997,7 +999,7 @@ function App() {
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
setSelectedDatasetName(datasetName);
|
||||
setUploadModalOpen(false);
|
||||
setUploadFiles(null);
|
||||
setUploadFilesByKind((current) => ({ ...current, [uploadKind]: null }));
|
||||
await refresh();
|
||||
} catch (err) {
|
||||
setActionError(String(err));
|
||||
@@ -1528,7 +1530,7 @@ function App() {
|
||||
<span>说明</span>
|
||||
<input value={datasetDescription} onChange={(event) => setDatasetDescription(event.target.value)} />
|
||||
</label>
|
||||
{datasetNameExists && <small className="formWarn">已存在同名数据集,不能重复创建,但可以继续上传追加文件。</small>}
|
||||
{datasetNameExists && <small className="formWarn">将追加到现有数据集的 {uploadKind},不会新建同名数据集。</small>}
|
||||
<div className="segmented">
|
||||
{(["images", "masks"] as const).map((kind) => (
|
||||
<button key={kind} type="button" className={uploadKind === kind ? "active" : ""} onClick={() => setUploadKind(kind)}>
|
||||
@@ -1536,15 +1538,25 @@ function App() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="uploadKindCounts">
|
||||
<span className={uploadKind === "images" ? "active" : ""}>images 已选择 {uploadFilesByKind.images?.length ?? 0}</span>
|
||||
<span className={uploadKind === "masks" ? "active" : ""}>masks 已选择 {uploadFilesByKind.masks?.length ?? 0}</span>
|
||||
</div>
|
||||
<label className="drop">
|
||||
{uploadFiles?.length ? <PackageOpen size={24} /> : <UploadCloud size={24} />}
|
||||
<span>{uploadFiles?.length ? `${uploadFiles.length} 个文件待上传` : `选择 ${uploadKind} 图片、视频或压缩包`}</span>
|
||||
{activeUploadFiles?.length ? <PackageOpen size={24} /> : <UploadCloud size={24} />}
|
||||
<span>{activeUploadFiles?.length ? `${activeUploadFiles.length} 个 ${uploadKind} 文件待上传` : `选择 ${uploadKind} 图片、视频或压缩包`}</span>
|
||||
<small>.png/.jpg/.tif/.mp4/.zip/.tar.gz 均可上传</small>
|
||||
<input multiple type="file" accept={uploadAccept} onChange={(event) => setUploadFiles(event.target.files)} />
|
||||
<input
|
||||
key={uploadKind}
|
||||
multiple
|
||||
type="file"
|
||||
accept={uploadAccept}
|
||||
onChange={(event) => setUploadFilesByKind((current) => ({ ...current, [uploadKind]: event.target.files }))}
|
||||
/>
|
||||
</label>
|
||||
<div className="modalActions">
|
||||
<button className="primary" disabled={busy || datasetNameExists} onClick={createDataset}><Boxes size={17} />创建数据集</button>
|
||||
<button className="primary secondary" disabled={busy || !uploadFiles?.length} onClick={uploadDatasetFiles}><UploadCloud size={17} />上传文件</button>
|
||||
<button className="primary secondary" disabled={busy || !activeUploadFiles?.length} onClick={uploadDatasetFiles}><UploadCloud size={17} />{datasetNameExists ? "追加文件" : "上传文件"}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -839,6 +839,30 @@ textarea {
|
||||
font-weight: 760;
|
||||
}
|
||||
|
||||
.uploadKindCounts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.uploadKindCounts span {
|
||||
min-width: 0;
|
||||
padding: 7px 9px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
background: rgba(8, 10, 8, 0.72);
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.uploadKindCounts span.active {
|
||||
border-color: rgba(157, 226, 111, 0.58);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.drop {
|
||||
min-height: 128px;
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user