离线下载列表页

This commit is contained in:
HFO4 2020-02-09 13:54:39 +08:00
parent 8dfb7fa8d0
commit 508bf79cc8
8 changed files with 861 additions and 349 deletions

View File

@ -32,6 +32,7 @@ import {
IconButton
} from "@material-ui/core";
import DownloadingCard from "./DownloadingCard";
import FinishedCard from "./FinishedCard";
const styles = theme => ({
actions: {
@ -63,58 +64,6 @@ const styles = theme => ({
gird: {
marginTop: "30px"
},
iconImgBig: {
color: "#d32f2f",
fontSize: "30px"
},
iconVideoBig: {
color: "#d50000",
fontSize: "30px"
},
iconAudioBig: {
color: "#651fff",
fontSize: "30px"
},
iconPdfBig: {
color: "#f44336",
fontSize: "30px"
},
iconWordBig: {
color: "#538ce5",
fontSize: "30px"
},
iconPptBig: {
color: "rgb(239, 99, 63)",
fontSize: "30px"
},
iconExcelBig: {
color: "#4caf50",
fontSize: "30px"
},
iconTextBig: {
color: "#607d8b",
fontSize: "30px"
},
iconFileBig: {
color: "#424242",
fontSize: "30px"
},
iconTorrentBig: {
color: "#5c6bc0",
fontSize: "30px"
},
iconZipBig: {
color: "#f9a825",
fontSize: "30px"
},
iconAndroidBig: {
color: "#8bc34a",
fontSize: "30px"
},
iconExeBig: {
color: "#1a237e",
fontSize: "30px"
},
hide: {
display: "none"
},
@ -128,6 +77,9 @@ const styles = theme => ({
textAlign: "center",
marginTop: "20px",
marginBottom: "20px"
},
margin:{
marginTop:theme.spacing(2),
}
});
const mapStateToProps = state => {
@ -180,6 +132,7 @@ const getIcon = (classes, name) => {
class DownloadComponent extends Component {
page = 0;
interval = 0;
state = {
downloading: [],
@ -190,8 +143,13 @@ class DownloadComponent extends Component {
componentDidMount = () => {
this.loadDownloading();
this.loadMore();
};
componentWillUnmount() {
clearTimeout(this.interval);
}
loadDownloading = () => {
this.setState({
loading: true
@ -202,6 +160,11 @@ class DownloadComponent extends Component {
downloading: response.data,
loading: false
});
// 设定自动更新
clearTimeout(this.interval);
if(response.data.length > 0){
this.interval = setTimeout(this.loadDownloading,1000 * response.data[0].interval);
}
})
.catch(error => {
this.props.toggleSnackbar(
@ -217,13 +180,13 @@ class DownloadComponent extends Component {
this.setState({
loading: true
});
axios
.get("/RemoteDownload/ListFinished?page=" + ++this.page)
API
.get("/aria2/finished?page=" + ++this.page)
.then(response => {
this.setState({
finishedList: response.data,
finishedList: [...this.state.finishedList,...response.data],
loading: false,
continue: response.data.length < 10 ? false : true
continue: response.data.length >= 10
});
})
.catch(error => {
@ -234,43 +197,6 @@ class DownloadComponent extends Component {
});
};
cancelDownload = id => {
axios
.post("/RemoteDownload/Cancel", {
id: id
})
.then(response => {
if (response.data.error !== 0) {
this.props.toggleSnackbar(
"top",
"right",
response.message,
"error"
);
} else {
this.setState({
downloading: this.state.downloading.filter(value => {
return value.id !== id;
})
});
this.props.toggleSnackbar(
"top",
"right",
"取消成功",
"success"
);
}
})
.catch(error => {
this.props.toggleSnackbar(
"top",
"right",
error.message,
"error"
);
});
};
render() {
const { classes } = this.props;
@ -300,50 +226,13 @@ class DownloadComponent extends Component {
已完成
</Typography>
<div className={classes.loadMore}>
{this.state.finishedList.map(value => {
return (
<Card className={classes.card} key={value.id}>
{JSON.stringify(value.fileName) !== "[]" && (
<div className={classes.iconContainer}>
{getIcon(classes, value.fileName)}
</div>
)}
{this.state.finishedList.map((value, k) => {
if (value.files) {
return (
<FinishedCard key={k} task={value}/>
)
}
<CardContent className={classes.content}>
<Typography
color="primary"
variant="h6"
style={{ textAlign: "left" }}
noWrap
>
{value.fileName}
</Typography>
<Typography
variant="subtitle1"
color="textSecondary"
noWrap
style={{ textAlign: "left" }}
>
{(() => {
switch (value.status) {
case "canceled":
return <div>已取消</div>;
case "error":
return (
<div>
错误{value.msg}
</div>
);
case "success":
return <div>成功</div>;
default:
break;
}
})()}
</Typography>
</CardContent>
</Card>
);
})}
<Button
size="large"

View File

@ -1,4 +1,4 @@
import React, {useState, useCallback, useEffect} from "react";
import React, { useState, useCallback, useEffect } from "react";
import {
Card,
CardContent,
@ -12,15 +12,15 @@ import {
} from "@material-ui/core";
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../actions";
import {hex2bin, sizeToString} from "../../untils";
import PermMediaIcon from '@material-ui/icons/PermMedia';
import { hex2bin, sizeToString } from "../../untils";
import PermMediaIcon from "@material-ui/icons/PermMedia";
import TypeIcon from "../FileManager/TypeIcon";
import MuiExpansionPanel from "@material-ui/core/ExpansionPanel";
import MuiExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary";
import MuiExpansionPanelDetails from "@material-ui/core/ExpansionPanelDetails";
import withStyles from "@material-ui/core/styles/withStyles";
import Divider from "@material-ui/core/Divider";
import { ExpandMore, HighlightOff} from "@material-ui/icons";
import { ExpandMore, HighlightOff } from "@material-ui/icons";
import classNames from "classnames";
import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell";
@ -31,7 +31,9 @@ import Tooltip from "@material-ui/core/Tooltip";
import API, { baseURL } from "../../middleware/Api";
import Button from "@material-ui/core/Button";
import Grid from "@material-ui/core/Grid";
import TimeAgo from 'timeago-react';
import TimeAgo from "timeago-react";
import SelectFileDialog from "../Modals/SelectFile";
import {useHistory} from "react-router";
const ExpansionPanel = withStyles({
root: {
@ -107,9 +109,9 @@ const useStyles = makeStyles(theme => ({
iconBig: {
fontSize: "30px"
},
iconMultiple:{
iconMultiple: {
fontSize: "30px",
color:"#607D8B",
color: "#607D8B"
},
progress: {
marginTop: 8,
@ -130,26 +132,26 @@ const useStyles = makeStyles(theme => ({
scroll: {
overflowY: "auto"
},
action:{
padding:theme.spacing(2),
textAlign:"right",
action: {
padding: theme.spacing(2),
textAlign: "right"
},
actionButton:{
marginLeft: theme.spacing(1),
actionButton: {
marginLeft: theme.spacing(1)
},
info:{
padding:theme.spacing(2),
info: {
padding: theme.spacing(2)
},
infoTitle:{
fontWeight:700,
infoTitle: {
fontWeight: 700
},
infoValue:{
color:theme.palette.text.secondary,
infoValue: {
color: theme.palette.text.secondary
},
bitmap:{
bitmap: {
width: "100%",
height: "50px",
backgroundColor:theme.palette.background.default,
backgroundColor: theme.palette.background.default
}
}));
@ -157,10 +159,12 @@ export default function DownloadingCard(props) {
let canvasRef = React.createRef();
const classes = useStyles();
const theme = useTheme();
let history = useHistory();
const [expanded, setExpanded] = React.useState("");
const [task, setTask] = React.useState(props.task);
const [loading, setLoading] = React.useState(false);
const [selectDialogOpen,setSelectDialogOpen] = React.useState(false);
const handleChange = panel => (event, newExpanded) => {
setExpanded(newExpanded ? panel : false);
@ -173,33 +177,34 @@ export default function DownloadingCard(props) {
[dispatch]
);
useEffect(()=>{
useEffect(() => {
setTask(props.task);
},[props.task]);
}, [props.task]);
useEffect(()=>{
if (task.info.bitfield===""){
return
useEffect(() => {
if (task.info.bitfield === "") {
return;
}
let result = "";
task.info.bitfield.match(/.{1,2}/g).forEach(str => {
result += hex2bin(str);
});
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
const context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
context.strokeStyle = theme.palette.primary.main;
for (let i = 0;i<canvas.width;i++){
let bit = result[Math.round(((i+1)/canvas.width)*result.length)];
bit = bit?bit:result.slice(-1);
if(bit === "1"){
for (let i = 0; i < canvas.width; i++) {
let bit =
result[Math.round(((i + 1) / canvas.width) * task.info.numPieces)];
bit = bit ? bit : result.slice(-1);
if (bit === "1") {
context.beginPath();
context.moveTo(i,0);
context.moveTo(i, 0);
context.lineTo(i, canvas.height);
context.stroke();
}
}
},[task.info.bitfield,theme]);
}, [task.info.bitfield,task.info.numPieces, theme]);
const getPercent = (completed, total) => {
if (total == 0) {
@ -208,55 +213,44 @@ export default function DownloadingCard(props) {
return (completed / total) * 100;
};
const deleteFile = (index)=>{
const deleteFile = index => {
setLoading(true);
let current = activeFiles();
let newIndex = [];
let newFiles = [];
current.map((v)=>{
if (v.index !== index && v.selected){
current.map(v => {
if (v.index !== index && v.selected) {
newIndex.push(parseInt(v.index));
newFiles.push({
...v,
selected:"true",
selected: "true"
});
}else{
} else {
newFiles.push({
...v,
selected:"false",
selected: "false"
});
}
});
API.put("/aria2/select/"+task.info.gid,{
indexes:newIndex,
API.put("/aria2/select/" + task.info.gid, {
indexes: newIndex
})
.then(response => {
setTask({
...task,
info:{
info: {
...task.info,
files:newFiles,
files: newFiles
}
});
ToggleSnackbar(
"top",
"right",
"文件已删除",
"success"
);
ToggleSnackbar("top", "right", "文件已删除", "success");
})
.catch(error => {
ToggleSnackbar(
"top",
"right",
error.message,
"error"
);
}).finally(()=>{
ToggleSnackbar("top", "right", error.message, "error");
})
.finally(() => {
setLoading(false);
});
});
};
const getDownloadName = useCallback(() => {
@ -267,16 +261,16 @@ export default function DownloadingCard(props) {
}, [task]);
const activeFiles = useCallback(() => {
return task.info.files.filter((v)=> v.selected==="true");
return task.info.files.filter(v => v.selected === "true");
}, [task.info.files]);
const getIcon = useCallback(() => {
if (task.info.bittorrent.mode === "multi") {
return (
<Badge badgeContent={activeFiles().length} color="secondary">
<PermMediaIcon className={classes.iconMultiple}/>
<PermMediaIcon className={classes.iconMultiple} />
</Badge>
)
);
} else {
return (
<TypeIcon
@ -287,8 +281,46 @@ export default function DownloadingCard(props) {
}
}, [task, classes]);
const cancel = e => {
setLoading(true);
API.delete("/aria2/task/" + task.info.gid, )
.then(response => {
ToggleSnackbar("top", "right", "任务已取消,状态会在稍后更新", "success");
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
})
.finally(() => {
setLoading(false);
});
};
const changeSelectedFile = fileIndex =>{
setLoading(true);
API.put("/aria2/select/" + task.info.gid, {
indexes: fileIndex
})
.then(response => {
ToggleSnackbar("top", "right", "操作成功,状态会在稍后更新", "success");
setSelectDialogOpen(false);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
})
.finally(() => {
setLoading(false);
});
};
return (
<Card className={classes.card}>
<SelectFileDialog
open = {selectDialogOpen}
onClose ={()=>setSelectDialogOpen(false)}
modalsLoading={loading}
files={props.task.info.files}
onSubmit={changeSelectedFile}
/>
<ExpansionPanel
square
expanded={expanded === task.info.gid}
@ -302,19 +334,14 @@ export default function DownloadingCard(props) {
<CardContent className={classes.content}>
<Typography color="primary" noWrap>
<Tooltip title={getDownloadName()}>
<span>
{getDownloadName()}</span>
<span>{getDownloadName()}</span>
</Tooltip>
</Typography>
<LinearProgress
color="secondary"
variant="determinate"
className={classes.progress}
value={getPercent(
task.downloaded,
task.total
)}
value={getPercent(task.downloaded, task.total)}
/>
<Typography
variant="body2"
@ -360,138 +387,177 @@ export default function DownloadingCard(props) {
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Divider />
<div className={classes.scroll}>
<Table size="small">
<TableBody>
{activeFiles().map((value, key) => {
return (
<TableRow
key={value.index}
style={{
background:
"linear-gradient(to right, " +
(theme.palette.type ===
"dark"
? darken(
theme.palette
.primary.main,
0.4
)
: lighten(
theme.palette
.primary.main,
0.85
)) +
" 0%," +
(theme.palette.type ===
"dark"
? darken(
theme.palette
.primary.main,
0.4
)
: lighten(
theme.palette
.primary.main,
0.85
)) +
" " +
getPercent(
value.completedLength,
value.length
).toFixed(0) +
"%," +
theme.palette.background
.paper +
" " +
getPercent(
value.completedLength,
value.length
).toFixed(0) +
"%," +
theme.palette.background
.paper +
" 100%)"
}}
>
<TableCell
component="th"
scope="row"
{task.info.bittorrent.mode === "multi" &&
<div className={classes.scroll}>
<Table size="small">
<TableBody>
{activeFiles().map((value, key) => {
return (
<TableRow
key={value.index}
style={{
background:
"linear-gradient(to right, " +
(theme.palette.type ===
"dark"
? darken(
theme.palette
.primary.main,
0.4
)
: lighten(
theme.palette
.primary.main,
0.85
)) +
" 0%," +
(theme.palette.type ===
"dark"
? darken(
theme.palette
.primary.main,
0.4
)
: lighten(
theme.palette
.primary.main,
0.85
)) +
" " +
getPercent(
value.completedLength,
value.length
).toFixed(0) +
"%," +
theme.palette.background
.paper +
" " +
getPercent(
value.completedLength,
value.length
).toFixed(0) +
"%," +
theme.palette.background
.paper +
" 100%)"
}}
>
<Typography
className={
classes.subFileName
}
<TableCell
component="th"
scope="row"
>
<TypeIcon
<Typography
className={
classes.subFileIcon
classes.subFileName
}
fileName={value.path}
/>
{value.path}
</Typography>
</TableCell>
<TableCell
component="th"
scope="row"
>
<Typography noWrap>
{" "}
{sizeToString(value.length)}
</Typography>
</TableCell>
<TableCell
component="th"
scope="row"
>
<Typography noWrap>
{getPercent(
value.completedLength,
value.length
).toFixed(2)}
%
</Typography>
</TableCell>
<TableCell>
<Tooltip title="删除此文件">
<IconButton onClick={()=>deleteFile(value.index)} disabled={loading} size={"small"}>
<HighlightOff/>
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
<Divider/>
</div>
>
<TypeIcon
className={
classes.subFileIcon
}
fileName={value.path}
/>
{value.path}
</Typography>
</TableCell>
<TableCell
component="th"
scope="row"
>
<Typography noWrap>
{" "}
{sizeToString(value.length)}
</Typography>
</TableCell>
<TableCell
component="th"
scope="row"
>
<Typography noWrap>
{getPercent(
value.completedLength,
value.length
).toFixed(2)}
%
</Typography>
</TableCell>
<TableCell>
<Tooltip title="删除此文件">
<IconButton
onClick={() =>
deleteFile(
value.index
)
}
disabled={loading}
size={"small"}
>
<HighlightOff />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
}
<div className={classes.action}>
<Button className={classes.actionButton} variant="outlined" color="secondary">
选择要下载的文件
<Button
className={classes.actionButton}
variant="outlined"
color="secondary"
onClick={() => window.location.href="/#/home?path=" + encodeURIComponent(task.dst)}
>
打开存放目录
</Button>
<Button className={classes.actionButton} variant="contained" color="secondary">
{task.info.bittorrent.mode === "multi" &&
<Button
className={classes.actionButton}
variant="outlined"
color="secondary"
disabled={loading}
onClick={() => setSelectDialogOpen(true)}
>
选择要下载的文件
</Button>
}
<Button
className={classes.actionButton}
onClick={cancel}
variant="contained"
color="secondary"
disabled={loading}
>
取消任务
</Button>
</div>
<Divider/>
<Divider />
<div className={classes.info}>
{task.info.bitfield !==""&&<canvas width={"700"} height={"100"} ref={canvasRef} className={classes.bitmap}/>}
{task.info.bitfield !== "" && (
<canvas
width={"700"}
height={"100"}
ref={canvasRef}
className={classes.bitmap}
/>
)}
<Grid container >
<Grid container xs={12} sm={4} >
<Grid container>
<Grid container xs={12} sm={4}>
<Grid item xs={4} className={classes.infoTitle}>
更新于
</Grid>
<Grid item xs={8} className={classes.infoValue}>
<TimeAgo
datetime={parseInt(task.update + "000")}
locale='zh_CN'
locale="zh_CN"
/>
</Grid>
</Grid>
<Grid container xs={12} sm={4} >
<Grid container xs={12} sm={4}>
<Grid item xs={4} className={classes.infoTitle}>
上传大小
</Grid>
@ -499,42 +565,74 @@ export default function DownloadingCard(props) {
{sizeToString(task.info.uploadLength)}
</Grid>
</Grid>
<Grid container xs={12} sm={4} >
<Grid container xs={12} sm={4}>
<Grid item xs={4} className={classes.infoTitle}>
上传速度
</Grid>
<Grid item xs={8} className={classes.infoValue}>
{sizeToString(task.info.uploadLength)} / s
{sizeToString(task.info.uploadSpeed)} / s
</Grid>
</Grid>
{task.info.bittorrent.mode !== ""&&
<><Grid container xs={12} sm={8} >
<Grid item xs={2} className={classes.infoTitle}>
InfoHash
</Grid>
<Grid item xs={10} className={classes.infoValue}>
{task.info.infoHash}
</Grid>
</Grid>
<Grid container xs={12} sm={4} >
<Grid item xs={4} className={classes.infoTitle}>
做种者
{task.info.bittorrent.mode !== "" && (
<>
<Grid container xs={12} sm={8}>
<Grid
item
sm={2}
xs={4}
className={classes.infoTitle}
>
InfoHash
</Grid>
<Grid
item
sm={10}
xs={8}
style={{
wordBreak:"break-all",
}}
className={classes.infoValue}
>
{task.info.infoHash}
</Grid>
</Grid>
<Grid item xs={8} className={classes.infoValue}>
{task.info.numSeeders}
<Grid container xs={12} sm={4}>
<Grid
item
xs={4}
className={classes.infoTitle}
>
做种者
</Grid>
<Grid
item
xs={8}
className={classes.infoValue}
>
{task.info.numSeeders}
</Grid>
</Grid>
</Grid>
<Grid container xs={12} sm={4} >
<Grid item xs={4} className={classes.infoTitle}>
做种中
<Grid container xs={12} sm={4}>
<Grid
item
xs={4}
className={classes.infoTitle}
>
做种中
</Grid>
<Grid
item
xs={8}
className={classes.infoValue}
>
{task.info.seeder === "true"
? "是"
: "否"}
</Grid>
</Grid>
<Grid item xs={8} className={classes.infoValue}>
{task.info.seeder === "true"?"是":"否"}
</Grid>
</Grid>
</>
}
<Grid container xs={12} sm={4} >
</>
)}
<Grid container xs={12} sm={4}>
<Grid item xs={4} className={classes.infoTitle}>
分片大小
</Grid>
@ -542,7 +640,7 @@ export default function DownloadingCard(props) {
{sizeToString(task.info.pieceLength)}
</Grid>
</Grid>
<Grid container xs={12} sm={4} >
<Grid container xs={12} sm={4}>
<Grid item xs={4} className={classes.infoTitle}>
分片数量
</Grid>
@ -550,7 +648,6 @@ export default function DownloadingCard(props) {
{task.info.numPieces}
</Grid>
</Grid>
</Grid>
</div>
</ExpansionPanelDetails>

View File

@ -0,0 +1,400 @@
import React, { useState, useCallback, useEffect } from "react";
import {
Card,
CardContent,
darken,
IconButton,
lighten,
LinearProgress,
makeStyles,
Typography,
useTheme
} from "@material-ui/core";
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../actions";
import { hex2bin, sizeToString } from "../../untils";
import PermMediaIcon from "@material-ui/icons/PermMedia";
import TypeIcon from "../FileManager/TypeIcon";
import MuiExpansionPanel from "@material-ui/core/ExpansionPanel";
import MuiExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary";
import MuiExpansionPanelDetails from "@material-ui/core/ExpansionPanelDetails";
import withStyles from "@material-ui/core/styles/withStyles";
import Divider from "@material-ui/core/Divider";
import { ExpandMore, HighlightOff } from "@material-ui/icons";
import classNames from "classnames";
import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody";
import Table from "@material-ui/core/Table";
import Badge from "@material-ui/core/Badge";
import Tooltip from "@material-ui/core/Tooltip";
import API, { baseURL } from "../../middleware/Api";
import Button from "@material-ui/core/Button";
import Grid from "@material-ui/core/Grid";
import TimeAgo from "timeago-react";
import SelectFileDialog from "../Modals/SelectFile";
const ExpansionPanel = withStyles({
root: {
maxWidth: "100%",
boxShadow: "none",
"&:not(:last-child)": {
borderBottom: 0
},
"&:before": {
display: "none"
},
"&$expanded": {}
},
expanded: {}
})(MuiExpansionPanel);
const ExpansionPanelSummary = withStyles({
root: {
minHeight: 0,
padding: 0,
"&$expanded": {
minHeight: 56
}
},
content: {
maxWidth: "100%",
margin: 0,
display: "flex",
"&$expanded": {
margin: "0"
}
},
expanded: {}
})(MuiExpansionPanelSummary);
const ExpansionPanelDetails = withStyles(theme => ({
root: {
display: "block",
padding: theme.spacing(0)
}
}))(MuiExpansionPanelDetails);
const useStyles = makeStyles(theme => ({
card: {
marginTop: "20px",
justifyContent: "space-between"
},
iconContainer: {
width: "90px",
height: "96px",
padding: " 35px 29px 29px 29px",
paddingLeft: "35px",
[theme.breakpoints.down("sm")]: {
display: "none"
}
},
content: {
width: "100%",
minWidth: 0,
[theme.breakpoints.up("sm")]: {
borderInlineStart: "1px " + theme.palette.divider + " solid"
},
textAlign:"left",
},
contentSide: {
minWidth: 0,
paddingTop: "24px",
paddingRight: "28px",
[theme.breakpoints.down("sm")]: {
display: "none"
}
},
iconBig: {
fontSize: "30px"
},
iconMultiple: {
fontSize: "30px",
color: "#607D8B"
},
progress: {
marginTop: 8,
marginBottom: 4
},
expand: {
transition: ".15s transform ease-in-out"
},
expanded: {
transform: "rotate(180deg)"
},
subFileName: {
display: "flex"
},
subFileIcon: {
marginRight: "20px"
},
scroll: {
overflowY: "auto"
},
action: {
padding: theme.spacing(2),
textAlign: "right"
},
actionButton: {
marginLeft: theme.spacing(1)
},
info: {
padding: theme.spacing(2)
},
infoTitle: {
fontWeight: 700
},
infoValue: {
color: theme.palette.text.secondary
},
}));
export default function FinishedCard(props) {
const classes = useStyles();
const theme = useTheme();
const [expanded, setExpanded] = React.useState(false);
const handleChange = panel => (event, newExpanded) => {
setExpanded(!!newExpanded);
};
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
(vertical, horizontal, msg, color) =>
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
[dispatch]
);
const getPercent = (completed, total) => {
if (total == 0) {
return 0;
}
return (completed / total) * 100;
};
const getDownloadName = useCallback(() => {
return props.task.name === "." ? "[未知]" : props.task.name;
}, [props.task.name]);
const activeFiles = useCallback(() => {
return props.task.files.filter(v => v.selected === "true");
}, [props.task.files]);
const getIcon = useCallback(() => {
if (props.task.files.length > 1) {
return (
<Badge badgeContent={activeFiles().length} color="secondary">
<PermMediaIcon className={classes.iconMultiple} />
</Badge>
);
} else {
return (
<TypeIcon
className={classes.iconBig}
fileName={getDownloadName(props.task)}
/>
);
}
}, [props.task, classes]);
const getTaskError = error =>{
try{
let res = JSON.parse(error)
return res.msg + "" + res.error
}catch (e) {
return "文件转存失败"
}
};
return (
<Card className={classes.card}>
<ExpansionPanel
square
expanded={expanded}
onChange={handleChange("")}
>
<ExpansionPanelSummary
aria-controls="panel1d-content"
id="panel1d-header"
>
<div className={classes.iconContainer}>{getIcon()}</div>
<CardContent className={classes.content}>
<Typography color="primary" noWrap>
<Tooltip title={getDownloadName()}>
<span>{getDownloadName()}</span>
</Tooltip>
</Typography>
{props.task.status === 3&&
<Typography
variant="body2"
color="error"
noWrap
>
下载出错{props.task.error}
</Typography>
}
{props.task.status === 5&&
<Typography
variant="body2"
color="textSecondary"
noWrap
>
已取消{props.task.error !== "" &&<span>{props.task.error}</span>}
</Typography>
}
{(props.task.status === 4 && props.task.task_status === 4)&&
<Typography
variant="body2"
style={{
color:theme.palette.success.main,
}}
noWrap
>
已完成
</Typography>
}
{(props.task.status === 4 && props.task.task_status === 0)&&
<Typography
variant="body2"
style={{
color:theme.palette.success.light,
}}
noWrap
>
已完成转存排队中
</Typography>
}
{(props.task.status === 4 && props.task.task_status === 1)&&
<Typography
variant="body2"
style={{
color:theme.palette.success.light,
}}
noWrap
>
已完成转存处理中
</Typography>
}
{(props.task.status === 4 && props.task.task_status === 2)&&
<Typography
variant="body2"
color={"error"}
noWrap
>
{getTaskError(props.task.task_error)}
</Typography>
}
</CardContent>
<CardContent className={classes.contentSide}>
<IconButton>
<ExpandMore
className={classNames(
{
[classes.expanded]:
expanded
},
classes.expand
)}
/>
</IconButton>
</CardContent>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Divider />
{props.task.files.length>1 &&
<div className={classes.scroll}>
<Table>
<TableBody>
{activeFiles().map((value, key) => {
return (
<TableRow
key={value.index}
>
<TableCell
component="th"
scope="row"
>
<Typography
className={
classes.subFileName
}
>
<TypeIcon
className={
classes.subFileIcon
}
fileName={value.path}
/>
{value.path}
</Typography>
</TableCell>
<TableCell
component="th"
scope="row"
>
<Typography noWrap>
{" "}
{sizeToString(value.length)}
</Typography>
</TableCell>
<TableCell
component="th"
scope="row"
>
<Typography noWrap>
{getPercent(
value.completedLength,
value.length
).toFixed(2)}
%
</Typography>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
}
<div className={classes.action}>
<Button
className={classes.actionButton}
variant="outlined"
color="secondary"
onClick={() => window.location.href="/#/home?path=" + encodeURIComponent(props.task.dst)}
>
打开存放目录
</Button>
</div>
<Divider />
<div className={classes.info}>
<Grid container>
<Grid container xs={12} sm={6}>
<Grid item xs={4} className={classes.infoTitle}>
创建日期
</Grid>
<Grid item xs={8} className={classes.infoValue}>
{props.task.create}
</Grid>
</Grid>
<Grid container xs={12} sm={6}>
<Grid item xs={4} className={classes.infoTitle}>
最后更新
</Grid>
<Grid item xs={8} className={classes.infoValue}>
{props.task.update}
</Grid>
</Grid>
</Grid>
</div>
</ExpansionPanelDetails>
</ExpansionPanel>
</Card>
);
}

View File

@ -53,7 +53,6 @@ class FileManager extends Component {
componentWillUnmount() {
this.props.setSelectedTarget([]);
this.props.navitateTo("/");
this.props.closeAllModals();
}

View File

@ -177,7 +177,10 @@ class NavigatorComponent extends Component {
}
componentDidMount = () => {
this.renderPath();
var url = new URL(fixUrlHash(window.location.href));
var c = url.searchParams.get("path");
this.renderPath(c === null ? "/":c);
if (!this.props.isShare) {
// 如果是在个人文件管理页,首次加载时打开侧边栏
this.props.handleDesktopToggle(true);

View File

@ -0,0 +1,124 @@
import React, {useState, useCallback, useEffect} from "react";
import { makeStyles } from "@material-ui/core";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
DialogContentText,
CircularProgress
} from "@material-ui/core";
import {
toggleSnackbar,
setModalsLoading,
refreshFileList
} from "../../actions/index";
import PathSelector from "../FileManager/PathSelector";
import { useDispatch } from "react-redux";
import API from "../../middleware/Api";
import FormGroup from "@material-ui/core/FormGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Checkbox from "@material-ui/core/Checkbox";
import MenuItem from "@material-ui/core/MenuItem";
const useStyles = makeStyles(theme => ({
contentFix: {
padding: "10px 24px 0px 24px"
},
wrapper: {
margin: theme.spacing(1),
position: "relative"
},
buttonProgress: {
color: theme.palette.secondary.light,
position: "absolute",
top: "50%",
left: "50%",
marginTop: -12,
marginLeft: -12
},
content:{
padding:0,
}
}));
export default function SelectFileDialog(props) {
const [files,setFiles] = useState(props.files);
useEffect(()=>{
setFiles(props.files);
},[props.files]);
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
(vertical, horizontal, msg, color) =>
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
[dispatch]
);
const handleChange = index => event =>{
let filesCopy = [...files];
filesCopy.map((v,k)=>{
if (v.index === index){
filesCopy[k] = {...filesCopy[k],selected:event.target.checked ? "true" : "false"};
}
});
setFiles(filesCopy);
};
const submit = e =>{
let index = [];
files.map(v=>{
if(v.selected === "true"){
index.push(parseInt(v.index));
}
});
props.onSubmit(index);
};
const classes = useStyles();
return (
<Dialog
open={props.open}
onClose={props.onClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">选择要下载的文件</DialogTitle>
<DialogContent dividers={"paper"} className={classes.content}>
{files.map((v, k) => {
return (
<MenuItem key={k}>
<FormGroup row>
<FormControlLabel
control={
<Checkbox
onChange={handleChange(v.index)}
checked={v.selected === "true"}
value="checkedA"
/>
}
label={v.path}
/>
</FormGroup></MenuItem>
);
})}
</DialogContent>
<DialogActions>
<Button onClick={props.onClose}>取消</Button>
<div className={classes.wrapper}>
<Button color="primary" onClick={submit} disabled={props.modalsLoading}>
确定
{props.modalsLoading && (
<CircularProgress
size={24}
className={classes.buttonProgress}
/>
)}
</Button>
</div>
</DialogActions>
</Dialog>
);
}

View File

@ -622,7 +622,7 @@ class NavbarCompoment extends Component {
position="fixed"
className={classes.appBar}
color={
this.props.selected.length <= 1 &&
this.props.theme.palette.type !== "dark" && this.props.selected.length <= 1 &&
!(!this.props.isMultiple && this.props.withFile)
? "primary"
: "default"

View File

@ -4,7 +4,7 @@ const statusHelper = {
return path == "/home"
},
isSharePage(path){
return path.startsWith("/s/")
return path && path.startsWith("/s/")
},
isMobile(){
return window.innerWidth < 600;