diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 14152c0..b8118a8 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -619,6 +619,8 @@ function App() { const [busy, setBusy] = useState(false); const [actionError, setActionError] = useState(""); const [uploadModalOpen, setUploadModalOpen] = useState(false); + const [copyModalOpen, setCopyModalOpen] = useState(false); + const [preprocessModalOpen, setPreprocessModalOpen] = useState(false); const [datasetName, setDatasetName] = useState("demo_dataset"); const [datasetDescription, setDatasetDescription] = useState(""); const [copyDatasetName, setCopyDatasetName] = useState(""); @@ -1004,6 +1006,22 @@ function App() { } } + function selectDataset(dataset: UploadedDataset) { + setSelectedDatasetName(dataset.name); + setDatasetName(dataset.name); + } + + function openCopyDataset(dataset: UploadedDataset) { + selectDataset(dataset); + setCopyDatasetName(`${dataset.name}_copy`); + setCopyModalOpen(true); + } + + function openPreprocessDataset(dataset: UploadedDataset) { + selectDataset(dataset); + setPreprocessModalOpen(true); + } + async function copySelectedDataset() { if (!selectedDataset || !copyDatasetName.trim()) return; if (copyNameExists) { @@ -1019,6 +1037,7 @@ function App() { }); setSelectedDatasetName(copied.name); setDatasetName(copied.name); + setCopyModalOpen(false); await refresh(); } catch (err) { setActionError(String(err)); @@ -1027,15 +1046,16 @@ function App() { } } - async function deleteSelectedDataset() { - if (!selectedDataset) return; - if (!window.confirm(`删除数据集 ${selectedDataset.name}?此操作会删除上传目录。`)) return; + async function deleteDataset(dataset: UploadedDataset) { + if (!window.confirm(`删除数据集 ${dataset.name}?此操作会删除上传目录。`)) return; setBusy(true); try { setActionError(""); - await api(`/api/datasets/${encodeURIComponent(selectedDataset.name)}`, { method: "DELETE" }); - setSelectedDatasetName(""); - setDatasetName("demo_dataset"); + await api(`/api/datasets/${encodeURIComponent(dataset.name)}`, { method: "DELETE" }); + if (selectedDatasetName === dataset.name || datasetName === dataset.name) { + setSelectedDatasetName(""); + setDatasetName("demo_dataset"); + } await refresh(); } catch (err) { setActionError(String(err)); @@ -1054,6 +1074,7 @@ function App() { body: JSON.stringify(jobPayload(next, datasetParamsForTask(next))) }); await inspectJob(job); + setPreprocessModalOpen(false); navigate("training"); await refresh(); } catch (err) { @@ -1430,49 +1451,26 @@ function App() {
{datasets.map((dataset) => ( - - ))} -
- - {copyNameExists && 复制目标名称已存在。} -
- - -
- - -
-
- 图片预处理 - 使用手册 -
-
- - - - - -
-
- {manualDatasetOps.map((task) => ( - + +
+ + + +
+
))}
@@ -1553,6 +1551,67 @@ function App() { )} + {copyModalOpen && selectedDataset && ( +
+
+
+
+

Copy

+

复制数据集

+ {selectedDataset.name} +
+ +
+
+ + {copyNameExists && 复制目标名称已存在。} +
+ +
+
+
+
+ )} + + {preprocessModalOpen && selectedDataset && ( +
+
+
+
+

Preprocess

+

图片预处理

+ {selectedDataset.name} +
+ +
+
+ + + + + +
+
+ {manualDatasetOps.map((task) => ( + + ))} +
+
+
+ )} +
diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 111a697..76e14ef 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -747,14 +747,12 @@ textarea { } .datasetManager, -.preprocessBox, .datasetDetailStack { display: grid; gap: 12px; } -.datasetManager, -.preprocessBox { +.datasetManager { padding-top: 14px; border-top: 1px solid var(--line); } @@ -789,6 +787,21 @@ textarea { box-shadow: 0 28px 80px rgba(0, 0, 0, 0.55); } +.datasetActionModal { + width: min(520px, 100%); + max-height: calc(100vh - 56px); + overflow: auto; + padding: 18px; + border: 1px solid rgba(157, 226, 111, 0.32); + border-radius: 8px; + background: #151815; + box-shadow: 0 28px 80px rgba(0, 0, 0, 0.55); +} + +.datasetActionModal.wide { + width: min(760px, 100%); +} + .modalHead { display: flex; align-items: center; @@ -904,6 +917,11 @@ textarea { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.opGrid.preprocessOps { + grid-template-columns: repeat(3, minmax(0, 1fr)); + margin-top: 12px; +} + .datasetMiniList { display: grid; gap: 7px; @@ -911,35 +929,72 @@ textarea { overflow: auto; } -.datasetMiniList button { +.datasetMiniItem { min-width: 0; display: grid; - gap: 3px; - padding: 9px; - text-align: left; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 8px; + padding: 6px; border: 1px solid var(--line); border-radius: 6px; background: #101310; color: var(--ink); } -.datasetMiniList button.selected { +.datasetMiniItem.selected { border-color: var(--green); background: rgba(157, 226, 111, 0.08); } -.datasetMiniList span, -.datasetMiniList small { +.datasetMiniMain { + min-width: 0; + display: grid; + gap: 3px; + padding: 3px; + text-align: left; + background: transparent; + color: var(--ink); +} + +.datasetMiniMain span, +.datasetMiniMain small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.datasetMiniList small { +.datasetMiniMain small { color: var(--muted); } +.datasetRowActions { + display: flex; + gap: 4px; +} + +.datasetRowActions button { + width: 30px; + height: 30px; + display: grid; + place-items: center; + border-radius: 5px; + border: 1px solid var(--line); + background: #0b0d0b; + color: var(--muted); +} + +.datasetRowActions button:hover { + color: var(--green); + border-color: rgba(157, 226, 111, 0.45); +} + +.datasetRowActions button:last-child:hover { + color: var(--red); + border-color: rgba(240, 113, 103, 0.5); +} + .preprocessControls { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));