This commit is contained in:
Aaron Liu 2025-04-27 10:25:05 +08:00
parent 0680171f46
commit 57dded5b34
4 changed files with 141 additions and 74 deletions

View File

@ -622,7 +622,12 @@
"copyLinkToClipboard": "Copy link to clipboard"
},
"download": {
"noFilesFound": "No files found",
"filterByName": "Filter by name",
"selectAll": "Select all",
"reverseSelect": "Reverse select",
"cancelTaskConfirm": "Are you sure to cancel this download task?",
"saveChanges": "Save changes",
"failedToLoad": "Failed to load.",
"active": "Active",
"finished": "Finished",

View File

@ -622,6 +622,10 @@
"copyLinkToClipboard": "复制链接到剪切板"
},
"download": {
"noFilesFound": "没有找到任何文件",
"filterByName": "按名称过滤",
"selectAll": "全选",
"reverseSelect": "反选",
"cancelTaskConfirm": "确定要取消此任务吗?",
"saveChanges": "保存更改",
"failedToLoad": "加载失败",

View File

@ -622,6 +622,10 @@
"copyLinkToClipboard": "復制連結到剪下板"
},
"download": {
"noFilesFound": "沒有找到任何檔案",
"filterByName": "按名稱過濾",
"selectAll": "全選",
"reverseSelect": "反選",
"cancelTaskConfirm": "確定要取消此任務嗎?",
"saveChanges": "儲存更改",
"failedToLoad": "載入失敗",

View File

@ -2,10 +2,12 @@ import {
Box,
Button,
Grow,
Stack,
Table,
TableCell,
TableContainer,
TableRow,
TextField,
Typography,
useTheme,
} from "@mui/material";
@ -19,10 +21,7 @@ import { DownloadTaskFile, TaskSummary } from "../../../api/workflow.ts";
import { useAppDispatch } from "../../../redux/hooks.ts";
import { confirmOperation } from "../../../redux/thunks/dialog.ts";
import { fileBase, sizeToString } from "../../../util";
import {
StyledCheckbox,
StyledTableContainerPaper,
} from "../../Common/StyledComponents.tsx";
import { StyledCheckbox, StyledTableContainerPaper } from "../../Common/StyledComponents.tsx";
import FileIcon from "../../FileManager/Explorer/FileIcon.tsx";
import Dismiss from "../../Icons/Dismiss.tsx";
import { getProgressColor } from "./TaskCard.tsx";
@ -41,6 +40,7 @@ const DownloadFileList = ({ taskId, summary, downloading, readonly }: DownloadFi
const [changeApplied, setChangeApplied] = useState<boolean>(true);
const [loading, setLoading] = useState<boolean>(false);
const [height, setHeight] = useState(33);
const [filterText, setFilterText] = useState("");
const files = summary?.props?.download?.files;
const { t } = useTranslation();
const theme = useTheme();
@ -51,6 +51,16 @@ const DownloadFileList = ({ taskId, summary, downloading, readonly }: DownloadFi
return getProgressColor(theme);
}, [theme]);
const filteredFiles = useMemo(() => {
if (!files) {
return [];
}
if (!filterText) {
return files;
}
return files.filter((file) => file.name.toLowerCase().includes(filterText.toLowerCase()));
}, [files, filterText]);
const setFileSelected = (value: DownloadTaskFile, selected: boolean) => {
setSelectedMask((prev) => {
return {
@ -83,6 +93,25 @@ const DownloadFileList = ({ taskId, summary, downloading, readonly }: DownloadFi
});
};
const selectAll = () => {
const newMask = { ...selectedMask };
filteredFiles.forEach((file) => {
newMask[file.index] = true;
});
setSelectedMask(newMask);
setChangeApplied(false);
};
const reverseSelect = () => {
const newMask = { ...selectedMask };
filteredFiles.forEach((file) => {
const currentSelection = newMask[file.index] ?? file.selected;
newMask[file.index] = !currentSelection;
});
setSelectedMask(newMask);
setChangeApplied(false);
};
const cancelTask = () => {
dispatch(confirmOperation(t("download.cancelTaskConfirm"))).then(() => {
setLoading(true);
@ -102,79 +131,104 @@ const DownloadFileList = ({ taskId, summary, downloading, readonly }: DownloadFi
return (
<Box sx={{ py: readonly ? 0 : 2 }}>
{files && (
<TableContainer component={StyledTableContainerPaper}>
<TableVirtuoso
style={{
height: height,
overflow: "auto",
maxHeight: 300,
}}
totalListHeightChanged={(h) => {
setHeight(h + 0.5);
}}
components={{
// eslint-disable-next-line react/display-name
Table: (props) => <Table {...props} size={"small"} />,
// eslint-disable-next-line react/display-name
TableRow: (props) => {
const index = props["data-index"];
const percentage = (files[index]?.progress ?? 0) * 100;
const progressBgColor = theme.palette.background.default;
return (
<TableRow
{...props}
key={index}
sx={{
background: `linear-gradient(to right, ${progressColor} 0%,${progressColor} ${percentage}%,${progressBgColor} ${percentage}%,${progressBgColor} 100%)`,
}}
/>
);
},
}}
data={files}
itemContent={(_index, value) => (
<>
<TableCell component="th" scope="row" sx={{ height: 33, minWidth: 50 }}>
<StyledCheckbox
onChange={(e) => {
setFileSelected(value, e.target.checked);
}}
disabled={!downloading || readonly}
disableRipple
checked={selectedMask[value.index] ?? value.selected}
size="small"
/>
</TableCell>
<TableCell component="th" scope="row" sx={{ minWidth: 300, width: "100%" }}>
<Typography variant={"body2"} sx={{ display: "flex", alignItems: "center" }}>
<FileIcon
sx={{ px: 0, py: 0, mr: 1, height: "20px" }}
variant={"small"}
iconProps={{
fontSize: "small",
}}
file={{
type: FileType.file,
name: value.name,
<>
{!readonly && files.length > 20 && (
<Stack direction="row" spacing={1} sx={{ mb: 1 }}>
<TextField
label={t("download.filterByName")}
variant="outlined"
size="small"
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
sx={{ flexGrow: 1 }}
/>
<Button variant="outlined" onClick={selectAll} disabled={loading || !downloading}>
{t("download.selectAll")}
</Button>
<Button variant="outlined" onClick={reverseSelect} disabled={loading || !downloading}>
{t("download.reverseSelect")}
</Button>
</Stack>
)}
<TableContainer component={StyledTableContainerPaper}>
<TableVirtuoso
style={{
height: height,
overflow: "auto",
maxHeight: 300,
}}
totalListHeightChanged={(h) => {
setHeight(h + 0.5);
}}
components={{
// eslint-disable-next-line react/display-name
Table: (props) => <Table {...props} size={"small"} />,
// eslint-disable-next-line react/display-name
TableRow: (props) => {
const index = props["data-index"];
const percentage = (files[index]?.progress ?? 0) * 100;
const progressBgColor = theme.palette.background.default;
return (
<TableRow
{...props}
key={index}
sx={{
background: `linear-gradient(to right, ${progressColor} 0%,${progressColor} ${percentage}%,${progressBgColor} ${percentage}%,${progressBgColor} 100%)`,
}}
/>
<Typography variant="inherit">{fileBase(value.name)}</Typography>
</Typography>
</TableCell>
<TableCell component="th" scope="row" sx={{ minWidth: 120 }}>
<Typography noWrap variant={"body2"}>
{sizeToString(value.size)}
</Typography>
</TableCell>
<TableCell component="th" scope="row" sx={{ minWidth: 105 }}>
<Typography noWrap variant={"body2"}>
{((value.progress ?? 0) * 100).toFixed(2)} %
</Typography>
</TableCell>
</>
);
},
}}
data={filteredFiles}
itemContent={(_index, value) => (
<>
<TableCell component="th" scope="row" sx={{ height: 33, minWidth: 50 }}>
<StyledCheckbox
onChange={(e) => {
setFileSelected(value, e.target.checked);
}}
disabled={!downloading || readonly}
disableRipple
checked={selectedMask[value.index] ?? value.selected}
size="small"
/>
</TableCell>
<TableCell component="th" scope="row" sx={{ minWidth: 300, width: "100%" }}>
<Typography variant={"body2"} sx={{ display: "flex", alignItems: "center" }}>
<FileIcon
sx={{ px: 0, py: 0, mr: 1, height: "20px" }}
variant={"small"}
iconProps={{
fontSize: "small",
}}
file={{
type: FileType.file,
name: value.name,
}}
/>
<Typography variant="inherit">{fileBase(value.name)}</Typography>
</Typography>
</TableCell>
<TableCell component="th" scope="row" sx={{ minWidth: 120 }}>
<Typography noWrap variant={"body2"}>
{sizeToString(value.size)}
</Typography>
</TableCell>
<TableCell component="th" scope="row" sx={{ minWidth: 105 }}>
<Typography noWrap variant={"body2"}>
{((value.progress ?? 0) * 100).toFixed(2)} %
</Typography>
</TableCell>
</>
)}
/>
{filteredFiles.length === 0 && (
<Typography variant="body2" color="text.secondary" align="center" sx={{ my: 1 }}>
{t("download.noFilesFound")}
</Typography>
)}
/>
</TableContainer>
</TableContainer>
</>
)}
{downloading && !readonly && summary?.phase == "monitor" && (
<Box