Style: format all codes with prettier

This commit is contained in:
HFO4 2020-12-08 19:38:11 +08:00
parent 2cebcb3711
commit 397bf4569c
147 changed files with 7572 additions and 6642 deletions

View File

@ -30,7 +30,9 @@ import ResetForm from "./component/Login/ResetForm";
import Reset from "./component/Login/Reset";
import PageLoading from "./component/Placeholder/PageLoading";
import CodeViewer from "./component/Viewer/Code";
const PDFViewer = React.lazy(() => import(/* webpackChunkName: "pdf" */ "./component/Viewer/PDF"));
const PDFViewer = React.lazy(() =>
import(/* webpackChunkName: "pdf" */ "./component/Viewer/PDF")
);
export default function App() {
const themeConfig = useSelector(state => state.siteConfig.theme);
@ -101,10 +103,7 @@ export default function App() {
<FileManager />
</AuthRoute>
<AuthRoute
path={`${path}video`}
isLogin={isLogin}
>
<AuthRoute path={`${path}video`} isLogin={isLogin}>
<VideoPreview />
</AuthRoute>
@ -117,7 +116,7 @@ export default function App() {
</AuthRoute>
<AuthRoute path={`${path}pdf`} isLogin={isLogin}>
<Suspense fallback={<PageLoading/>}>
<Suspense fallback={<PageLoading />}>
<PDFViewer />
</Suspense>
</AuthRoute>
@ -137,11 +136,8 @@ export default function App() {
<Route path={`${path}search`} isLogin={isLogin}>
<SearchResult />
</Route>
<Route
path={`${path}setting`}
isLogin={isLogin}
>
<Route path={`${path}setting`} isLogin={isLogin}>
<UserSetting />
</Route>
@ -197,7 +193,7 @@ export default function App() {
</Route>
<Route path={`${path}s/:id/pdf(/)*`}>
<Suspense fallback={<PageLoading/>}>
<Suspense fallback={<PageLoading />}>
<PDFViewer />
</Suspense>
</Route>

View File

@ -1,30 +1,30 @@
export * from './explorer'
export * from "./explorer";
export const setNavigator = (path, navigatorLoading) => {
return {
type: 'SET_NAVIGATOR',
path,
navigatorLoading
}
}
return {
type: "SET_NAVIGATOR",
path,
navigatorLoading
};
};
export const navigateTo = path => {
return (dispatch, getState) => {
const state = getState()
const navigatorLoading = path !== state.navigator.path
dispatch(setNavigator(path, navigatorLoading))
}
return (dispatch, getState) => {
const state = getState();
const navigatorLoading = path !== state.navigator.path;
dispatch(setNavigator(path, navigatorLoading));
};
};
export const navigateUp = () => {
return (dispatch, getState) => {
const state = getState()
const pathSplit = state.navigator.path.split("/");
pathSplit.pop();
const newPath = pathSplit.length===1? "/":pathSplit.join("/");
const navigatorLoading = newPath !== state.navigator.path
dispatch(setNavigator(newPath, navigatorLoading))
}
return (dispatch, getState) => {
const state = getState();
const pathSplit = state.navigator.path.split("/");
pathSplit.pop();
const newPath = pathSplit.length === 1 ? "/" : pathSplit.join("/");
const navigatorLoading = newPath !== state.navigator.path;
dispatch(setNavigator(newPath, navigatorLoading));
};
};
export const drawerToggleAction = open => {
@ -34,11 +34,11 @@ export const drawerToggleAction = open => {
};
};
export const dragAndDrop = (source,target) => {
export const dragAndDrop = (source, target) => {
return {
type: "DRAG_AND_DROP",
source: source,
target: target,
target: target
};
};
@ -49,9 +49,9 @@ export const changeViewMethod = method => {
};
};
export const toggleDaylightMode = ()=>{
export const toggleDaylightMode = () => {
return {
type: "TOGGLE_DAYLIGHT_MODE",
type: "TOGGLE_DAYLIGHT_MODE"
};
};
@ -110,10 +110,10 @@ export const openRenameDialog = () => {
};
};
export const openResaveDialog = (key) => {
export const openResaveDialog = key => {
return {
type: "OPEN_RESAVE_DIALOG",
key:key,
key: key
};
};
@ -135,21 +135,20 @@ export const openShareDialog = () => {
};
};
export const applyThemes = (theme)=>{
export const applyThemes = theme => {
return {
type:'APPLY_THEME',
theme:theme,
type: "APPLY_THEME",
theme: theme
};
};
export const setSessionStatus = (status)=>{
export const setSessionStatus = status => {
return {
type:'SET_SESSION_STATUS',
status:status,
type: "SET_SESSION_STATUS",
status: status
};
};
export const openMusicDialog = () => {
return {
type: "OPEN_MUSIC_DIALOG"
@ -192,11 +191,11 @@ export const openCopyDialog = () => {
};
};
export const openLoadingDialog = (text) => {
export const openLoadingDialog = text => {
return {
type: "OPEN_LOADING_DIALOG",
text: text,
}
text: text
};
};
export const closeAllModals = () => {
@ -265,4 +264,4 @@ export const setSiteConfig = config => {
type: "SET_SITE_CONFIG",
config: config
};
};
};

View File

@ -8,23 +8,34 @@ import React, { useCallback, useState } from "react";
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../../actions";
const unitTransform = (v)=>{
if(v<1024){
return [Math.round(v),1]
const unitTransform = v => {
if (v < 1024) {
return [Math.round(v), 1];
}
if(v<1024*1024){
return [Math.round(v/1024),1024]
if (v < 1024 * 1024) {
return [Math.round(v / 1024), 1024];
}
if(v<1024*1024*1024){
return [Math.round(v/(1024*1024)),1024*1024]
if (v < 1024 * 1024 * 1024) {
return [Math.round(v / (1024 * 1024)), 1024 * 1024];
}
if(v<1024*1024*1024*1024){
return [Math.round(v/(1024*1024*1024)),1024*1024*1024]
if (v < 1024 * 1024 * 1024 * 1024) {
return [Math.round(v / (1024 * 1024 * 1024)), 1024 * 1024 * 1024];
}
return [Math.round(v/(1024*1024*1024*1024)),1024*1024*1024*1024]
}
return [
Math.round(v / (1024 * 1024 * 1024 * 1024)),
1024 * 1024 * 1024 * 1024
];
};
export default function SizeInput({onChange,min,value,required,label,max,suffix}){
export default function SizeInput({
onChange,
min,
value,
required,
label,
max,
suffix
}) {
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
(vertical, horizontal, msg, color) =>
@ -32,67 +43,86 @@ export default function SizeInput({onChange,min,value,required,label,max,suffix}
[dispatch]
);
const [unit,setUnit] = useState(1);
const [unit, setUnit] = useState(1);
let first = true;
const transform = useCallback(()=>{
const transform = useCallback(() => {
const res = unitTransform(value);
if(first && value !== 0){
if (first && value !== 0) {
setUnit(res[1]);
first = false;
}
return res;
},[value]);
}, [value]);
return (
<FormControl>
<InputLabel htmlFor="component-helper">
{label}
</InputLabel>
<InputLabel htmlFor="component-helper">{label}</InputLabel>
<Input
style={{width:200,}}
style={{ width: 200 }}
value={transform()[0]}
type={"number"}
inputProps={{min:min,step:1}}
onChange={e=>{
if (e.target.value * unit < max){
inputProps={{ min: min, step: 1 }}
onChange={e => {
if (e.target.value * unit < max) {
onChange({
target:{value:(e.target.value * unit).toString()}
})
}else{
ToggleSnackbar("top", "right", "超出最大尺寸限制", "warning");
target: {
value: (e.target.value * unit).toString()
}
});
} else {
ToggleSnackbar(
"top",
"right",
"超出最大尺寸限制",
"warning"
);
}
}}
required = {required}
required={required}
endAdornment={
<InputAdornment position="end">
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={unit}
onChange={(e)=>{
if (transform()[0] * e.target.value < max){
onChange={e => {
if (transform()[0] * e.target.value < max) {
onChange({
target:{value:(transform()[0] * e.target.value).toString()}
})
target: {
value: (
transform()[0] * e.target.value
).toString()
}
});
setUnit(e.target.value);
}else{
ToggleSnackbar("top", "right", "超出最大尺寸限制", "warning");
} else {
ToggleSnackbar(
"top",
"right",
"超出最大尺寸限制",
"warning"
);
}
}}
>
<MenuItem value={1}>B{suffix && suffix}</MenuItem>
<MenuItem value={1024}>KB{suffix && suffix}</MenuItem>
<MenuItem value={1024*1024}>MB{suffix && suffix}</MenuItem>
<MenuItem value={1024*1024*1024}>GB{suffix && suffix}</MenuItem>
<MenuItem value={1024*1024*1024*1024}>TB{suffix && suffix}</MenuItem>
<MenuItem value={1024}>
KB{suffix && suffix}
</MenuItem>
<MenuItem value={1024 * 1024}>
MB{suffix && suffix}
</MenuItem>
<MenuItem value={1024 * 1024 * 1024}>
GB{suffix && suffix}
</MenuItem>
<MenuItem value={1024 * 1024 * 1024 * 1024}>
TB{suffix && suffix}
</MenuItem>
</Select>
</InputAdornment>
}
/>
</FormControl>
)
}
);
}

View File

@ -13,7 +13,24 @@ import ListItemText from "@material-ui/core/ListItemText";
import { lighten, makeStyles, useTheme } from "@material-ui/core/styles";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import { Assignment, CloudDownload, Contacts, Group, Home, Image, InsertDriveFile, Language, ListAlt, Mail, Palette, Person, Settings, SettingsEthernet, Share, Storage } from "@material-ui/icons";
import {
Assignment,
CloudDownload,
Contacts,
Group,
Home,
Image,
InsertDriveFile,
Language,
ListAlt,
Mail,
Palette,
Person,
Settings,
SettingsEthernet,
Share,
Storage
} from "@material-ui/icons";
import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import MenuIcon from "@material-ui/icons/Menu";
@ -139,21 +156,21 @@ const useStyles = makeStyles(theme => ({
},
subMenu: {
backgroundColor: theme.palette.background.default,
paddingTop:0,
paddingBottom:0,
paddingTop: 0,
paddingBottom: 0
},
active: {
backgroundColor: lighten(theme.palette.primary.main, 0.8),
color: theme.palette.primary.main,
"&:hover":{
backgroundColor: lighten(theme.palette.primary.main, 0.7),
},
"&:hover": {
backgroundColor: lighten(theme.palette.primary.main, 0.7)
}
},
activeText:{
fontWeight: 500,
activeText: {
fontWeight: 500
},
activeIcon:{
color: theme.palette.primary.main,
activeIcon: {
color: theme.palette.primary.main
}
}));
@ -201,7 +218,7 @@ const items = [
title: "图像处理",
path: "image",
icon: <Image />
},
}
]
},
{
@ -242,9 +259,9 @@ const items = [
title: "常规任务",
path: "task",
icon: <ListAlt />
},
],
},
}
]
}
];
export default function Dashboard({ content }) {
@ -339,19 +356,29 @@ export default function Dashboard({ content }) {
}
button
className={clsx({
[classes.active]:
location.pathname.startsWith("/admin/" + item.path)
[classes.active]: location.pathname.startsWith(
"/admin/" + item.path
)
})}
key={item.title}
>
<ListItemIcon className={clsx({
[classes.activeIcon]:
location.pathname.startsWith("/admin/" + item.path)
})}>{item.icon}</ListItemIcon>
<ListItemText className={clsx({
[classes.activeText]:
location.pathname.startsWith("/admin/" + item.path)
})} primary={item.title} />
<ListItemIcon
className={clsx({
[classes.activeIcon]: location.pathname.startsWith(
"/admin/" + item.path
)
})}
>
{item.icon}
</ListItemIcon>
<ListItemText
className={clsx({
[classes.activeText]: location.pathname.startsWith(
"/admin/" + item.path
)
})}
primary={item.title}
/>
</ListItem>
);
}
@ -384,16 +411,20 @@ export default function Dashboard({ content }) {
}
className={clsx({
[classes.sub]: open,
[classes.active]:
location.pathname.startsWith("/admin/" + sub.path)
[classes.active]: location.pathname.startsWith(
"/admin/" + sub.path
)
})}
button
key={sub.title}
>
<ListItemIcon className={clsx({
[classes.activeIcon]:
location.pathname.startsWith("/admin/" + sub.path)
})}>
<ListItemIcon
className={clsx({
[classes.activeIcon]: location.pathname.startsWith(
"/admin/" + sub.path
)
})}
>
{sub.icon}
</ListItemIcon>
<ListItemText

View File

@ -63,11 +63,11 @@ export default function AddGroup({ open, onClose, onSubmit }) {
const submit = e => {
e.preventDefault();
const groupCopy = {...group};
const groupCopy = { ...group };
groupCopy.time = parseInt(groupCopy.time) * 86400;
groupCopy.price = parseInt(groupCopy.price) * 100;
groupCopy.score = parseInt(groupCopy.score);
groupCopy.id = (new Date()).valueOf();
groupCopy.id = new Date().valueOf();
groupCopy.des = groupCopy.des.split("\n");
onSubmit(groupCopy);
};
@ -218,7 +218,9 @@ export default function AddGroup({ open, onClose, onSubmit }) {
control={
<Switch
checked={group.highlight}
onChange={handleCheckChange("highlight")}
onChange={handleCheckChange(
"highlight"
)}
/>
}
label="突出展示"

View File

@ -10,16 +10,15 @@ import Input from "@material-ui/core/Input";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import SizeInput from "../Common/SizeInput";
import {makeStyles} from "@material-ui/core/styles";
import { makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles(() => ({
formContainer: {
margin:"8px 0 8px 0",
margin: "8px 0 8px 0"
}
}));
export default function AddPack({ open, onClose,onSubmit }) {
export default function AddPack({ open, onClose, onSubmit }) {
const classes = useStyles();
const [pack, setPack] = useState({
name: "",
@ -38,14 +37,14 @@ export default function AddPack({ open, onClose,onSubmit }) {
const submit = e => {
e.preventDefault();
const packCopy = {...pack};
const packCopy = { ...pack };
packCopy.size = parseInt(packCopy.size);
packCopy.time = parseInt(packCopy.time) * 86400;
packCopy.price = parseInt(packCopy.price) * 100;
packCopy.score = parseInt(packCopy.score);
packCopy.id = (new Date()).valueOf();
packCopy.id = new Date().valueOf();
onSubmit(packCopy);
}
};
return (
<Dialog
@ -99,8 +98,8 @@ export default function AddPack({ open, onClose,onSubmit }) {
<Input
type={"number"}
inputProps={{
min:1,
step:1
min: 1,
step: 1
}}
value={pack.time}
onChange={handleChange("time")}
@ -120,8 +119,8 @@ export default function AddPack({ open, onClose,onSubmit }) {
<Input
type={"number"}
inputProps={{
min:0.01,
step:0.01
min: 0.01,
step: 0.01
}}
value={pack.price}
onChange={handleChange("price")}
@ -141,19 +140,19 @@ export default function AddPack({ open, onClose,onSubmit }) {
<Input
type={"number"}
inputProps={{
min:0,
step:1
min: 0,
step: 1
}}
value={pack.score}
onChange={handleChange("score")}
required
/>
<FormHelperText id="component-helper-text">
使用积分购买时的价格填写为 0 表示不能使用积分购买
使用积分购买时的价格填写为 0
表示不能使用积分购买
</FormHelperText>
</FormControl>
</div>
</DialogContentText>
</DialogContent>
<DialogActions>

View File

@ -17,20 +17,19 @@ import { toggleSnackbar } from "../../../actions";
import API from "../../../middleware/Api";
const useStyles = makeStyles(() => ({
formContainer: {
margin:"8px 0 8px 0",
margin: "8px 0 8px 0"
}
}));
export default function AddRedeem({ open, onClose,products ,onSuccess}) {
export default function AddRedeem({ open, onClose, products, onSuccess }) {
const classes = useStyles();
const [input, setInput] = useState({
num:1,
id:0,
time:1,
num: 1,
id: 0,
time: 1
});
const [loading,setLoading] = useState(false);
const [loading, setLoading] = useState(false);
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
@ -52,13 +51,13 @@ export default function AddRedeem({ open, onClose,products ,onSuccess}) {
input.num = parseInt(input.num);
input.id = parseInt(input.id);
input.time = parseInt(input.time);
input.type=2;
for (let i=0;i<products.length;i++){
if (products[i].id === input.id){
if(products[i].group_id !== undefined){
input.type = 1
}else{
input.type = 0
input.type = 2;
for (let i = 0; i < products.length; i++) {
if (products[i].id === input.id) {
if (products[i].group_id !== undefined) {
input.type = 1;
} else {
input.type = 0;
}
break;
}
@ -71,10 +70,10 @@ export default function AddRedeem({ open, onClose,products ,onSuccess}) {
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
}).then(()=>{
})
.then(() => {
setLoading(false);
});
});
};
return (
@ -97,9 +96,9 @@ export default function AddRedeem({ open, onClose,products ,onSuccess}) {
<Input
type={"number"}
inputProps={{
step:1,
min:1,
max:100,
step: 1,
min: 1,
max: 100
}}
value={input.num}
onChange={handleChange("num")}
@ -118,12 +117,18 @@ export default function AddRedeem({ open, onClose,products ,onSuccess}) {
</InputLabel>
<Select
value={input.id}
onChange={e=>{
handleChange("id")(e)
onChange={e => {
handleChange("id")(e);
}}
>
{products.map(v=>(
<MenuItem key={v.id} value={v.id} data-type={"1"}>{v.name}</MenuItem>
{products.map(v => (
<MenuItem
key={v.id}
value={v.id}
data-type={"1"}
>
{v.name}
</MenuItem>
))}
<MenuItem value={0}>积分</MenuItem>
</Select>
@ -138,8 +143,8 @@ export default function AddRedeem({ open, onClose,products ,onSuccess}) {
<Input
type={"number"}
inputProps={{
step:1,
min:1,
step: 1,
min: 1
}}
value={input.time}
onChange={handleChange("time")}
@ -150,12 +155,14 @@ export default function AddRedeem({ open, onClose,products ,onSuccess}) {
</FormHelperText>
</FormControl>
</div>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button disabled={loading} onClick={onClose} color="default">
<Button
disabled={loading}
onClick={onClose}
color="default"
>
取消
</Button>
<Button disabled={loading} type={"submit"} color="primary">

View File

@ -7,7 +7,7 @@ import DialogActions from "@material-ui/core/DialogActions";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
export default function AlertDialog({title, msg, open, onClose }) {
export default function AlertDialog({ title, msg, open, onClose }) {
return (
<Dialog
open={open}
@ -15,14 +15,10 @@ export default function AlertDialog({title, msg, open, onClose }) {
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{title}
</DialogTitle>
<DialogTitle id="alert-dialog-title">{title}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
<Typography>
{msg}
</Typography>
<Typography>{msg}</Typography>
</DialogContentText>
</DialogContent>
<DialogActions>

View File

@ -36,7 +36,7 @@ const useStyles = makeStyles(theme => ({
}
}));
export default function CreateTheme({ open, onClose,onSubmit }) {
export default function CreateTheme({ open, onClose, onSubmit }) {
const classes = useStyles();
const [theme, setTheme] = useState({
palette: {
@ -121,7 +121,7 @@ export default function CreateTheme({ open, onClose,onSubmit }) {
"#9c27b0"
]}
color={theme.palette.primary.main}
onChangeComplete={(c) => {
onChangeComplete={c => {
setTheme({
...theme,
palette: {
@ -197,7 +197,7 @@ export default function CreateTheme({ open, onClose,onSubmit }) {
"#d500f9"
]}
color={theme.palette.secondary.main}
onChangeComplete={(c) => {
onChangeComplete={c => {
setTheme({
...theme,
palette: {
@ -235,7 +235,7 @@ export default function CreateTheme({ open, onClose,onSubmit }) {
<div className={classes.picker}>
<CompactPicker
color={theme.palette.primary.contrastText}
onChangeComplete={(c) => {
onChangeComplete={c => {
setTheme({
...theme,
palette: {
@ -273,7 +273,7 @@ export default function CreateTheme({ open, onClose,onSubmit }) {
<div className={classes.picker}>
<CompactPicker
color={theme.palette.secondary.contrastText}
onChangeComplete={(c) => {
onChangeComplete={c => {
setTheme({
...theme,
palette: {
@ -339,7 +339,7 @@ export default function CreateTheme({ open, onClose,onSubmit }) {
<Button onClick={onClose} color="default">
取消
</Button>
<Button onClick={()=>onSubmit(theme)} color="primary">
<Button onClick={() => onSubmit(theme)} color="primary">
创建
</Button>
</DialogActions>

View File

@ -13,13 +13,13 @@ import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../../actions";
import API from "../../../middleware/Api";
export default function FileFilter({setFilter,setSearch,open, onClose }) {
const [input,setInput] = useState({
policy_id:"all",
user_id:"",
export default function FileFilter({ setFilter, setSearch, open, onClose }) {
const [input, setInput] = useState({
policy_id: "all",
user_id: ""
});
const [policies,setPolicies] = useState([]);
const [keywords,setKeywords] = useState("");
const [policies, setPolicies] = useState([]);
const [keywords, setKeywords] = useState("");
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
@ -29,10 +29,10 @@ export default function FileFilter({setFilter,setSearch,open, onClose }) {
);
const handleChange = name => event => {
setInput({...input,[name]:event.target.value})
}
setInput({ ...input, [name]: event.target.value });
};
useEffect(()=>{
useEffect(() => {
API.post("/admin/policy/list", {
page: 1,
page_size: 10000,
@ -45,25 +45,25 @@ export default function FileFilter({setFilter,setSearch,open, onClose }) {
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
},[])
}, []);
const submit = () => {
const res = {};
Object.keys(input).forEach(v=>{
if(input[v] !== "all" && input[v] !== ""){
Object.keys(input).forEach(v => {
if (input[v] !== "all" && input[v] !== "") {
res[v] = input[v];
}
})
});
setFilter(res);
if (keywords !== ""){
if (keywords !== "") {
setSearch({
name:keywords,
name: keywords
});
}else{
} else {
setSearch({});
}
onClose();
}
};
return (
<Dialog
@ -74,12 +74,12 @@ export default function FileFilter({setFilter,setSearch,open, onClose }) {
fullWidth
maxWidth={"xs"}
>
<DialogTitle id="alert-dialog-title">
过滤条件
</DialogTitle>
<DialogTitle id="alert-dialog-title">过滤条件</DialogTitle>
<DialogContent>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">存储策略</InputLabel>
<InputLabel id="demo-simple-select-label">
存储策略
</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
@ -92,21 +92,28 @@ export default function FileFilter({setFilter,setSearch,open, onClose }) {
return null;
}
return (
<MenuItem
key={v.ID}
value={v.ID.toString()}
>
<MenuItem key={v.ID} value={v.ID.toString()}>
{v.Name}
</MenuItem>
);
})}
</Select>
</FormControl>
<FormControl fullWidth style={{marginTop:16}}>
<TextField value={input.user_id} onChange={handleChange("user_id")} id="standard-basic" label="上传者ID" />
<FormControl fullWidth style={{ marginTop: 16 }}>
<TextField
value={input.user_id}
onChange={handleChange("user_id")}
id="standard-basic"
label="上传者ID"
/>
</FormControl>
<FormControl fullWidth style={{marginTop:16}}>
<TextField value={keywords} onChange={e=>setKeywords(e.target.value)} id="standard-basic" label="搜索 文件名" />
<FormControl fullWidth style={{ marginTop: 16 }}>
<TextField
value={keywords}
onChange={e => setKeywords(e.target.value)}
id="standard-basic"
label="搜索 文件名"
/>
</FormControl>
</DialogContent>
<DialogActions>

View File

@ -11,7 +11,7 @@ import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import React from "react";
export default function MagicVar({isFile, open, onClose, isSlave }) {
export default function MagicVar({ isFile, open, onClose, isSlave }) {
return (
<Dialog
open={open}
@ -20,7 +20,7 @@ export default function MagicVar({isFile, open, onClose, isSlave }) {
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{isFile?"文件名魔法变量":"路径魔法变量"}
{isFile ? "文件名魔法变量" : "路径魔法变量"}
</DialogTitle>
<DialogContent>
<TableContainer>
@ -34,77 +34,113 @@ export default function MagicVar({isFile, open, onClose, isSlave }) {
</TableHead>
<TableBody>
<TableRow>
<TableCell component="th" scope="row">{"{randomkey16}"}</TableCell>
<TableCell component="th" scope="row">
{"{randomkey16}"}
</TableCell>
<TableCell>16位随机字符</TableCell>
<TableCell>N6IimT5XZP324ACK</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{randomkey8}"}</TableCell>
<TableCell component="th" scope="row">
{"{randomkey8}"}
</TableCell>
<TableCell>8位随机字符</TableCell>
<TableCell>gWz78q30</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{timestamp}"}</TableCell>
<TableCell component="th" scope="row">
{"{timestamp}"}
</TableCell>
<TableCell>秒级时间戳</TableCell>
<TableCell>1582692933</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{timestamp_nano}"}</TableCell>
<TableCell component="th" scope="row">
{"{timestamp_nano}"}
</TableCell>
<TableCell>纳秒级时间戳</TableCell>
<TableCell>1582692933231834600</TableCell>
</TableRow>
{!isSlave && <TableRow>
<TableCell component="th" scope="row">{"{uid}"}</TableCell>
<TableCell>用户ID</TableCell>
<TableCell>1</TableCell>
</TableRow>}
{isFile && <TableRow>
<TableCell component="th" scope="row">{"{originname}"}</TableCell>
<TableCell>原始文件名</TableCell>
<TableCell>MyPico.mp4</TableCell>
</TableRow>}
{!isFile && !isSlave && <TableRow>
<TableCell component="th" scope="row">{"{path}"}</TableCell>
<TableCell>用户上传路径</TableCell>
<TableCell>///</TableCell>
</TableRow>}
{!isSlave && (
<TableRow>
<TableCell component="th" scope="row">
{"{uid}"}
</TableCell>
<TableCell>用户ID</TableCell>
<TableCell>1</TableCell>
</TableRow>
)}
{isFile && (
<TableRow>
<TableCell component="th" scope="row">
{"{originname}"}
</TableCell>
<TableCell>原始文件名</TableCell>
<TableCell>MyPico.mp4</TableCell>
</TableRow>
)}
{!isFile && !isSlave && (
<TableRow>
<TableCell component="th" scope="row">
{"{path}"}
</TableCell>
<TableCell>用户上传路径</TableCell>
<TableCell>///</TableCell>
</TableRow>
)}
<TableRow>
<TableCell component="th" scope="row">{"{date}"}</TableCell>
<TableCell component="th" scope="row">
{"{date}"}
</TableCell>
<TableCell>日期</TableCell>
<TableCell>20060102</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{datetime}"}</TableCell>
<TableCell component="th" scope="row">
{"{datetime}"}
</TableCell>
<TableCell>日期时间</TableCell>
<TableCell>20060102150405</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{year}"}</TableCell>
<TableCell component="th" scope="row">
{"{year}"}
</TableCell>
<TableCell>年份</TableCell>
<TableCell>2006</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{month}"}</TableCell>
<TableCell component="th" scope="row">
{"{month}"}
</TableCell>
<TableCell>月份</TableCell>
<TableCell>01</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{day}"}</TableCell>
<TableCell component="th" scope="row">
{"{day}"}
</TableCell>
<TableCell></TableCell>
<TableCell>02</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{hour}"}</TableCell>
<TableCell component="th" scope="row">
{"{hour}"}
</TableCell>
<TableCell>小时</TableCell>
<TableCell>15</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{minute}"}</TableCell>
<TableCell component="th" scope="row">
{"{minute}"}
</TableCell>
<TableCell>分钟</TableCell>
<TableCell>04</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">{"{second}"}</TableCell>
<TableCell component="th" scope="row">
{"{second}"}
</TableCell>
<TableCell></TableCell>
<TableCell>05</TableCell>
</TableRow>

View File

@ -10,34 +10,34 @@ import Select from "@material-ui/core/Select";
import TextField from "@material-ui/core/TextField";
import React, { useState } from "react";
export default function ShareFilter({setFilter,setSearch,open, onClose }) {
const [input,setInput] = useState({
is_dir:"all",
user_id:"",
export default function ShareFilter({ setFilter, setSearch, open, onClose }) {
const [input, setInput] = useState({
is_dir: "all",
user_id: ""
});
const [keywords,setKeywords] = useState("");
const [keywords, setKeywords] = useState("");
const handleChange = name => event => {
setInput({...input,[name]:event.target.value})
}
setInput({ ...input, [name]: event.target.value });
};
const submit = () => {
const res = {};
Object.keys(input).forEach(v=>{
if(input[v] !== "all" && input[v] !== ""){
Object.keys(input).forEach(v => {
if (input[v] !== "all" && input[v] !== "") {
res[v] = input[v];
}
})
});
setFilter(res);
if (keywords !== ""){
if (keywords !== "") {
setSearch({
source_name:keywords,
source_name: keywords
});
}else{
} else {
setSearch({});
}
onClose();
}
};
return (
<Dialog
@ -48,12 +48,12 @@ export default function ShareFilter({setFilter,setSearch,open, onClose }) {
fullWidth
maxWidth={"xs"}
>
<DialogTitle id="alert-dialog-title">
过滤条件
</DialogTitle>
<DialogTitle id="alert-dialog-title">过滤条件</DialogTitle>
<DialogContent>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">源文件类型</InputLabel>
<InputLabel id="demo-simple-select-label">
源文件类型
</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
@ -65,11 +65,21 @@ export default function ShareFilter({setFilter,setSearch,open, onClose }) {
<MenuItem value={"0"}>文件</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth style={{marginTop:16}}>
<TextField value={input.user_id} onChange={handleChange("user_id")} id="standard-basic" label="上传者ID" />
<FormControl fullWidth style={{ marginTop: 16 }}>
<TextField
value={input.user_id}
onChange={handleChange("user_id")}
id="standard-basic"
label="上传者ID"
/>
</FormControl>
<FormControl fullWidth style={{marginTop:16}}>
<TextField value={keywords} onChange={e=>setKeywords(e.target.value)} id="standard-basic" label="搜索 文件名" />
<FormControl fullWidth style={{ marginTop: 16 }}>
<TextField
value={keywords}
onChange={e => setKeywords(e.target.value)}
id="standard-basic"
label="搜索 文件名"
/>
</FormControl>
</DialogContent>
<DialogActions>

View File

@ -13,13 +13,13 @@ import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../../actions";
import API from "../../../middleware/Api";
export default function UserFilter({setFilter,setSearch,open, onClose }) {
const [input,setInput] = useState({
group_id:"all",
status:"all",
export default function UserFilter({ setFilter, setSearch, open, onClose }) {
const [input, setInput] = useState({
group_id: "all",
status: "all"
});
const [groups,setGroups] = useState([]);
const [keywords,setKeywords] = useState("");
const [groups, setGroups] = useState([]);
const [keywords, setKeywords] = useState("");
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
@ -29,10 +29,10 @@ export default function UserFilter({setFilter,setSearch,open, onClose }) {
);
const handleChange = name => event => {
setInput({...input,[name]:event.target.value})
}
setInput({ ...input, [name]: event.target.value });
};
useEffect(()=>{
useEffect(() => {
API.get("/admin/groups")
.then(response => {
setGroups(response.data);
@ -40,26 +40,26 @@ export default function UserFilter({setFilter,setSearch,open, onClose }) {
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
},[])
}, []);
const submit = () => {
const res = {};
Object.keys(input).forEach(v=>{
if(input[v] !== "all"){
Object.keys(input).forEach(v => {
if (input[v] !== "all") {
res[v] = input[v];
}
})
});
setFilter(res);
if (keywords !== ""){
if (keywords !== "") {
setSearch({
nick:keywords,
email:keywords,
nick: keywords,
email: keywords
});
}else{
} else {
setSearch({});
}
onClose();
}
};
return (
<Dialog
@ -70,12 +70,12 @@ export default function UserFilter({setFilter,setSearch,open, onClose }) {
fullWidth
maxWidth={"xs"}
>
<DialogTitle id="alert-dialog-title">
过滤条件
</DialogTitle>
<DialogTitle id="alert-dialog-title">过滤条件</DialogTitle>
<DialogContent>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">用户组</InputLabel>
<InputLabel id="demo-simple-select-label">
用户组
</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
@ -88,18 +88,17 @@ export default function UserFilter({setFilter,setSearch,open, onClose }) {
return null;
}
return (
<MenuItem
key={v.ID}
value={v.ID.toString()}
>
<MenuItem key={v.ID} value={v.ID.toString()}>
{v.Name}
</MenuItem>
);
})}
</Select>
</FormControl>
<FormControl fullWidth style={{marginTop:16}}>
<InputLabel id="demo-simple-select-label">用户状态</InputLabel>
<FormControl fullWidth style={{ marginTop: 16 }}>
<InputLabel id="demo-simple-select-label">
用户状态
</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
@ -113,8 +112,13 @@ export default function UserFilter({setFilter,setSearch,open, onClose }) {
<MenuItem value={"3"}>超额使用被封禁</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth style={{marginTop:16}}>
<TextField value={keywords} onChange={e=>setKeywords(e.target.value)} id="standard-basic" label="搜索 昵称 / 用户名" />
<FormControl fullWidth style={{ marginTop: 16 }}>
<TextField
value={keywords}
onChange={e => setKeywords(e.target.value)}
id="standard-basic"
label="搜索 昵称 / 用户名"
/>
</FormControl>
</DialogContent>
<DialogActions>

View File

@ -1,14 +1,14 @@
import React, {useCallback, useEffect, useState} from "react";
import {useParams} from "react-router";
import React, { useCallback, useEffect, useState } from "react";
import { useParams } from "react-router";
import API from "../../../middleware/Api";
import {useDispatch} from "react-redux";
import {toggleSnackbar} from "../../../actions";
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../../actions";
import GroupForm from "./GroupForm";
export default function EditGroupPreload( ) {
const [group,setGroup] = useState({});
export default function EditGroupPreload() {
const [group, setGroup] = useState({});
const {id } = useParams();
const { id } = useParams();
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
@ -17,14 +17,13 @@ export default function EditGroupPreload( ) {
[dispatch]
);
useEffect(()=>{
useEffect(() => {
setGroup({});
API.get("/admin/group/" + id)
.then(response => {
// 布尔值转换
["ShareEnabled", "WebDAVEnabled"].forEach(v => {
response.data[v] = response.data[v]?"true":"false";
response.data[v] = response.data[v] ? "true" : "false";
});
[
"archive_download",
@ -33,8 +32,11 @@ export default function EditGroupPreload( ) {
"share_download",
"aria2"
].forEach(v => {
if (response.data.OptionsSerialized[v] !== undefined){
response.data.OptionsSerialized[v] = response.data.OptionsSerialized[v]?"true":"false";
if (response.data.OptionsSerialized[v] !== undefined) {
response.data.OptionsSerialized[v] = response.data
.OptionsSerialized[v]
? "true"
: "false";
}
});
@ -42,40 +44,41 @@ export default function EditGroupPreload( ) {
["MaxStorage", "SpeedLimit"].forEach(v => {
response.data[v] = response.data[v].toString();
});
[
"compress_size",
"decompress_size",
].forEach(v => {
if (response.data.OptionsSerialized[v] !== undefined){
response.data.OptionsSerialized[v] = response.data.OptionsSerialized[v].toString();
["compress_size", "decompress_size"].forEach(v => {
if (response.data.OptionsSerialized[v] !== undefined) {
response.data.OptionsSerialized[
v
] = response.data.OptionsSerialized[v].toString();
}
});
response.data.PolicyList = response.data.PolicyList[0];
// JSON转换
if(response.data.OptionsSerialized.aria2_options === undefined){
response.data.OptionsSerialized.aria2_options = "{}"
}else{
if (
response.data.OptionsSerialized.aria2_options === undefined
) {
response.data.OptionsSerialized.aria2_options = "{}";
} else {
try {
response.data.OptionsSerialized.aria2_options = JSON.stringify(response.data.OptionsSerialized.aria2_options);
}catch (e) {
ToggleSnackbar("top", "right", "Aria2 设置项格式错误", "warning");
response.data.OptionsSerialized.aria2_options = JSON.stringify(
response.data.OptionsSerialized.aria2_options
);
} catch (e) {
ToggleSnackbar(
"top",
"right",
"Aria2 设置项格式错误",
"warning"
);
return;
}
}
setGroup(response.data);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
},[id]);
}, [id]);
return (
<div>
{group.ID !== undefined &&
<GroupForm group={group}/>
}
</div>
);
return <div>{group.ID !== undefined && <GroupForm group={group} />}</div>;
}

View File

@ -69,7 +69,6 @@ function useQuery() {
return new URLSearchParams(useLocation().search);
}
export default function Group() {
const classes = useStyles();
const [groups, setGroups] = useState([]);
@ -91,37 +90,41 @@ export default function Group() {
);
const loadList = () => {
API.post("/admin/group/list", {
page: page,
page_size: pageSize,
order_by: "id desc",
})
.then(response => {
setGroups(response.data.items);
setStatics(response.data.statics);
setTotal(response.data.total);
setPolicies(response.data.policies);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
API.post("/admin/group/list", {
page: page,
page_size: pageSize,
order_by: "id desc"
})
.then(response => {
setGroups(response.data.items);
setStatics(response.data.statics);
setTotal(response.data.total);
setPolicies(response.data.policies);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
};
useEffect(()=>{
if(query.get("code") === "0"){
useEffect(() => {
if (query.get("code") === "0") {
ToggleSnackbar("top", "right", "授权成功", "success");
}else if (query.get("msg") && query.get("msg")!==""){
ToggleSnackbar("top", "right", query.get("msg") + ", "+ query.get("err"), "warning");
} else if (query.get("msg") && query.get("msg") !== "") {
ToggleSnackbar(
"top",
"right",
query.get("msg") + ", " + query.get("err"),
"warning"
);
}
},[location])
}, [location]);
useEffect(() => {
loadList();
}, [page, pageSize]);
const deletePolicy = (id) =>{
API.delete("/admin/group/" + id,)
const deletePolicy = id => {
API.delete("/admin/group/" + id)
.then(() => {
loadList();
ToggleSnackbar("top", "right", "用户组已删除", "success");
@ -129,7 +132,7 @@ export default function Group() {
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
}
};
return (
<div>
@ -156,7 +159,7 @@ export default function Group() {
<TableContainer className={classes.container}>
<Table aria-label="sticky table" size={"small"}>
<TableHead>
<TableRow style={{height:52}}>
<TableRow style={{ height: 52 }}>
{columns.map(column => (
<TableCell
key={column.id}
@ -174,16 +177,20 @@ export default function Group() {
<TableCell>{row.ID}</TableCell>
<TableCell>{row.Name}</TableCell>
<TableCell>
{row.PolicyList !== null && row.PolicyList.map((pid,key)=>{
let res = "";
if (policies[pid]){
res += policies[pid].Name;
}
if (key !== row.PolicyList.length-1){
res += " / ";
}
return res
})}
{row.PolicyList !== null &&
row.PolicyList.map((pid, key) => {
let res = "";
if (policies[pid]) {
res += policies[pid].Name;
}
if (
key !==
row.PolicyList.length - 1
) {
res += " / ";
}
return res;
})}
</TableCell>
<TableCell align={"right"}>
{statics[row.ID] !== undefined &&
@ -195,16 +202,28 @@ export default function Group() {
</TableCell>
<TableCell align={"right"}>
<Tooltip title={"删除"}>
<IconButton onClick={()=>deletePolicy(row.ID)} size={"small"}>
<Delete/>
<IconButton
onClick={() =>
deletePolicy(row.ID)
}
size={"small"}
>
<Delete />
</IconButton>
</Tooltip>
<Tooltip title={"编辑"}>
<IconButton onClick={()=>history.push("/admin/group/edit/" + row.ID)} size={"small"}>
<Edit/>
<IconButton
onClick={() =>
history.push(
"/admin/group/edit/" +
row.ID
)
}
size={"small"}
>
<Edit />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))}

View File

@ -152,8 +152,9 @@ export default function GroupForm(props) {
"share_download",
"aria2"
].forEach(v => {
if (groupCopy.OptionsSerialized[v] !== undefined){
groupCopy.OptionsSerialized[v] = groupCopy.OptionsSerialized[v] === "true";
if (groupCopy.OptionsSerialized[v] !== undefined) {
groupCopy.OptionsSerialized[v] =
groupCopy.OptionsSerialized[v] === "true";
}
});
@ -161,19 +162,20 @@ export default function GroupForm(props) {
["MaxStorage", "SpeedLimit"].forEach(v => {
groupCopy[v] = parseInt(groupCopy[v]);
});
[
"compress_size",
"decompress_size",
].forEach(v => {
if (groupCopy.OptionsSerialized[v] !== undefined){
groupCopy.OptionsSerialized[v] = parseInt(groupCopy.OptionsSerialized[v]);
["compress_size", "decompress_size"].forEach(v => {
if (groupCopy.OptionsSerialized[v] !== undefined) {
groupCopy.OptionsSerialized[v] = parseInt(
groupCopy.OptionsSerialized[v]
);
}
});
groupCopy.PolicyList = [parseInt(groupCopy.PolicyList)];
// JSON转换
try {
groupCopy.OptionsSerialized.aria2_options = JSON.parse(groupCopy.OptionsSerialized.aria2_options);
}catch (e) {
groupCopy.OptionsSerialized.aria2_options = JSON.parse(
groupCopy.OptionsSerialized.aria2_options
);
} catch (e) {
ToggleSnackbar("top", "right", "Aria2 设置项格式错误", "warning");
return;
}
@ -184,7 +186,12 @@ export default function GroupForm(props) {
})
.then(() => {
history.push("/admin/group");
ToggleSnackbar("top", "right", "用户组已"+ (props.group ? "保存" : "添加"), "success");
ToggleSnackbar(
"top",
"right",
"用户组已" + (props.group ? "保存" : "添加"),
"success"
);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
@ -192,7 +199,6 @@ export default function GroupForm(props) {
.then(() => {
setLoading(false);
});
};
return (
@ -205,68 +211,71 @@ export default function GroupForm(props) {
</Typography>
<div className={classes.formContainer}>
{group.ID !== 3 && (
<>
<div className={classes.form}>
<FormControl fullWidth>
<InputLabel htmlFor="component-helper">
用户组名
</InputLabel>
<Input
value={group.Name}
onChange={handleChange("Name")}
required
/>
<FormHelperText id="component-helper-text">
用户组的名称
</FormHelperText>
</FormControl>
</div>
{group.ID !== 3 && <>
<div className={classes.form}>
<FormControl fullWidth>
<InputLabel htmlFor="component-helper">
用户组名
</InputLabel>
<Input
value={group.Name}
onChange={handleChange("Name")}
required
/>
<div className={classes.form}>
<FormControl fullWidth>
<InputLabel htmlFor="component-helper">
存储策略
</InputLabel>
<Select
labelId="demo-mutiple-chip-label"
id="demo-mutiple-chip"
value={group.PolicyList}
onChange={handleChange(
"PolicyList"
)}
input={
<Input id="select-multiple-chip" />
}
>
{Object.keys(policies).map(pid => (
<MenuItem key={pid} value={pid}>
{policies[pid]}
</MenuItem>
))}
</Select>
<FormHelperText id="component-helper-text">
指定用户组的存储策略
</FormHelperText>
</FormControl>
</div>
<div className={classes.form}>
<FormControl fullWidth>
<SizeInput
value={group.MaxStorage}
onChange={handleChange(
"MaxStorage"
)}
min={0}
max={9223372036854775807}
label={"初始容量"}
required
/>
</FormControl>
<FormHelperText id="component-helper-text">
用户组的名称
用户组下的用户初始可用最大容量
</FormHelperText>
</FormControl>
</div>
<div className={classes.form}>
<FormControl fullWidth>
<InputLabel htmlFor="component-helper">
存储策略
</InputLabel>
<Select
labelId="demo-mutiple-chip-label"
id="demo-mutiple-chip"
value={group.PolicyList}
onChange={handleChange("PolicyList")}
input={<Input id="select-multiple-chip" />}
>
{Object.keys(policies).map(pid => (
<MenuItem
key={pid}
value={pid}
>
{policies[pid]}
</MenuItem>
))}
</Select>
<FormHelperText id="component-helper-text">
指定用户组的存储策略
</FormHelperText>
</FormControl>
</div>
<div className={classes.form}>
<FormControl fullWidth>
<SizeInput
value={group.MaxStorage}
onChange={handleChange("MaxStorage")}
min={0}
max={9223372036854775807}
label={"初始容量"}
required
/>
</FormControl>
<FormHelperText id="component-helper-text">
用户组下的用户初始可用最大容量
</FormHelperText>
</div>
</>}
</div>
</>
)}
<div className={classes.form}>
<FormControl fullWidth>
@ -286,27 +295,29 @@ export default function GroupForm(props) {
</FormHelperText>
</div>
{group.ID !== 3 && <div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.ShareEnabled === "true"
}
onChange={handleCheckChange(
"ShareEnabled"
)}
/>
}
label="允许创建分享"
/>
<FormHelperText id="component-helper-text">
关闭后用户无法创建分享链接
</FormHelperText>
</FormControl>
</div>}
{group.ID !== 3 && (
<div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.ShareEnabled ===
"true"
}
onChange={handleCheckChange(
"ShareEnabled"
)}
/>
}
label="允许创建分享"
/>
<FormHelperText id="component-helper-text">
关闭后用户无法创建分享链接
</FormHelperText>
</FormControl>
</div>
)}
<div className={classes.form}>
<FormControl fullWidth>
@ -330,26 +341,30 @@ export default function GroupForm(props) {
</FormControl>
</div>
{group.ID !== 3 && <div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.WebDAVEnabled === "true"
}
onChange={handleCheckChange(
"WebDAVEnabled"
)}
/>
}
label="WebDAV"
/>
<FormHelperText id="component-helper-text">
关闭后用户无法通过 WebDAV 协议连接至网盘
</FormHelperText>
</FormControl>
</div>}
{group.ID !== 3 && (
<div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.WebDAVEnabled ===
"true"
}
onChange={handleCheckChange(
"WebDAVEnabled"
)}
/>
}
label="WebDAV"
/>
<FormHelperText id="component-helper-text">
关闭后用户无法通过 WebDAV
协议连接至网盘
</FormHelperText>
</FormControl>
</div>
)}
<div className={classes.form}>
<FormControl fullWidth>
@ -374,28 +389,29 @@ export default function GroupForm(props) {
</FormControl>
</div>
{group.ID !== 3 && <div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.OptionsSerialized
.aria2 === "true"
}
onChange={handleOptionCheckChange(
"aria2"
)}
/>
}
label="离线下载"
/>
<FormHelperText id="component-helper-text">
是否允许用户创建离线下载任务
</FormHelperText>
</FormControl>
</div>}
{group.ID !== 3 && (
<div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.OptionsSerialized
.aria2 === "true"
}
onChange={handleOptionCheckChange(
"aria2"
)}
/>
}
label="离线下载"
/>
<FormHelperText id="component-helper-text">
是否允许用户创建离线下载任务
</FormHelperText>
</FormControl>
</div>
)}
<Collapse in={group.OptionsSerialized.aria2 === "true"}>
<div className={classes.form}>
@ -445,27 +461,29 @@ export default function GroupForm(props) {
</FormControl>
</div>
{group.ID !== 3 && <div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.OptionsSerialized
.archive_task === "true"
}
onChange={handleOptionCheckChange(
"archive_task"
)}
/>
}
label="压缩/解压缩 任务"
/>
<FormHelperText id="component-helper-text">
是否用户创建 压缩/解压缩 任务
</FormHelperText>
</FormControl>
</div>}
{group.ID !== 3 && (
<div className={classes.form}>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={
group.OptionsSerialized
.archive_task === "true"
}
onChange={handleOptionCheckChange(
"archive_task"
)}
/>
}
label="压缩/解压缩 任务"
/>
<FormHelperText id="component-helper-text">
是否用户创建 压缩/解压缩 任务
</FormHelperText>
</FormControl>
</div>
)}
<Collapse
in={group.OptionsSerialized.archive_task === "true"}

View File

@ -17,11 +17,31 @@ import ListItemText from "@material-ui/core/ListItemText";
import Paper from "@material-ui/core/Paper";
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import { Description, Favorite, FileCopy, Forum, GitHub, Home, Launch, Lock, People, Public, Telegram } from "@material-ui/icons";
import {
Description,
Favorite,
FileCopy,
Forum,
GitHub,
Home,
Launch,
Lock,
People,
Public,
Telegram
} from "@material-ui/icons";
import axios from "axios";
import React, { useCallback, useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts";
import {
CartesianGrid,
Legend,
Line,
LineChart,
Tooltip,
XAxis,
YAxis
} from "recharts";
import ResponsiveContainer from "recharts/lib/component/ResponsiveContainer";
import TimeAgo from "timeago-react";
import { toggleSnackbar } from "../../actions";
@ -98,13 +118,15 @@ export default function Index() {
[dispatch]
);
const ResetSiteURL = ()=>{
const ResetSiteURL = () => {
setOpen(false);
API.patch("/admin/setting",{
options:[{
key:"siteURL",
value:window.location.origin,
}],
API.patch("/admin/setting", {
options: [
{
key: "siteURL",
value: window.location.origin
}
]
})
.then(() => {
setSiteURL(window.location.origin);
@ -113,7 +135,7 @@ export default function Index() {
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
}
};
useEffect(() => {
API.get("/admin/summary")
@ -136,7 +158,10 @@ export default function Index() {
});
setVersion(response.data.version);
setSiteURL(response.data.siteURL);
if (response.data.siteURL === "" || response.data.siteURL !== window.location.origin){
if (
response.data.siteURL === "" ||
response.data.siteURL !== window.location.origin
) {
setOpen(true);
}
})
@ -165,27 +190,36 @@ export default function Index() {
<Grid container spacing={3}>
<Dialog
open={open}
onClose={()=>setOpen(false)}
onClose={() => setOpen(false)}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"确定站点URL设置"}</DialogTitle>
<DialogTitle id="alert-dialog-title">
{"确定站点URL设置"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
<Typography>
{siteURL === "" && "您尚未设定站点URL是否要将其设定为当前的 "+ window.location.origin + " ?"}
{siteURL !== "" && "您设置的站点URL与当前实际不一致是否要将其设定为当前的 "+ window.location.origin + " ?"}
{siteURL === "" &&
"您尚未设定站点URL是否要将其设定为当前的 " +
window.location.origin +
" ?"}
{siteURL !== "" &&
"您设置的站点URL与当前实际不一致是否要将其设定为当前的 " +
window.location.origin +
" ?"}
</Typography>
<Typography>
此设置非常重要请确保其与您站点的实际地址一致你可以在 参数设置 - 站点信息 中更改此设置
此设置非常重要请确保其与您站点的实际地址一致你可以在
参数设置 - 站点信息 中更改此设置
</Typography>
</DialogContentText>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={()=>setOpen(false)} color="default">
<Button onClick={() => setOpen(false)} color="default">
忽略
</Button>
<Button onClick={()=>ResetSiteURL()} color="primary">
<Button onClick={() => ResetSiteURL()} color="primary">
更改
</Button>
</DialogActions>
@ -294,7 +328,10 @@ export default function Index() {
Cloudreve
</Typography>
<Typography className={classes.version}>
{version.backend} {version.is_pro === "true" && <Chip size="small" label="Pro" />}
{version.backend}{" "}
{version.is_pro === "true" && (
<Chip size="small" label="Pro" />
)}
</Typography>
</div>
</div>
@ -334,9 +371,7 @@ export default function Index() {
<ListItem
button
onClick={() =>
window.open(
"https://docs.cloudreve.org/"
)
window.open("https://docs.cloudreve.org/")
}
>
<ListItemIcon>
@ -385,7 +420,7 @@ export default function Index() {
)
}
>
<ListItemIcon style={{color:"#ff789d"}}>
<ListItemIcon style={{ color: "#ff789d" }}>
<Favorite />
</ListItemIcon>
<ListItemText primary="捐助开发者" />
@ -400,58 +435,72 @@ export default function Index() {
<Grid item xs={12} md={8} lg={9}>
<Paper className={classes.paper}>
<List>
{news && news.map((v) => (
<>
<ListItem button alignItems="flex-start"
onClick={()=>window.open("https://forum.cloudreve.org/d/" + v.id)}
>
<ListItemAvatar>
<Avatar
alt="Travis Howard"
src={
newsUsers[
v.relationships.startUser
.data.id
] &&
newsUsers[
v.relationships.startUser
.data.id
].avatarUrl
}
/>
</ListItemAvatar>
<ListItemText
primary={v.attributes.title}
secondary={
<React.Fragment>
<Typography
component="span"
variant="body2"
className={classes.inline}
color="textPrimary"
>
{newsUsers[
{news &&
news.map(v => (
<>
<ListItem
button
alignItems="flex-start"
onClick={() =>
window.open(
"https://forum.cloudreve.org/d/" +
v.id
)
}
>
<ListItemAvatar>
<Avatar
alt="Travis Howard"
src={
newsUsers[
v.relationships
.startUser.data.id
] &&
newsUsers[
newsUsers[
v.relationships
.startUser.data.id
].avatarUrl
}
/>
</ListItemAvatar>
<ListItemText
primary={v.attributes.title}
secondary={
<React.Fragment>
<Typography
component="span"
variant="body2"
className={
classes.inline
}
color="textPrimary"
>
{newsUsers[
v.relationships
.startUser.data
.id
].username}{" "}
</Typography>
发表于{" "}
<TimeAgo
datetime={v.attributes.startTime}
locale='zh_CN'
/>
</React.Fragment>
}
/>
</ListItem>
<Divider />
</>
))}
] &&
newsUsers[
v.relationships
.startUser
.data.id
].username}{" "}
</Typography>
发表于{" "}
<TimeAgo
datetime={
v.attributes
.startTime
}
locale="zh_CN"
/>
</React.Fragment>
}
/>
</ListItem>
<Divider />
</>
))}
</List>
</Paper>
</Grid>

View File

@ -20,11 +20,10 @@ const useStyles = makeStyles(theme => ({
},
content: {
padding: theme.spacing(2)
},
}
}));
export default function AddPolicyParent( ) {
export default function AddPolicyParent() {
const classes = useStyles();
const { type } = useParams();
@ -32,14 +31,14 @@ export default function AddPolicyParent( ) {
return (
<div>
<Paper square className={classes.content}>
{type==="local"&&<LocalGuide/>}
{type==="remote"&&<RemoteGuide/>}
{type==="qiniu"&&<QiniuGuide/>}
{type==="oss"&&<OSSGuide/>}
{type==="upyun"&&<UpyunGuide/>}
{type==="cos"&&<COSGuide/>}
{type==="onedrive"&&<OneDriveGuide/>}
{type==="s3"&&(<S3Guide/>)}
{type === "local" && <LocalGuide />}
{type === "remote" && <RemoteGuide />}
{type === "qiniu" && <QiniuGuide />}
{type === "oss" && <OSSGuide />}
{type === "upyun" && <UpyunGuide />}
{type === "cos" && <COSGuide />}
{type === "onedrive" && <OneDriveGuide />}
{type === "s3" && <S3Guide />}
</Paper>
</div>
);

View File

@ -24,16 +24,15 @@ const useStyles = makeStyles(theme => ({
},
content: {
padding: theme.spacing(2)
},
}
}));
export default function EditPolicyPreload( ) {
export default function EditPolicyPreload() {
const classes = useStyles();
const [type,setType] = useState("");
const [policy,setPolicy] = useState({});
const [type, setType] = useState("");
const [policy, setPolicy] = useState({});
const { mode,id } = useParams();
const { mode, id } = useParams();
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
@ -42,46 +41,52 @@ export default function EditPolicyPreload( ) {
[dispatch]
);
useEffect(()=>{
useEffect(() => {
setType("");
API.get("/admin/policy/" + id)
.then(response => {
response.data.IsOriginLinkEnable = response.data.IsOriginLinkEnable ? "true" : "false";
response.data.AutoRename = response.data.AutoRename ? "true" : "false";
response.data.MaxSize = response.data.MaxSize.toString();
response.data.IsPrivate = response.data.IsPrivate ? "true" : "false";
response.data.OptionsSerialized.file_type =
response.data.OptionsSerialized.file_type ?
response.data.OptionsSerialized.file_type.join(","):
"";
response.data.IsOriginLinkEnable = response.data
.IsOriginLinkEnable
? "true"
: "false";
response.data.AutoRename = response.data.AutoRename
? "true"
: "false";
response.data.MaxSize = response.data.MaxSize.toString();
response.data.IsPrivate = response.data.IsPrivate
? "true"
: "false";
response.data.OptionsSerialized.file_type = response.data
.OptionsSerialized.file_type
? response.data.OptionsSerialized.file_type.join(",")
: "";
setPolicy(response.data);
setType(response.data.Type);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
},[id]);
}, [id]);
return (
<div>
<Paper square className={classes.content}>
{mode === "guide" &&
{mode === "guide" && (
<>
{type==="local"&&<LocalGuide policy={policy}/>}
{type==="remote"&&<RemoteGuide policy={policy}/>}
{type==="qiniu"&&<QiniuGuide policy={policy}/>}
{type==="oss"&&<OSSGuide policy={policy}/>}
{type==="upyun"&&<UpyunGuide policy={policy}/>}
{type==="cos"&&<COSGuide policy={policy}/>}
{type==="onedrive"&&<OneDriveGuide policy={policy}/>}
{type==="s3"&&<S3Guide policy={policy}/>}
{type === "local" && <LocalGuide policy={policy} />}
{type === "remote" && <RemoteGuide policy={policy} />}
{type === "qiniu" && <QiniuGuide policy={policy} />}
{type === "oss" && <OSSGuide policy={policy} />}
{type === "upyun" && <UpyunGuide policy={policy} />}
{type === "cos" && <COSGuide policy={policy} />}
{type === "onedrive" && (
<OneDriveGuide policy={policy} />
)}
{type === "s3" && <S3Guide policy={policy} />}
</>
}
)}
{mode === "pro" && type !== "" &&
<EditPro policy={policy}/>
}
{mode === "pro" && type !== "" && <EditPro policy={policy} />}
</Paper>
</div>
);

View File

@ -67,16 +67,17 @@ const useStyles = makeStyles(theme => ({
marginRight: theme.spacing(1)
},
viewButtonLabel: { textTransform: "none" },
"@global":{
"code":{
"@global": {
code: {
color: "rgba(0, 0, 0, 0.87)",
display: "inline-block",
padding: "2px 6px",
fontFamily:" Consolas, \"Liberation Mono\", Menlo, Courier, monospace",
fontFamily:
' Consolas, "Liberation Mono", Menlo, Courier, monospace',
borderRadius: "2px",
backgroundColor: "rgba(255,229,100,0.1)",
},
},
backgroundColor: "rgba(255,229,100,0.1)"
}
}
}));
const steps = [
@ -119,25 +120,31 @@ export default function COSGuide(props) {
const [skipped, setSkipped] = React.useState(new Set());
const [magicVar, setMagicVar] = useState("");
const [useCDN, setUseCDN] = useState("false");
const [policy, setPolicy] = useState(props.policy?props.policy:{
Type: "cos",
Name: "",
SecretKey: "",
AccessKey: "",
BaseURL: "",
Server: "",
IsPrivate: "true",
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
MaxSize: "0",
OptionsSerialized: {
file_type: "",
}
});
const [policyID,setPolicyID] = useState(props.policy?props.policy.ID:0);
const [region,setRegion] = useState("ap-chengdu");
const [policy, setPolicy] = useState(
props.policy
? props.policy
: {
Type: "cos",
Name: "",
SecretKey: "",
AccessKey: "",
BaseURL: "",
Server: "",
IsPrivate: "true",
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
MaxSize: "0",
OptionsSerialized: {
file_type: ""
}
}
);
const [policyID, setPolicyID] = useState(
props.policy ? props.policy.ID : 0
);
const [region, setRegion] = useState("ap-chengdu");
const handleChange = name => event => {
setPolicy({
@ -174,8 +181,8 @@ export default function COSGuide(props) {
const policyCopy = { ...policy };
policyCopy.OptionsSerialized = { ...policyCopy.OptionsSerialized };
if (useCDN === "false"){
policyCopy.BaseURL = policy.Server
if (useCDN === "false") {
policyCopy.BaseURL = policy.Server;
}
// 类型转换
@ -198,7 +205,12 @@ export default function COSGuide(props) {
policy: policyCopy
})
.then(response => {
ToggleSnackbar("top", "right", "存储策略已"+ (props.policy ? "保存" : "添加"), "success");
ToggleSnackbar(
"top",
"right",
"存储策略已" + (props.policy ? "保存" : "添加"),
"success"
);
setActiveStep(4);
setPolicyID(response.data);
})
@ -212,7 +224,7 @@ export default function COSGuide(props) {
setLoading(false);
};
const createCORS = ()=>{
const createCORS = () => {
setLoading(true);
API.post("/admin/policy/cors", {
id: policyID
@ -227,14 +239,13 @@ export default function COSGuide(props) {
.then(() => {
setLoading(false);
});
};
}
const creatCallback = ()=>{
const creatCallback = () => {
setLoading(true);
API.post("/admin/policy/scf", {
id: policyID,
region:region,
region: region
})
.then(() => {
ToggleSnackbar("top", "right", "回调云函数已添加", "success");
@ -246,11 +257,13 @@ export default function COSGuide(props) {
.then(() => {
setLoading(false);
});
}
};
return (
<div>
<Typography variant={"h6"}>{props.policy ? "修改" : "添加"} 腾讯云 COS 存储策略</Typography>
<Typography variant={"h6"}>
{props.policy ? "修改" : "添加"} 腾讯云 COS 存储策略
</Typography>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
@ -279,15 +292,15 @@ export default function COSGuide(props) {
setActiveStep(1);
}}
>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>0</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
在使用 腾讯云 COS 储策略前请确保您在 参数设置 - 站点信息
- 站点URL 中填写的 地址与实际相符并且
在使用 腾讯云 COS 储策略前请确保您在 参数设置
- 站点信息 - 站点URL 中填写的
地址与实际相符并且
<strong>能够被外网正常访问</strong>
</Typography>
</div>
@ -301,7 +314,9 @@ export default function COSGuide(props) {
<Typography variant={"body2"}>
前往
<Link
href={"https://console.cloud.tencent.com/cos5"}
href={
"https://console.cloud.tencent.com/cos5"
}
target={"_blank"}
>
COS 管理控制台
@ -317,7 +332,8 @@ export default function COSGuide(props) {
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
转到所创建存储桶的基础配置页面<code>空间名称</code>
转到所创建存储桶的基础配置页面
<code>空间名称</code>
</Typography>
<div className={classes.form}>
<FormControl fullWidth>
@ -326,8 +342,9 @@ export default function COSGuide(props) {
</InputLabel>
<Input
inputProps={{
pattern:"[a-z0-9-]+-[0-9]+",
title:"空间名格式不正确, 举例ccc-1252109809"
pattern: "[a-z0-9-]+-[0-9]+",
title:
"空间名格式不正确, 举例ccc-1252109809"
}}
required
value={policy.BucketName}
@ -382,8 +399,9 @@ export default function COSGuide(props) {
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
转到所创建 Bucket 的基础配置填写<code>基本信息</code>
给出的 <code>访问域名</code>
转到所创建 Bucket 的基础配置填写
<code>基本信息</code> {" "}
<code>访问域名</code>
</Typography>
<div className={classes.form}>
<DomainInput
@ -409,8 +427,8 @@ export default function COSGuide(props) {
<RadioGroup
required
value={useCDN}
onChange={e=>{
setUseCDN(e.target.value)
onChange={e => {
setUseCDN(e.target.value);
}}
row
>
@ -442,11 +460,17 @@ export default function COSGuide(props) {
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
前往
<Link href={"https://console.cloud.tencent.com/cdn/access/guid"} target={"_blank"}>
<Link
href={
"https://console.cloud.tencent.com/cdn/access/guid"
}
target={"_blank"}
>
腾讯云 CDN 管理控制台
</Link>
创建 CDN 加速域名并设定源站为刚创建的 COS 存储桶在下方填写
CDN 加速域名并选择是否使用 HTTPS
创建 CDN 加速域名并设定源站为刚创建的 COS
存储桶在下方填写 CDN
加速域名并选择是否使用 HTTPS
</Typography>
<div className={classes.form}>
<DomainInput
@ -462,17 +486,24 @@ export default function COSGuide(props) {
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>{getNumber(6,[
useCDN === "true"
])}</div>
<div className={classes.stepNumber}>
{getNumber(6, [useCDN === "true"])}
</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
在腾讯云
<Link href={"https://console.cloud.tencent.com/cam/capi"} target={"_blank"}>
<Link
href={
"https://console.cloud.tencent.com/cam/capi"
}
target={"_blank"}
>
访问密钥
</Link>
页面获取 一对访问密钥并填写在下方请确保这对密钥拥有 COS SCF 服务的访问权限
页面获取
一对访问密钥并填写在下方请确保这对密钥拥有
COS SCF 服务的访问权限
</Typography>
<div className={classes.form}>
<FormControl fullWidth>
@ -482,8 +513,8 @@ export default function COSGuide(props) {
<Input
required
inputProps={{
pattern:"\\S+" ,
title:"不能含有空格"
pattern: "\\S+",
title: "不能含有空格"
}}
value={policy.AccessKey}
onChange={handleChange("AccessKey")}
@ -498,8 +529,8 @@ export default function COSGuide(props) {
<Input
required
inputProps={{
pattern:"\\S+" ,
title:"不能含有空格"
pattern: "\\S+",
title: "不能含有空格"
}}
value={policy.SecretKey}
onChange={handleChange("SecretKey")}
@ -511,9 +542,9 @@ export default function COSGuide(props) {
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>{getNumber(7,[
useCDN === "true"
])}</div>
<div className={classes.stepNumber}>
{getNumber(7, [useCDN === "true"])}
</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
@ -567,9 +598,9 @@ export default function COSGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("path")
onClick={e => {
e.preventDefault();
setMagicVar("path");
}}
>
路径魔法变量列表
@ -602,9 +633,9 @@ export default function COSGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("file")
onClick={e => {
e.preventDefault();
setMagicVar("file");
}}
>
文件名魔法变量列表
@ -703,14 +734,22 @@ export default function COSGuide(props) {
<RadioGroup
required
value={policy.IsOriginLinkEnable}
onChange={e=>{
if (policy.IsPrivate === "true" && e.target.value==="true"){
ToggleSnackbar("top", "right","私有空间无法开启此功能", "warning");
return
onChange={e => {
if (
policy.IsPrivate === "true" &&
e.target.value === "true"
) {
ToggleSnackbar(
"top",
"right",
"私有空间无法开启此功能",
"warning"
);
return;
}
handleChange(
"IsOriginLinkEnable"
)(e)
handleChange("IsOriginLinkEnable")(
e
);
}}
row
>
@ -755,10 +794,7 @@ export default function COSGuide(props) {
)}
{activeStep === 3 && (
<form
className={classes.stepContent}
onSubmit={submitPolicy}
>
<form className={classes.stepContent} onSubmit={submitPolicy}>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>1</div>
@ -952,11 +988,13 @@ export default function COSGuide(props) {
{activeStep === 4 && (
<form className={classes.stepContent}>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}/>
<div className={classes.stepNumberContainer} />
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
COS 存储桶 需要正确配置跨域策略后才能使用 Web 端上传文件Cloudreve
可以帮您自动设置您也可以参考文档步骤手动设置如果您已设置过此 Bucket 的跨域策略此步骤可以跳过
COS 存储桶 需要正确配置跨域策略后才能使用 Web
端上传文件Cloudreve
可以帮您自动设置您也可以参考文档步骤手动设置如果您已设置过此
Bucket 的跨域策略此步骤可以跳过
</Typography>
<div className={classes.form}>
<Button
@ -964,7 +1002,7 @@ export default function COSGuide(props) {
color={"secondary"}
variant={"contained"}
className={classes.button}
onClick={()=>createCORS()}
onClick={() => createCORS()}
classes={{ label: classes.viewButtonLabel }}
>
Cloudreve 帮我设置
@ -976,16 +1014,18 @@ export default function COSGuide(props) {
<Button
color={"default"}
className={classes.button}
onClick={()=>{
setActiveStep(prevActiveStep => prevActiveStep + 1);
onClick={() => {
setActiveStep(
prevActiveStep => prevActiveStep + 1
);
setSkipped(prevSkipped => {
const newSkipped = new Set(prevSkipped.values());
const newSkipped = new Set(
prevSkipped.values()
);
newSkipped.add(activeStep);
return newSkipped;
});
}
}
}}
>
跳过
</Button>{" "}
@ -996,16 +1036,28 @@ export default function COSGuide(props) {
{activeStep === 5 && (
<form className={classes.stepContent}>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}/>
<div className={classes.stepNumberContainer} />
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
COS 存储桶 客户端直传需要借助腾讯云的
<Link href={"https://console.cloud.tencent.com/scf/index?rid=16"} target={"_blank"}>云函数</Link>
产品以确保上传回调可控如果您打算将此存储策略自用或者分配给可信赖用户组此步骤可以跳过
如果是作为公有使用请务必创建回调云函数<br/><br/>
<Link
href={
"https://console.cloud.tencent.com/scf/index?rid=16"
}
target={"_blank"}
>
云函数
</Link>
产品以确保上传回调可控如果您打算将此存储策略自用或者分配给可信赖用户组此步骤可以跳过
如果是作为公有使用请务必创建回调云函数
<br />
<br />
</Typography>
<Typography variant={"body2"}>
Cloudreve 可以尝试帮你自动创建回调云函数请选择
COS 存储桶 所在地域后继续
创建可能会花费数秒钟请耐心等待创建前请确保您的腾讯云账号已开启云函数服务
</Typography>
<Typography variant={"body2"}>Cloudreve 可以尝试帮你自动创建回调云函数请选择 COS 存储桶 所在地域后继续
创建可能会花费数秒钟请耐心等待创建前请确保您的腾讯云账号已开启云函数服务</Typography>
<div className={classes.form}>
<FormControl>
@ -1014,19 +1066,41 @@ export default function COSGuide(props) {
</InputLabel>
<Select
value={region}
onChange={e=>setRegion(e.target.value)}
onChange={e =>
setRegion(e.target.value)
}
required
>
<MenuItem value={"ap-beijing"}>华北地区(北京)</MenuItem>
<MenuItem value={"ap-chengdu"}>西南地区(成都)</MenuItem>
<MenuItem value={"ap-guangzhou"}>华南地区(广州)</MenuItem>
<MenuItem value={"ap-guangzhou-open"}>华南地区(广州Open)</MenuItem>
<MenuItem value={"ap-hongkong"}>港澳台地区(中国香港)</MenuItem>
<MenuItem value={"ap-mumbai"}>亚太南部(孟买)</MenuItem>
<MenuItem value={"ap-shanghai"}>华东地区(上海)</MenuItem>
<MenuItem value={"ap-singapore"}>亚太东南(新加坡)</MenuItem>
<MenuItem value={"na-siliconvalley"}>美国西部(硅谷)</MenuItem>
<MenuItem value={"na-toronto"}>北美地区(多伦多)</MenuItem>
<MenuItem value={"ap-beijing"}>
华北地区(北京)
</MenuItem>
<MenuItem value={"ap-chengdu"}>
西南地区(成都)
</MenuItem>
<MenuItem value={"ap-guangzhou"}>
华南地区(广州)
</MenuItem>
<MenuItem value={"ap-guangzhou-open"}>
华南地区(广州Open)
</MenuItem>
<MenuItem value={"ap-hongkong"}>
港澳台地区(中国香港)
</MenuItem>
<MenuItem value={"ap-mumbai"}>
亚太南部(孟买)
</MenuItem>
<MenuItem value={"ap-shanghai"}>
华东地区(上海)
</MenuItem>
<MenuItem value={"ap-singapore"}>
亚太东南(新加坡)
</MenuItem>
<MenuItem value={"na-siliconvalley"}>
美国西部(硅谷)
</MenuItem>
<MenuItem value={"na-toronto"}>
北美地区(多伦多)
</MenuItem>
</Select>
</FormControl>
</div>
@ -1037,7 +1111,7 @@ export default function COSGuide(props) {
color={"secondary"}
variant={"contained"}
className={classes.button}
onClick={()=>creatCallback()}
onClick={() => creatCallback()}
classes={{ label: classes.viewButtonLabel }}
>
Cloudreve 帮我创建
@ -1049,16 +1123,18 @@ export default function COSGuide(props) {
<Button
color={"default"}
className={classes.button}
onClick={()=>{
setActiveStep(prevActiveStep => prevActiveStep + 1);
onClick={() => {
setActiveStep(
prevActiveStep => prevActiveStep + 1
);
setSkipped(prevSkipped => {
const newSkipped = new Set(prevSkipped.values());
const newSkipped = new Set(
prevSkipped.values()
);
newSkipped.add(activeStep);
return newSkipped;
});
}
}
}}
>
跳过
</Button>{" "}
@ -1069,7 +1145,9 @@ export default function COSGuide(props) {
{activeStep === 6 && (
<>
<form className={classes.stepContent}>
<Typography>存储策略已{props.policy ? "保存" : "添加"}</Typography>
<Typography>
存储策略已{props.policy ? "保存" : "添加"}
</Typography>
<Typography variant={"body2"} color={"textSecondary"}>
要使用此存储策略请到用户组管理页面为相应用户组绑定此存储策略
</Typography>

View File

@ -104,7 +104,7 @@ export default function LocalGuide(props) {
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
BaseURL: "",
IsPrivate:"true",
IsPrivate: "true",
MaxSize: "0",
OptionsSerialized: {
file_type: ""
@ -249,9 +249,9 @@ export default function LocalGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("path")
onClick={e => {
e.preventDefault();
setMagicVar("path");
}}
>
路径魔法变量列表
@ -284,9 +284,9 @@ export default function LocalGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("file")
onClick={e => {
e.preventDefault();
setMagicVar("file");
}}
>
文件名魔法变量列表

View File

@ -95,26 +95,30 @@ export default function RemoteGuide(props) {
const [activeStep, setActiveStep] = useState(0);
const [loading, setLoading] = useState(false);
const [skipped,] = React.useState(new Set());
const [skipped] = React.useState(new Set());
const [magicVar, setMagicVar] = useState("");
// const [useCDN, setUseCDN] = useState("false");
const [policy, setPolicy] = useState(props.policy?props.policy:{
Type: "qiniu",
Name: "",
SecretKey: "",
AccessKey: "",
BaseURL: "",
IsPrivate: "true",
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
MaxSize: "0",
OptionsSerialized: {
file_type: "",
mimetype:"",
}
});
const [policy, setPolicy] = useState(
props.policy
? props.policy
: {
Type: "qiniu",
Name: "",
SecretKey: "",
AccessKey: "",
BaseURL: "",
IsPrivate: "true",
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
MaxSize: "0",
OptionsSerialized: {
file_type: "",
mimetype: ""
}
}
);
const handleChange = name => event => {
setPolicy({
@ -171,7 +175,12 @@ export default function RemoteGuide(props) {
policy: policyCopy
})
.then(() => {
ToggleSnackbar("top", "right", "存储策略已"+ (props.policy ? "保存" : "添加"), "success");
ToggleSnackbar(
"top",
"right",
"存储策略已" + (props.policy ? "保存" : "添加"),
"success"
);
setActiveStep(5);
})
.catch(error => {
@ -186,7 +195,9 @@ export default function RemoteGuide(props) {
return (
<div>
<Typography variant={"h6"}>{props.policy ? "修改" : "添加"} 七牛 存储策略</Typography>
<Typography variant={"h6"}>
{props.policy ? "修改" : "添加"} 七牛 存储策略
</Typography>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
@ -215,15 +226,14 @@ export default function RemoteGuide(props) {
setActiveStep(1);
}}
>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>0</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
在使用七牛存储策略前请确保您在 参数设置 - 站点信息
- 站点URL 中填写的 地址与实际相符并且
在使用七牛存储策略前请确保您在 参数设置 -
站点信息 - 站点URL 中填写的 地址与实际相符并且
<strong>能够被外网正常访问</strong>
</Typography>
</div>
@ -394,9 +404,9 @@ export default function RemoteGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("path")
onClick={e => {
e.preventDefault();
setMagicVar("path");
}}
>
路径魔法变量列表
@ -429,9 +439,9 @@ export default function RemoteGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("file")
onClick={e => {
e.preventDefault();
setMagicVar("file");
}}
>
文件名魔法变量列表
@ -530,14 +540,22 @@ export default function RemoteGuide(props) {
<RadioGroup
required
value={policy.IsOriginLinkEnable}
onChange={e=>{
if (policy.IsPrivate === "true" && e.target.value==="true"){
ToggleSnackbar("top", "right","私有空间无法开启此功能", "warning");
return
onChange={e => {
if (
policy.IsPrivate === "true" &&
e.target.value === "true"
) {
ToggleSnackbar(
"top",
"right",
"私有空间无法开启此功能",
"warning"
);
return;
}
handleChange(
"IsOriginLinkEnable"
)(e)
handleChange("IsOriginLinkEnable")(
e
);
}}
row
>
@ -762,9 +780,9 @@ export default function RemoteGuide(props) {
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>
{getNumber(3,[
{getNumber(3, [
policy.MaxSize !== "0",
policy.OptionsSerialized.file_type !== "",
policy.OptionsSerialized.file_type !== ""
])}
</div>
</div>
@ -789,8 +807,7 @@ export default function RemoteGuide(props) {
...policy,
OptionsSerialized: {
...policy.OptionsSerialized,
mimetype:
"image/*"
mimetype: "image/*"
}
});
} else {
@ -829,16 +846,18 @@ export default function RemoteGuide(props) {
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>
{getNumber(4,[
{getNumber(4, [
policy.MaxSize !== "0",
policy.OptionsSerialized.file_type !== "",
policy.OptionsSerialized.file_type !==
""
])}
</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
输入允许上传的 MimeType多个请以半角逗号 ,
隔开七牛服务器会侦测文件内容以判断 MimeType再用判断值跟指定值进行匹配匹配成功则允许上传
隔开七牛服务器会侦测文件内容以判断
MimeType再用判断值跟指定值进行匹配匹配成功则允许上传
</Typography>
<div className={classes.form}>
<FormControl fullWidth>
@ -925,7 +944,9 @@ export default function RemoteGuide(props) {
{activeStep === 5 && (
<>
<form className={classes.stepContent}>
<Typography>存储策略已{props.policy ? "保存" : "添加"}</Typography>
<Typography>
存储策略已{props.policy ? "保存" : "添加"}
</Typography>
<Typography variant={"body2"} color={"textSecondary"}>
要使用此存储策略请到用户组管理页面为相应用户组绑定此存储策略
</Typography>

View File

@ -39,13 +39,13 @@ const useStyles = makeStyles(theme => ({
subStepContainer: {
display: "flex",
marginBottom: 20,
padding:10,
padding: 10,
transition: theme.transitions.create("background-color", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
"&:focus-within":{
backgroundColor:theme.palette.background.default,
"&:focus-within": {
backgroundColor: theme.palette.background.default
}
},
stepNumber: {
@ -57,35 +57,35 @@ const useStyles = makeStyles(theme => ({
borderRadius: " 50%"
},
stepNumberContainer: {
marginRight: 10,
marginRight: 10
},
stepFooter:{
marginTop: 32,
stepFooter: {
marginTop: 32
},
button: {
marginRight: theme.spacing(1),
marginRight: theme.spacing(1)
},
"@global":{
"code":{
"@global": {
code: {
color: "rgba(0, 0, 0, 0.87)",
display: "inline-block",
padding: "2px 6px",
fontSize: "14px",
fontFamily:" Consolas, \"Liberation Mono\", Menlo, Courier, monospace",
fontFamily:
' Consolas, "Liberation Mono", Menlo, Courier, monospace',
borderRadius: "2px",
backgroundColor: "rgba(255,229,100,0.1)",
backgroundColor: "rgba(255,229,100,0.1)"
},
"pre":{
pre: {
margin: "24px 0",
padding: "12px 18px",
overflow: "auto",
direction: "ltr",
borderRadius: "4px",
backgroundColor: "#272c34",
color:"#fff",
color: "#fff"
}
},
}
}));
const steps = [
@ -117,25 +117,29 @@ export default function RemoteGuide(props) {
const [activeStep, setActiveStep] = useState(0);
const [loading, setLoading] = useState(false);
const [skipped,] = React.useState(new Set());
const [magicVar,setMagicVar] = useState("");
const [useCDN,setUseCDN] = useState("false");
const [policy, setPolicy] = useState(props.policy?props.policy:{
Type:"remote",
Name:"",
Server:"https://example.com:5212",
SecretKey:randomStr(64),
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable:"false",
BaseURL:"",
IsPrivate:"true",
MaxSize:"0",
OptionsSerialized:{
file_type:"",
},
});
const [skipped] = React.useState(new Set());
const [magicVar, setMagicVar] = useState("");
const [useCDN, setUseCDN] = useState("false");
const [policy, setPolicy] = useState(
props.policy
? props.policy
: {
Type: "remote",
Name: "",
Server: "https://example.com:5212",
SecretKey: randomStr(64),
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
BaseURL: "",
IsPrivate: "true",
MaxSize: "0",
OptionsSerialized: {
file_type: ""
}
}
);
const handleChange = name => event => {
setPolicy({
@ -147,10 +151,10 @@ export default function RemoteGuide(props) {
const handleOptionChange = name => event => {
setPolicy({
...policy,
OptionsSerialized:{
OptionsSerialized: {
...policy.OptionsSerialized,
[name]: event.target.value
},
}
});
};
@ -165,13 +169,13 @@ export default function RemoteGuide(props) {
[dispatch]
);
const testSlave = ()=>{
const testSlave = () => {
setLoading(true);
// 测试路径是否可用
API.post("/admin/policy/test/slave", {
server: policy.Server,
secret:policy.SecretKey,
secret: policy.SecretKey
})
.then(() => {
ToggleSnackbar("top", "right", "通信正常", "success");
@ -188,21 +192,27 @@ export default function RemoteGuide(props) {
e.preventDefault();
setLoading(true);
const policyCopy = {...policy};
policyCopy.OptionsSerialized = {...policyCopy.OptionsSerialized};
const policyCopy = { ...policy };
policyCopy.OptionsSerialized = { ...policyCopy.OptionsSerialized };
// 处理存储策略
if (useCDN === "false" || policy.IsOriginLinkEnable === "false"){
policyCopy.BaseURL = ""
if (useCDN === "false" || policy.IsOriginLinkEnable === "false") {
policyCopy.BaseURL = "";
}
// 类型转换
policyCopy.AutoRename = policyCopy.AutoRename === "true";
policyCopy.IsOriginLinkEnable = policyCopy.IsOriginLinkEnable === "true";
policyCopy.IsOriginLinkEnable =
policyCopy.IsOriginLinkEnable === "true";
policyCopy.MaxSize = parseInt(policyCopy.MaxSize);
policyCopy.IsPrivate = policyCopy.IsPrivate === "true";
policyCopy.OptionsSerialized.file_type = policyCopy.OptionsSerialized.file_type.split(",");
if (policyCopy.OptionsSerialized.file_type.length === 1 && policyCopy.OptionsSerialized.file_type[0] === ""){
policyCopy.OptionsSerialized.file_type = policyCopy.OptionsSerialized.file_type.split(
","
);
if (
policyCopy.OptionsSerialized.file_type.length === 1 &&
policyCopy.OptionsSerialized.file_type[0] === ""
) {
policyCopy.OptionsSerialized.file_type = [];
}
@ -210,7 +220,12 @@ export default function RemoteGuide(props) {
policy: policyCopy
})
.then(() => {
ToggleSnackbar("top", "right", "存储策略已" + (props.policy ? "保存" : "添加"), "success");
ToggleSnackbar(
"top",
"right",
"存储策略已" + (props.policy ? "保存" : "添加"),
"success"
);
setActiveStep(5);
})
.catch(error => {
@ -221,12 +236,13 @@ export default function RemoteGuide(props) {
});
setLoading(false);
}
};
return (
<div>
<Typography variant={"h6"}>{props.policy ? "修改" : "添加"}从机存储策略</Typography>
<Typography variant={"h6"}>
{props.policy ? "修改" : "添加"}从机存储策略
</Typography>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
@ -250,14 +266,14 @@ export default function RemoteGuide(props) {
{activeStep === 0 && (
<form
className={classes.stepContent}
onSubmit={e=>{
onSubmit={e => {
e.preventDefault();
setActiveStep(1);
}}
>
<Alert severity="info" style={{marginBottom:10,}}>
从机存储策略允许你使用同样运行了 Cloudreve 的服务器作为存储端
用户上传下载流量通过 HTTP 直传
<Alert severity="info" style={{ marginBottom: 10 }}>
从机存储策略允许你使用同样运行了 Cloudreve
的服务器作为存储端 用户上传下载流量通过 HTTP 直传
</Alert>
<div className={classes.subStepContainer}>
@ -266,7 +282,8 @@ export default function RemoteGuide(props) {
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
将和主站相同版本的 Cloudreve 程序拷贝至要作为从机的服务器上
将和主站相同版本的 Cloudreve
程序拷贝至要作为从机的服务器上
</Typography>
</div>
</div>
@ -288,7 +305,7 @@ export default function RemoteGuide(props) {
<Input
required
inputProps={{
minlength:64,
minlength: 64
}}
value={policy.SecretKey}
onChange={handleChange("SecretKey")}
@ -304,23 +321,33 @@ export default function RemoteGuide(props) {
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
修改从机配置文件<br/>在从机端 Cloudreve 的同级目录下新建
<code>conf.ini</code>/ Cloudreve
修改从机配置文件
<br />
在从机端 Cloudreve 的同级目录下新建
<code>conf.ini</code>
文件填入从机配置启动/重启从机端 Cloudreve
以下为一个可供参考的配置例子其中密钥部分已帮您填写为上一步所生成的
</Typography>
<pre>
[System]<br/>
Mode = slave<br/>
Listen = :5212<br/>
<br/>
[Slave]<br/>
Secret = {policy.SecretKey}<br/>
<br/>
[CORS]<br/>
AllowOrigins = *<br/>
AllowMethods = OPTIONS,GET,POST<br/>
AllowHeaders = *<br/>
</pre>
<pre>
[System]
<br />
Mode = slave
<br />
Listen = :5212
<br />
<br />
[Slave]
<br />
Secret = {policy.SecretKey}
<br />
<br />
[CORS]
<br />
AllowOrigins = *<br />
AllowMethods = OPTIONS,GET,POST
<br />
AllowHeaders = *<br />
</pre>
<Typography variant={"body2"}>
从机端配置文件格式大致与主站端相同区别在于
<ul>
@ -331,11 +358,15 @@ export default function RemoteGuide(props) {
字段必须更改为<code>slave</code>
</li>
<li>
必须指定<code>Slave</code><code>Secret</code>
必须指定<code>Slave</code>
<code>Secret</code>
字段其值为第二步里填写或生成的密钥
</li>
<li>必须启动跨域配置<code>CORS</code>
具体可参考上文范例或官方文档如果配置不正确用户将无法通过 Web 端向从机上传文件
<li>
必须启动跨域配置<code>CORS</code>
字段的内容
具体可参考上文范例或官方文档如果配置不正确用户将无法通过
Web 端向从机上传文件
</li>
</ul>
</Typography>
@ -348,8 +379,11 @@ export default function RemoteGuide(props) {
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
填写从机地址<br/>
如果主站启用了 HTTPS从机也需要启用并在下方填入 HTTPS 协议的地址
填写从机地址
<br />
如果主站启用了
HTTPS从机也需要启用并在下方填入 HTTPS
协议的地址
</Typography>
<div className={classes.form}>
<FormControl fullWidth>
@ -379,7 +413,7 @@ export default function RemoteGuide(props) {
<div className={classes.form}>
<Button
disabled={loading}
onClick={()=>testSlave()}
onClick={() => testSlave()}
variant={"outlined"}
color={"primary"}
>
@ -399,14 +433,13 @@ export default function RemoteGuide(props) {
下一步
</Button>
</div>
</form>
)}
{activeStep === 1 && (
<form
className={classes.stepContent}
onSubmit={e=> {
onSubmit={e => {
e.preventDefault();
setActiveStep(2);
}}
@ -418,13 +451,14 @@ export default function RemoteGuide(props) {
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
请在下方输入文件的存储目录路径可以为绝对路径或相对路径相对于
从机的 Cloudreve路径中可以使用魔法变量文件在上传时会自动替换这些变量为相应值
从机的
Cloudreve路径中可以使用魔法变量文件在上传时会自动替换这些变量为相应值
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("path")
onClick={e => {
e.preventDefault();
setMagicVar("path");
}}
>
路径魔法变量列表
@ -457,9 +491,9 @@ export default function RemoteGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("file")
onClick={e => {
e.preventDefault();
setMagicVar("file");
}}
>
文件名魔法变量列表
@ -500,7 +534,9 @@ export default function RemoteGuide(props) {
命名规则
</InputLabel>
<Input
required={policy.AutoRename === "true"}
required={
policy.AutoRename === "true"
}
value={policy.FileNameRule}
onChange={handleChange(
"FileNameRule"
@ -516,7 +552,7 @@ export default function RemoteGuide(props) {
<Button
color={"default"}
className={classes.button}
onClick={()=> setActiveStep(0)}
onClick={() => setActiveStep(0)}
>
上一步
</Button>
@ -535,9 +571,9 @@ export default function RemoteGuide(props) {
{activeStep === 2 && (
<form
className={classes.stepContent}
onSubmit={e=> {
onSubmit={e => {
e.preventDefault();
setActiveStep(3);
setActiveStep(3);
}}
>
<div className={classes.subStepContainer}>
@ -590,7 +626,8 @@ export default function RemoteGuide(props) {
<Typography variant={"body2"}>
是否要对下载/直链使用 CDN
<br />
开启后用户访问文件时的 URL 中的域名部分会被替换为 CDN 域名
开启后用户访问文件时的 URL
中的域名部分会被替换为 CDN 域名
</Typography>
<div className={classes.form}>
@ -598,14 +635,16 @@ export default function RemoteGuide(props) {
<RadioGroup
required
value={useCDN}
onChange={e=>{
if (e.target.value === "false"){
onChange={e => {
if (
e.target.value === "false"
) {
setPolicy({
...policy,
BaseURL: ""
});
}
setUseCDN(e.target.value)
setUseCDN(e.target.value);
}}
row
>
@ -626,7 +665,6 @@ export default function RemoteGuide(props) {
</RadioGroup>
</FormControl>
</div>
</div>
</div>
@ -644,26 +682,26 @@ export default function RemoteGuide(props) {
<DomainInput
value={policy.BaseURL}
onChange={handleChange("BaseURL")}
required={policy.IsOriginLinkEnable === "true" && useCDN === "true"}
required={
policy.IsOriginLinkEnable ===
"true" && useCDN === "true"
}
label={"CDN 前缀"}
/>
</div>
</div>
</div>
</Collapse>
</Collapse>
<div className={classes.stepFooter}>
<Button
color={"default"}
className={classes.button}
onClick={()=> setActiveStep(1)}
onClick={() => setActiveStep(1)}
>
上一步
</Button>
{" "}
</Button>{" "}
<Button
disabled={loading}
type={"submit"}
@ -673,18 +711,17 @@ export default function RemoteGuide(props) {
下一步
</Button>
</div>
</form>
)}
{activeStep === 3 && (
<form className={classes.stepContent}
onSubmit={e=> {
e.preventDefault();
setActiveStep(4)
}}
<form
className={classes.stepContent}
onSubmit={e => {
e.preventDefault();
setActiveStep(4);
}}
>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>1</div>
@ -698,17 +735,21 @@ export default function RemoteGuide(props) {
<FormControl required component="fieldset">
<RadioGroup
required
value={policy.MaxSize === "0" ? "false" : "true"}
onChange={e=>{
if(e.target.value === "true"){
value={
policy.MaxSize === "0"
? "false"
: "true"
}
onChange={e => {
if (e.target.value === "true") {
setPolicy({
...policy,
MaxSize: "10485760",
MaxSize: "10485760"
});
}else{
} else {
setPolicy({
...policy,
MaxSize: "0",
MaxSize: "0"
});
}
}}
@ -752,15 +793,15 @@ export default function RemoteGuide(props) {
label={"单文件大小限制"}
/>
</div>
</div>
</div>
</Collapse>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>{policy.MaxSize !== "0" ? "3" : "2"}</div>
<div className={classes.stepNumber}>
{policy.MaxSize !== "0" ? "3" : "2"}
</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
@ -771,23 +812,29 @@ export default function RemoteGuide(props) {
<FormControl required component="fieldset">
<RadioGroup
required
value={policy.OptionsSerialized.file_type === "" ? "false" : "true"}
onChange={e=>{
if(e.target.value === "true"){
value={
policy.OptionsSerialized
.file_type === ""
? "false"
: "true"
}
onChange={e => {
if (e.target.value === "true") {
setPolicy({
...policy,
OptionsSerialized: {
...policy.OptionsSerialized,
file_type:"jpg,png,mp4,zip,rar",
},
file_type:
"jpg,png,mp4,zip,rar"
}
});
}else{
} else {
setPolicy({
...policy,
OptionsSerialized: {
...policy.OptionsSerialized,
file_type:"",
},
file_type: ""
}
});
}
}}
@ -816,11 +863,14 @@ export default function RemoteGuide(props) {
<Collapse in={policy.OptionsSerialized.file_type !== ""}>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>{policy.MaxSize !== "0" ? "4" : "3"}</div>
<div className={classes.stepNumber}>
{policy.MaxSize !== "0" ? "4" : "3"}
</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
输入允许上传的文件扩展名多个请以半角逗号 , 隔开
输入允许上传的文件扩展名多个请以半角逗号 ,
隔开
</Typography>
<div className={classes.form}>
<FormControl fullWidth>
@ -828,28 +878,28 @@ export default function RemoteGuide(props) {
扩展名列表
</InputLabel>
<Input
value={policy.OptionsSerialized.file_type}
value={
policy.OptionsSerialized
.file_type
}
onChange={handleOptionChange(
"file_type"
)}
/>
</FormControl>
</div>
</div>
</div>
</Collapse>
<div className={classes.stepFooter}>
<Button
color={"default"}
className={classes.button}
onClick={()=> setActiveStep(2)}
onClick={() => setActiveStep(2)}
>
上一步
</Button>
{" "}
</Button>{" "}
<Button
disabled={loading}
type={"submit"}
@ -859,16 +909,13 @@ export default function RemoteGuide(props) {
下一步
</Button>
</div>
</form>
)}
{activeStep === 4 && (
<form className={classes.stepContent} onSubmit={submitPolicy}>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
</div>
<div className={classes.stepNumberContainer}></div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
最后一步为此存储策略命名
@ -891,11 +938,10 @@ export default function RemoteGuide(props) {
<Button
color={"default"}
className={classes.button}
onClick={()=> setActiveStep(3)}
onClick={() => setActiveStep(3)}
>
上一步
</Button>
{" "}
</Button>{" "}
<Button
disabled={loading}
type={"submit"}
@ -905,31 +951,30 @@ export default function RemoteGuide(props) {
完成
</Button>
</div>
</form>
)}
{activeStep === 5 && (
<>
<form className={classes.stepContent}>
<Typography>
存储策略已{props.policy ? "保存" : "添加"}
</Typography>
<Typography variant={"body2"} color={"textSecondary"}>
要使用此存储策略请到用户组管理页面为相应用户组绑定此存储策略
</Typography>
</form>
<form className={classes.stepContent}>
<Typography>
存储策略已{props.policy ? "保存" : "添加"}
</Typography>
<Typography variant={"body2"} color={"textSecondary"}>
要使用此存储策略请到用户组管理页面为相应用户组绑定此存储策略
</Typography>
</form>
<div className={classes.stepFooter}>
<Button
color={"primary"}
className={classes.button}
onClick={()=> history.push("/admin/policy")}
onClick={() => history.push("/admin/policy")}
>
返回存储策略列表
</Button>
</div>
</>
)}
</>
)}
<MagicVar
open={magicVar === "file"}

View File

@ -118,29 +118,29 @@ const steps = [
];
const regions = {
"us-east-2":"US East (Ohio)",
"us-east-1":"US East (N. Virginia)",
"us-west-1":"US West (N. California)",
"us-west-2":"US West (Oregon)",
"af-south-1":"Africa (Cape Town)",
"ap-east-1":"Asia Pacific (Hong Kong)",
"ap-south-1":"Asia Pacific (Mumbai)",
"ap-northeast-3":"Asia Pacific (Osaka-Local)",
"ap-northeast-2":"Asia Pacific (Seoul)",
"ap-southeast-1":"Asia Pacific (Singapore)",
"ap-southeast-2":"Asia Pacific (Sydney)",
"ap-northeast-1":"Asia Pacific (Tokyo)",
"ca-central-1":"Canada (Central)",
"cn-north-1":"China (Beijing)",
"cn-northwest-1":"China (Ningxia)",
"eu-central-1":"Europe (Frankfurt)",
"eu-west-1":"Europe (Ireland)",
"eu-west-2":"Europe (London)",
"eu-south-1":"Europe (Milan)",
"eu-west-3":"Europe (Paris)",
"eu-north-1":"Europe (Stockholm)",
"me-south-1":"Middle East (Bahrain)",
"sa-east-1":"South America (São Paulo)",
"us-east-2": "US East (Ohio)",
"us-east-1": "US East (N. Virginia)",
"us-west-1": "US West (N. California)",
"us-west-2": "US West (Oregon)",
"af-south-1": "Africa (Cape Town)",
"ap-east-1": "Asia Pacific (Hong Kong)",
"ap-south-1": "Asia Pacific (Mumbai)",
"ap-northeast-3": "Asia Pacific (Osaka-Local)",
"ap-northeast-2": "Asia Pacific (Seoul)",
"ap-southeast-1": "Asia Pacific (Singapore)",
"ap-southeast-2": "Asia Pacific (Sydney)",
"ap-northeast-1": "Asia Pacific (Tokyo)",
"ca-central-1": "Canada (Central)",
"cn-north-1": "China (Beijing)",
"cn-northwest-1": "China (Ningxia)",
"eu-central-1": "Europe (Frankfurt)",
"eu-west-1": "Europe (Ireland)",
"eu-west-2": "Europe (London)",
"eu-south-1": "Europe (Milan)",
"eu-west-3": "Europe (Paris)",
"eu-north-1": "Europe (Stockholm)",
"me-south-1": "Middle East (Bahrain)",
"sa-east-1": "South America (São Paulo)"
};
export default function S3Guide(props) {
@ -171,7 +171,7 @@ export default function S3Guide(props) {
MaxSize: "0",
OptionsSerialized: {
file_type: "",
region: "us-east-2",
region: "us-east-2"
}
}
);
@ -278,9 +278,11 @@ export default function S3Guide(props) {
<div>
<AlertDialog
open={alertOpen}
onClose={()=>setAlertOpen(false)}
onClose={() => setAlertOpen(false)}
title={"警告"}
msg={"S3 类型存储策略目前仅可用于自己使用,或者是给受信任的用户组使用。"}
msg={
"S3 类型存储策略目前仅可用于自己使用,或者是给受信任的用户组使用。"
}
/>
<Typography variant={"h6"}>
{props.policy ? "修改" : "添加"} Amazon S3 存储策略
@ -436,9 +438,11 @@ export default function S3Guide(props) {
freeSolo
value={policy.OptionsSerialized.region}
onInputChange={(_, value) =>
handleOptionChange("region")({target:{value:value}})
handleOptionChange("region")({
target: { value: value }
})
}
renderOption={(option) => (
renderOption={option => (
<React.Fragment>
{regions[option]}
</React.Fragment>

View File

@ -94,25 +94,29 @@ export default function UpyunGuide(props) {
const [activeStep, setActiveStep] = useState(0);
const [loading, setLoading] = useState(false);
const [skipped,] = React.useState(new Set());
const [skipped] = React.useState(new Set());
const [magicVar, setMagicVar] = useState("");
const [policy, setPolicy] = useState(props.policy?props.policy:{
Type: "upyun",
Name: "",
SecretKey: "",
AccessKey: "",
BaseURL: "",
IsPrivate: "false",
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
MaxSize: "0",
OptionsSerialized: {
file_type: "",
token:"",
}
});
const [policy, setPolicy] = useState(
props.policy
? props.policy
: {
Type: "upyun",
Name: "",
SecretKey: "",
AccessKey: "",
BaseURL: "",
IsPrivate: "false",
DirNameRule: "uploads/{year}/{month}/{day}",
AutoRename: "true",
FileNameRule: "{randomkey8}_{originname}",
IsOriginLinkEnable: "false",
MaxSize: "0",
OptionsSerialized: {
file_type: "",
token: ""
}
}
);
const handleChange = name => event => {
setPolicy({
@ -169,7 +173,12 @@ export default function UpyunGuide(props) {
policy: policyCopy
})
.then(() => {
ToggleSnackbar("top", "right", "存储策略已"+ (props.policy ? "保存" : "添加"), "success");
ToggleSnackbar(
"top",
"right",
"存储策略已" + (props.policy ? "保存" : "添加"),
"success"
);
setActiveStep(5);
})
.catch(error => {
@ -184,7 +193,9 @@ export default function UpyunGuide(props) {
return (
<div>
<Typography variant={"h6"}>{props.policy ? "修改" : "添加"} 又拍云 存储策略</Typography>
<Typography variant={"h6"}>
{props.policy ? "修改" : "添加"} 又拍云 存储策略
</Typography>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
@ -213,15 +224,14 @@ export default function UpyunGuide(props) {
setActiveStep(1);
}}
>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>0</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
在使用又拍云存储策略前请确保您在 参数设置 - 站点信息
- 站点URL 中填写的 地址与实际相符并且
在使用又拍云存储策略前请确保您在 参数设置 -
站点信息 - 站点URL 中填写的 地址与实际相符并且
<strong>能够被外网正常访问</strong>
</Typography>
</div>
@ -235,7 +245,9 @@ export default function UpyunGuide(props) {
<Typography variant={"body2"}>
前往
<Link
href={"https://console.upyun.com/services/create/file/"}
href={
"https://console.upyun.com/services/create/file/"
}
target={"_blank"}
>
又拍云面板
@ -309,7 +321,8 @@ export default function UpyunGuide(props) {
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
填写为云存储服务绑定的域名并根据实际情况选择是否使用 HTTPS
填写为云存储服务绑定的域名并根据实际情况选择是否使用
HTTPS
</Typography>
<div className={classes.form}>
<DomainInput
@ -328,8 +341,10 @@ export default function UpyunGuide(props) {
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
此步骤可保持默认并跳过但是强烈建议您跟随此步骤操作<br/>
前往所创建云存储服务的 功能配置 面板转到 访问配置 选项卡开启 Token 防盗链并设定密码
此步骤可保持默认并跳过但是强烈建议您跟随此步骤操作
<br />
前往所创建云存储服务的 功能配置 面板转到
访问配置 选项卡开启 Token 防盗链并设定密码
</Typography>
<div className={classes.form}>
<FormControl required component="fieldset">
@ -362,9 +377,7 @@ export default function UpyunGuide(props) {
<Collapse in={policy.IsPrivate === "true"}>
<div className={classes.subStepContainer}>
<div className={classes.stepNumberContainer}>
<div className={classes.stepNumber}>
6
</div>
<div className={classes.stepNumber}>6</div>
</div>
<div className={classes.subStepContent}>
<Typography variant={"body2"}>
@ -377,13 +390,14 @@ export default function UpyunGuide(props) {
</InputLabel>
<Input
value={
policy.OptionsSerialized
.token
policy.OptionsSerialized.token
}
onChange={handleOptionChange(
"token"
)}
required={policy.IsPrivate === "true"}
required={
policy.IsPrivate === "true"
}
/>
</FormControl>
</div>
@ -391,7 +405,6 @@ export default function UpyunGuide(props) {
</div>
</Collapse>
<div className={classes.stepFooter}>
<Button
disabled={loading}
@ -425,9 +438,9 @@ export default function UpyunGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("path")
onClick={e => {
e.preventDefault();
setMagicVar("path");
}}
>
路径魔法变量列表
@ -460,9 +473,9 @@ export default function UpyunGuide(props) {
可用魔法变量可参考{" "}
<Link
color={"secondary"}
onClick={(e) => {
e.preventDefault()
setMagicVar("file")
onClick={e => {
e.preventDefault();
setMagicVar("file");
}}
>
文件名魔法变量列表
@ -561,14 +574,22 @@ export default function UpyunGuide(props) {
<RadioGroup
required
value={policy.IsOriginLinkEnable}
onChange={e=>{
if (policy.IsPrivate === "true" && e.target.value==="true"){
ToggleSnackbar("top", "right","开启 Token 防盗链后无法使用直链功能", "warning");
return
onChange={e => {
if (
policy.IsPrivate === "true" &&
e.target.value === "true"
) {
ToggleSnackbar(
"top",
"right",
"开启 Token 防盗链后无法使用直链功能",
"warning"
);
return;
}
handleChange(
"IsOriginLinkEnable"
)(e)
handleChange("IsOriginLinkEnable")(
e
);
}}
row
>
@ -855,7 +876,9 @@ export default function UpyunGuide(props) {
{activeStep === 5 && (
<>
<form className={classes.stepContent}>
<Typography>存储策略已{props.policy ? "保存" : "添加"}</Typography>
<Typography>
存储策略已{props.policy ? "保存" : "添加"}
</Typography>
<Typography variant={"body2"} color={"textSecondary"}>
要使用此存储策略请到用户组管理页面为相应用户组绑定此存储策略
</Typography>

View File

@ -74,7 +74,6 @@ function useQuery() {
return new URLSearchParams(useLocation().search);
}
export default function Policy() {
const classes = useStyles();
// const [loading, setLoading] = useState(false);
@ -108,38 +107,42 @@ export default function Policy() {
[dispatch]
);
useEffect(()=>{
if(query.get("code") === "0"){
useEffect(() => {
if (query.get("code") === "0") {
ToggleSnackbar("top", "right", "授权成功", "success");
}else if (query.get("msg") && query.get("msg")!==""){
ToggleSnackbar("top", "right", query.get("msg") + ", "+ query.get("err"), "warning");
} else if (query.get("msg") && query.get("msg") !== "") {
ToggleSnackbar(
"top",
"right",
query.get("msg") + ", " + query.get("err"),
"warning"
);
}
},[location])
}, [location]);
const loadList = () => {
API.post("/admin/policy/list", {
page: page,
page_size: pageSize,
order_by: "id desc",
conditions: filter === "all" ? {} : { type: filter }
})
.then(response => {
setPolicies(response.data.items);
setStatics(response.data.statics);
setTotal(response.data.total);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
API.post("/admin/policy/list", {
page: page,
page_size: pageSize,
order_by: "id desc",
conditions: filter === "all" ? {} : { type: filter }
})
.then(response => {
setPolicies(response.data.items);
setStatics(response.data.statics);
setTotal(response.data.total);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
};
useEffect(() => {
loadList();
}, [page, pageSize,filter]);
}, [page, pageSize, filter]);
const deletePolicy = (id) =>{
API.delete("/admin/policy/" + id,)
const deletePolicy = id => {
API.delete("/admin/policy/" + id)
.then(() => {
loadList();
ToggleSnackbar("top", "right", "存储策略已删除", "success");
@ -147,7 +150,7 @@ export default function Policy() {
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
}
};
const open = Boolean(anchorEl);
@ -165,10 +168,10 @@ export default function Policy() {
<div className={classes.headerRight}>
<Select
style={{
marginRight:8,
marginRight: 8
}}
value={filter}
onChange={e=>setFilter(e.target.value)}
onChange={e => setFilter(e.target.value)}
>
<MenuItem value={"all"}>全部</MenuItem>
<MenuItem value={"local"}>本机</MenuItem>
@ -194,7 +197,7 @@ export default function Policy() {
<TableContainer className={classes.container}>
<Table aria-label="sticky table" size={"small"}>
<TableHead>
<TableRow style={{height:52}}>
<TableRow style={{ height: 52 }}>
{columns.map(column => (
<TableCell
key={column.id}
@ -226,19 +229,26 @@ export default function Policy() {
</TableCell>
<TableCell align={"right"}>
<Tooltip title={"删除"}>
<IconButton onClick={()=>deletePolicy(row.ID)} size={"small"}>
<Delete/>
<IconButton
onClick={() =>
deletePolicy(row.ID)
}
size={"small"}
>
<Delete />
</IconButton>
</Tooltip>
<Tooltip title={"编辑"}>
<IconButton onClick={(e)=>{
setEditID(row.ID)
handleClick(e)
}} size={"small"}>
<Edit/>
<IconButton
onClick={e => {
setEditID(row.ID);
handleClick(e);
}}
size={"small"}
>
<Edit />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))}
@ -264,14 +274,22 @@ export default function Policy() {
onClose={handleClose}
keepMounted
>
<MenuItem onClick={e=>{
handleClose(e);
history.push("/admin/policy/edit/pro/"+editID);
}}>专家模式编辑</MenuItem>
<MenuItem onClick={e=>{
handleClose(e);
history.push("/admin/policy/edit/guide/"+editID);
}}>向导模式编辑</MenuItem>
<MenuItem
onClick={e => {
handleClose(e);
history.push("/admin/policy/edit/pro/" + editID);
}}
>
专家模式编辑
</MenuItem>
<MenuItem
onClick={e => {
handleClose(e);
history.push("/admin/policy/edit/guide/" + editID);
}}
>
向导模式编辑
</MenuItem>
</Menu>
</div>
);

View File

@ -320,7 +320,8 @@ export default function Access() {
target={"_blank"}
>
应用管理页面
</Link>{" "}
</Link>{" "}
获取到的的 秘钥
</FormHelperText>
</FormControl>
</div>

View File

@ -152,7 +152,9 @@ export default function Aria2() {
Aria2 的配置文件中开启 RPC
服务更多信息及指引请参考文档的{" "}
<Link
href={"https://docs.cloudreve.org/use/aria2"}
href={
"https://docs.cloudreve.org/use/aria2"
}
target={"_blank"}
>
离线下载

View File

@ -36,18 +36,18 @@ export default function ImageSetting() {
const classes = useStyles();
const [loading, setLoading] = useState(false);
const [options, setOptions] = useState({
gravatar_server:"",
avatar_path:"",
avatar_size:"",
avatar_size_l:"",
avatar_size_m:"",
avatar_size_s:"",
thumb_width:"",
thumb_height:"",
captcha_height:"1",
captcha_width:"1",
captcha_mode:"3",
captcha_CaptchaLen:"",
gravatar_server: "",
avatar_path: "",
avatar_size: "",
avatar_size_l: "",
avatar_size_m: "",
avatar_size_s: "",
thumb_width: "",
thumb_height: "",
captcha_height: "1",
captcha_width: "1",
captcha_mode: "3",
captcha_CaptchaLen: ""
});
const handleChange = name => event => {
@ -77,34 +77,33 @@ export default function ImageSetting() {
// eslint-disable-next-line
}, []);
const submit = e => {
e.preventDefault();
setLoading(true);
const option = [];
Object.keys(options).forEach(k=>{
Object.keys(options).forEach(k => {
option.push({
key:k,
value:options[k],
key: k,
value: options[k]
});
})
API.patch("/admin/setting",{
options:option,
});
API.patch("/admin/setting", {
options: option
})
.then(() => {
ToggleSnackbar("top", "right", "设置已更改", "success");
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
}).then(()=>{
})
.then(() => {
setLoading(false);
});
});
};
return (
<div>
<form onSubmit={submit}>
<div className={classes.root}>
<Typography variant="h6" gutterBottom>
头像
@ -167,8 +166,8 @@ export default function ImageSetting() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.avatar_size_s}
onChange={handleChange("avatar_size_s")}
@ -185,8 +184,8 @@ export default function ImageSetting() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.avatar_size_m}
onChange={handleChange("avatar_size_m")}
@ -203,8 +202,8 @@ export default function ImageSetting() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.avatar_size_l}
onChange={handleChange("avatar_size_l")}
@ -212,7 +211,6 @@ export default function ImageSetting() {
/>
</FormControl>
</div>
</div>
</div>
@ -230,8 +228,8 @@ export default function ImageSetting() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.thumb_width}
onChange={handleChange("thumb_width")}
@ -250,8 +248,8 @@ export default function ImageSetting() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.thumb_height}
onChange={handleChange("thumb_height")}
@ -260,7 +258,6 @@ export default function ImageSetting() {
</FormControl>
</div>
</div>
</div>
<div className={classes.root}>
@ -276,8 +273,8 @@ export default function ImageSetting() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.captcha_width}
onChange={handleChange("captcha_width")}
@ -294,8 +291,8 @@ export default function ImageSetting() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.captcha_height}
onChange={handleChange("captcha_height")}
@ -324,7 +321,6 @@ export default function ImageSetting() {
</FormHelperText>
</FormControl>
</div>
</div>
</div>

View File

@ -53,7 +53,7 @@ export default function Mail() {
replyTo: "",
smtpUser: "",
smtpPass: "",
smtpEncryption:"",
smtpEncryption: "",
mail_keepalive: "30",
mail_activation_template: "",
mail_reset_pwd_template: ""
@ -254,7 +254,7 @@ export default function Mail() {
SMTP 端口
</InputLabel>
<Input
inputProps={{ min: 1 ,step:1}}
inputProps={{ min: 1, step: 1 }}
type={"number"}
value={options.smtpPort}
onChange={handleChange("smtpPort")}
@ -342,7 +342,7 @@ export default function Mail() {
SMTP 连接有效期 ()
</InputLabel>
<Input
inputProps={{ min: 1 ,step:1}}
inputProps={{ min: 1, step: 1 }}
type={"number"}
value={options.mail_keepalive}
onChange={handleChange("mail_keepalive")}
@ -363,7 +363,6 @@ export default function Mail() {
</Typography>
<div className={classes.formContainer}>
<div className={classes.form}>
<FormControl fullWidth>
<InputLabel htmlFor="component-helper">
@ -403,7 +402,6 @@ export default function Mail() {
</FormHelperText>
</FormControl>
</div>
</div>
</div>

View File

@ -76,28 +76,28 @@ export default function SiteInformation() {
// eslint-disable-next-line
}, []);
const submit = e => {
e.preventDefault();
setLoading(true);
const option = [];
Object.keys(options).forEach(k=>{
Object.keys(options).forEach(k => {
option.push({
key:k,
value:options[k],
key: k,
value: options[k]
});
})
API.patch("/admin/setting",{
options:option,
});
API.patch("/admin/setting", {
options: option
})
.then(() => {
ToggleSnackbar("top", "right", "设置已更改", "success");
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
}).then(()=>{
})
.then(() => {
setLoading(false);
});
});
};
return (
@ -254,7 +254,6 @@ export default function SiteInformation() {
value={options.pwa_display}
onChange={handleChange("pwa_display")}
>
<MenuItem value={"fullscreen"}>
fullscreen
</MenuItem>

View File

@ -56,9 +56,9 @@ export default function Theme() {
const [theme, setTheme] = useState({});
const [options, setOptions] = useState({
themes: "{}",
defaultTheme:"",
home_view_method:"icon",
share_view_method:"list",
defaultTheme: "",
home_view_method: "icon",
share_view_method: "list"
});
const [themeConfig, setThemeConfig] = useState({});
const [themeConfigError, setThemeConfigError] = useState({});
@ -72,39 +72,44 @@ export default function Theme() {
);
const deleteTheme = color => {
if(color === options.defaultTheme){
if (color === options.defaultTheme) {
ToggleSnackbar("top", "right", "不能删除默认配色", "warning");
return
return;
}
if (Object.keys(theme).length <=1){
if (Object.keys(theme).length <= 1) {
ToggleSnackbar("top", "right", "请至少保留一个配色方案", "warning");
return
return;
}
const themeCopy = {...theme};
const themeCopy = { ...theme };
delete themeCopy[color];
const resStr = JSON.stringify(themeCopy);
setOptions({
...options,
themes:resStr,
})
}
themes: resStr
});
};
const addTheme = newTheme => {
setCreate(false);
if (theme[newTheme.palette.primary.main] !== undefined){
ToggleSnackbar("top", "right", "主色调不能与已有配色重复", "warning");
return
if (theme[newTheme.palette.primary.main] !== undefined) {
ToggleSnackbar(
"top",
"right",
"主色调不能与已有配色重复",
"warning"
);
return;
}
const res = {
...theme,
[newTheme.palette.primary.main]:newTheme,
[newTheme.palette.primary.main]: newTheme
};
const resStr = JSON.stringify(res);
setOptions({
...options,
themes:resStr,
})
}
themes: resStr
});
};
useEffect(() => {
const res = JSON.parse(options.themes);
@ -165,7 +170,6 @@ export default function Theme() {
return (
<div>
<form onSubmit={submit}>
<div className={classes.root}>
<Typography variant="h6" gutterBottom>
主题配色
@ -236,14 +240,31 @@ export default function Theme() {
const res = JSON.parse(
e.target.value
);
if(
!('palette' in res) ||
!('primary' in res.palette) ||
!('main' in res.palette.primary) ||
!('secondary' in res.palette) ||
!('main' in res.palette.secondary)
){
throw e
if (
!(
"palette" in
res
) ||
!(
"primary" in
res.palette
) ||
!(
"main" in
res.palette
.primary
) ||
!(
"secondary" in
res.palette
) ||
!(
"main" in
res.palette
.secondary
)
) {
throw e;
}
setTheme({
...theme,
@ -267,7 +288,11 @@ export default function Theme() {
/>
</TableCell>
<TableCell>
<IconButton onClick={()=>deleteTheme(k)}>
<IconButton
onClick={() =>
deleteTheme(k)
}
>
<Delete />
</IconButton>
</TableCell>
@ -279,17 +304,19 @@ export default function Theme() {
<Button
variant="outlined"
color="primary"
style={{marginTop:8}}
onClick={()=>setCreate(true)}
style={{ marginTop: 8 }}
onClick={() => setCreate(true)}
>
新建配色方案
</Button>
</div>
<Alert severity="info" style={{marginTop:8}}>
<Alert severity="info" style={{ marginTop: 8 }}>
<Typography variant="body2">
完整的配置项可在 {" "}
完整的配置项可在{" "}
<Link
href={"https://material-ui.com/zh/customization/default-theme/"}
href={
"https://material-ui.com/zh/customization/default-theme/"
}
target={"_blank"}
>
默认主题 - Material-UI
@ -297,7 +324,6 @@ export default function Theme() {
查阅
</Typography>
</Alert>
</div>
<div className={classes.form}>
@ -309,7 +335,7 @@ export default function Theme() {
value={options.defaultTheme}
onChange={handleChange("defaultTheme")}
>
{Object.keys(theme).map(k=>(
{Object.keys(theme).map(k => (
<MenuItem key={k} value={k}>
<div
className={
@ -319,24 +345,18 @@ export default function Theme() {
<div
style={{
backgroundColor:
theme[k].palette
.primary
.main
theme[k].palette
.primary.main
}}
className={
classes.colorDot
}
className={classes.colorDot}
/>
<div
style={{
backgroundColor:
theme[k].palette
.secondary
.main
theme[k].palette
.secondary.main
}}
className={
classes.colorDot
}
className={classes.colorDot}
/>
</div>
</MenuItem>
@ -347,7 +367,6 @@ export default function Theme() {
</FormHelperText>
</FormControl>
</div>
</div>
</div>
@ -368,7 +387,9 @@ export default function Theme() {
required
>
<MenuItem value={"icon"}>大图标</MenuItem>
<MenuItem value={"smallIcon"}>小图标</MenuItem>
<MenuItem value={"smallIcon"}>
小图标
</MenuItem>
<MenuItem value={"list"}>列表</MenuItem>
</Select>
<FormHelperText id="component-helper-text">
@ -390,7 +411,9 @@ export default function Theme() {
required
>
<MenuItem value={"icon"}>大图标</MenuItem>
<MenuItem value={"smallIcon"}>小图标</MenuItem>
<MenuItem value={"smallIcon"}>
小图标
</MenuItem>
<MenuItem value={"list"}>列表</MenuItem>
</Select>
<FormHelperText id="component-helper-text">
@ -399,7 +422,6 @@ export default function Theme() {
</FormControl>
</div>
</div>
</div>
<div className={classes.root}>
@ -414,7 +436,11 @@ export default function Theme() {
</div>
</form>
<CreateTheme onSubmit={addTheme} open={create} onClose={()=>setCreate(false)}/>
<CreateTheme
onSubmit={addTheme}
open={create}
onClose={() => setCreate(false)}
/>
</div>
);
}

View File

@ -37,26 +37,26 @@ export default function UploadDownload() {
const [loading, setLoading] = useState(false);
const [options, setOptions] = useState({
max_worker_num: "1",
max_parallel_transfer:"1",
temp_path:"",
maxEditSize:"0",
onedrive_chunk_retries:"0",
archive_timeout:"0",
download_timeout:"0",
preview_timeout:"0",
doc_preview_timeout:"0",
upload_credential_timeout:"0",
upload_session_timeout:"0",
slave_api_timeout:"0",
onedrive_monitor_timeout:"0",
share_download_session_timeout:"0",
onedrive_callback_check:"0",
reset_after_upload_failed:"0",
onedrive_source_timeout:"0",
max_parallel_transfer: "1",
temp_path: "",
maxEditSize: "0",
onedrive_chunk_retries: "0",
archive_timeout: "0",
download_timeout: "0",
preview_timeout: "0",
doc_preview_timeout: "0",
upload_credential_timeout: "0",
upload_session_timeout: "0",
slave_api_timeout: "0",
onedrive_monitor_timeout: "0",
share_download_session_timeout: "0",
onedrive_callback_check: "0",
reset_after_upload_failed: "0",
onedrive_source_timeout: "0"
});
const handleCheckChange = name => event => {
const value= event.target.checked ? "1" : "0";
const value = event.target.checked ? "1" : "0";
setOptions({
...options,
[name]: value
@ -90,57 +90,56 @@ export default function UploadDownload() {
// eslint-disable-next-line
}, []);
const submit = e => {
e.preventDefault();
setLoading(true);
const option = [];
Object.keys(options).forEach(k=>{
Object.keys(options).forEach(k => {
option.push({
key:k,
value:options[k],
key: k,
value: options[k]
});
})
API.patch("/admin/setting",{
options:option,
});
API.patch("/admin/setting", {
options: option
})
.then(() => {
ToggleSnackbar("top", "right", "设置已更改", "success");
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
}).then(()=>{
})
.then(() => {
setLoading(false);
});
});
};
return (
<div>
<form onSubmit={submit}>
<div className={classes.root}>
<Typography variant="h6" gutterBottom>
存储与传输
</Typography>
<div className={classes.formContainer}>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
Worker 数量
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.max_worker_num}
onChange={handleChange("max_worker_num")}
required
/>
<FormHelperText id="component-helper-text">
任务队列最多并行执行的任务数保存后需要重启 Cloudreve 生效
任务队列最多并行执行的任务数保存后需要重启
Cloudreve 生效
</FormHelperText>
</FormControl>
</div>
@ -153,11 +152,13 @@ export default function UploadDownload() {
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.max_parallel_transfer}
onChange={handleChange("max_parallel_transfer")}
onChange={handleChange(
"max_parallel_transfer"
)}
required
/>
<FormHelperText id="component-helper-text">
@ -199,22 +200,25 @@ export default function UploadDownload() {
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
OneDrive 分片错误重试
</InputLabel>
<Input
type={"number"}
inputProps={{
min:0,
step:1,
min: 0,
step: 1
}}
value={options.onedrive_chunk_retries}
onChange={handleChange("onedrive_chunk_retries")}
onChange={handleChange(
"onedrive_chunk_retries"
)}
required
/>
<FormHelperText id="component-helper-text">
OneDrive 存储策略分片上传失败后重试的最大次数只适用于服务端上传或中转
OneDrive
存储策略分片上传失败后重试的最大次数只适用于服务端上传或中转
</FormHelperText>
</FormControl>
</div>
@ -225,7 +229,8 @@ export default function UploadDownload() {
control={
<Switch
checked={
options.reset_after_upload_failed === "1"
options.reset_after_upload_failed ===
"1"
}
onChange={handleCheckChange(
"reset_after_upload_failed"
@ -239,7 +244,6 @@ export default function UploadDownload() {
</FormHelperText>
</FormControl>
</div>
</div>
</div>
@ -249,17 +253,16 @@ export default function UploadDownload() {
</Typography>
<div className={classes.formContainer}>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
打包下载
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.archive_timeout}
onChange={handleChange("archive_timeout")}
@ -269,15 +272,15 @@ export default function UploadDownload() {
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
下载会话
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.download_timeout}
onChange={handleChange("download_timeout")}
@ -287,15 +290,15 @@ export default function UploadDownload() {
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
预览链接
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.preview_timeout}
onChange={handleChange("preview_timeout")}
@ -305,54 +308,60 @@ export default function UploadDownload() {
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
Office 文档预览连接
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.doc_preview_timeout}
onChange={handleChange("doc_preview_timeout")}
onChange={handleChange(
"doc_preview_timeout"
)}
required
/>
</FormControl>
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
上传凭证
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.upload_credential_timeout}
onChange={handleChange("upload_credential_timeout")}
onChange={handleChange(
"upload_credential_timeout"
)}
required
/>
</FormControl>
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
上传会话
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.upload_session_timeout}
onChange={handleChange("upload_session_timeout")}
onChange={handleChange(
"upload_session_timeout"
)}
required
/>
<FormHelperText id="component-helper-text">
@ -362,15 +371,15 @@ export default function UploadDownload() {
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
从机API请求
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.slave_api_timeout}
onChange={handleChange("slave_api_timeout")}
@ -380,18 +389,22 @@ export default function UploadDownload() {
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
分享下载会话
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.share_download_session_timeout}
onChange={handleChange("share_download_session_timeout")}
value={
options.share_download_session_timeout
}
onChange={handleChange(
"share_download_session_timeout"
)}
required
/>
<FormHelperText id="component-helper-text">
@ -401,69 +414,77 @@ export default function UploadDownload() {
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
OneDrive 客户端上传监控间隔
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.onedrive_monitor_timeout}
onChange={handleChange("onedrive_monitor_timeout")}
onChange={handleChange(
"onedrive_monitor_timeout"
)}
required
/>
<FormHelperText id="component-helper-text">
每间隔所设定时间Cloudreve 会向 OneDrive 请求检查客户端上传情况已确保客户端上传可控
每间隔所设定时间Cloudreve 会向 OneDrive
请求检查客户端上传情况已确保客户端上传可控
</FormHelperText>
</FormControl>
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
OneDrive 回调等待
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
step:1,
min: 1,
step: 1
}}
value={options.onedrive_callback_check}
onChange={handleChange("onedrive_callback_check")}
onChange={handleChange(
"onedrive_callback_check"
)}
required
/>
<FormHelperText id="component-helper-text">
OneDrive 客户端上传完成后等待回调的最大时间如果超出会被认为上传失败
OneDrive
客户端上传完成后等待回调的最大时间如果超出会被认为上传失败
</FormHelperText>
</FormControl>
</div>
<div className={classes.form}>
<FormControl >
<FormControl>
<InputLabel htmlFor="component-helper">
OneDrive 下载请求缓存
</InputLabel>
<Input
type={"number"}
inputProps={{
min:1,
max:3659,
step:1,
min: 1,
max: 3659,
step: 1
}}
value={options.onedrive_source_timeout}
onChange={handleChange("onedrive_source_timeout")}
onChange={handleChange(
"onedrive_source_timeout"
)}
required
/>
<FormHelperText id="component-helper-text">
OneDrive 获取文件下载 URL 后可将结果缓存减轻热门文件下载API请求频率
OneDrive 获取文件下载 URL
后可将结果缓存减轻热门文件下载API请求频率
</FormHelperText>
</FormControl>
</div>
</div>
</div>

View File

@ -1,14 +1,14 @@
import React, {useCallback, useEffect, useState} from "react";
import {useParams} from "react-router";
import React, { useCallback, useEffect, useState } from "react";
import { useParams } from "react-router";
import API from "../../../middleware/Api";
import {useDispatch} from "react-redux";
import {toggleSnackbar} from "../../../actions";
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../../actions";
import UserForm from "./UserForm";
export default function EditUserPreload( ) {
const [user,setUser] = useState({});
export default function EditUserPreload() {
const [user, setUser] = useState({});
const {id } = useParams();
const { id } = useParams();
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
@ -17,8 +17,7 @@ export default function EditUserPreload( ) {
[dispatch]
);
useEffect(()=>{
useEffect(() => {
setUser({});
API.get("/admin/user/" + id)
.then(response => {
@ -31,13 +30,7 @@ export default function EditUserPreload( ) {
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
},[id]);
}, [id]);
return (
<div>
{user.ID !== undefined &&
<UserForm user={user}/>
}
</div>
);
return <div>{user.ID !== undefined && <UserForm user={user} />}</div>;
}

View File

@ -43,7 +43,7 @@ export default function UserForm(props) {
Nick: "",
Password: "", // 为空时只读
Status: "0", // 转换类型
GroupID: "2", // 转换类型
GroupID: "2" // 转换类型
}
);
const [groups, setGroups] = useState([]);
@ -76,21 +76,26 @@ export default function UserForm(props) {
const submit = e => {
e.preventDefault();
const userCopy = {...user};
const userCopy = { ...user };
// 整型转换
["Status", "GroupID","Score"].forEach(v => {
["Status", "GroupID", "Score"].forEach(v => {
userCopy[v] = parseInt(userCopy[v]);
});
setLoading(true);
API.post("/admin/user", {
user: userCopy,
password:userCopy.Password,
password: userCopy.Password
})
.then(() => {
history.push("/admin/user");
ToggleSnackbar("top", "right", "用户已"+ (props.user ? "保存" : "添加"), "success");
ToggleSnackbar(
"top",
"right",
"用户已" + (props.user ? "保存" : "添加"),
"success"
);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
@ -110,7 +115,6 @@ export default function UserForm(props) {
</Typography>
<div className={classes.formContainer}>
<div className={classes.form}>
<FormControl fullWidth>
<InputLabel htmlFor="component-helper">
@ -162,9 +166,7 @@ export default function UserForm(props) {
</InputLabel>
<Select
value={user.GroupID}
onChange={handleChange(
"GroupID"
)}
onChange={handleChange("GroupID")}
required
>
{groups.map(v => {
@ -194,19 +196,18 @@ export default function UserForm(props) {
</InputLabel>
<Select
value={user.Status}
onChange={handleChange(
"Status"
)}
onChange={handleChange("Status")}
required
>
<MenuItem value={"0"}>正常</MenuItem>
<MenuItem value={"1"}>未激活</MenuItem>
<MenuItem value={"2"}>被封禁</MenuItem>
<MenuItem value={"3"}>超额使用被封禁</MenuItem>
<MenuItem value={"3"}>
超额使用被封禁
</MenuItem>
</Select>
</FormControl>
</div>
</div>
</div>
<div className={classes.root}>

View File

@ -1,148 +1,153 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { connect } from 'react-redux'
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import {} from "../../actions";
import classNames from "classnames";
import ErrorIcon from "@material-ui/icons/Error";
import InfoIcon from "@material-ui/icons/Info";
import CloseIcon from "@material-ui/icons/Close";
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import WarningIcon from "@material-ui/icons/Warning";
import { green, amber } from "@material-ui/core/colors";
import {
} from "../../actions"
import classNames from 'classnames';
import ErrorIcon from '@material-ui/icons/Error';
import InfoIcon from '@material-ui/icons/Info';
import CloseIcon from '@material-ui/icons/Close';
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
import WarningIcon from '@material-ui/icons/Warning';
import { green, amber } from '@material-ui/core/colors';
import { withStyles, SnackbarContent, Snackbar, IconButton } from '@material-ui/core';
withStyles,
SnackbarContent,
Snackbar,
IconButton
} from "@material-ui/core";
const mapStateToProps = state => {
return {
snackbar:state.viewUpdate.snackbar,
}
}
snackbar: state.viewUpdate.snackbar
};
};
const mapDispatchToProps = () => {
return {
}
}
return {};
};
const variantIcon = {
success: CheckCircleIcon,
warning: WarningIcon,
error: ErrorIcon,
info: InfoIcon,
};
info: InfoIcon
};
const styles1 = theme => ({
success: {
backgroundColor: green[600],
},
error: {
backgroundColor: theme.palette.error.dark,
},
info: {
backgroundColor: theme.palette.primary.dark,
},
warning: {
backgroundColor: amber[700],
},
icon: {
fontSize: 20,
},
iconVariant: {
backgroundColor: green[600]
},
error: {
backgroundColor: theme.palette.error.dark
},
info: {
backgroundColor: theme.palette.primary.dark
},
warning: {
backgroundColor: amber[700]
},
icon: {
fontSize: 20
},
iconVariant: {
opacity: 0.9,
marginRight: theme.spacing(1),
},
message: {
display: 'flex',
alignItems: 'center',
},
})
marginRight: theme.spacing(1)
},
message: {
display: "flex",
alignItems: "center"
}
});
function MySnackbarContent(props) {
const { classes, className, message, onClose, variant, ...other } = props;
const Icon = variantIcon[variant];
return (
<SnackbarContent
className={classNames(classes[variant], className)}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<Icon className={classNames(classes.icon, classes.iconVariant)} />
{message}
</span>
}
action={[
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={onClose}
>
<CloseIcon className={classes.icon} />
</IconButton>,
]}
{...other}
/>
<SnackbarContent
className={classNames(classes[variant], className)}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<Icon
className={classNames(
classes.icon,
classes.iconVariant
)}
/>
{message}
</span>
}
action={[
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={onClose}
>
<CloseIcon className={classes.icon} />
</IconButton>
]}
{...other}
/>
);
}
}
MySnackbarContent.propTypes = {
classes: PropTypes.object.isRequired,
className: PropTypes.string,
message: PropTypes.node,
onClose: PropTypes.func,
variant: PropTypes.oneOf(['success', 'warning', 'error', 'info']).isRequired,
classes: PropTypes.object.isRequired,
className: PropTypes.string,
message: PropTypes.node,
onClose: PropTypes.func,
variant: PropTypes.oneOf(["success", "warning", "error", "info"]).isRequired
};
const MySnackbarContentWrapper = withStyles(styles1)(MySnackbarContent);
const styles = theme => ({
margin: {
margin: theme.spacing(1),
},
})
margin: theme.spacing(1)
}
});
class SnackbarCompoment extends Component {
state = {
open: false
};
state={
open:false,
}
UNSAFE_componentWillReceiveProps = (nextProps)=>{
if(nextProps.snackbar.toggle !== this.props.snackbar.toggle){
this.setState({open:true});
UNSAFE_componentWillReceiveProps = nextProps => {
if (nextProps.snackbar.toggle !== this.props.snackbar.toggle) {
this.setState({ open: true });
}
}
};
handleClose = () => {
this.setState({ open: false });
};
handleClose= ()=>{
this.setState({open:false});
}
render() {
return (
<Snackbar
anchorOrigin={{
vertical: this.props.snackbar.vertical,
horizontal: this.props.snackbar.horizontal,
}}
open={this.state.open}
autoHideDuration={6000}
onClose={this.handleClose}
>
<MySnackbarContentWrapper
anchorOrigin={{
vertical: this.props.snackbar.vertical,
horizontal: this.props.snackbar.horizontal
}}
open={this.state.open}
autoHideDuration={6000}
onClose={this.handleClose}
variant={this.props.snackbar.color}
message={this.props.snackbar.msg}
/>
</Snackbar>
>
<MySnackbarContentWrapper
onClose={this.handleClose}
variant={this.props.snackbar.color}
message={this.props.snackbar.msg}
/>
</Snackbar>
);
}
}
const AlertBar = connect(
mapStateToProps,
mapDispatchToProps
)( withStyles(styles)(SnackbarCompoment))
)(withStyles(styles)(SnackbarCompoment));
export default AlertBar
export default AlertBar;

View File

@ -16,11 +16,10 @@ const useStyles = makeStyles(() => ({
left: "auto",
zIndex: 5,
position: "fixed"
},
}
}));
export default function RemoteDownloadButton() {
const classes = useStyles();
const dispatch = useDispatch();
@ -29,19 +28,18 @@ export default function RemoteDownloadButton() {
[dispatch]
);
return (
<>
<Modals/>
<AutoHidden enable>
<Fab
className={classes.fab}
color="secondary"
onClick={()=>OpenRemoteDownloadDialog()}
>
<Add/>
</Fab>
</AutoHidden>
</>
<Modals />
<AutoHidden enable>
<Fab
className={classes.fab}
color="secondary"
onClick={() => OpenRemoteDownloadDialog()}
>
<Add />
</Fab>
</AutoHidden>
</>
);
}

View File

@ -1,8 +1,7 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect } from "react";
import Zoom from "@material-ui/core/Zoom";
function AutoHidden ({ children, enable }){
function AutoHidden({ children, enable }) {
const [hidden, setHidden] = useState(false);
let prev = window.scrollY;
@ -10,33 +9,29 @@ function AutoHidden ({ children, enable }){
const show = 50;
useEffect(() => {
const handleNavigation = (e) => {
const handleNavigation = e => {
const window = e.currentTarget;
if (prev > window.scrollY) {
if (lastUpdate - window.scrollY > show){
if (lastUpdate - window.scrollY > show) {
lastUpdate = window.scrollY;
setHidden(false);
}
} else if (prev < window.scrollY) {
if (window.scrollY - lastUpdate > show){
if (window.scrollY - lastUpdate > show) {
lastUpdate = window.scrollY;
setHidden(true);
}
}
prev = window.scrollY;
};
if (enable){
window.addEventListener('scroll', e => handleNavigation(e));
if (enable) {
window.addEventListener("scroll", e => handleNavigation(e));
}
// eslint-disable-next-line
}, [enable])
}, [enable]);
return (
<Zoom in={!hidden}>
{children}
</Zoom>
)
return <Zoom in={!hidden}>{children}</Zoom>;
}
export default AutoHidden
export default AutoHidden;

View File

@ -5,12 +5,16 @@ import SpeedDialIcon from "@material-ui/lab/SpeedDialIcon";
import SpeedDialAction from "@material-ui/lab/SpeedDialAction";
import CreateNewFolderIcon from "@material-ui/icons/CreateNewFolder";
import PublishIcon from "@material-ui/icons/Publish";
import { openCreateFileDialog, openCreateFolderDialog, toggleSnackbar } from "../../actions";
import {useDispatch} from "react-redux";
import {
openCreateFileDialog,
openCreateFolderDialog,
toggleSnackbar
} from "../../actions";
import { useDispatch } from "react-redux";
import AutoHidden from "./AutoHidden";
import statusHelper from "../../utils/page"
import statusHelper from "../../utils/page";
import Backdrop from "@material-ui/core/Backdrop";
import {FolderUpload,FilePlus} from "mdi-material-ui";
import { FolderUpload, FilePlus } from "mdi-material-ui";
const useStyles = makeStyles(() => ({
fab: {
@ -29,11 +33,11 @@ const useStyles = makeStyles(() => ({
zIndex: 9999,
right: 7
},
'@global': {
'.MuiSpeedDialAction-staticTooltipLabel': {
width:100,
},
},
"@global": {
".MuiSpeedDialAction-staticTooltipLabel": {
width: 100
}
}
}));
export default function UploadButton(props) {
@ -49,13 +53,11 @@ export default function UploadButton(props) {
[dispatch]
);
const OpenNewFolderDialog = useCallback(
() =>
dispatch(openCreateFolderDialog()),
() => dispatch(openCreateFolderDialog()),
[dispatch]
);
const OpenNewFileDialog = useCallback(
() =>
dispatch(openCreateFileDialog()),
() => dispatch(openCreateFileDialog()),
[dispatch]
);
@ -63,20 +65,13 @@ export default function UploadButton(props) {
setQueued(props.Queued);
}, [props.Queued]);
const openUpload = id =>{
const uploadButton = document.getElementsByClassName(
id
)[0];
if (document.body.contains(uploadButton)) {
uploadButton.click();
} else {
ToggleSnackbar(
"top",
"right",
"上传组件还未加载完成",
"warning"
);
}
const openUpload = id => {
const uploadButton = document.getElementsByClassName(id)[0];
if (document.body.contains(uploadButton)) {
uploadButton.click();
} else {
ToggleSnackbar("top", "right", "上传组件还未加载完成", "warning");
}
};
const uploadClicked = () => {
if (open) {
@ -112,44 +107,58 @@ export default function UploadButton(props) {
ariaLabel="SpeedDial openIcon example"
hidden={false}
tooltipTitle="上传文件"
icon={<SpeedDialIcon openIcon={!statusHelper.isMobile()&&<PublishIcon />} />}
icon={
<SpeedDialIcon
openIcon={
!statusHelper.isMobile() && <PublishIcon />
}
/>
}
onClose={handleClose}
FabProps={{
onClick: () => !statusHelper.isMobile() && uploadClicked(),
color: "secondary",
onClick: () =>
!statusHelper.isMobile() && uploadClicked(),
color: "secondary"
}}
onOpen={handleOpen}
open={open}
>
{statusHelper.isMobile() && <SpeedDialAction
key="UploadFile"
icon={<PublishIcon />}
tooltipOpen
tooltipTitle="上传文件"
onClick= {() => uploadClicked()}
title={"上传文件"}/>}
{!statusHelper.isMobile() && <SpeedDialAction
key="UploadFolder"
icon={<FolderUpload />}
tooltipOpen
tooltipTitle="上传目录"
onClick= {() => openUpload("uploadFolderForm")}
title={"上传目录"}/>}
{statusHelper.isMobile() && (
<SpeedDialAction
key="UploadFile"
icon={<PublishIcon />}
tooltipOpen
tooltipTitle="上传文件"
onClick={() => uploadClicked()}
title={"上传文件"}
/>
)}
{!statusHelper.isMobile() && (
<SpeedDialAction
key="UploadFolder"
icon={<FolderUpload />}
tooltipOpen
tooltipTitle="上传目录"
onClick={() => openUpload("uploadFolderForm")}
title={"上传目录"}
/>
)}
<SpeedDialAction
key="NewFolder"
icon={<CreateNewFolderIcon />}
tooltipOpen
tooltipTitle="新建目录"
onClick= {() => OpenNewFolderDialog()}
title={"新建目录"}/>
onClick={() => OpenNewFolderDialog()}
title={"新建目录"}
/>
<SpeedDialAction
key="NewFile"
icon={<FilePlus />}
tooltipOpen
tooltipTitle="新建文件"
onClick= {() => OpenNewFileDialog()}
title={"新建文件"}/>
onClick={() => OpenNewFileDialog()}
title={"新建文件"}
/>
</SpeedDial>
</Badge>
</AutoHidden>

View File

@ -1,7 +1,7 @@
import React from "react";
import { makeStyles } from "@material-ui/core";
import SaveIcon from "@material-ui/icons/Save";
import CheckIcon from '@material-ui/icons/Check';
import CheckIcon from "@material-ui/icons/Check";
import AutoHidden from "./AutoHidden";
import statusHelper from "../../utils/page";
import Fab from "@material-ui/core/Fab";
@ -40,16 +40,16 @@ const useStyles = makeStyles(theme => ({
},
buttonSuccess: {
backgroundColor: green[500],
'&:hover': {
backgroundColor: green[700],
},
},
"&:hover": {
backgroundColor: green[700]
}
}
}));
export default function SaveButton(props) {
const classes = useStyles();
const buttonClassname = clsx({
[classes.buttonSuccess]: props.status==="success",
[classes.buttonSuccess]: props.status === "success"
});
return (
@ -64,14 +64,19 @@ export default function SaveButton(props) {
disabled={props.status === "loading"}
aria-label="add"
>
{props.status==="success" ? <CheckIcon /> : <SaveIcon />}
{props.status === "success" ? (
<CheckIcon />
) : (
<SaveIcon />
)}
</Fab>
</Tooltip>
{props.status === "loading"&&<CircularProgress
size={68}
className={classes.fabProgress}
/>}
{props.status === "loading" && (
<CircularProgress
size={68}
className={classes.fabProgress}
/>
)}
</div>
</div>
</AutoHidden>

View File

@ -53,8 +53,8 @@ const styles = theme => ({
marginTop: "20px",
marginBottom: "20px"
},
margin:{
marginTop:theme.spacing(2),
margin: {
marginTop: theme.spacing(2)
}
});
const mapStateToProps = () => {
@ -101,8 +101,11 @@ class DownloadComponent extends Component {
});
// 设定自动更新
clearTimeout(this.interval);
if(response.data.length > 0){
this.interval = setTimeout(this.loadDownloading,1000 * response.data[0].interval);
if (response.data.length > 0) {
this.interval = setTimeout(
this.loadDownloading,
1000 * response.data[0].interval
);
}
})
.catch(error => {
@ -119,11 +122,13 @@ class DownloadComponent extends Component {
this.setState({
loading: true
});
API
.get("/aria2/finished?page=" + ++this.page)
API.get("/aria2/finished?page=" + ++this.page)
.then(response => {
this.setState({
finishedList: [...this.state.finishedList,...response.data],
finishedList: [
...this.state.finishedList,
...response.data
],
loading: false,
continue: response.data.length >= 10
});
@ -142,7 +147,7 @@ class DownloadComponent extends Component {
return (
<div className={classes.layout}>
{user.group.allowRemoteDownload&& <RemoteDownloadButton/>}
{user.group.allowRemoteDownload && <RemoteDownloadButton />}
<Typography
color="textSecondary"
variant="h4"
@ -157,7 +162,7 @@ class DownloadComponent extends Component {
</IconButton>
</Typography>
{this.state.downloading.map((value, k) => (
<DownloadingCard key={ k } task={value} />
<DownloadingCard key={k} task={value} />
))}
<Typography
color="textSecondary"
@ -169,12 +174,9 @@ class DownloadComponent extends Component {
<div className={classes.loadMore}>
{this.state.finishedList.map((value, k) => {
if (value.files) {
return (
<FinishedCard key={k} task={value}/>
)
return <FinishedCard key={k} task={value} />;
}
return null
return null;
})}
<Button
size="large"

View File

@ -1,4 +1,10 @@
import { Divider, ListItemIcon, MenuItem, Typography, withStyles } from "@material-ui/core";
import {
Divider,
ListItemIcon,
MenuItem,
Typography,
withStyles
} from "@material-ui/core";
import Menu from "@material-ui/core/Menu";
import { Archive, Unarchive } from "@material-ui/icons";
import RenameIcon from "@material-ui/icons/BorderColor";
@ -12,27 +18,49 @@ import MoveIcon from "@material-ui/icons/Input";
import LinkIcon from "@material-ui/icons/InsertLink";
import OpenIcon from "@material-ui/icons/OpenInNew";
import ShareIcon from "@material-ui/icons/Share";
import { FolderUpload, MagnetOn ,FilePlus} from "mdi-material-ui";
import { FolderUpload, MagnetOn, FilePlus } from "mdi-material-ui";
import PropTypes from "prop-types";
import React, { Component } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { openCompressDialog,openCreateFileDialog ,refreshFileList} from "../../actions";
import { changeContextMenu, navigateTo, openCopyDialog, openCreateFolderDialog, openDecompressDialog, openGetSourceDialog, openLoadingDialog, openMoveDialog, openMusicDialog, openRemoteDownloadDialog, openRemoveDialog, openRenameDialog, openShareDialog, openTorrentDownloadDialog, setNavigatorLoadingStatus, setSelectedTarget, showImgPreivew, toggleSnackbar } from "../../actions/index";
import {
openCompressDialog,
openCreateFileDialog,
refreshFileList
} from "../../actions";
import {
changeContextMenu,
navigateTo,
openCopyDialog,
openCreateFolderDialog,
openDecompressDialog,
openGetSourceDialog,
openLoadingDialog,
openMoveDialog,
openMusicDialog,
openRemoteDownloadDialog,
openRemoveDialog,
openRenameDialog,
openShareDialog,
openTorrentDownloadDialog,
setNavigatorLoadingStatus,
setSelectedTarget,
showImgPreivew,
toggleSnackbar
} from "../../actions/index";
import { isCompressFile, isPreviewable, isTorrent } from "../../config";
import Auth from "../../middleware/Auth";
import { allowSharePreview } from "../../utils/index";
import pathHelper from "../../utils/page";
import RefreshIcon from "@material-ui/icons/Refresh";
const styles = () => ({
propover: {
minWidth: "200px!important"
},
divider:{
marginTop:4,
marginBottom:4,
divider: {
marginTop: 4,
marginBottom: 4
}
});
@ -45,7 +73,7 @@ const mapStateToProps = state => {
withFile: state.explorer.selectProps.withFile,
path: state.navigator.path,
selected: state.explorer.selected,
keywords: state.explorer.keywords,
keywords: state.explorer.keywords
};
};
@ -113,7 +141,7 @@ const mapDispatchToProps = dispatch => {
},
refreshFileList: () => {
dispatch(refreshFileList());
},
}
};
};
@ -195,8 +223,8 @@ class ContextMenuCompoment extends Component {
this.props.selected[0].path === "/"
? this.props.selected[0].path + this.props.selected[0].name
: this.props.selected[0].path +
"/" +
this.props.selected[0].name;
"/" +
this.props.selected[0].name;
switch (isPreviewable(this.props.selected[0].name)) {
case "img":
this.props.showImgPreivew(this.props.selected[0]);
@ -205,15 +233,18 @@ class ContextMenuCompoment extends Component {
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/doc?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/doc?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/doc?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/doc?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "audio":
@ -223,60 +254,72 @@ class ContextMenuCompoment extends Component {
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/video?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/video?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/video?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/video?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "pdf":
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/pdf?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/pdf?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/pdf?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/pdf?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "edit":
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/text?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/text?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/text?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/text?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "code":
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/code?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/code?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/code?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/code?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
default:
@ -310,21 +353,26 @@ class ContextMenuCompoment extends Component {
>
{this.props.menuType === "empty" && (
<div>
<MenuItem onClick={()=>{
this.props.refreshFileList();
this.props.changeContextMenu(this.props.menuType, false)
}
}>
<MenuItem
onClick={() => {
this.props.refreshFileList();
this.props.changeContextMenu(
this.props.menuType,
false
);
}}
>
<ListItemIcon>
<RefreshIcon />
</ListItemIcon>
<Typography variant="inherit">
刷新
</Typography>
<Typography variant="inherit">刷新</Typography>
</MenuItem>
<Divider className={classes.divider}/>
<MenuItem onClick={()=>this.clickUpload("uploadFileForm")}>
<Divider className={classes.divider} />
<MenuItem
onClick={() =>
this.clickUpload("uploadFileForm")
}
>
<ListItemIcon>
<UploadIcon />
</ListItemIcon>
@ -332,7 +380,11 @@ class ContextMenuCompoment extends Component {
上传文件
</Typography>
</MenuItem>
<MenuItem onClick={()=>this.clickUpload("uploadFolderForm")}>
<MenuItem
onClick={() =>
this.clickUpload("uploadFolderForm")
}
>
<ListItemIcon>
<FolderUpload />
</ListItemIcon>
@ -355,7 +407,7 @@ class ContextMenuCompoment extends Component {
</MenuItem>
)}
<Divider className={classes.divider}/>
<Divider className={classes.divider} />
<MenuItem
onClick={() =>
this.props.openCreateFolderDialog()
@ -394,7 +446,9 @@ class ContextMenuCompoment extends Component {
进入
</Typography>
</MenuItem>
{isHomePage && <Divider className={classes.divider} />}
{isHomePage && (
<Divider className={classes.divider} />
)}
</div>
)}
{!this.props.isMultiple &&
@ -428,7 +482,9 @@ class ContextMenuCompoment extends Component {
下载
</Typography>
</MenuItem>
{isHomePage && <Divider className={classes.divider} />}
{isHomePage && (
<Divider className={classes.divider} />
)}
</div>
)}
@ -546,7 +602,7 @@ class ContextMenuCompoment extends Component {
重命名
</Typography>
</MenuItem>
{this.props.keywords === "" &&
{this.props.keywords === "" && (
<MenuItem
onClick={() =>
this.props.openCopyDialog()
@ -559,13 +615,12 @@ class ContextMenuCompoment extends Component {
复制
</Typography>
</MenuItem>
}
)}
</div>
)}
{isHomePage && (
<div>
{this.props.keywords === "" &&
{this.props.keywords === "" && (
<MenuItem
onClick={() =>
this.props.openMoveDialog()
@ -578,7 +633,7 @@ class ContextMenuCompoment extends Component {
移动
</Typography>
</MenuItem>
}
)}
<Divider className={classes.divider} />
<MenuItem

View File

@ -1,74 +1,78 @@
import React from 'react'
import { useDragLayer } from 'react-dnd'
import Preview from './Preview'
import React from "react";
import { useDragLayer } from "react-dnd";
import Preview from "./Preview";
const layerStyles = {
position: 'fixed',
pointerEvents: 'none',
zIndex: 100,
left: 0,
top: 0,
width: '100%',
height: '100%',
}
position: "fixed",
pointerEvents: "none",
zIndex: 100,
left: 0,
top: 0,
width: "100%",
height: "100%"
};
function snapToGrid(x, y) {
const snappedX = Math.round(x / 32) * 32
const snappedY = Math.round(y / 32) * 32
return [snappedX, snappedY]
const snappedX = Math.round(x / 32) * 32;
const snappedY = Math.round(y / 32) * 32;
return [snappedX, snappedY];
}
function getItemStyles(initialOffset, currentOffset, isSnapToGrid) {
if (!initialOffset || !currentOffset) {
return {
display: 'none',
if (!initialOffset || !currentOffset) {
return {
display: "none"
};
}
}
let { x, y } = currentOffset
if (isSnapToGrid) {
x -= initialOffset.x
y -= initialOffset.y
;[x, y] = snapToGrid(x, y)
x += initialOffset.x
y += initialOffset.y
}
const transform = `translate(${x}px, ${y}px)`
return {
transform,
WebkitTransform: transform,
opacity:y>200?1:0.4,
}
let { x, y } = currentOffset;
if (isSnapToGrid) {
x -= initialOffset.x;
y -= initialOffset.y;
[x, y] = snapToGrid(x, y);
x += initialOffset.x;
y += initialOffset.y;
}
const transform = `translate(${x}px, ${y}px)`;
return {
transform,
WebkitTransform: transform,
opacity: y > 200 ? 1 : 0.4
};
}
const CustomDragLayer = props => {
const {
itemType,
isDragging,
item,
initialOffset,
currentOffset,
} = useDragLayer(monitor => ({
item: monitor.getItem(),
itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset(),
isDragging: monitor.isDragging(),
}))
function renderItem() {
switch (itemType) {
case "object":
return <Preview object={item.object} />
default:
return null
const {
itemType,
isDragging,
item,
initialOffset,
currentOffset
} = useDragLayer(monitor => ({
item: monitor.getItem(),
itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset(),
isDragging: monitor.isDragging()
}));
function renderItem() {
switch (itemType) {
case "object":
return <Preview object={item.object} />;
default:
return null;
}
}
}
if (!isDragging) {
return null
}
return (
<div style={layerStyles}>
<div
style={getItemStyles(initialOffset, currentOffset, props.snapToGrid)}
>
{renderItem()}
</div>
</div>
)
}
export default CustomDragLayer
if (!isDragging) {
return null;
}
return (
<div style={layerStyles}>
<div
style={getItemStyles(
initialOffset,
currentOffset,
props.snapToGrid
)}
>
{renderItem()}
</div>
</div>
);
};
export default CustomDragLayer;

View File

@ -1,19 +1,19 @@
import React from "react";
import { useDrop } from "react-dnd";
import Folder from "../Folder"
export default function FolderDropWarpper({ folder}) {
import Folder from "../Folder";
export default function FolderDropWarpper({ folder }) {
const [{ canDrop, isOver }, drop] = useDrop({
accept: "object",
drop: () => ({ folder }),
collect: monitor => ({
isOver: monitor.isOver(),
canDrop: (monitor.canDrop())
canDrop: monitor.canDrop()
})
});
const isActive = canDrop && isOver;
return (
<div ref={drop}>
<Folder folder={folder} isActive={isActive}/></div>
<Folder folder={folder} isActive={isActive} />
</div>
);
}

View File

@ -5,21 +5,21 @@ import { useSelector } from "react-redux";
import { makeStyles } from "@material-ui/core";
const useStyles = makeStyles(() => ({
dragging:{
width:"200px",
dragging: {
width: "200px"
},
cardDragged: {
position: "absolute",
"transform-origin": "bottom left",
},
"transform-origin": "bottom left"
}
}));
const diliverIcon = (object,viewMethod,classes)=>{
const diliverIcon = (object, viewMethod, classes) => {
return (
<>
{object.type === "dir" && viewMethod !== "list" && (
<div className={classes.dragging}>
<SmallIcon file={object} />
<SmallIcon file={object} />
</div>
)}
{object.type === "file" && viewMethod === "icon" && (
@ -27,15 +27,14 @@ const diliverIcon = (object,viewMethod,classes)=>{
<FileIcon file={object} />
</div>
)}
{object.type === "file" &&
viewMethod === "smallIcon" && (
<div className={classes.dragging}>
{object.type === "file" && viewMethod === "smallIcon" && (
<div className={classes.dragging}>
<SmallIcon file={object} />
</div>
)}
</div>
)}
</>
)
}
);
};
const Preview = props => {
const selected = useSelector(state => state.explorer.selected);
@ -45,22 +44,24 @@ const Preview = props => {
const classes = useStyles();
return (
<>
{selected.length === 0 && diliverIcon(props.object,viewMethod,classes)}
{selected.length>0&&<>
{selected.slice(0, 3).map((card, i) => (
<div
key={card.id}
className={classes.cardDragged}
style={{
zIndex: selected.length - i,
transform: `rotateZ(${-i * 2.5}deg)`,
}}
>
{diliverIcon(card,viewMethod,classes)}
</div>
))}
{selected.length === 0 &&
diliverIcon(props.object, viewMethod, classes)}
{selected.length > 0 && (
<>
{selected.slice(0, 3).map((card, i) => (
<div
key={card.id}
className={classes.cardDragged}
style={{
zIndex: selected.length - i,
transform: `rotateZ(${-i * 2.5}deg)`
}}
>
{diliverIcon(card, viewMethod, classes)}
</div>
))}
</>
}
)}
</>
);
};

View File

@ -1,4 +1,15 @@
import { CircularProgress, Grid, Paper, Table, TableBody, TableCell, TableHead, TableRow, Typography, withStyles } from "@material-ui/core";
import {
CircularProgress,
Grid,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Typography,
withStyles
} from "@material-ui/core";
import TableSortLabel from "@material-ui/core/TableSortLabel";
import SadIcon from "@material-ui/icons/SentimentVeryDissatisfied";
import EmptyIcon from "@material-ui/icons/Unarchive";
@ -8,8 +19,14 @@ import React, { Component } from "react";
import { configure, GlobalHotKeys } from "react-hotkeys";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { changeContextMenu, navigateTo, navigateUp, openRemoveDialog, setSelectedTarget } from "../../actions/index";
import explorer from "../../redux/explorer"
import {
changeContextMenu,
navigateTo,
navigateUp,
openRemoveDialog,
setSelectedTarget
} from "../../actions/index";
import explorer from "../../redux/explorer";
import { isMac } from "../../utils";
import pathHelper from "../../utils/page";
import ContextMenu from "./ContextMenu";
@ -161,7 +178,7 @@ class ExplorerCompoment extends Component {
super();
this.keyMap = {
DELETE_FILE: "del",
SELECT_ALL: `${isMac() ? 'command' : 'ctrl'}+a`
SELECT_ALL: `${isMac() ? "command" : "ctrl"}+a`
};
this.handlers = {
@ -187,8 +204,8 @@ class ExplorerCompoment extends Component {
};
configure({
ignoreTags: ['input', 'select', 'textarea'],
})
ignoreTags: ["input", "select", "textarea"]
});
}
contextMenu = e => {
@ -218,236 +235,198 @@ class ExplorerCompoment extends Component {
const { classes } = this.props;
const isHomePage = pathHelper.isHomePage(this.props.location.pathname);
const showView = !this.props.loading && (this.props.dirList.length !== 0 ||
this.props.fileList.length !== 0)
const showView =
!this.props.loading &&
(this.props.dirList.length !== 0 ||
this.props.fileList.length !== 0);
const listView = (
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>
<TableSortLabel
active={
this.props.sortMethod ===
"namePos" ||
this.props.sortMethod ===
"nameRev"
}
direction={
this.props.sortMethod ===
"namePos"
? "asc"
: "des"
}
onClick={() => {
this.props.changeSort(
this.props.sortMethod ===
"namePos"
? "nameRev"
: "namePos"
);
}}
>
名称
{this.props.sortMethod ===
"namePos" ||
this.props.sortMethod ===
"nameRev" ? (
<span
className={
classes.visuallyHidden
}
>
{this.props.sortMethod ===
"nameRev"
? "sorted descending"
: "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
<TableCell className={classes.hideAuto}>
<TableSortLabel
active={
this.props.sortMethod ===
"sizePos" ||
this.props.sortMethod ===
"sizeRes"
}
direction={
this.props.sortMethod ===
"sizePos"
? "asc"
: "des"
}
onClick={() => {
this.props.changeSort(
this.props.sortMethod ===
"sizePos"
? "sizeRes"
: "sizePos"
);
}}
>
大小
{this.props.sortMethod ===
"sizePos" ||
this.props.sortMethod ===
"sizeRes" ? (
<span
className={
classes.visuallyHidden
}
>
{this.props.sortMethod ===
"sizeRes"
? "sorted descending"
: "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
<TableCell className={classes.hideAuto}>
<TableSortLabel
active={
this.props.sortMethod ===
"timePos" ||
this.props.sortMethod ===
"timeRev"
}
direction={
this.props.sortMethod ===
"timePos"
? "asc"
: "des"
}
onClick={() => {
this.props.changeSort(
this.props.sortMethod ===
"timePos"
? "timeRev"
: "timePos"
);
}}
>
日期
{this.props.sortMethod ===
"timePos" ||
this.props.sortMethod ===
"timeRev" ? (
<span
className={
classes.visuallyHidden
}
>
{this.props.sortMethod ===
"sizeRes"
? "sorted descending"
: "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{pathHelper.isMobile() &&
this.props.path !== "/" && (
<ObjectIcon
file={{
type: "up",
name: "上级目录"
}}
/>
)}
{this.props.dirList.map((value, index) => (
<ObjectIcon key={value.id} file={value} index={index}/>
))}
{this.props.fileList.map((value, index) => (
<ObjectIcon key={value.id} file={value} index={index}/>
))}
</TableBody>
</Table>
)
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>
<TableSortLabel
active={
this.props.sortMethod === "namePos" ||
this.props.sortMethod === "nameRev"
}
direction={
this.props.sortMethod === "namePos"
? "asc"
: "des"
}
onClick={() => {
this.props.changeSort(
this.props.sortMethod === "namePos"
? "nameRev"
: "namePos"
);
}}
>
名称
{this.props.sortMethod === "namePos" ||
this.props.sortMethod === "nameRev" ? (
<span className={classes.visuallyHidden}>
{this.props.sortMethod === "nameRev"
? "sorted descending"
: "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
<TableCell className={classes.hideAuto}>
<TableSortLabel
active={
this.props.sortMethod === "sizePos" ||
this.props.sortMethod === "sizeRes"
}
direction={
this.props.sortMethod === "sizePos"
? "asc"
: "des"
}
onClick={() => {
this.props.changeSort(
this.props.sortMethod === "sizePos"
? "sizeRes"
: "sizePos"
);
}}
>
大小
{this.props.sortMethod === "sizePos" ||
this.props.sortMethod === "sizeRes" ? (
<span className={classes.visuallyHidden}>
{this.props.sortMethod === "sizeRes"
? "sorted descending"
: "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
<TableCell className={classes.hideAuto}>
<TableSortLabel
active={
this.props.sortMethod === "timePos" ||
this.props.sortMethod === "timeRev"
}
direction={
this.props.sortMethod === "timePos"
? "asc"
: "des"
}
onClick={() => {
this.props.changeSort(
this.props.sortMethod === "timePos"
? "timeRev"
: "timePos"
);
}}
>
日期
{this.props.sortMethod === "timePos" ||
this.props.sortMethod === "timeRev" ? (
<span className={classes.visuallyHidden}>
{this.props.sortMethod === "sizeRes"
? "sorted descending"
: "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{pathHelper.isMobile() && this.props.path !== "/" && (
<ObjectIcon
file={{
type: "up",
name: "上级目录"
}}
/>
)}
{this.props.dirList.map((value, index) => (
<ObjectIcon key={value.id} file={value} index={index} />
))}
{this.props.fileList.map((value, index) => (
<ObjectIcon key={value.id} file={value} index={index} />
))}
</TableBody>
</Table>
);
const normalView = (
<div className={classes.flexFix}>
{this.props.dirList.length !== 0 &&
(
<>
<Typography
data-clickAway={"true"}
variant="body2"
className={classes.typeHeader}
>
文件夹
</Typography>
<Grid
data-clickAway={"true"}
container
spacing={0}
alignItems="flex-start"
>
{this.props.dirList.map(
(value, index) => (
<Grid
key={value.id}
item
xs={6}
md={3}
sm={4}
lg={2}
>
<ObjectIcon
key={value.id}
file={value}
index={index}
/>
</Grid>
)
)}
</Grid>
</>
)}
{this.props.fileList.length !== 0 &&
(
<>
<Typography
data-clickAway={"true"}
variant="body2"
className={classes.typeHeader}
>
文件
</Typography>
<Grid
data-clickAway={"true"}
container
spacing={0}
alignItems="flex-start"
>
{this.props.fileList.map(
(value, index) => (
<Grid
key={value.id}
item
xs={6}
md={3}
sm={4}
lg={2}
>
<ObjectIcon
key={value.id}
index={index}
file={value}
/>
</Grid>
)
)}
</Grid>
</>
)}
</div>
)
const view = this.props.viewMethod === "list" ? listView : normalView
<div className={classes.flexFix}>
{this.props.dirList.length !== 0 && (
<>
<Typography
data-clickAway={"true"}
variant="body2"
className={classes.typeHeader}
>
文件夹
</Typography>
<Grid
data-clickAway={"true"}
container
spacing={0}
alignItems="flex-start"
>
{this.props.dirList.map((value, index) => (
<Grid
key={value.id}
item
xs={6}
md={3}
sm={4}
lg={2}
>
<ObjectIcon
key={value.id}
file={value}
index={index}
/>
</Grid>
))}
</Grid>
</>
)}
{this.props.fileList.length !== 0 && (
<>
<Typography
data-clickAway={"true"}
variant="body2"
className={classes.typeHeader}
>
文件
</Typography>
<Grid
data-clickAway={"true"}
container
spacing={0}
alignItems="flex-start"
>
{this.props.fileList.map((value, index) => (
<Grid
key={value.id}
item
xs={6}
md={3}
sm={4}
lg={2}
>
<ObjectIcon
key={value.id}
index={index}
file={value}
/>
</Grid>
))}
</Grid>
</>
)}
</div>
);
const view = this.props.viewMethod === "list" ? listView : normalView;
return (
<div
onContextMenu={this.contextMenu}

View File

@ -1,4 +1,10 @@
import { ButtonBase, Divider, Tooltip, Typography, withStyles } from "@material-ui/core";
import {
ButtonBase,
Divider,
Tooltip,
Typography,
withStyles
} from "@material-ui/core";
import { lighten } from "@material-ui/core/styles";
import classNames from "classnames";
import PropTypes from "prop-types";
@ -95,7 +101,7 @@ const styles = theme => ({
backgroundColor: theme.palette.background.paper,
borderRadius: "90%",
paddingTop: "2px",
color: theme.palette.text.secondary,
color: theme.palette.text.secondary
},
hide: {
display: "none"

View File

@ -1,17 +1,22 @@
import React, { Component } from 'react'
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import { connect } from "react-redux"
import { withRouter } from "react-router-dom"
import { closeAllModals, navigateTo, setSelectedTarget, toggleSnackbar } from "../../actions"
import React, { Component } from "react";
import { DndProvider } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import {
closeAllModals,
navigateTo,
setSelectedTarget,
toggleSnackbar
} from "../../actions";
import { changeSubTitle } from "../../redux/viewUpdate/action";
import pathHelper from "../../utils/page"
import DragLayer from "./DnD/DragLayer"
import Explorer from "./Explorer"
import Modals from "./Modals"
import Navigator from "./Navigator/Navigator"
import pathHelper from "../../utils/page";
import DragLayer from "./DnD/DragLayer";
import Explorer from "./Explorer";
import Modals from "./Modals";
import Navigator from "./Navigator/Navigator";
const mapStateToProps = ()=>({});
const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch => {
return {
@ -27,14 +32,14 @@ const mapDispatchToProps = dispatch => {
closeAllModals: () => {
dispatch(closeAllModals());
},
navigateTo:path=>{
navigateTo: path => {
dispatch(navigateTo(path));
},
}
};
};
class FileManager extends Component {
constructor(props){
constructor(props) {
super(props);
this.image = React.createRef();
}
@ -45,28 +50,28 @@ class FileManager extends Component {
}
componentDidMount() {
if (pathHelper.isHomePage(this.props.location.pathname)){
if (pathHelper.isHomePage(this.props.location.pathname)) {
this.props.changeSubTitle(null);
}
}
render() {
return (
<DndProvider backend={HTML5Backend}>
<Modals share={this.props.share}/>
<Navigator isShare={this.props.isShare} share={this.props.share}/>
<Explorer share={this.props.share}/>
<DragLayer/>
</DndProvider>
<Modals share={this.props.share} />
<Navigator
isShare={this.props.isShare}
share={this.props.share}
/>
<Explorer share={this.props.share} />
<DragLayer />
</DndProvider>
);
}
}
FileManager.propTypes = {
};
FileManager.propTypes = {};
export default connect(
mapStateToProps,
mapDispatchToProps
)((withRouter(FileManager)));
)(withRouter(FileManager));

View File

@ -1,9 +1,9 @@
import React from "react";
import React from "react";
import FolderIcon from "@material-ui/icons/Folder";
import classNames from "classnames";
import { ButtonBase, Typography, Tooltip, makeStyles } from "@material-ui/core";
import { useSelector } from "react-redux";
import {lighten} from "@material-ui/core/styles";
import { lighten } from "@material-ui/core/styles";
const useStyles = makeStyles(theme => ({
container: {
padding: "7px"
@ -46,7 +46,7 @@ const useStyles = makeStyles(theme => ({
backgroundColor: theme.palette.background.paper,
borderRadius: "90%",
paddingTop: "2px",
color: theme.palette.text.secondary,
color: theme.palette.text.secondary
},
folderNameSelected: {
color:
@ -65,12 +65,12 @@ const useStyles = makeStyles(theme => ({
overflow: "hidden",
marginRight: "20px"
},
active:{
border: "2px solid " + theme.palette.primary.light,
},
active: {
border: "2px solid " + theme.palette.primary.light
}
}));
export default function Folder({ folder,isActive }) {
export default function Folder({ folder, isActive }) {
const selected = useSelector(state => state.explorer.selected);
const classes = useStyles();
@ -87,9 +87,9 @@ export default function Folder({ folder,isActive }) {
{
[classes.selected]: isSelected,
[classes.notSelected]: !isSelected,
[classes.active]: isActive,
[classes.active]: isActive
},
classes.button,
classes.button
)}
>
<div

View File

@ -6,9 +6,9 @@ import { showImgPreivew } from "../../actions/index";
import { imgPreviewSuffix } from "../../config";
import { withStyles } from "@material-ui/core";
import pathHelper from "../../utils/page";
import {withRouter} from "react-router";
import {PhotoSlider} from "react-photo-view";
import 'react-photo-view/dist/index.css';
import { withRouter } from "react-router";
import { PhotoSlider } from "react-photo-view";
import "react-photo-view/dist/index.css";
import * as explorer from "../../redux/explorer/reducer";
const styles = () => ({});
@ -39,21 +39,22 @@ class ImagPreviewComponent extends Component {
const items = [];
let firstOne = 0;
if (nextProps.first.id !== "") {
if (pathHelper.isSharePage(this.props.location.pathname) && !nextProps.first.path){
if (
pathHelper.isSharePage(this.props.location.pathname) &&
!nextProps.first.path
) {
const newImg = {
intro: nextProps.first.name,
src:
baseURL +
"/share/preview/" +nextProps.first.key
src: baseURL + "/share/preview/" + nextProps.first.key
};
firstOne = 0;
items.push(newImg);
this.setState({
photoIndex:firstOne,
photoIndex: firstOne,
items: items,
isOpen: true
});
return
return;
}
// eslint-disable-next-line
nextProps.other.map(value => {
@ -63,21 +64,22 @@ class ImagPreviewComponent extends Component {
.toLowerCase();
if (imgPreviewSuffix.indexOf(fileType) !== -1) {
let src = "";
if (pathHelper.isSharePage(this.props.location.pathname)){
src = baseURL +
"/share/preview/" + value.key
src = src + "?path=" + encodeURIComponent( (value.path === "/"
? value.path + value.name
: value.path + "/" + value.name))
}else{
src = baseURL +
"/file/preview/" +
value.id
if (pathHelper.isSharePage(this.props.location.pathname)) {
src = baseURL + "/share/preview/" + value.key;
src =
src +
"?path=" +
encodeURIComponent(
value.path === "/"
? value.path + value.name
: value.path + "/" + value.name
);
} else {
src = baseURL + "/file/preview/" + value.id;
}
const newImg = {
intro: value.name,
src:src,
src: src
};
if (
value.path === nextProps.first.path &&
@ -89,7 +91,7 @@ class ImagPreviewComponent extends Component {
}
});
this.setState({
photoIndex:firstOne,
photoIndex: firstOne,
items: items,
isOpen: true
});
@ -104,22 +106,23 @@ class ImagPreviewComponent extends Component {
};
render() {
const { photoIndex, isOpen,items } = this.state;
const { photoIndex, isOpen, items } = this.state;
return (
<div>
{isOpen && (<PhotoSlider
images={items}
visible={isOpen}
onClose={() => this.handleClose()}
index={photoIndex}
onIndexChange={(n) =>
this.setState({
photoIndex: n,
})
}
/>)}
{isOpen && (
<PhotoSlider
images={items}
visible={isOpen}
onClose={() => this.handleClose()}
index={photoIndex}
onIndexChange={n =>
this.setState({
photoIndex: n
})
}
/>
)}
</div>
);
}

View File

@ -8,7 +8,7 @@ import { withStyles } from "@material-ui/core";
import Lightbox from "react-image-lightbox";
import "react-image-lightbox/style.css";
import pathHelper from "../../utils/page";
import {withRouter} from "react-router";
import { withRouter } from "react-router";
const styles = () => ({});
@ -38,21 +38,22 @@ class ImgPreviewCompoment extends Component {
const items = [];
let firstOne = 0;
if (nextProps.first !== null) {
if (pathHelper.isSharePage(this.props.location.pathname) && !nextProps.first.path){
if (
pathHelper.isSharePage(this.props.location.pathname) &&
!nextProps.first.path
) {
const newImg = {
title: nextProps.first.name,
src:
baseURL +
"/share/preview/" +nextProps.first.key
src: baseURL + "/share/preview/" + nextProps.first.key
};
firstOne = 0;
items.push(newImg);
this.setState({
photoIndex:firstOne,
photoIndex: firstOne,
items: items,
isOpen: true
});
return
return;
}
// eslint-disable-next-line
nextProps.other.map(value => {
@ -62,21 +63,22 @@ class ImgPreviewCompoment extends Component {
.toLowerCase();
if (imgPreviewSuffix.indexOf(fileType) !== -1) {
let src = "";
if (pathHelper.isSharePage(this.props.location.pathname)){
src = baseURL +
"/share/preview/" + value.key
src = src + "?path=" + encodeURIComponent( (value.path === "/"
? value.path + value.name
: value.path + "/" + value.name))
}else{
src = baseURL +
"/file/preview/" +
value.id
if (pathHelper.isSharePage(this.props.location.pathname)) {
src = baseURL + "/share/preview/" + value.key;
src =
src +
"?path=" +
encodeURIComponent(
value.path === "/"
? value.path + value.name
: value.path + "/" + value.name
);
} else {
src = baseURL + "/file/preview/" + value.id;
}
const newImg = {
title: value.name,
src:src,
src: src
};
if (
value.path === nextProps.first.path &&
@ -88,7 +90,7 @@ class ImgPreviewCompoment extends Component {
}
});
this.setState({
photoIndex:firstOne,
photoIndex: firstOne,
items: items,
isOpen: true
});
@ -103,34 +105,42 @@ class ImgPreviewCompoment extends Component {
};
render() {
const { photoIndex, isOpen,items } = this.state;
const { photoIndex, isOpen, items } = this.state;
return (
<div>
{isOpen && (<Lightbox
mainSrc={items[photoIndex].src}
nextSrc={items[(photoIndex + 1) % items.length].src}
prevSrc={items[(photoIndex + items.length - 1) % items.length].src}
onCloseRequest={() => this.handleClose()}
imageLoadErrorMessage = "无法加载此图像"
imageCrossOrigin = "anonymous"
imageTitle = {items[photoIndex].title}
onMovePrevRequest={() =>
this.setState({
photoIndex: (photoIndex + items.length - 1) % items.length,
})
}
reactModalStyle={{
overlay:{
zIndex:10000
},
}}
onMoveNextRequest={() =>
this.setState({
photoIndex: (photoIndex + 1) % items.length,
})
}
/>)}
{isOpen && (
<Lightbox
mainSrc={items[photoIndex].src}
nextSrc={items[(photoIndex + 1) % items.length].src}
prevSrc={
items[
(photoIndex + items.length - 1) % items.length
].src
}
onCloseRequest={() => this.handleClose()}
imageLoadErrorMessage="无法加载此图像"
imageCrossOrigin="anonymous"
imageTitle={items[photoIndex].title}
onMovePrevRequest={() =>
this.setState({
photoIndex:
(photoIndex + items.length - 1) %
items.length
})
}
reactModalStyle={{
overlay: {
zIndex: 10000
}
}}
onMoveNextRequest={() =>
this.setState({
photoIndex: (photoIndex + 1) % items.length
})
}
/>
)}
</div>
);
}

View File

@ -20,7 +20,7 @@ import {
DialogContent,
DialogTitle,
DialogContentText,
CircularProgress,
CircularProgress
} from "@material-ui/core";
import Loading from "../Modals/Loading";
import CopyDialog from "../Modals/Copy";
@ -100,7 +100,7 @@ class ModalsCompoment extends Component {
downloadURL: "",
remoteDownloadPathSelect: false,
source: "",
purchaseCallback:null,
purchaseCallback: null
};
handleInputChange = e => {
@ -121,17 +121,25 @@ class ModalsCompoment extends Component {
// 打包下载
if (nextProps.loading === true) {
if (nextProps.loadingText === "打包中...") {
if (pathHelper.isSharePage(this.props.location.pathname) && this.props.share && this.props.share.score > 0){
if (
pathHelper.isSharePage(this.props.location.pathname) &&
this.props.share &&
this.props.share.score > 0
) {
this.scoreHandler(this.archiveDownload);
return
return;
}
this.archiveDownload();
} else if (nextProps.loadingText === "获取下载地址...") {
if (pathHelper.isSharePage(this.props.location.pathname) && this.props.share && this.props.share.score > 0){
if (
pathHelper.isSharePage(this.props.location.pathname) &&
this.props.share &&
this.props.share.score > 0
) {
this.scoreHandler(this.Download);
return
return;
}
this.Download();
this.Download();
}
}
return;
@ -165,9 +173,9 @@ class ModalsCompoment extends Component {
}
};
scoreHandler = callback =>{
callback();
}
scoreHandler = callback => {
callback();
};
Download = () => {
let reqURL = "";
@ -176,8 +184,8 @@ class ModalsCompoment extends Component {
this.props.selected[0].path === "/"
? this.props.selected[0].path + this.props.selected[0].name
: this.props.selected[0].path +
"/" +
this.props.selected[0].name;
"/" +
this.props.selected[0].name;
reqURL =
"/share/download/" +
this.props.selected[0].key +
@ -213,7 +221,7 @@ class ModalsCompoment extends Component {
} else {
items.push(value.id);
}
return null
return null;
});
let reqURL = "/file/archive";
@ -221,13 +229,9 @@ class ModalsCompoment extends Component {
items: items,
dirs: dirs
};
if (pathHelper.isSharePage(
this.props.location.pathname
)) {
reqURL =
"/share/archive/" +
window.shareInfo.key;
postBody["path"] = this.props.selected[0].path
if (pathHelper.isSharePage(this.props.location.pathname)) {
reqURL = "/share/archive/" + window.shareInfo.key;
postBody["path"] = this.props.selected[0].path;
}
API.post(reqURL, postBody)
@ -279,7 +283,7 @@ class ModalsCompoment extends Component {
.then(response => {
if (response.rawData.code === 0) {
this.onClose();
setTimeout(this.props.refreshFileList,500);
setTimeout(this.props.refreshFileList, 500);
} else {
this.props.toggleSnackbar(
"top",
@ -401,10 +405,10 @@ class ModalsCompoment extends Component {
// 检查重名
if (
this.props.dirList.findIndex((value) => {
this.props.dirList.findIndex(value => {
return value.name === newName;
}) !== -1 ||
this.props.fileList.findIndex((value) => {
this.props.fileList.findIndex(value => {
return value.name === newName;
}) !== -1
) {
@ -442,7 +446,7 @@ class ModalsCompoment extends Component {
e.preventDefault();
this.props.setModalsLoading(true);
if (
this.props.dirList.findIndex((value) => {
this.props.dirList.findIndex(value => {
return value.name === this.state.newFolderName;
}) !== -1
) {
@ -483,7 +487,7 @@ class ModalsCompoment extends Component {
e.preventDefault();
this.props.setModalsLoading(true);
if (
this.props.dirList.findIndex((value) => {
this.props.dirList.findIndex(value => {
return value.name === this.state.newFileName;
}) !== -1
) {
@ -523,18 +527,18 @@ class ModalsCompoment extends Component {
submitTorrentDownload = e => {
e.preventDefault();
this.props.setModalsLoading(true);
API
.post("/aria2/torrent/" + this.props.selected[0].id, {
dst: this.state.selectedPath === "//" ? "/" : this.state.selectedPath
})
API.post("/aria2/torrent/" + this.props.selected[0].id, {
dst:
this.state.selectedPath === "//" ? "/" : this.state.selectedPath
})
.then(() => {
this.props.toggleSnackbar(
"top",
"right",
"任务已创建",
"success"
);
this.onClose();
this.props.toggleSnackbar(
"top",
"right",
"任务已创建",
"success"
);
this.onClose();
this.props.setModalsLoading(false);
})
.catch(error => {
@ -551,19 +555,19 @@ class ModalsCompoment extends Component {
submitDownload = e => {
e.preventDefault();
this.props.setModalsLoading(true);
API
.post("/aria2/url", {
url: this.state.downloadURL,
dst: this.state.selectedPath === "//" ? "/" : this.state.selectedPath
})
API.post("/aria2/url", {
url: this.state.downloadURL,
dst:
this.state.selectedPath === "//" ? "/" : this.state.selectedPath
})
.then(() => {
this.props.toggleSnackbar(
"top",
"right",
"任务已创建",
"success"
);
this.onClose();
this.props.toggleSnackbar(
"top",
"right",
"任务已创建",
"success"
);
this.onClose();
this.props.setModalsLoading(false);
})
.catch(error => {
@ -607,7 +611,7 @@ class ModalsCompoment extends Component {
downloadURL: "",
shareUrl: "",
remoteDownloadPathSelect: false,
source: "",
source: ""
});
this.newNameSuffix = "";
this.props.closeAllModals();
@ -930,7 +934,7 @@ class ModalsCompoment extends Component {
: "")
: baseURL +
"/file/preview/" +
this.props.selected[0].id
this.props.selected[0].id
}
/>
)}

View File

@ -1,41 +1,37 @@
import React from "react";
import DropDownItem from "./DropDownItem";
export default function DropDown(props) {
let timer;
let first = props.folders.length;
const status = [];
for (let index = 0; index < props.folders.length; index++) {
status[index] = false;
}
const setActiveStatus = (id,value)=>{
const setActiveStatus = (id, value) => {
status[id] = value;
if (value){
if (value) {
clearTimeout(timer);
}else{
} else {
let shouldClose = true;
status.forEach(element => {
if (element){
if (element) {
shouldClose = false;
}
});
if (shouldClose){
if (first<=0){
timer = setTimeout(()=>{
if (shouldClose) {
if (first <= 0) {
timer = setTimeout(() => {
props.onClose();
},100)
}else{
}, 100);
} else {
first--;
}
}
}
console.log(status);
}
};
return (
<>
@ -45,7 +41,7 @@ export default function DropDown(props) {
path={"/" + props.folders.slice(0, id).join("/")}
navigateTo={props.navigateTo}
id={id}
setActiveStatus = {setActiveStatus}
setActiveStatus={setActiveStatus}
folder={folder}
/>
))}

View File

@ -1,11 +1,7 @@
import React, { useEffect } from "react";
import { makeStyles } from "@material-ui/core";
import FolderIcon from "@material-ui/icons/Folder";
import {
MenuItem,
ListItemIcon,
ListItemText
} from "@material-ui/core";
import { MenuItem, ListItemIcon, ListItemText } from "@material-ui/core";
import { useDrop } from "react-dnd";
import classNames from "classnames";
@ -36,9 +32,9 @@ export default function DropDownItem(props) {
const isActive = canDrop && isOver;
useEffect(() => {
props.setActiveStatus(props.id,isActive);
props.setActiveStatus(props.id, isActive);
// eslint-disable-next-line
}, [isActive])
}, [isActive]);
const classes = useStyles();
return (

View File

@ -21,9 +21,11 @@ import {
openCreateFolderDialog,
openShareDialog,
drawerToggleAction,
setShareUserPopover, openResaveDialog, openCompressDialog
setShareUserPopover,
openResaveDialog,
openCompressDialog
} from "../../../actions/index";
import explorer from "../../../redux/explorer"
import explorer from "../../../redux/explorer";
import API from "../../../middleware/Api";
import { setCookie, setGetParameter, fixUrlHash } from "../../../utils/index";
import {
@ -40,11 +42,10 @@ import pathHelper from "../../../utils/page";
import classNames from "classnames";
import Auth from "../../../middleware/Auth";
import Avatar from "@material-ui/core/Avatar";
import {Archive} from "@material-ui/icons";
import { Archive } from "@material-ui/icons";
import { FilePlus } from "mdi-material-ui";
import { openCreateFileDialog } from "../../../actions";
const mapStateToProps = state => {
return {
path: state.navigator.path,
@ -100,12 +101,12 @@ const mapDispatchToProps = dispatch => {
setShareUserPopover: e => {
dispatch(setShareUserPopover(e));
},
openResave: (key) => {
openResave: key => {
dispatch(openResaveDialog(key));
},
openCompressDialog: ()=>{
dispatch(openCompressDialog())
},
openCompressDialog: () => {
dispatch(openCompressDialog());
}
};
};
@ -182,7 +183,7 @@ class NavigatorComponent extends Component {
componentDidMount = () => {
const url = new URL(fixUrlHash(window.location.href));
const c = url.searchParams.get("path");
this.renderPath(c === null ? "/":c);
this.renderPath(c === null ? "/" : c);
if (!this.props.isShare) {
// 如果是在个人文件管理页,首次加载时打开侧边栏
@ -330,7 +331,7 @@ class NavigatorComponent extends Component {
const presentPath = this.props.path.split("/");
const newTarget = [
{
id:this.currentID,
id: this.currentID,
type: "dir",
name: presentPath.pop(),
path: presentPath.length === 1 ? "/" : presentPath.join("/")
@ -384,7 +385,7 @@ class NavigatorComponent extends Component {
render() {
const { classes } = this.props;
const isHomePage = pathHelper.isHomePage(this.props.location.pathname);
const isHomePage = pathHelper.isHomePage(this.props.location.pathname);
const user = Auth.GetUser();
const presentFolderMenu = (
@ -401,45 +402,42 @@ class NavigatorComponent extends Component {
</ListItemIcon>
刷新
</MenuItem>
{this.props.keywords === "" &&
isHomePage && (
<div>
<Divider />
{this.props.keywords === "" && isHomePage && (
<div>
<Divider />
<MenuItem onClick={() => this.performAction("share")}>
<ListItemIcon>
<ShareIcon />
</ListItemIcon>
分享
</MenuItem>
{user.group.compress && (
<MenuItem
onClick={() => this.performAction("share")}
>
<ListItemIcon>
<ShareIcon />
</ListItemIcon>
分享
</MenuItem>
{user.group.compress && <MenuItem
onClick={() => this.performAction("compress")}
>
<ListItemIcon>
<Archive />
</ListItemIcon>
压缩
</MenuItem>}
<Divider />
<MenuItem
onClick={() => this.performAction("newfolder")}
>
<ListItemIcon>
<NewFolderIcon />
</ListItemIcon>
创建文件夹
</MenuItem>
<MenuItem
onClick={() => this.performAction("newFile")}
>
<ListItemIcon>
<FilePlus />
</ListItemIcon>
创建文件
</MenuItem>
</div>
)}
)}
<Divider />
<MenuItem
onClick={() => this.performAction("newfolder")}
>
<ListItemIcon>
<NewFolderIcon />
</ListItemIcon>
创建文件夹
</MenuItem>
<MenuItem onClick={() => this.performAction("newFile")}>
<ListItemIcon>
<FilePlus />
</ListItemIcon>
创建文件
</MenuItem>
</div>
)}
</Menu>
);
@ -614,7 +612,11 @@ class NavigatorComponent extends Component {
>
<Avatar
style={{ height: 23, width: 23 }}
src={"/api/v3/user/avatar/"+this.props.share.creator.key + "/s"}
src={
"/api/v3/user/avatar/" +
this.props.share.creator.key +
"/s"
}
/>
</IconButton>
)}

View File

@ -1,4 +1,4 @@
import React,{useEffect} from "react";
import React, { useEffect } from "react";
import ExpandMore from "@material-ui/icons/ExpandMore";
import { Button } from "@material-ui/core";
import { makeStyles } from "@material-ui/core";
@ -13,14 +13,14 @@ const useStyles = makeStyles(theme => ({
active: {
border: "2px solid " + theme.palette.primary.light
},
button:{
textTransform: "none",
button: {
textTransform: "none"
}
}));
export default function PathButton(props) {
const inputRef = React.useRef(null)
const inputRef = React.useRef(null);
const [{ canDrop, isOver }, drop] = useDrop({
accept: "object",
drop: () => {
@ -45,35 +45,36 @@ export default function PathButton(props) {
const isActive = canDrop && isOver;
useEffect(() => {
if(props.more && isActive){
if (props.more && isActive) {
inputRef.current.click();
}
// eslint-disable-next-line
}, [isActive])
}, [isActive]);
const classes = useStyles();
return (
<span onClick={props.onClick} ref={inputRef} >
<Button
ref={drop}
className={classNames({
[classes.active]: isActive
},classes.button)}
component="span"
title={props.title}
>
{props.more && <MoreIcon />}
{!props.more && (
<>
{props.folder}
{props.last && (
<ExpandMore className={classes.expandMore} />
)}
</>
)}
</Button>
<span onClick={props.onClick} ref={inputRef}>
<Button
ref={drop}
className={classNames(
{
[classes.active]: isActive
},
classes.button
)}
component="span"
title={props.title}
>
{props.more && <MoreIcon />}
{!props.more && (
<>
{props.folder}
{props.last && (
<ExpandMore className={classes.expandMore} />
)}
</>
)}
</Button>
</span>
);
}

View File

@ -99,12 +99,12 @@ export default function ObjectIcon(props) {
};
const selectFile = e => {
dispatch(selectFileAction(props.file, e, props.index))
dispatch(selectFileAction(props.file, e, props.index));
};
const enterFolder = () => {
NavitateTo(
path === "/" ? path + props.file.name : path + "/" + props.file.name
);
NavitateTo(
path === "/" ? path + props.file.name : path + "/" + props.file.name
);
};
const handleClick = e => {
if (props.file.type === "up") {
@ -156,18 +156,18 @@ export default function ObjectIcon(props) {
if (isShare) {
history.push(
selected[0].key +
"/doc?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/doc?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
history.push(
"/doc?p=" +
encodeURIComponent(previewPath) +
"&id=" +
selected[0].id
encodeURIComponent(previewPath) +
"&id=" +
selected[0].id
);
return;
case "audio":
@ -177,58 +177,73 @@ export default function ObjectIcon(props) {
if (isShare) {
history.push(
selected[0].key +
"/video?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/video?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
history.push(
"/video?p=" +
encodeURIComponent(previewPath) +
"&id=" +
selected[0].id
encodeURIComponent(previewPath) +
"&id=" +
selected[0].id
);
return;
case "edit":
if (isShare) {
history.push(
selected[0].key +
"/text?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/text?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
history.push("/text?p=" + encodeURIComponent(previewPath) + "&id=" + selected[0].id);
history.push(
"/text?p=" +
encodeURIComponent(previewPath) +
"&id=" +
selected[0].id
);
return;
case "pdf":
if (isShare) {
history.push(
selected[0].key +
"/pdf?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/pdf?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
history.push("/pdf?p=" + encodeURIComponent(previewPath) + "&id=" + selected[0].id);
history.push(
"/pdf?p=" +
encodeURIComponent(previewPath) +
"&id=" +
selected[0].id
);
return;
case "code":
if (isShare) {
history.push(
selected[0].key +
"/code?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/code?name=" +
encodeURIComponent(selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
history.push("/code?p=" + encodeURIComponent(previewPath) + "&id=" + selected[0].id);
history.push(
"/code?p=" +
encodeURIComponent(previewPath) +
"&id=" +
selected[0].id
);
return;
default:
OpenLoadingDialog("获取下载地址...");

View File

@ -1,13 +1,11 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import FolderIcon from '@material-ui/icons/Folder'
import RightIcon from "@material-ui/icons/KeyboardArrowRight"
import UpIcon from "@material-ui/icons/ArrowUpward"
import { connect } from 'react-redux'
import classNames from 'classnames';
import {
toggleSnackbar,
} from "../../actions/index"
import React, { Component } from "react";
import PropTypes from "prop-types";
import FolderIcon from "@material-ui/icons/Folder";
import RightIcon from "@material-ui/icons/KeyboardArrowRight";
import UpIcon from "@material-ui/icons/ArrowUpward";
import { connect } from "react-redux";
import classNames from "classnames";
import { toggleSnackbar } from "../../actions/index";
import {
MenuList,
@ -16,130 +14,162 @@ import {
ListItemIcon,
ListItemText,
withStyles,
ListItemSecondaryAction,
} from '@material-ui/core';
import API from '../../middleware/Api'
ListItemSecondaryAction
} from "@material-ui/core";
import API from "../../middleware/Api";
const mapStateToProps = state => {
return {
keywords: state.explorer.keywords,
}
}
keywords: state.explorer.keywords
};
};
const mapDispatchToProps = dispatch => {
return {
toggleSnackbar:(vertical,horizontal,msg,color)=>{
dispatch(toggleSnackbar(vertical,horizontal,msg,color))
},
}
}
toggleSnackbar: (vertical, horizontal, msg, color) => {
dispatch(toggleSnackbar(vertical, horizontal, msg, color));
}
};
};
const styles = theme => ({
iconWhite:{
color: theme.palette.common.white,
iconWhite: {
color: theme.palette.common.white
},
selected: {
backgroundColor: theme.palette.primary.main+"!important",
'& $primary, & $icon': {
color: theme.palette.common.white,
},
},
backgroundColor: theme.palette.primary.main + "!important",
"& $primary, & $icon": {
color: theme.palette.common.white
}
},
primary: {},
icon: {},
buttonIcon:{},
selector:{
minWidth: "300px",
buttonIcon: {},
selector: {
minWidth: "300px"
},
container:{
container: {
maxHeight: "330px",
overflowY:" auto",
overflowY: " auto"
}
})
});
class PathSelectorCompoment extends Component {
state = {
presentPath:"/",
dirList:[],
selectedTarget:null,
}
presentPath: "/",
dirList: [],
selectedTarget: null
};
componentDidMount= ()=>{
componentDidMount = () => {
const toBeLoad = this.props.presentPath;
this.enterFolder(this.props.keywords === "" ? toBeLoad : "/");
}
};
back = ()=>{
back = () => {
const paths = this.state.presentPath.split("/");
paths.pop();
const toBeLoad = paths.join("/");
this.enterFolder(toBeLoad===""?"/":toBeLoad);
}
this.enterFolder(toBeLoad === "" ? "/" : toBeLoad);
};
enterFolder = (toBeLoad)=>{
API.get((this.props.api ? this.props.api : '/directory')+encodeURIComponent(toBeLoad),)
.then( (response)=> {
const dirList = response.data.objects.filter( (x)=> {
return (x.type === "dir" && (this.props.selected.findIndex((value)=>{
return (value.name === x.name )&&(value.path === x.path);
}))===-1);
});
if(toBeLoad ==="/"){
dirList.unshift({name:"/",path:""})
}
this.setState({
presentPath:toBeLoad,
dirList:dirList,
selectedTarget:null,
enterFolder = toBeLoad => {
API.get(
(this.props.api ? this.props.api : "/directory") +
encodeURIComponent(toBeLoad)
)
.then(response => {
const dirList = response.data.objects.filter(x => {
return (
x.type === "dir" &&
this.props.selected.findIndex(value => {
return (
value.name === x.name && value.path === x.path
);
}) === -1
);
});
if (toBeLoad === "/") {
dirList.unshift({ name: "/", path: "" });
}
this.setState({
presentPath: toBeLoad,
dirList: dirList,
selectedTarget: null
});
})
})
.catch((error) =>{
this.props.toggleSnackbar("top","right",error.message,"warning");
});
}
.catch(error => {
this.props.toggleSnackbar(
"top",
"right",
error.message,
"warning"
);
});
};
handleSelect = (index) =>{
this.setState({selectedTarget:index});
handleSelect = index => {
this.setState({ selectedTarget: index });
this.props.onSelect(this.state.dirList[index]);
}
};
render() {
const { classes} = this.props;
const { classes } = this.props;
return (
<div className={classes.container}>
<MenuList className={classes.selector}>
{this.state.presentPath!=="/"&&
<MenuItem onClick={this.back}>
<ListItemIcon >
<UpIcon />
</ListItemIcon>
<ListItemText primary="返回上一层" />
</MenuItem>
}
{this.state.dirList.map((value,index)=>(
<MenuItem classes={{
selected:classes.selected
}} key={index} selected={this.state.selectedTarget === index} onClick={()=>this.handleSelect(index)}>
<ListItemIcon className={classes.icon}>
<FolderIcon />
</ListItemIcon>
<ListItemText classes={{ primary: classes.primary }} primary={value.name} />
{value.name!=="/"&&<ListItemSecondaryAction className={classes.buttonIcon}>
<IconButton className={classNames({
[classes.iconWhite]:this.state.selectedTarget === index,
})} onClick={()=>this.enterFolder(value.path === "/"?value.path+value.name:value.path+"/"+value.name)}>
<RightIcon />
</IconButton>
</ListItemSecondaryAction>}
</MenuItem>
))}
</MenuList>
<MenuList className={classes.selector}>
{this.state.presentPath !== "/" && (
<MenuItem onClick={this.back}>
<ListItemIcon>
<UpIcon />
</ListItemIcon>
<ListItemText primary="返回上一层" />
</MenuItem>
)}
{this.state.dirList.map((value, index) => (
<MenuItem
classes={{
selected: classes.selected
}}
key={index}
selected={this.state.selectedTarget === index}
onClick={() => this.handleSelect(index)}
>
<ListItemIcon className={classes.icon}>
<FolderIcon />
</ListItemIcon>
<ListItemText
classes={{ primary: classes.primary }}
primary={value.name}
/>
{value.name !== "/" && (
<ListItemSecondaryAction
className={classes.buttonIcon}
>
<IconButton
className={classNames({
[classes.iconWhite]:
this.state.selectedTarget ===
index
})}
onClick={() =>
this.enterFolder(
value.path === "/"
? value.path + value.name
: value.path +
"/" +
value.name
)
}
>
<RightIcon />
</IconButton>
</ListItemSecondaryAction>
)}
</MenuItem>
))}
</MenuList>
</div>
);
}
@ -147,12 +177,11 @@ class PathSelectorCompoment extends Component {
PathSelectorCompoment.propTypes = {
classes: PropTypes.object.isRequired,
presentPath:PropTypes.string.isRequired,
selected:PropTypes.array.isRequired,
presentPath: PropTypes.string.isRequired,
selected: PropTypes.array.isRequired
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(PathSelectorCompoment))
)(withStyles(styles)(PathSelectorCompoment));

View File

@ -1,43 +1,44 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { connect } from 'react-redux'
import classNames from 'classnames';
import { withStyles, ButtonBase, Typography, Tooltip } from '@material-ui/core';
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import classNames from "classnames";
import { withStyles, ButtonBase, Typography, Tooltip } from "@material-ui/core";
import TypeIcon from "./TypeIcon";
import {lighten} from "@material-ui/core/styles";
import { lighten } from "@material-ui/core/styles";
const styles = theme => ({
container: {
padding: "7px",
padding: "7px"
},
selected: {
"&:hover": {
border: "1px solid #d0d0d0",
border: "1px solid #d0d0d0"
},
backgroundColor:
theme.palette.type === "dark"
? "#fff"
: lighten(theme.palette.primary.main,0.8),
: lighten(theme.palette.primary.main, 0.8)
},
notSelected: {
"&:hover": {
backgroundColor: theme.palette.background.default,
border: "1px solid #d0d0d0",
border: "1px solid #d0d0d0"
},
backgroundColor: theme.palette.background.paper,
backgroundColor: theme.palette.background.paper
},
button: {
height: "50px",
border: "1px solid "+theme.palette.divider,
border: "1px solid " + theme.palette.divider,
width: "100%",
borderRadius: "6px",
boxSizing: "border-box",
transition: "background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
transition:
"background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
display: "flex",
justifyContent: "left",
alignItems: "initial",
alignItems: "initial"
},
icon: {
margin: "10px 10px 10px 16px",
@ -46,83 +47,94 @@ const styles = theme => ({
backgroundColor: theme.palette.background.paper,
borderRadius: "90%",
paddingTop: "2px",
color: theme.palette.text.secondary,
color: theme.palette.text.secondary
},
folderNameSelected: {
color: theme.palette.type === "dark" ? theme.palette.background.paper : theme.palette.primary.dark,
fontWeight: "500",
color:
theme.palette.type === "dark"
? theme.palette.background.paper
: theme.palette.primary.dark,
fontWeight: "500"
},
folderNameNotSelected: {
color: theme.palette.text.secondary,
color: theme.palette.text.secondary
},
folderName: {
marginTop: "15px",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
overflow: "hidden",
marginRight: "20px",
},
})
marginRight: "20px"
}
});
const mapStateToProps = state => {
return {
selected: state.explorer.selected,
}
}
selected: state.explorer.selected
};
};
const mapDispatchToProps = () => {
return {
}
}
return {};
};
class SmallIconCompoment extends Component {
state = {
}
state = {};
render() {
const { classes } = this.props;
const isSelected = (this.props.selected.findIndex((value) => {
return value === this.props.file;
})) !== -1;
const isSelected =
this.props.selected.findIndex(value => {
return value === this.props.file;
}) !== -1;
return (
<ButtonBase
focusRipple
className={classNames({
<ButtonBase
focusRipple
className={classNames(
{
[classes.selected]: isSelected,
[classes.notSelected]: !isSelected,
}, classes.button)}
>
<div className={classNames(classes.icon, {
[classes.notSelected]: !isSelected
},
classes.button
)}
>
<div
className={classNames(classes.icon, {
[classes.iconSelected]: isSelected,
[classes.iconNotSelected]: !isSelected,
})}><TypeIcon fileName={this.props.file.name}/></div>
<Tooltip title={this.props.file.name} aria-label={this.props.file.name}>
<Typography className={classNames(classes.folderName, {
[classes.iconNotSelected]: !isSelected
})}
>
<TypeIcon fileName={this.props.file.name} />
</div>
<Tooltip
title={this.props.file.name}
aria-label={this.props.file.name}
>
<Typography
className={classNames(classes.folderName, {
[classes.folderNameSelected]: isSelected,
[classes.folderNameNotSelected]: !isSelected,
[classes.folderNameNotSelected]: !isSelected
})}
variant="body2"
>{this.props.file.name}</Typography>
</Tooltip>
</ButtonBase>
>
{this.props.file.name}
</Typography>
</Tooltip>
</ButtonBase>
);
}
}
SmallIconCompoment.propTypes = {
classes: PropTypes.object.isRequired,
file: PropTypes.object.isRequired,
file: PropTypes.object.isRequired
};
const SmallIcon = connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(SmallIconCompoment))
)(withStyles(styles)(SmallIconCompoment));
export default SmallIcon
export default SmallIcon;

View File

@ -7,10 +7,10 @@ import classNames from "classnames";
import { sizeToString } from "../../utils/index";
import { withStyles, TableCell, TableRow, Typography } from "@material-ui/core";
import TypeIcon from "./TypeIcon";
import {lighten} from "@material-ui/core/styles";
import { lighten } from "@material-ui/core/styles";
import pathHelper from "../../utils/page";
import {withRouter} from "react-router";
import KeyboardReturnIcon from '@material-ui/icons/KeyboardReturn';
import { withRouter } from "react-router";
import KeyboardReturnIcon from "@material-ui/icons/KeyboardReturn";
const styles = theme => ({
selected: {
@ -18,20 +18,20 @@ const styles = theme => ({
backgroundColor:
theme.palette.type === "dark"
? theme.palette.background.paper
: lighten(theme.palette.primary.main,0.8),
: lighten(theme.palette.primary.main, 0.8)
},
selectedShared: {
"&:hover": {},
backgroundColor:
theme.palette.type === "dark"
? lighten(theme.palette.background.paper,0.15)
: lighten(theme.palette.primary.main,0.8),
? lighten(theme.palette.background.paper, 0.15)
: lighten(theme.palette.primary.main, 0.8)
},
notSelected: {
"&:hover": {
backgroundColor: theme.palette.background.default,
backgroundColor: theme.palette.background.default
}
},
icon: {
@ -39,12 +39,13 @@ const styles = theme => ({
marginRight: "20px",
color: theme.palette.text.secondary
},
tableIcon:{
tableIcon: {
marginRight: "20px",
verticalAlign: "middle",
verticalAlign: "middle"
},
folderNameSelected: {
color: theme.palette.type === "dark" ? "#fff" : theme.palette.primary.dark,
color:
theme.palette.type === "dark" ? "#fff" : theme.palette.primary.dark,
fontWeight: "500",
userSelect: "none"
},
@ -54,7 +55,7 @@ const styles = theme => ({
},
folderName: {
marginRight: "20px",
display: "flex",
display: "flex"
},
hideAuto: {
[theme.breakpoints.down("sm")]: {
@ -86,10 +87,15 @@ class TableRowCompoment extends Component {
let icon;
if (this.props.file.type === "dir") {
icon = <FolderIcon className={classes.icon} />;
}else if (this.props.file.type === "up"){
icon = <KeyboardReturnIcon className={classes.icon}/>
} else if (this.props.file.type === "up") {
icon = <KeyboardReturnIcon className={classes.icon} />;
} else {
icon = <TypeIcon className={classes.tableIcon} fileName={this.props.file.name}/>
icon = (
<TypeIcon
className={classes.tableIcon}
fileName={this.props.file.name}
/>
);
}
const isSelected =
@ -102,15 +108,17 @@ class TableRowCompoment extends Component {
onContextMenu={this.props.contextMenu}
onClick={this.props.handleClick}
onDoubleClick={this.props.handleDoubleClick.bind(this)}
className={classNames(
{
[classes.selected]: isSelected&&!isShare,
[classes.selectedShared]: isSelected&&isShare,
[classes.notSelected]: !isSelected
}
)}
className={classNames({
[classes.selected]: isSelected && !isShare,
[classes.selectedShared]: isSelected && isShare,
[classes.notSelected]: !isSelected
})}
>
<TableCell component="th" scope="row" className={classes.tableRow}>
<TableCell
component="th"
scope="row"
className={classes.tableRow}
>
<Typography
variant="body2"
className={classNames(classes.folderName, {
@ -122,7 +130,9 @@ class TableRowCompoment extends Component {
{this.props.file.name}
</Typography>
</TableCell>
<TableCell className={classNames(classes.hideAuto,classes.tableRow)}>
<TableCell
className={classNames(classes.hideAuto, classes.tableRow)}
>
<Typography
variant="body2"
className={classNames(classes.folderName, {
@ -131,11 +141,14 @@ class TableRowCompoment extends Component {
})}
>
{" "}
{this.props.file.type !== "dir" &&this.props.file.type !== "up"&&
{this.props.file.type !== "dir" &&
this.props.file.type !== "up" &&
sizeToString(this.props.file.size)}
</Typography>
</TableCell>
<TableCell className={classNames(classes.hideAuto,classes.tableRow)}>
<TableCell
className={classNames(classes.hideAuto, classes.tableRow)}
>
<Typography
variant="body2"
className={classNames(classes.folderName, {

View File

@ -8,7 +8,13 @@ import {
Android,
FileExcelBox,
FilePowerpointBox,
FileWordBox, LanguageC, LanguageCpp, LanguageGo, LanguageJavascript, LanguagePhp, LanguagePython,
FileWordBox,
LanguageC,
LanguageCpp,
LanguageGo,
LanguageJavascript,
LanguagePhp,
LanguagePython,
MagnetOn,
ScriptText,
WindowRestore,
@ -80,22 +86,22 @@ const icons = {
color: "#16b3da",
icon: LanguageGo
},
python:{
python: {
color: "#3776ab",
icon: LanguagePython
},
c:{
c: {
color: "#a8b9cc",
icon: LanguageC,
icon: LanguageC
},
cpp:{
cpp: {
color: "#004482",
icon: LanguageCpp,
icon: LanguageCpp
},
js:{
js: {
color: "#f4d003",
icon: LanguageJavascript,
},
icon: LanguageJavascript
}
};
const getColor = (theme, color) =>
@ -133,7 +139,7 @@ const TypeIcon = props => {
>
<IconComponent
style={{
color: theme.palette.background.paper,
color: theme.palette.background.paper
}}
/>
</Avatar>

View File

@ -1,19 +1,12 @@
import React, { useCallback, useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import { makeStyles } from "@material-ui/core";
import {
toggleSnackbar,
} from "../../actions/index";
import { toggleSnackbar } from "../../actions/index";
import { useHistory } from "react-router-dom";
import API from "../../middleware/Api";
import {
Button,
Paper,
Avatar,
Typography
} from "@material-ui/core";
import { Button, Paper, Avatar, Typography } from "@material-ui/core";
import EmailIcon from "@material-ui/icons/EmailOutlined";
import {useLocation} from "react-router";
import { useLocation } from "react-router";
const useStyles = makeStyles(theme => ({
layout: {
width: "auto",
@ -42,7 +35,7 @@ const useStyles = makeStyles(theme => ({
},
submit: {
marginTop: theme.spacing(3)
},
}
}));
function useQuery() {
@ -53,8 +46,8 @@ function Activation() {
const query = useQuery();
const location = useLocation();
const [success,setSuccess] = useState(false);
const [email,setEmail] = useState("");
const [success, setSuccess] = useState(false);
const [email, setEmail] = useState("");
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
@ -66,10 +59,10 @@ function Activation() {
const classes = useStyles();
useEffect(() => {
API.get("/user/activate/" + query.get("id") + "?sign=" + query.get("sign"))
API.get(
"/user/activate/" + query.get("id") + "?sign=" + query.get("sign")
)
.then(response => {
setEmail(response.data);
setSuccess(true);
@ -83,28 +76,29 @@ function Activation() {
return (
<div className={classes.layout}>
{success && <Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<EmailIcon />
</Avatar>
<Typography component="h1" variant="h5">
激活成功
</Typography>
<Typography style={{marginTop:"20px"}}>您的账号已被成功激活</Typography>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={()=>history.push("/login?username="+email)}
>
返回登录
</Button>
</Paper>}
{success && (
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<EmailIcon />
</Avatar>
<Typography component="h1" variant="h5">
激活成功
</Typography>
<Typography style={{ marginTop: "20px" }}>
您的账号已被成功激活
</Typography>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={() => history.push("/login?username=" + email)}
>
返回登录
</Button>
</Paper>
)}
</div>
);
}

View File

@ -11,5 +11,5 @@ function getURL() {
export default makeAsyncScriptLoader(getURL, {
callbackName,
globalName,
})(ReCAPTCHA);
globalName
})(ReCAPTCHA);

View File

@ -57,7 +57,11 @@ export default class ReCAPTCHA extends React.Component {
}
explicitRender() {
if (this.props.grecaptcha && this.props.grecaptcha.render && this._widgetId === undefined) {
if (
this.props.grecaptcha &&
this.props.grecaptcha.render &&
this._widgetId === undefined
) {
const wrapper = document.createElement("div");
this._widgetId = this.props.grecaptcha.render(wrapper, {
sitekey: this.props.sitekey,
@ -70,11 +74,15 @@ export default class ReCAPTCHA extends React.Component {
size: this.props.size,
stoken: this.props.stoken,
hl: this.props.hl,
badge: this.props.badge,
badge: this.props.badge
});
this.captcha.appendChild(wrapper);
}
if (this._executeRequested && this.props.grecaptcha && this._widgetId !== undefined) {
if (
this._executeRequested &&
this.props.grecaptcha &&
this._widgetId !== undefined
) {
this._executeRequested = false;
this.execute();
}
@ -152,7 +160,7 @@ ReCAPTCHA.propTypes = {
size: PropTypes.oneOf(["compact", "normal", "invisible"]),
stoken: PropTypes.string,
hl: PropTypes.string,
badge: PropTypes.oneOf(["bottomright", "bottomleft", "inline"]),
badge: PropTypes.oneOf(["bottomright", "bottomleft", "inline"])
};
ReCAPTCHA.defaultProps = {
// eslint-disable-next-line @typescript-eslint/no-empty-function
@ -161,5 +169,5 @@ ReCAPTCHA.defaultProps = {
type: "image",
tabindex: 0,
size: "normal",
badge: "bottomright",
};
badge: "bottomright"
};

View File

@ -51,7 +51,7 @@ const useStyles = makeStyles(theme => ({
display: "flex",
width: "100%",
justifyContent: "space-between"
},
}
}));
function useQuery() {
@ -68,9 +68,9 @@ function ResetForm() {
const handleInputChange = name => e => {
setInput({
...input,
[name]:e.target.value
})
}
[name]: e.target.value
});
};
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
(vertical, horizontal, msg, color) =>
@ -81,15 +81,15 @@ function ResetForm() {
const submit = e => {
e.preventDefault();
if (input.password !== input.password_repeat){
if (input.password !== input.password_repeat) {
ToggleSnackbar("top", "right", "两次密码输入不一致", "warning");
return
return;
}
setLoading(true);
API.patch("/user/reset", {
secret: query.get("sign"),
id: query.get("id"),
Password: input.password,
Password: input.password
})
.then(() => {
setLoading(false);
@ -100,7 +100,7 @@ function ResetForm() {
setLoading(false);
ToggleSnackbar("top", "right", error.message, "warning");
});
}
};
const classes = useStyles();

View File

@ -1,8 +1,8 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import KeyIcon from '@material-ui/icons/VpnKeyOutlined';
import { toggleSnackbar, } from "../../actions/index"
import axios from 'axios'
import React, { Component } from "react";
import { connect } from "react-redux";
import KeyIcon from "@material-ui/icons/VpnKeyOutlined";
import { toggleSnackbar } from "../../actions/index";
import axios from "axios";
import {
withStyles,
@ -14,120 +14,138 @@ import {
InputLabel,
Paper,
Avatar,
Typography,
} from '@material-ui/core';
Typography
} from "@material-ui/core";
const styles = theme => ({
layout: {
width: 'auto',
marginTop: '110px',
width: "auto",
marginTop: "110px",
marginLeft: theme.spacing(3),
marginRight: theme.spacing(3),
[theme.breakpoints.up(1100 + theme.spacing(3) * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto',
},
marginLeft: "auto",
marginRight: "auto"
}
},
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing(3)}px`,
display: "flex",
flexDirection: "column",
alignItems: "center",
padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing(
3
)}px`
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
backgroundColor: theme.palette.secondary.main
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(1)
},
submit: {
marginTop: theme.spacing(3),
marginTop: theme.spacing(3)
},
link: {
marginTop: "10px",
display:"flex",
display: "flex",
width: "100%",
justifyContent: "space-between",
justifyContent: "space-between"
},
captchaContainer:{
display:"flex",
captchaContainer: {
display: "flex",
marginTop: "10px",
[theme.breakpoints.down("sm")]: {
display: "block",
},
display: "block"
}
}
})
});
const mapStateToProps = () => {
return {
}
}
return {};
};
const mapDispatchToProps = dispatch => {
return {
toggleSnackbar: (vertical, horizontal, msg, color) => {
dispatch(toggleSnackbar(vertical, horizontal, msg, color))
},
}
}
dispatch(toggleSnackbar(vertical, horizontal, msg, color));
}
};
};
class ResetPwdFormCompoment extends Component {
state = {
pwd: "",
pwdRepeat: "",
loading: false
};
state={
pwd:"",
pwdRepeat:"",
loading:false,
}
login = e=>{
login = e => {
e.preventDefault();
if(this.state.pwdRepeat !== this.state.pwd){
this.props.toggleSnackbar("top","right","两次密码输入不一致","warning");
if (this.state.pwdRepeat !== this.state.pwd) {
this.props.toggleSnackbar(
"top",
"right",
"两次密码输入不一致",
"warning"
);
return;
}
this.setState({
loading:true,
loading: true
});
axios.post('/Member/Reset',{
pwd:this.state.pwd,
key:window.resetKey,
}).then( (response)=> {
if(response.data.code!=="200"){
axios
.post("/Member/Reset", {
pwd: this.state.pwd,
key: window.resetKey
})
.then(response => {
if (response.data.code !== "200") {
this.setState({
loading: false
});
this.props.toggleSnackbar(
"top",
"right",
response.data.message,
"warning"
);
} else {
this.setState({
loading: false,
pwd: "",
pwdRepeat: ""
});
this.props.toggleSnackbar(
"top",
"right",
"密码重设成功",
"success"
);
}
})
.catch(error => {
this.setState({
loading:false,
loading: false
});
this.props.toggleSnackbar("top","right",response.data.message,"warning");
}else{
this.setState({
loading:false,
pwd:"",
pwdRepeat:"",
});
this.props.toggleSnackbar("top","right","密码重设成功","success");
}
})
.catch((error) =>{
this.setState({
loading:false,
this.props.toggleSnackbar(
"top",
"right",
error.message,
"error"
);
});
this.props.toggleSnackbar("top","right",error.message,"error");
});
}
};
handleChange = name => event => {
this.setState({ [name]: event.target.value });
};
render() {
const { classes } = this.props;
return (
<div className={classes.layout}>
<Paper className={classes.paper}>
@ -140,25 +158,27 @@ class ResetPwdFormCompoment extends Component {
<form className={classes.form} onSubmit={this.login}>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="email">新密码</InputLabel>
<Input
id="pwd"
type="password"
name="pwd"
onChange={this.handleChange("pwd")}
autoComplete
value={this.state.pwd}
autoFocus />
<Input
id="pwd"
type="password"
name="pwd"
onChange={this.handleChange("pwd")}
autoComplete
value={this.state.pwd}
autoFocus
/>
</FormControl>
<FormControl margin="normal" required fullWidth>
<InputLabel htmlFor="email">重复新密码</InputLabel>
<Input
id="pwdRepeat"
type="password"
name="pwdRepeat"
onChange={this.handleChange("pwdRepeat")}
autoComplete
value={this.state.pwdRepeat}
autoFocus />
<Input
id="pwdRepeat"
type="password"
name="pwdRepeat"
onChange={this.handleChange("pwdRepeat")}
autoComplete
value={this.state.pwdRepeat}
autoFocus
/>
</FormControl>
<Button
type="submit"
@ -169,30 +189,26 @@ class ResetPwdFormCompoment extends Component {
className={classes.submit}
>
重设密码
</Button> </form> <Divider/>
<div className={classes.link}>
<div>
<Link href={"/Login"}>
返回登录
</Link>
</div>
<div>
<Link href={"/SignUp"}>
注册账号
</Link>
</div>
</Button>{" "}
</form>{" "}
<Divider />
<div className={classes.link}>
<div>
<Link href={"/Login"}>返回登录</Link>
</div>
<div>
<Link href={"/SignUp"}>注册账号</Link>
</div>
</div>
</Paper>
</div>
);
}
}
const ResetPwdForm = connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(ResetPwdFormCompoment))
)(withStyles(styles)(ResetPwdFormCompoment));
export default ResetPwdForm
export default ResetPwdForm;

View File

@ -8,9 +8,7 @@ import {
DialogTitle,
CircularProgress
} from "@material-ui/core";
import {
toggleSnackbar,
} from "../../actions/index";
import { toggleSnackbar } from "../../actions/index";
import PathSelector from "../FileManager/PathSelector";
import { useDispatch } from "react-redux";
import API from "../../middleware/Api";
@ -35,7 +33,7 @@ import {
RhombusOutline,
Square,
SquareOutline,
Triangle,
Triangle
} from "mdi-material-ui";
const useStyles = makeStyles(theme => ({
@ -148,12 +146,12 @@ export default function AddTag(props) {
[dispatch]
);
const submitNewLink = ()=>{
const submitNewLink = () => {
setLoading(true);
API.post("/tag/link", {
path: input.path,
name:input.tagName
name: input.tagName
})
.then(response => {
setLoading(false);
@ -161,7 +159,7 @@ export default function AddTag(props) {
props.onSuccess({
type: 1,
name: input.tagName,
expression:input.path,
expression: input.path,
color: theme.palette.text.secondary,
icon: "FolderHeartOutline",
id: response.data
@ -173,7 +171,7 @@ export default function AddTag(props) {
.then(() => {
setLoading(false);
});
}
};
const submitNewTag = () => {
setLoading(true);
@ -203,19 +201,19 @@ export default function AddTag(props) {
});
};
const submit = () => {
if (value === 0) {
submitNewTag();
}else{
submitNewLink();
}
if (value === 0) {
submitNewTag();
} else {
submitNewLink();
}
};
const selectPath = ()=>{
const selectPath = () => {
setInput({
...input,
path: selectedPath === "//" ? "/" : selectedPath
});
setPathSelectDialog(false);
}
};
const classes = useStyles();
@ -232,13 +230,21 @@ export default function AddTag(props) {
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">选择目录</DialogTitle>
<PathSelector presentPath="/" selected={[]} onSelect={setMoveTarget} />
<PathSelector
presentPath="/"
selected={[]}
onSelect={setMoveTarget}
/>
<DialogActions>
<Button onClick={() => setPathSelectDialog(false)}>
取消
</Button>
<Button onClick={selectPath} color="primary" disabled={selectedPath === ""}>
<Button
onClick={selectPath}
color="primary"
disabled={selectedPath === ""}
>
确定
</Button>
</DialogActions>
@ -355,7 +361,7 @@ export default function AddTag(props) {
className={classes.textField}
/>
<Button
onClick={()=>setPathSelectDialog(true)}
onClick={() => setPathSelectDialog(true)}
style={{
marginLeft: theme.spacing(1),
alignSelf: "flex-end"
@ -379,8 +385,8 @@ export default function AddTag(props) {
(value === 0 &&
(input.filename === "" ||
input.tagName === "")) ||
(value === 1 && (input.tagName === "" ||
input.path === ""))
(value === 1 &&
(input.tagName === "" || input.path === ""))
}
>
确定

View File

@ -9,10 +9,7 @@ import {
DialogContentText,
CircularProgress
} from "@material-ui/core";
import {
toggleSnackbar,
setModalsLoading,
} from "../../actions/index";
import { toggleSnackbar, setModalsLoading } from "../../actions/index";
import PathSelector from "../FileManager/PathSelector";
import { useDispatch } from "react-redux";
import API from "../../middleware/Api";
@ -21,7 +18,7 @@ import TextField from "@material-ui/core/TextField";
const useStyles = makeStyles(theme => ({
contentFix: {
padding: "10px 24px 0px 24px",
backgroundColor:theme.palette.background.default,
backgroundColor: theme.palette.background.default
},
wrapper: {
margin: theme.spacing(1),
@ -85,11 +82,11 @@ export default function CompressDialog(props) {
});
API.post("/file/compress", {
src:{
dirs:dirs,
items:items,
src: {
dirs: dirs,
items: items
},
name:fileName,
name: fileName,
dst: selectedPath === "//" ? "/" : selectedPath
})
.then(() => {
@ -121,7 +118,14 @@ export default function CompressDialog(props) {
{selectedPath !== "" && (
<DialogContent className={classes.contentFix}>
<DialogContentText>
<TextField onChange={e=>setFileName(e.target.value)} value={fileName} fullWidth autoFocus id="standard-basic" label="压缩文件名" />
<TextField
onChange={e => setFileName(e.target.value)}
value={fileName}
fullWidth
autoFocus
id="standard-basic"
label="压缩文件名"
/>
</DialogContentText>
</DialogContent>
)}
@ -131,7 +135,11 @@ export default function CompressDialog(props) {
<Button
onClick={submitMove}
color="primary"
disabled={selectedPath === "" || fileName ==="" || props.modalsLoading}
disabled={
selectedPath === "" ||
fileName === "" ||
props.modalsLoading
}
>
确定
{props.modalsLoading && (

View File

@ -12,7 +12,7 @@ import {
import {
toggleSnackbar,
setModalsLoading,
refreshFileList,
refreshFileList
} from "../../actions/index";
import PathSelector from "../FileManager/PathSelector";
import { useDispatch } from "react-redux";

View File

@ -120,12 +120,12 @@ export default function CreatShare(props) {
password: "",
downloads: 1,
expires: 24 * 3600,
showPassword: false,
showPassword: false
});
const [shareOption, setShareOption] = React.useState({
password: false,
expire: false,
preview:true,
preview: true
});
const handleChange = prop => event => {
@ -180,10 +180,9 @@ export default function CreatShare(props) {
const onClose = () => {
props.onClose();
setTimeout(()=>{
setTimeout(() => {
setShareURL("");
},500)
}, 500);
};
const submitShare = e => {
@ -195,7 +194,7 @@ export default function CreatShare(props) {
password: values.password,
downloads: shareOption.expire ? values.downloads : -1,
expire: values.expires,
preview:shareOption.preview,
preview: shareOption.preview
};
API.post("/share", submitFormBody)
@ -205,11 +204,11 @@ export default function CreatShare(props) {
password: "",
downloads: 1,
expires: 24 * 3600,
showPassword: false,
showPassword: false
});
setShareOption({
password: false,
expire: false,
expire: false
});
props.setModalsLoading(false);
})
@ -412,7 +411,9 @@ export default function CreatShare(props) {
</ListItem>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>是否允许在分享页面预览文件内容</Typography>
<Typography>
是否允许在分享页面预览文件内容
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
</List>

View File

@ -1,4 +1,4 @@
import React, {useState} from "react";
import React, { useState } from "react";
import { makeStyles } from "@material-ui/core";
import { Dialog } from "@material-ui/core";
import DialogTitle from "@material-ui/core/DialogTitle";
@ -15,7 +15,7 @@ const useStyles = makeStyles(theme => ({
formIcon: {
marginTop: 21,
marginRight: 19,
color:theme.palette.text.secondary,
color: theme.palette.text.secondary
},
input: {
width: 250
@ -54,17 +54,17 @@ export default function CreateWebDAVAccount(props) {
const handleInputChange = name => e => {
setValue({
...value,
[name]: e.target.value,
[name]: e.target.value
});
};
const selectPath = ()=>{
const selectPath = () => {
setValue({
...value,
path: selectedPath === "//" ? "/" : selectedPath
});
setPathSelectDialog(false);
}
};
return (
<Dialog
@ -78,13 +78,21 @@ export default function CreateWebDAVAccount(props) {
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">选择目录</DialogTitle>
<PathSelector presentPath="/" selected={[]} onSelect={setMoveTarget} />
<PathSelector
presentPath="/"
selected={[]}
onSelect={setMoveTarget}
/>
<DialogActions>
<Button onClick={() => setPathSelectDialog(false)}>
取消
</Button>
<Button onClick={selectPath} color="primary" disabled={selectedPath === ""}>
<Button
onClick={selectPath}
color="primary"
disabled={selectedPath === ""}
>
确定
</Button>
</DialogActions>
@ -115,7 +123,11 @@ export default function CreateWebDAVAccount(props) {
label="相对根目录"
/>
<br />
<Button className={classes.button} color="primary" onClick={()=>setPathSelectDialog(true)}>
<Button
className={classes.button}
color="primary"
onClick={() => setPathSelectDialog(true)}
>
选择目录
</Button>
</div>
@ -124,7 +136,11 @@ export default function CreateWebDAVAccount(props) {
</div>
<DialogActions>
<Button onClick={props.onClose}>取消</Button>
<Button disabled={value.path === "" || value.name === ""} color="primary" onClick={()=>props.callback(value)}>
<Button
disabled={value.path === "" || value.name === ""}
color="primary"
onClick={() => props.callback(value)}
>
确定
</Button>
</DialogActions>

View File

@ -9,14 +9,11 @@ import {
DialogContentText,
CircularProgress
} from "@material-ui/core";
import {
toggleSnackbar,
setModalsLoading,
} from "../../actions/index";
import { toggleSnackbar, setModalsLoading } from "../../actions/index";
import PathSelector from "../FileManager/PathSelector";
import { useDispatch } from "react-redux";
import API from "../../middleware/Api";
import {filePath} from "../../utils";
import { filePath } from "../../utils";
const useStyles = makeStyles(theme => ({
contentFix: {
@ -68,7 +65,7 @@ export default function DecompressDialog(props) {
}
SetModalsLoading(true);
API.post("/file/decompress", {
src:filePath(props.selected[0]),
src: filePath(props.selected[0]),
dst: selectedPath === "//" ? "/" : selectedPath
})
.then(() => {

View File

@ -5,7 +5,7 @@ import DialogContent from "@material-ui/core/DialogContent";
import Dialog from "@material-ui/core/Dialog";
import DialogContentText from "@material-ui/core/DialogContentText";
import { blue } from "@material-ui/core/colors";
import {useSelector} from "react-redux";
import { useSelector } from "react-redux";
const useStyles = makeStyles({
avatar: {
@ -23,24 +23,17 @@ const useStyles = makeStyles({
export default function LoadingDialog() {
const classes = useStyles();
const open = useSelector(
state => state.viewUpdate.modals.loading,
);
const text = useSelector(
state => state.viewUpdate.modals.loadingText,
);
const open = useSelector(state => state.viewUpdate.modals.loading);
const text = useSelector(state => state.viewUpdate.modals.loadingText);
return (
<Dialog aria-labelledby="simple-dialog-title" open={open}>
<DialogContent>
<DialogContentText className={classes.loadingContainer}>
<CircularProgress color="secondary" />
<div className={classes.loading}>
{text}
</div>
<div className={classes.loading}>{text}</div>
</DialogContentText>
</DialogContent>
</Dialog>
);
}
}

View File

@ -1,4 +1,4 @@
import React, {useState, useEffect} from "react";
import React, { useState, useEffect } from "react";
import { makeStyles } from "@material-ui/core";
import {
Button,
@ -29,35 +29,37 @@ const useStyles = makeStyles(theme => ({
marginTop: -12,
marginLeft: -12
},
content:{
padding:0,
content: {
padding: 0
}
}));
export default function SelectFileDialog(props) {
const [files,setFiles] = useState(props.files);
const [files, setFiles] = useState(props.files);
useEffect(()=>{
useEffect(() => {
setFiles(props.files);
},[props.files]);
}, [props.files]);
const handleChange = index => event =>{
const handleChange = index => event => {
const filesCopy = [...files];
// eslint-disable-next-line
filesCopy.map((v,k)=>{
if (v.index === index){
filesCopy[k] = {...filesCopy[k],selected:event.target.checked ? "true" : "false"};
filesCopy.map((v, k) => {
if (v.index === index) {
filesCopy[k] = {
...filesCopy[k],
selected: event.target.checked ? "true" : "false"
};
}
});
setFiles(filesCopy);
};
const submit = () =>{
const submit = () => {
const index = [];
// eslint-disable-next-line
files.map(v=>{
if(v.selected === "true"){
files.map(v => {
if (v.selected === "true") {
index.push(parseInt(v.index));
}
});
@ -77,25 +79,30 @@ export default function SelectFileDialog(props) {
{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>
<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}>
<Button
color="primary"
onClick={submit}
disabled={props.modalsLoading}
>
确定
{props.modalsLoading && (
<CircularProgress

View File

@ -10,9 +10,9 @@ import classNames from "classnames";
const useStyles = makeStyles(() => ({
icon: {
color: 'rgb(255, 255, 255)',
opacity: "0.54",
},
color: "rgb(255, 255, 255)",
opacity: "0.54"
}
}));
const DarkModeSwitcher = ({ position }) => {
@ -24,8 +24,8 @@ const DarkModeSwitcher = ({ position }) => {
const isDayLight = (ThemeType && ThemeType === "light") || !ThemeType;
const isDark = ThemeType && ThemeType === "dark";
const toggleMode = () => {
Auth.SetPreference("theme_mode",isDayLight?"dark":"light");
ToggleThemeMode();
Auth.SetPreference("theme_mode", isDayLight ? "dark" : "light");
ToggleThemeMode();
};
const classes = useStyles();
return (
@ -35,9 +35,11 @@ const DarkModeSwitcher = ({ position }) => {
>
<IconButton
className={classNames({
[classes.icon]: "left" === position,
[classes.icon]: "left" === position
})}
onClick={toggleMode} color="inherit">
onClick={toggleMode}
color="inherit"
>
{isDayLight && <NightIcon />}
{isDark && <DayIcon />}
</IconButton>

View File

@ -1,4 +1,4 @@
import React, {useCallback, useState, Suspense} from "react";
import React, { useCallback, useState, Suspense } from "react";
import {
Divider,
List,
@ -8,7 +8,7 @@ import {
makeStyles,
withStyles
} from "@material-ui/core";
import { Clear, KeyboardArrowRight} from "@material-ui/icons";
import { Clear, KeyboardArrowRight } from "@material-ui/icons";
import classNames from "classnames";
import FolderShared from "@material-ui/icons/FolderShared";
import UploadIcon from "@material-ui/icons/CloudUpload";
@ -106,12 +106,11 @@ const useStyles = makeStyles(theme => ({
subMenu: {
marginLeft: theme.spacing(2)
},
overFlow:{
overFlow: {
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
textOverflow: "ellipsis"
}
}));
const icons = {
@ -132,7 +131,7 @@ const icons = {
FolderHeartOutline: FolderHeartOutline
};
const AddTag = React.lazy(() => import ("../Modals/AddTag" ));
const AddTag = React.lazy(() => import("../Modals/AddTag"));
export default function FileTag() {
const classes = useStyles();
@ -143,9 +142,11 @@ export default function FileTag() {
const isHomePage = pathHelper.isHomePage(location.pathname);
const [tagOpen, setTagOpen] = useState(true);
const [addTagModal,setAddTagModal] = useState(false);
const [tagHover,setTagHover] = useState(null);
const [tags,setTags] = useState(Auth.GetUser().tags?Auth.GetUser().tags:[]);
const [addTagModal, setAddTagModal] = useState(false);
const [tagHover, setTagHover] = useState(null);
const [tags, setTags] = useState(
Auth.GetUser().tags ? Auth.GetUser().tags : []
);
const dispatch = useDispatch();
const SearchMyFile = useCallback(k => dispatch(searchMyFile(k)), [
@ -159,7 +160,6 @@ export default function FileTag() {
[dispatch]
);
const getIcon = (icon, color) => {
if (icons[icon]) {
const IconComponent = icons[icon];
@ -179,19 +179,21 @@ export default function FileTag() {
return <Circle className={[classes.iconFix]} />;
};
const submitSuccess = tag =>{
const newTags = [...tags,tag];
const submitSuccess = tag => {
const newTags = [...tags, tag];
setTags(newTags);
const user = Auth.GetUser();
user.tags = newTags;
Auth.SetUser(user);
};
const submitDelete = id =>{
API.delete("/tag/"+id)
const submitDelete = id => {
API.delete("/tag/" + id)
.then(() => {
const newTags = tags.filter((v)=>{return v.id !== id});
setTags(newTags)
const newTags = tags.filter(v => {
return v.id !== id;
});
setTags(newTags);
const user = Auth.GetUser();
user.tags = newTags;
Auth.SetUser(user);
@ -204,131 +206,136 @@ export default function FileTag() {
return (
<>
<Suspense fallback={""}>
<AddTag onSuccess={submitSuccess} open={addTagModal} onClose={()=>setAddTagModal(false)}/>
<AddTag
onSuccess={submitSuccess}
open={addTagModal}
onClose={() => setAddTagModal(false)}
/>
</Suspense>
<ExpansionPanel
square
expanded={tagOpen && isHomePage}
onChange={() => isHomePage && setTagOpen(!tagOpen)}
>
<ExpansionPanelSummary
aria-controls="panel1d-content"
id="panel1d-header"
<ExpansionPanel
square
expanded={tagOpen && isHomePage}
onChange={() => isHomePage && setTagOpen(!tagOpen)}
>
<ListItem
button
key="我的文件"
onClick={() =>
!isHomePage && history.push("/home?path=%2F")
}
<ExpansionPanelSummary
aria-controls="panel1d-content"
id="panel1d-header"
>
<ListItemIcon>
<KeyboardArrowRight
className={classNames(
{
[classes.expanded]: tagOpen && isHomePage,
[classes.iconFix]: true
},
classes.expand
)}
/>
{!(tagOpen && isHomePage) && (
<FolderShared className={classes.iconFix} />
)}
</ListItemIcon>
<ListItemText primary="我的文件" />
</ListItem>
<Divider />
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<List onMouseLeave={()=>setTagHover(null)}>
<ListItem
button
id="pickfiles"
className={classes.hiddenButton}
>
<ListItemIcon>
<UploadIcon />
</ListItemIcon>
<ListItemText />
</ListItem>
<ListItem
button
id="pickfolder"
className={classes.hiddenButton}
>
<ListItemIcon>
<UploadIcon />
</ListItemIcon>
<ListItemText />
</ListItem>
{[
{
key: "视频",
id: "video",
icon: (
<VideoIcon
className={[
classes.iconFix,
classes.iconVideo
]}
/>
)
},
{
key: "图片",
id: "image",
icon: (
<ImageIcon
className={[
classes.iconFix,
classes.iconImg
]}
/>
)
},
{
key: "音频",
id: "audio",
icon: (
<MusicIcon
className={[
classes.iconFix,
classes.iconAudio
]}
/>
)
},
{
key: "文档",
id: "doc",
icon: (
<DocIcon
className={[
classes.iconFix,
classes.iconDoc
]}
/>
)
key="我的文件"
onClick={() =>
!isHomePage && history.push("/home?path=%2F")
}
].map(v => (
>
<ListItemIcon>
<KeyboardArrowRight
className={classNames(
{
[classes.expanded]:
tagOpen && isHomePage,
[classes.iconFix]: true
},
classes.expand
)}
/>
{!(tagOpen && isHomePage) && (
<FolderShared className={classes.iconFix} />
)}
</ListItemIcon>
<ListItemText primary="我的文件" />
</ListItem>
<Divider />
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<List onMouseLeave={() => setTagHover(null)}>
<ListItem
button
key={v.key}
onClick={() => SearchMyFile(v.id + "/internal")}
id="pickfiles"
className={classes.hiddenButton}
>
<ListItemIcon className={classes.subMenu}>
{v.icon}
<ListItemIcon>
<UploadIcon />
</ListItemIcon>
<ListItemText primary={v.key} />
<ListItemText />
</ListItem>
))}
{tags.map(v => (
<ListItem
button
id="pickfolder"
className={classes.hiddenButton}
>
<ListItemIcon>
<UploadIcon />
</ListItemIcon>
<ListItemText />
</ListItem>
{[
{
key: "视频",
id: "video",
icon: (
<VideoIcon
className={[
classes.iconFix,
classes.iconVideo
]}
/>
)
},
{
key: "图片",
id: "image",
icon: (
<ImageIcon
className={[
classes.iconFix,
classes.iconImg
]}
/>
)
},
{
key: "音频",
id: "audio",
icon: (
<MusicIcon
className={[
classes.iconFix,
classes.iconAudio
]}
/>
)
},
{
key: "文档",
id: "doc",
icon: (
<DocIcon
className={[
classes.iconFix,
classes.iconDoc
]}
/>
)
}
].map(v => (
<ListItem
button
key={v.key}
onClick={() => SearchMyFile(v.id + "/internal")}
>
<ListItemIcon className={classes.subMenu}>
{v.icon}
</ListItemIcon>
<ListItemText primary={v.key} />
</ListItem>
))}
{tags.map(v => (
<ListItem
button
key={v.id}
onMouseEnter={()=>setTagHover(v.id)}
onMouseEnter={() => setTagHover(v.id)}
onClick={() => {
if (v.type === 0) {
SearchMyFile("tag/" + v.id);
@ -345,26 +352,37 @@ export default function FileTag() {
v.type === 0 ? v.color : null
)}
</ListItemIcon>
<ListItemText className={classes.overFlow} primary={v.name} />
<ListItemText
className={classes.overFlow}
primary={v.name}
/>
{tagHover === v.id && <ListItemSecondaryAction onClick={()=>submitDelete(v.id)}>
<IconButton size={"small"} edge="end" aria-label="delete">
{tagHover === v.id && (
<ListItemSecondaryAction
onClick={() => submitDelete(v.id)}
>
<IconButton
size={"small"}
edge="end"
aria-label="delete"
>
<Clear />
</IconButton>
</ListItemSecondaryAction>}
</ListItemSecondaryAction>
)}
</ListItem>
))}
<ListItem button onClick={()=>setAddTagModal(true)}>
<ListItemIcon className={classes.subMenu}>
<TagPlus className={classes.iconFix} />
</ListItemIcon>
<ListItemText primary={"添加标签..."} />
</ListItem>
</List>{" "}
<Divider />
</ExpansionPanelDetails>
</ExpansionPanel>
</>
<ListItem button onClick={() => setAddTagModal(true)}>
<ListItemIcon className={classes.subMenu}>
<TagPlus className={classes.iconFix} />
</ListItemIcon>
<ListItemText primary={"添加标签..."} />
</ListItem>
</List>{" "}
<Divider />
</ExpansionPanelDetails>
</ExpansionPanel>
</>
);
}

View File

@ -68,7 +68,7 @@ import FileTag from "./FileTags";
import { Assignment, Devices, Settings } from "@material-ui/icons";
import Divider from "@material-ui/core/Divider";
vhCheck()
vhCheck();
const drawerWidth = 240;
const drawerWidthMobile = 270;
@ -365,15 +365,18 @@ class NavbarCompoment extends Component {
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/doc?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/doc?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/doc?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/doc?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "audio":
@ -383,60 +386,72 @@ class NavbarCompoment extends Component {
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/video?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/video?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/video?p=" +encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/video?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "edit":
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/text?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/text?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/text?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/text?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "pdf":
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/pdf?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/pdf?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/pdf?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/pdf?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
case "code":
if (isShare) {
this.props.history.push(
this.props.selected[0].key +
"/code?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
"/code?name=" +
encodeURIComponent(this.props.selected[0].name) +
"&share_path=" +
encodeURIComponent(previewPath)
);
return;
}
this.props.history.push(
"/code?p=" + encodeURIComponent(previewPath) + "&id=" + this.props.selected[0].id
"/code?p=" +
encodeURIComponent(previewPath) +
"&id=" +
this.props.selected[0].id
);
return;
default:
@ -568,7 +583,7 @@ class NavbarCompoment extends Component {
{pathHelper.isMobile() && (
<>
<Divider/>
<Divider />
<List>
<ListItem
button
@ -591,7 +606,9 @@ class NavbarCompoment extends Component {
onClick={this.signOut}
>
<ListItemIcon>
<LogoutVariant className={classes.iconFix} />
<LogoutVariant
className={classes.iconFix}
/>
</ListItemIcon>
<ListItemText primary="退出登录" />
</ListItem>
@ -736,8 +753,13 @@ class NavbarCompoment extends Component {
!(
!this.props.isMultiple && this.props.withFile
) && (
<Typography variant="h6" color="inherit" noWrap
onClick={() => {this.props.history.push("/")}}
<Typography
variant="h6"
color="inherit"
noWrap
onClick={() => {
this.props.history.push("/");
}}
>
{this.props.subTitle
? this.props.subTitle
@ -799,8 +821,7 @@ class NavbarCompoment extends Component {
(isHomePage || isSharePage) && (
<div className={classes.sectionForFile}>
{!this.props.isMultiple &&
this.props.withFile
&&
this.props.withFile &&
isPreviewable(
this.props.selected[0].name
) && (

View File

@ -18,12 +18,12 @@ import {
ListItemText,
Typography
} from "@material-ui/core";
import {withRouter} from "react-router";
import { withRouter } from "react-router";
import pathHelper from "../../utils/page";
import {HotKeys,configure} from "react-hotkeys";
import { HotKeys, configure } from "react-hotkeys";
configure({
ignoreTags:[],
ignoreTags: []
});
const mapStateToProps = () => {
@ -91,11 +91,10 @@ const styles = theme => ({
});
const keyMap = {
SEARCH: "enter",
SEARCH: "enter"
};
class SearchBarCompoment extends Component {
constructor(props) {
super(props);
this.state = {
@ -104,15 +103,15 @@ class SearchBarCompoment extends Component {
};
}
handlers={
SEARCH:(e)=>{
if(pathHelper.isHomePage(this.props.location.pathname)){
handlers = {
SEARCH: e => {
if (pathHelper.isHomePage(this.props.location.pathname)) {
this.searchMyFile();
}else{
} else {
this.searchShare();
}
e.target.blur();
},
}
};
handleChange = event => {
@ -131,14 +130,15 @@ class SearchBarCompoment extends Component {
};
searchMyFile = () => {
this.props.searchMyFile("keywords/"+this.input);
this.props.searchMyFile("keywords/" + this.input);
};
searchShare = () => {
this.props.history.push("/search?keywords="+encodeURIComponent(this.input));
this.props.history.push(
"/search?keywords=" + encodeURIComponent(this.input)
);
};
render() {
const { classes } = this.props;
const { anchorEl } = this.state;
@ -146,22 +146,22 @@ class SearchBarCompoment extends Component {
const isHomePage = pathHelper.isHomePage(this.props.location.pathname);
return (
<div className={classes.search}>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<HotKeys keyMap={keyMap} handlers={this.handlers}>
<InputBase
placeholder="搜索..."
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
onChange={this.handleChange}
onBlur={this.cancelSuggest}
value={this.state.input}
/>
</HotKeys>
<HotKeys keyMap={keyMap} handlers={this.handlers}>
<InputBase
placeholder="搜索..."
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
onChange={this.handleChange}
onBlur={this.cancelSuggest}
value={this.state.input}
/>
</HotKeys>
<Popper
id={id}
open={this.state.input !== ""}

View File

@ -9,7 +9,7 @@ import {
ListItemIcon,
ListItemText,
List,
Hidden,
Hidden
} from "@material-ui/core";
const drawerWidth = 240;
const styles = theme => ({

View File

@ -8,7 +8,11 @@ import {
AccountArrowRight,
AccountPlus
} from "mdi-material-ui";
import {setSessionStatus, setUserPopover, toggleSnackbar} from "../../actions";
import {
setSessionStatus,
setUserPopover,
toggleSnackbar
} from "../../actions";
import { withRouter } from "react-router-dom";
import Auth from "../../middleware/Auth";
import {
@ -38,9 +42,9 @@ const mapDispatchToProps = dispatch => {
toggleSnackbar: (vertical, horizontal, msg, color) => {
dispatch(toggleSnackbar(vertical, horizontal, msg, color));
},
setSessionStatus:status=>{
setSessionStatus: status => {
dispatch(setSessionStatus(status));
},
}
};
};
const styles = () => ({
@ -106,7 +110,9 @@ class UserAvatarPopoverCompoment extends Component {
render() {
const { classes } = this.props;
const user = Auth.GetUser();
const isAdminPage = pathHelper.isAdminPage(this.props.location.pathname);
const isAdminPage = pathHelper.isAdminPage(
this.props.location.pathname
);
return (
<Popover
@ -125,17 +131,17 @@ class UserAvatarPopoverCompoment extends Component {
{!Auth.Check() && (
<div className={classes.visitorMenu}>
<Divider />
<MenuItem onClick={() =>
this.props.history.push("/login")
}>
<MenuItem
onClick={() => this.props.history.push("/login")}
>
<ListItemIcon>
<AccountArrowRight />
</ListItemIcon>
登录
</MenuItem>
<MenuItem onClick={() =>
this.props.history.push("/signup")
}>
<MenuItem
onClick={() => this.props.history.push("/signup")}
>
<ListItemIcon>
<AccountPlus />
</ListItemIcon>
@ -149,16 +155,20 @@ class UserAvatarPopoverCompoment extends Component {
<div className={classes.largeAvatarContainer}>
<Avatar
className={classes.largeAvatar}
src={"/api/v3/user/avatar/"+user.id + "/l"}
src={
"/api/v3/user/avatar/" + user.id + "/l"
}
/>
</div>
<div className={classes.info}>
<Typography noWrap>{user.nickname}</Typography>
<Typography color="textSecondary"
style={{
fontSize: "0.875rem",
}}
noWrap>
<Typography
color="textSecondary"
style={{
fontSize: "0.875rem"
}}
noWrap
>
{user.user_name}
</Typography>
<Chip
@ -174,33 +184,33 @@ class UserAvatarPopoverCompoment extends Component {
</div>
<div>
<Divider />
{!isAdminPage && <MenuItem
style={{
padding:" 11px 16px 11px 16px",
}}
onClick={() =>{
this.handleClose();
this.props.history.push("/profile/"+user.id);
}
}
>
<ListItemIcon>
<HomeAccount />
</ListItemIcon>
个人主页
</MenuItem>}
{!isAdminPage && (
<MenuItem
style={{
padding: " 11px 16px 11px 16px"
}}
onClick={() => {
this.handleClose();
this.props.history.push(
"/profile/" + user.id
);
}}
>
<ListItemIcon>
<HomeAccount />
</ListItemIcon>
个人主页
</MenuItem>
)}
{user.group.id === 1 && (
<MenuItem
style={{
padding:" 11px 16px 11px 16px",
padding: " 11px 16px 11px 16px"
}}
onClick={() =>{
onClick={() => {
this.handleClose();
this.props.history.push("/admin/home");
}
}
}}
>
<ListItemIcon>
<DesktopMacDashboard />
@ -209,9 +219,12 @@ class UserAvatarPopoverCompoment extends Component {
</MenuItem>
)}
<MenuItem style={{
padding:" 11px 16px 11px 16px",
}} onClick={this.sigOut}>
<MenuItem
style={{
padding: " 11px 16px 11px 16px"
}}
onClick={this.sigOut}
>
<ListItemIcon>
<LogoutVariant />
</ListItemIcon>

View File

@ -4,12 +4,12 @@ import { connect } from "react-redux";
import { setUserPopover } from "../../actions";
import { withStyles, Typography } from "@material-ui/core";
import Auth from "../../middleware/Auth";
import DarkModeSwitcher from "./DarkModeSwitcher"
import DarkModeSwitcher from "./DarkModeSwitcher";
import Avatar from "@material-ui/core/Avatar";
const mapStateToProps = state => {
return {
isLogin:state.viewUpdate.isLogin,
isLogin: state.viewUpdate.isLogin
};
};
@ -76,12 +76,12 @@ const styles = theme => ({
flexAvatar: {
display: "flex",
justifyContent: "space-between",
alignItems: "end",
alignItems: "end"
},
groupName: {
marginLeft: "10px",
color: "#ffffff",
opacity: "0.54",
opacity: "0.54"
},
storageCircle: {
width: "200px"
@ -101,13 +101,11 @@ class UserInfoCompoment extends Component {
return (
<div className={classes.userNav}>
<div className={classes.flexAvatar}>
{ /* eslint-disable-next-line */}
{/* eslint-disable-next-line */}
<a onClick={this.showUserInfo} className={classes.avatar}>
{isLogin && (
<Avatar
src={
"/api/v3/user/avatar/"+user.id + "/l"
}
src={"/api/v3/user/avatar/" + user.id + "/l"}
className={classes.avatarImg}
/>
)}
@ -118,7 +116,7 @@ class UserInfoCompoment extends Component {
/>
)}
</a>
<DarkModeSwitcher position="left"/>
<DarkModeSwitcher position="left" />
</div>
<div className={classes.storageCircle}>
<Typography
@ -126,7 +124,7 @@ class UserInfoCompoment extends Component {
component="h2"
noWrap
>
{isLogin?user.nickname:"未登录"}
{isLogin ? user.nickname : "未登录"}
</Typography>
<Typography
className={classes.groupName}
@ -134,7 +132,7 @@ class UserInfoCompoment extends Component {
color="textSecondary"
noWrap
>
{isLogin?user.group.name:"游客"}
{isLogin ? user.group.name : "游客"}
</Typography>
</div>
</div>

View File

@ -1,20 +1,20 @@
import React from "react"
import ContentLoader from "react-content-loader"
import React from "react";
import ContentLoader from "react-content-loader";
const MyLoader = () => (
<ContentLoader
height={80}
width={200}
speed={2}
primaryColor="#f3f3f3"
secondaryColor="#e4e4e4"
<ContentLoader
height={80}
width={200}
speed={2}
primaryColor="#f3f3f3"
secondaryColor="#e4e4e4"
>
<rect x="4" y="4" rx="7" ry="7" width="392" height="116" />
<rect x="4" y="4" rx="7" ry="7" width="392" height="116" />
</ContentLoader>
)
);
function captchaPlacholder (){
return (<MyLoader />)
function captchaPlacholder() {
return <MyLoader />;
}
export default captchaPlacholder
export default captchaPlacholder;

View File

@ -1,4 +1,4 @@
import React from "react";
import React from "react";
class ErrorBoundary extends React.Component {
constructor(props) {
@ -25,4 +25,4 @@ class ErrorBoundary extends React.Component {
}
}
export default ErrorBoundary
export default ErrorBoundary;

View File

@ -1,5 +1,5 @@
import React from "react";
import { Facebook } from "react-content-loader";
import { Facebook } from "react-content-loader";
import { makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles(theme => ({
@ -14,9 +14,7 @@ const useStyles = makeStyles(theme => ({
}));
const MyLoader = props => {
return (
<Facebook className={props.className} />
);
return <Facebook className={props.className} />;
};
function PageLoading() {

View File

@ -1,33 +1,28 @@
import React from "react"
import { Code } from "react-content-loader"
import {makeStyles} from "@material-ui/core/styles";
import React from "react";
import { Code } from "react-content-loader";
import { makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles(theme => ({
loader:{
loader: {
width: "70%",
padding: 40,
[theme.breakpoints.down("md")]: {
width: "100%",
padding: 10,
},
},
padding: 10
}
}
}));
const MyLoader = props => <Code className={props.className} />;
const MyLoader = (props) => (
<Code className={props.className}/>
)
function TextLoading (){
function TextLoading() {
const classes = useStyles();
return (
<div>
<MyLoader className={classes.loader}/>
<MyLoader className={classes.loader} />
</div>
)
);
}
export default TextLoading
export default TextLoading;

View File

@ -1,6 +1,7 @@
import React, { useState, useCallback } from "react";
import {
Button, Dialog,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
@ -17,9 +18,9 @@ import {
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../actions";
import RightIcon from "@material-ui/icons/KeyboardArrowRight";
import {Add, Fingerprint, HighlightOff} from "@material-ui/icons";
import { Add, Fingerprint, HighlightOff } from "@material-ui/icons";
import API from "../../middleware/Api";
import {bufferDecode, bufferEncode} from "../../utils";
import { bufferDecode, bufferEncode } from "../../utils";
const useStyles = makeStyles(theme => ({
sectionTitle: {
@ -46,8 +47,8 @@ const useStyles = makeStyles(theme => ({
}));
export default function Authn(props) {
const [selected,setSelected] = useState("");
const [confirm,setConfirm] = useState(false);
const [selected, setSelected] = useState("");
const [confirm, setConfirm] = useState(false);
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
(vertical, horizontal, msg, color) =>
@ -57,41 +58,27 @@ export default function Authn(props) {
const deleteCredential = id => {
API.patch("/user/setting/authn", {
id:id,
id: id
})
.then(() => {
ToggleSnackbar(
"top",
"right",
"凭证已删除",
"success"
);
props.remove(id)
ToggleSnackbar("top", "right", "凭证已删除", "success");
props.remove(id);
})
.catch(error => {
ToggleSnackbar(
"top",
"right",
error.message,
"error"
);
}).then(()=>{
setConfirm(false);
});
ToggleSnackbar("top", "right", error.message, "error");
})
.then(() => {
setConfirm(false);
});
};
const classes = useStyles();
const addCredential = () =>{
if (!navigator.credentials){
ToggleSnackbar(
"top",
"right",
"当前浏览器或环境不支持",
"warning"
);
const addCredential = () => {
if (!navigator.credentials) {
ToggleSnackbar("top", "right", "当前浏览器或环境不支持", "warning");
return
return;
}
API.put("/user/authn", {})
.then(response => {
@ -112,7 +99,7 @@ export default function Authn(props) {
) {
credentialCreationOptions.publicKey.excludeCredentials[
i
].id = bufferDecode(
].id = bufferDecode(
credentialCreationOptions.publicKey
.excludeCredentials[i].id
);
@ -142,41 +129,28 @@ export default function Authn(props) {
})
.then(response => {
props.add(response.data);
ToggleSnackbar(
"top",
"right",
"验证器已添加",
"success"
);
ToggleSnackbar("top", "right", "验证器已添加", "success");
return;
})
.catch(error => {
console.log(error);
ToggleSnackbar(
"top",
"right",
error.message,
"error"
);
ToggleSnackbar("top", "right", error.message, "error");
});
};
return (
<div>
<Dialog
open={confirm}
onClose={()=>setConfirm(false)}
>
<Dialog open={confirm} onClose={() => setConfirm(false)}>
<DialogTitle>删除凭证</DialogTitle>
<DialogContent>
确定要吊销这个凭证吗
</DialogContent>
<DialogContent>确定要吊销这个凭证吗</DialogContent>
<DialogActions>
<Button onClick={()=>setConfirm(false)} color="default">
<Button onClick={() => setConfirm(false)} color="default">
取消
</Button>
<Button onClick={()=>deleteCredential(selected)} color="primary">
<Button
onClick={() => deleteCredential(selected)}
color="primary"
>
确定
</Button>
</DialogActions>
@ -187,32 +161,36 @@ export default function Authn(props) {
</Typography>
<Paper>
<List className={classes.desenList}>
{props.list.map((v) => (
{props.list.map(v => (
<>
<ListItem button
style={{
paddingRight:60,
}}
onClick={()=>{
setConfirm(true);
setSelected(v.id);
}}>
<ListItem
button
style={{
paddingRight: 60
}}
onClick={() => {
setConfirm(true);
setSelected(v.id);
}}
>
<ListItemIcon className={classes.iconFix}>
<Fingerprint />
</ListItemIcon>
<ListItemText primary={v.fingerprint} />
<ListItemSecondaryAction
onClick={()=>deleteCredential(v.id)}
onClick={() => deleteCredential(v.id)}
className={classes.flexContainer}
>
<HighlightOff className={classes.rightIcon} />
<HighlightOff
className={classes.rightIcon}
/>
</ListItemSecondaryAction>
</ListItem>
<Divider />
</>
))}
<ListItem button onClick={()=>addCredential()}>
<ListItem button onClick={() => addCredential()}>
<ListItemIcon className={classes.iconFix}>
<Add />
</ListItemIcon>

View File

@ -17,7 +17,7 @@ import {
TableRow,
Grid
} from "@material-ui/core";
import {withRouter} from "react-router";
import { withRouter } from "react-router";
import Pagination from "@material-ui/lab/Pagination";
const styles = theme => ({
@ -83,7 +83,7 @@ const styles = theme => ({
marginTop: "1px",
fontSize: "25px",
color: "#ffffff",
opacity: "0.81",
opacity: "0.81"
},
th: {
minWidth: "106px"
@ -97,7 +97,7 @@ const styles = theme => ({
cursor: "pointer"
},
navigator: {
padding:theme.spacing(2),
padding: theme.spacing(2)
},
pageInfo: {
marginTop: "14px",
@ -110,8 +110,8 @@ const styles = theme => ({
infoContainer: {
marginTop: "30px"
},
tableContainer:{
overflowX:"auto",
tableContainer: {
overflowX: "auto"
}
});
const mapStateToProps = () => {
@ -131,8 +131,8 @@ class ProfileCompoment extends Component {
listType: 0,
shareList: [],
page: 1,
user:null,
total:0,
user: null,
total: 0
};
handleChange = (event, listType) => {
@ -148,18 +148,29 @@ class ProfileCompoment extends Component {
this.loadList(1, "default");
};
loadList = (page,order) => {
API
.get("/user/profile/" + this.props.match.params.id + "?page=" + page + "&type=" + order)
loadList = (page, order) => {
API.get(
"/user/profile/" +
this.props.match.params.id +
"?page=" +
page +
"&type=" +
order
)
.then(response => {
this.setState({
shareList: response.data.items,
user:response.data.user,
total:response.data.total,
user: response.data.user,
total: response.data.total
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
this.props.toggleSnackbar(
"top",
"right",
error.message,
"error"
);
});
};
@ -182,17 +193,17 @@ class ProfileCompoment extends Component {
return (
<div className={classes.layout}>
{this.state.user === null &&
<div></div>
}
{this.state.user !== null &&
{this.state.user === null && <div></div>}
{this.state.user !== null && (
<Paper square>
<div className={classes.userNav}>
<div>
<Avatar
className={classes.avatarContainer}
src={
"/api/v3/user/avatar/"+this.state.user.id + "/l"
"/api/v3/user/avatar/" +
this.state.user.id +
"/l"
}
/>
</div>
@ -303,88 +314,109 @@ class ProfileCompoment extends Component {
this.state.listType === 1) && (
<div>
<div className={classes.tableContainer}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>文件名</TableCell>
<TableCell
className={classes.mobileHide}
>
分享日期
</TableCell>
<TableCell
className={[
classes.th,
classes.mobileHide
]}
>
下载次数
</TableCell>
<TableCell
className={[
classes.th,
classes.mobileHide
]}
>
浏览次数
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.shareList.map((row,id) => (
<TableRow
key={id}
className={classes.tableLink}
onClick={() =>
this.props.history.push(
"/s/" + row.key
)
}
>
<TableCell>
<Typography>
{row.source?row.source.name:"[已失效]"}
</Typography>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>文件名</TableCell>
<TableCell
className={
classes.mobileHide
}
>
分享日期
</TableCell>
<TableCell
nowrap={"nowrap"}
className={classes.mobileHide}
className={[
classes.th,
classes.mobileHide
]}
>
{row.create_date}
下载次数
</TableCell>
<TableCell
className={classes.mobileHide}
className={[
classes.th,
classes.mobileHide
]}
>
{row.downloads}
</TableCell>
<TableCell
className={classes.mobileHide}
>
{row.views}
浏览次数
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableHead>
<TableBody>
{this.state.shareList.map(
(row, id) => (
<TableRow
key={id}
className={
classes.tableLink
}
onClick={() =>
this.props.history.push(
"/s/" + row.key
)
}
>
<TableCell>
<Typography>
{row.source
? row.source
.name
: "[已失效]"}
</Typography>
</TableCell>
<TableCell
nowrap={"nowrap"}
className={
classes.mobileHide
}
>
{row.create_date}
</TableCell>
<TableCell
className={
classes.mobileHide
}
>
{row.downloads}
</TableCell>
<TableCell
className={
classes.mobileHide
}
>
{row.views}
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</div>
{this.state.shareList.length !== 0 &&
this.state.listType === 0 && (
<div className={classes.navigator}>
<Pagination
count={Math.ceil(this.state.total / 10)}
onChange={(e,v)=>this.loadList(
v,
this.state.listType === 0 ? "default" : "hot"
)}
color="secondary"
/>
</div>
)}
this.state.listType === 0 && (
<div className={classes.navigator}>
<Pagination
count={Math.ceil(
this.state.total / 10
)}
onChange={(e, v) =>
this.loadList(
v,
this.state.listType ===
0
? "default"
: "hot"
)
}
color="secondary"
/>
</div>
)}
</div>
)}
</Paper>
}
)}
</div>
);
}

View File

@ -10,7 +10,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody";
import API from "../../middleware/Api";
import TimeAgo from "timeago-react";
import {getTaskProgress, getTaskStatus, getTaskType} from "../../config";
import { getTaskProgress, getTaskStatus, getTaskType } from "../../config";
import Pagination from "@material-ui/lab/Pagination";
const useStyles = makeStyles(theme => ({
@ -28,7 +28,7 @@ const useStyles = makeStyles(theme => ({
},
content: {
marginTop: theme.spacing(4),
overflowX:"auto",
overflowX: "auto"
},
cardContent: {
padding: theme.spacing(2)
@ -39,11 +39,11 @@ const useStyles = makeStyles(theme => ({
create: {
marginTop: theme.spacing(2)
},
noWrap:{
wordBreak: "keepAll",
noWrap: {
wordBreak: "keepAll"
},
footer:{
padding:theme.spacing(2),
footer: {
padding: theme.spacing(2)
}
}));
@ -60,33 +60,32 @@ export default function Tasks() {
);
const loadList = page => {
API.get("/user/setting/tasks?page=" + page)
.then(response => {
setTasks(response.data.tasks);
setTotal(response.data.total);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
API.get("/user/setting/tasks?page=" + page)
.then(response => {
setTasks(response.data.tasks);
setTotal(response.data.total);
})
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
};
useEffect(() => {
loadList(page);
// eslint-disable-next-line
}, [page]);
const getError = error => {
if (error === ""){
return "-"
if (error === "") {
return "-";
}
try {
const res = JSON.parse(error)
return res.msg
}catch (e) {
return "未知"
const res = JSON.parse(error);
return res.msg;
} catch (e) {
return "未知";
}
}
};
const classes = useStyles();
@ -100,16 +99,26 @@ export default function Tasks() {
<TableHead>
<TableRow>
<TableCell nowrap="nowrap">创建于</TableCell>
<TableCell nowrap="nowrap" align="right">任务类型</TableCell>
<TableCell nowrap="nowrap" align="right">状态</TableCell>
<TableCell nowrap="nowrap" align="right">最后进度</TableCell>
<TableCell nowrap="nowrap" align="right">
任务类型
</TableCell>
<TableCell nowrap="nowrap" align="right">
状态
</TableCell>
<TableCell nowrap="nowrap" align="right">
最后进度
</TableCell>
<TableCell nowrap="nowrap">错误信息</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tasks.map((row, id) => (
<TableRow key={id}>
<TableCell nowrap="nowrap" component="th" scope="row">
<TableCell
nowrap="nowrap"
component="th"
scope="row"
>
<TimeAgo
datetime={row.create_date}
locale="zh_CN"
@ -118,22 +127,25 @@ export default function Tasks() {
<TableCell nowrap="nowrap" align="right">
{getTaskType(row.type)}
</TableCell>
<TableCell nowrap="nowrap" align="right">{getTaskStatus(row.status)}</TableCell>
<TableCell nowrap="nowrap" align="right">
{getTaskProgress(row.type,row.progress)}
{getTaskStatus(row.status)}
</TableCell>
<TableCell nowrap="nowrap" align="right">
{getTaskProgress(row.type, row.progress)}
</TableCell>
<TableCell className={classes.noWrap}>
{getError(row.error)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className={classes.footer}>
<Pagination count={Math.ceil(total / 10)}
onChange={(e,v)=>setPage(v)}
color="secondary"/>
<Pagination
count={Math.ceil(total / 10)}
onChange={(e, v) => setPage(v)}
color="secondary"
/>
</div>
</Paper>
</div>

View File

@ -12,7 +12,12 @@ import NickIcon from "@material-ui/icons/PermContactCalendar";
import LockIcon from "@material-ui/icons/Lock";
import VerifyIcon from "@material-ui/icons/VpnKey";
import ColorIcon from "@material-ui/icons/Palette";
import {applyThemes, changeViewMethod, toggleDaylightMode, toggleSnackbar} from "../../actions";
import {
applyThemes,
changeViewMethod,
toggleDaylightMode,
toggleSnackbar
} from "../../actions";
import axios from "axios";
import FingerprintIcon from "@material-ui/icons/Fingerprint";
import ToggleButton from "@material-ui/lab/ToggleButton";
@ -43,7 +48,7 @@ import API from "../../middleware/Api";
import Auth from "../../middleware/Auth";
import { withRouter } from "react-router";
import QRCode from "qrcode-react";
import {Brightness3, ListAlt, PermContactCalendar} from "@material-ui/icons";
import { Brightness3, ListAlt, PermContactCalendar } from "@material-ui/icons";
import { transformTime } from "../../utils";
import Authn from "./Authn";
@ -136,18 +141,18 @@ const styles = theme => ({
paddingText: {
paddingRight: theme.spacing(2)
},
qrcode:{
qrcode: {
width: 128,
marginTop: 16,
marginRight: 16,
},
marginRight: 16
}
});
const mapStateToProps = state => {
return {
title: state.siteConfig.title,
authn:state.siteConfig.authn,
viewMethod: state.viewUpdate.explorerViewMethod,
authn: state.siteConfig.authn,
viewMethod: state.viewUpdate.explorerViewMethod
};
};
@ -156,15 +161,15 @@ const mapDispatchToProps = dispatch => {
toggleSnackbar: (vertical, horizontal, msg, color) => {
dispatch(toggleSnackbar(vertical, horizontal, msg, color));
},
applyThemes: (color) => {
applyThemes: color => {
dispatch(applyThemes(color));
},
toggleDaylightMode:()=>{
dispatch(toggleDaylightMode())
toggleDaylightMode: () => {
dispatch(toggleDaylightMode());
},
changeView: method => {
dispatch(changeViewMethod(method));
},
}
};
};
@ -208,7 +213,7 @@ class UserSettingCompoment extends Component {
two_fa_secret: "",
prefer_theme: "",
themes: {},
authn:[],
authn: []
}
};
@ -432,10 +437,9 @@ class UserSettingCompoment extends Component {
this.setState({
loading: "changeTheme"
});
API
.patch("/user/setting/theme", {
theme: this.state.chosenTheme
})
API.patch("/user/setting/theme", {
theme: this.state.chosenTheme
})
.then(() => {
this.props.toggleSnackbar(
"top",
@ -507,7 +511,7 @@ class UserSettingCompoment extends Component {
};
init2FA = () => {
if (this.state.settings.two_factor){
if (this.state.settings.two_factor) {
this.setState({ twoFactor: true });
return;
}
@ -515,7 +519,7 @@ class UserSettingCompoment extends Component {
.then(response => {
this.setState({
two_fa_secret: response.data,
twoFactor: true,
twoFactor: true
});
})
.catch(error => {
@ -533,8 +537,8 @@ class UserSettingCompoment extends Component {
loading: "twoFactor"
});
API.patch("/user/setting/2fa", {
code: this.state.authCode
})
code: this.state.authCode
})
.then(() => {
this.props.toggleSnackbar(
"top",
@ -544,9 +548,9 @@ class UserSettingCompoment extends Component {
);
this.setState({
loading: "",
settings:{
settings: {
...this.state.settings,
two_factor:!this.state.settings.two_factor,
two_factor: !this.state.settings.two_factor
}
});
this.handleClose();
@ -570,17 +574,17 @@ class UserSettingCompoment extends Component {
handleAlignment = (event, chosenTheme) => this.setState({ chosenTheme });
toggleThemeMode = (current) => {
if (current !== null){
toggleThemeMode = current => {
if (current !== null) {
this.props.toggleDaylightMode();
Auth.SetPreference("theme_mode",null);
Auth.SetPreference("theme_mode", null);
}
}
};
render() {
const { classes } = this.props;
const user = Auth.GetUser();
const dark = Auth.GetPreference("theme_mode")
const dark = Auth.GetPreference("theme_mode");
return (
<div>
@ -675,9 +679,7 @@ class UserSettingCompoment extends Component {
</ListItemSecondaryAction>
</ListItem>
<Divider />
<ListItem
button
>
<ListItem button>
<ListItemIcon className={classes.iconFix}>
<GroupIcon />
</ListItemIcon>
@ -779,30 +781,29 @@ class UserSettingCompoment extends Component {
<Authn
list={this.state.settings.authn}
add = {
(credential)=>{
this.setState({
settings:{
...this.state.settings,
authn: [...this.state.settings.authn,credential],
}
})
}
}
remove={
(id)=>{
let credentials = [...this.state.settings.authn];
credentials = credentials.filter((v)=>{
return v.id !== id
})
this.setState({
settings:{
...this.state.settings,
authn: credentials,
}
})
}
}
add={credential => {
this.setState({
settings: {
...this.state.settings,
authn: [
...this.state.settings.authn,
credential
]
}
});
}}
remove={id => {
let credentials = [...this.state.settings.authn];
credentials = credentials.filter(v => {
return v.id !== id;
});
this.setState({
settings: {
...this.state.settings,
authn: credentials
}
});
}}
/>
<Typography
@ -831,8 +832,11 @@ class UserSettingCompoment extends Component {
<div className={classes.secondColor}></div>
</ListItemSecondaryAction>
</ListItem>
<Divider/>
<ListItem button onClick={()=>this.toggleThemeMode(dark)}>
<Divider />
<ListItem
button
onClick={() => this.toggleThemeMode(dark)}
>
<ListItemIcon className={classes.iconFix}>
<Brightness3 />
</ListItemIcon>
@ -845,18 +849,22 @@ class UserSettingCompoment extends Component {
className={classes.infoTextWithIcon}
color="textSecondary"
>
{dark&&(dark==="dark"
? "偏好开启"
: "偏好关闭")}
{dark === null&&"跟随系统"}
{dark &&
(dark === "dark"
? "偏好开启"
: "偏好关闭")}
{dark === null && "跟随系统"}
</Typography>
<RightIcon
className={classes.rightIconWithText}
/>
</ListItemSecondaryAction>
</ListItem>
<Divider/>
<ListItem button onClick={()=>this.toggleViewMethod()}>
<Divider />
<ListItem
button
onClick={() => this.toggleViewMethod()}
>
<ListItemIcon className={classes.iconFix}>
<ListAlt />
</ListItemIcon>
@ -869,9 +877,12 @@ class UserSettingCompoment extends Component {
className={classes.infoTextWithIcon}
color="textSecondary"
>
{this.props.viewMethod === "icon" && "大图标"}
{this.props.viewMethod === "list" && "列表"}
{this.props.viewMethod === "smallIcon" && "小图标"}
{this.props.viewMethod === "icon" &&
"大图标"}
{this.props.viewMethod === "list" &&
"列表"}
{this.props.viewMethod ===
"smallIcon" && "小图标"}
</Typography>
<RightIcon
className={classes.rightIconWithText}
@ -1101,26 +1112,36 @@ class UserSettingCompoment extends Component {
</DialogActions>
</Dialog>
<Dialog open={this.state.twoFactor} onClose={this.handleClose}>
<DialogTitle>{this.state.settings.two_factor?"关闭":"启用"}二步验证</DialogTitle>
<DialogTitle>
{this.state.settings.two_factor ? "关闭" : "启用"}
二步验证
</DialogTitle>
<DialogContent>
<div className={classes.flexContainerResponse}>
{!this.state.settings.two_factor && <div className={classes.qrcode}><QRCode
value={
"otpauth://totp/" +
this.props.title +
"?secret=" +
this.state.two_fa_secret
}
/></div>}
{!this.state.settings.two_factor && (
<div className={classes.qrcode}>
<QRCode
value={
"otpauth://totp/" +
this.props.title +
"?secret=" +
this.state.two_fa_secret
}
/>
</div>
)}
<div className={classes.desText}>
{!this.state.settings.two_factor && <Typography>
请使用任意二步验证APP或者支持二步验证的密码管理软件扫描左侧二维码添加本站扫描完成后请填写二步验证APP给出的6位验证码以开启二步验证
</Typography>}
{this.state.settings.two_factor && <Typography>
请验证当前二步验证代码
</Typography>}
{!this.state.settings.two_factor && (
<Typography>
请使用任意二步验证APP或者支持二步验证的密码管理软件扫描左侧二维码添加本站扫描完成后请填写二步验证APP给出的6位验证码以开启二步验证
</Typography>
)}
{this.state.settings.two_factor && (
<Typography>
请验证当前二步验证代码
</Typography>
)}
<TextField
id="standard-name"
label="6位验证码"
@ -1147,7 +1168,8 @@ class UserSettingCompoment extends Component {
this.state.authCode === ""
}
>
{this.state.settings.two_factor?"关闭":"启用"}二步验证
{this.state.settings.two_factor ? "关闭" : "启用"}
二步验证
</Button>
</DialogActions>
</Dialog>
@ -1199,10 +1221,7 @@ class UserSettingCompoment extends Component {
<TextField
id="standard-name"
className={classes.textField}
value={
window.location.origin +
"/dav"
}
value={window.location.origin + "/dav"}
margin="normal"
autoFocus
/>

View File

@ -58,8 +58,8 @@ export default function WebDAV() {
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
[dispatch]
);
const loadList = () =>{
const loadList = () => {
API.get("/webdav/accounts")
.then(response => {
setAccounts(response.data.accounts);
@ -67,10 +67,10 @@ export default function WebDAV() {
.catch(error => {
ToggleSnackbar("top", "right", error.message, "error");
});
}
};
useEffect(() => {
loadList();
// eslint-disable-next-line
loadList();
// eslint-disable-next-line
}, []);
const deleteAccount = id => {
@ -138,7 +138,9 @@ export default function WebDAV() {
{tab === 0 && (
<div>
<Alert severity="info">
WebDAV的地址为{window.location.origin + "/dav"}登陆用户名统一为{user.user_name}{" "}
WebDAV的地址为
{window.location.origin + "/dav"}
登陆用户名统一为{user.user_name}{" "}
密码为所创建账号的密码
</Alert>
<TableContainer className={classes.tableContainer}>

View File

@ -13,7 +13,7 @@ import {
TextField,
Avatar
} from "@material-ui/core";
import { withRouter} from "react-router";
import { withRouter } from "react-router";
const styles = theme => ({
card: {
@ -57,9 +57,8 @@ class LockedFileCompoment extends Component {
super(props);
const query = new URLSearchParams(this.props.location.search);
this.state = {
pwd: query.get("password"),
pwd: query.get("password")
};
}
handleChange = name => event => {
@ -85,7 +84,9 @@ class LockedFileCompoment extends Component {
<Avatar
aria-label="Recipe"
src={
"/api/v3/user/avatar/"+this.props.share.creator.key + "/l"
"/api/v3/user/avatar/" +
this.props.share.creator.key +
"/l"
}
/>
}

View File

@ -34,7 +34,7 @@ import { VisibilityOff, VpnKey } from "@material-ui/icons";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import {withRouter} from "react-router-dom";
import { withRouter } from "react-router-dom";
const styles = theme => ({
cardContainer: {
@ -79,9 +79,9 @@ const styles = theme => ({
marginLeft: theme.spacing(1),
height: 17
},
orderSelect:{
textAlign:"right",
marginTop: 5,
orderSelect: {
textAlign: "right",
marginTop: 5
}
});
const mapStateToProps = () => {
@ -102,11 +102,11 @@ class MyShareCompoment extends Component {
total: 0,
shareList: [],
showPwd: null,
orderBy:"created_at DESC",
orderBy: "created_at DESC"
};
componentDidMount = () => {
this.loadList(1,this.state.orderBy);
this.loadList(1, this.state.orderBy);
};
showPwd = pwd => {
@ -117,31 +117,34 @@ class MyShareCompoment extends Component {
this.setState({ showPwd: null });
};
removeShare = (id) => {
API
.delete("/share/"+id)
removeShare = id => {
API.delete("/share/" + id)
.then(() => {
let oldList = this.state.shareList;
oldList = oldList.filter(value => {
return value.key !== id;
});
this.setState({
shareList: oldList,
total:this.state.total-1,
});
this.props.toggleSnackbar(
"top",
"right",
"分享已取消",
"success"
);
if (oldList.length === 0){
this.loadList(1,this.state.orderBy);
}
let oldList = this.state.shareList;
oldList = oldList.filter(value => {
return value.key !== id;
});
this.setState({
shareList: oldList,
total: this.state.total - 1
});
this.props.toggleSnackbar(
"top",
"right",
"分享已取消",
"success"
);
if (oldList.length === 0) {
this.loadList(1, this.state.orderBy);
}
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
this.props.toggleSnackbar(
"top",
"right",
error.message,
"error"
);
});
};
@ -154,11 +157,10 @@ class MyShareCompoment extends Component {
const shareIndex = oldList.findIndex(value => {
return value.key === id;
});
API
.patch("/share/"+id, {
prop:"password",
value:oldList[shareIndex].password === "" ? newPwd : "",
})
API.patch("/share/" + id, {
prop: "password",
value: oldList[shareIndex].password === "" ? newPwd : ""
})
.then(response => {
oldList[shareIndex].password = response.data;
this.setState({
@ -166,7 +168,12 @@ class MyShareCompoment extends Component {
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
this.props.toggleSnackbar(
"top",
"right",
error.message,
"error"
);
});
};
@ -175,11 +182,10 @@ class MyShareCompoment extends Component {
const shareIndex = oldList.findIndex(value => {
return value.key === id;
});
API
.patch("/share/"+id, {
prop:"preview_enabled",
value:oldList[shareIndex].preview?"false":"true",
})
API.patch("/share/" + id, {
prop: "preview_enabled",
value: oldList[shareIndex].preview ? "false" : "true"
})
.then(response => {
oldList[shareIndex].preview = response.data;
this.setState({
@ -187,13 +193,25 @@ class MyShareCompoment extends Component {
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
this.props.toggleSnackbar(
"top",
"right",
error.message,
"error"
);
});
};
loadList = (page,orderBy) => {
loadList = (page, orderBy) => {
const order = orderBy.split(" ");
API.get("/share?page=" + page + "&order_by=" + order[0] + "&order=" + order[1])
API.get(
"/share?page=" +
page +
"&order_by=" +
order[0] +
"&order=" +
order[1]
)
.then(response => {
if (response.data.items.length === 0) {
this.props.toggleSnackbar(
@ -217,14 +235,14 @@ class MyShareCompoment extends Component {
this.setState({
page: value
});
this.loadList(value,this.state.orderBy);
this.loadList(value, this.state.orderBy);
};
handleOrderChange = event => {
handleOrderChange = event => {
this.setState({
orderBy:event.target.value,
orderBy: event.target.value
});
this.loadList(this.state.page,event.target.value);
this.loadList(this.state.page, event.target.value);
};
isExpired = share => {
@ -239,19 +257,34 @@ class MyShareCompoment extends Component {
<Grid container>
<Grid sm={6} xs={6}>
<Typography color="textSecondary" variant="h4">
我的分享
</Typography>
</Grid>
</Grid>
<Grid sm={6} xs={6} className={classes.orderSelect}>
<FormControl>
<Select color={"secondary"} onChange={this.handleOrderChange} value={this.state.orderBy}>
<MenuItem value={"created_at DESC"}>创建日期由晚到早</MenuItem>
<MenuItem value={"created_at ASC"}>创建日期由早到晚</MenuItem>
<MenuItem value={"downloads DESC"}>下载次数由大到小</MenuItem>
<MenuItem value={"downloads ASC"}>下载次数由小到大</MenuItem>
<MenuItem value={"views DESC"}>浏览次数由大到小</MenuItem>
<MenuItem value={"views ASC"}>浏览次数由小到大</MenuItem>
<Select
color={"secondary"}
onChange={this.handleOrderChange}
value={this.state.orderBy}
>
<MenuItem value={"created_at DESC"}>
创建日期由晚到早
</MenuItem>
<MenuItem value={"created_at ASC"}>
创建日期由早到晚
</MenuItem>
<MenuItem value={"downloads DESC"}>
下载次数由大到小
</MenuItem>
<MenuItem value={"downloads ASC"}>
下载次数由小到大
</MenuItem>
<MenuItem value={"views DESC"}>
浏览次数由大到小
</MenuItem>
<MenuItem value={"views ASC"}>
浏览次数由小到大
</MenuItem>
</Select>
</FormControl>
</Grid>
@ -333,7 +366,14 @@ class MyShareCompoment extends Component {
<Tooltip placement="top" title="打开">
<IconButton
onClick={() =>
this.props.history.push("/s/"+value.key + (value.password === "" ?"":"?password=" + value.password))
this.props.history.push(
"/s/" +
value.key +
(value.password === ""
? ""
: "?password=" +
value.password)
)
}
>
<OpenIcon />
@ -358,9 +398,7 @@ class MyShareCompoment extends Component {
placement="top"
title="查看密码"
onClick={() =>
this.showPwd(
value.password
)
this.showPwd(value.password)
}
>
<IconButton>
@ -374,9 +412,7 @@ class MyShareCompoment extends Component {
placement="top"
title="变更为私密分享"
onClick={() =>
this.changePermission(
value.key
)
this.changePermission(value.key)
}
>
<IconButton>
@ -392,9 +428,7 @@ class MyShareCompoment extends Component {
: "允许预览"
}
onClick={() =>
this.changePreviewOption(
value.key
)
this.changePreviewOption(value.key)
}
>
<IconButton>
@ -423,10 +457,10 @@ class MyShareCompoment extends Component {
</Grid>
<div className={classes.loadMore}>
<Pagination
count={Math.ceil(this.state.total / 18)}
onChange={this.handlePageChange}
color="secondary"
/>
count={Math.ceil(this.state.total / 18)}
onChange={this.handlePageChange}
color="secondary"
/>
</div>{" "}
<Dialog
open={this.state.showPwd !== null}

View File

@ -1,35 +1,32 @@
import React from "react";
import SentimentVeryDissatisfiedIcon from '@material-ui/icons/SentimentVeryDissatisfied';
import {lighten, makeStyles} from "@material-ui/core/styles";
import SentimentVeryDissatisfiedIcon from "@material-ui/icons/SentimentVeryDissatisfied";
import { lighten, makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles(theme=>({
icon:{
fontSize: "160px",
const useStyles = makeStyles(theme => ({
icon: {
fontSize: "160px"
},
emptyContainer: {
bottom: "0",
height: "300px",
margin: "50px auto",
width: "300px",
color: lighten(theme.palette.text.disabled,0.4),
color: lighten(theme.palette.text.disabled, 0.4),
textAlign: "center",
paddingTop: "20px"
},
emptyInfoBig: {
fontSize: "25px",
color: lighten(theme.palette.text.disabled,0.4),
},
color: lighten(theme.palette.text.disabled, 0.4)
}
}));
export default function Notice(props) {
const classes = useStyles();
return (
<div className={classes.emptyContainer}>
<SentimentVeryDissatisfiedIcon className={classes.icon}/>
<div className={classes.emptyInfoBig}>
{props.msg}
</div>
<SentimentVeryDissatisfiedIcon className={classes.icon} />
<div className={classes.emptyInfoBig}>{props.msg}</div>
</div>
)
}
);
}

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from "react";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import { MenuBook } from "@material-ui/icons";
import { Typography } from "@material-ui/core";
import Divider from "@material-ui/core/Divider";
@ -14,8 +14,8 @@ const useStyles = makeStyles(theme => ({
readMeContainer: {
marginTop: 30,
[theme.breakpoints.down("sm")]: {
marginTop: theme.spacing(2),
},
marginTop: theme.spacing(2)
}
},
readMeHeader: {
padding: "10px 16px",
@ -44,18 +44,24 @@ const useStyles = makeStyles(theme => ({
},
".for-container .for-markdown-preview pre": {
backgroundColor: theme.palette.background.default + "!important",
color: theme.palette.type ==="dark"?"#fff !important":"rgba(0, 0, 0, 0.87);!important",
color:
theme.palette.type === "dark"
? "#fff !important"
: "rgba(0, 0, 0, 0.87);!important"
},
".for-container .for-markdown-preview code": {
backgroundColor: theme.palette.background.default + "!important"
},
".for-container .for-markdown-preview a": {
color: theme.palette.type==="dark"?"#67aeff !important":"#0366d6 !important",
},
".for-container .for-markdown-preview table th":{
backgroundColor: theme.palette.background.default + "!important",
color:
theme.palette.type === "dark"
? "#67aeff !important"
: "#0366d6 !important"
},
".for-container .for-markdown-preview table th": {
backgroundColor: theme.palette.background.default + "!important"
}
}
}));

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../actions";
import OpenIcon from "@material-ui/icons/OpenInNew";
import Pagination from "@material-ui/lab/Pagination";
@ -12,14 +12,14 @@ import {
CardHeader,
Typography,
Grid,
IconButton,
IconButton
} from "@material-ui/core";
import API from "../../middleware/Api";
import TypeIcon from "../FileManager/TypeIcon";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import {useHistory} from "react-router-dom";
import { useHistory } from "react-router-dom";
import { makeStyles } from "@material-ui/core/styles";
import { useLocation } from "react-router";
import TimeAgo from "timeago-react";
@ -126,13 +126,13 @@ export default function SearchResult() {
}, []);
useEffect(() => {
const keywords = query.get("keywords");
if (keywords) {
search(keywords, page, orderBy);
} else {
ToggleSnackbar("top", "right", "请输入搜索关键词", "warning");
}
}, [location]);
const keywords = query.get("keywords");
if (keywords) {
search(keywords, page, orderBy);
} else {
ToggleSnackbar("top", "right", "请输入搜索关键词", "warning");
}
}, [location]);
const handlePageChange = (event, value) => {
setPage(value);
@ -219,10 +219,7 @@ export default function SearchResult() {
<Tooltip placement="top" title="打开">
<IconButton
onClick={() =>
history.push(
"/s/" +
value.key
)
history.push("/s/" + value.key)
}
>
<OpenIcon />
@ -248,10 +245,15 @@ export default function SearchResult() {
</Typography>
</Tooltip>
}
subheader={<span>分享于{" "}<TimeAgo
datetime={value.create_date}
locale="zh_CN"
/></span>}
subheader={
<span>
分享于{" "}
<TimeAgo
datetime={value.create_date}
locale="zh_CN"
/>
</span>
}
/>
</Card>
</Grid>

View File

@ -41,12 +41,12 @@ export default function SharePreload() {
} else {
SetSubTitle();
}
}, [share,SetSubTitle,ToggleSnackbar]);
}, [share, SetSubTitle, ToggleSnackbar]);
useEffect(() => {
return () => {
SetSubTitle();
}
};
// eslint-disable-next-line
}, []);
@ -69,12 +69,12 @@ export default function SharePreload() {
ToggleSnackbar("top", "right", error.message, "error");
}
});
}, [id, password,ToggleSnackbar]);
}, [id, password, ToggleSnackbar]);
return (
<Suspense fallback={<PageLoading />}>
{share === undefined && <PageLoading />}
{share === null && <Notice msg={"分享不存在或已过期"}/>}
{share === null && <Notice msg={"分享不存在或已过期"} />}
{share && share.locked && (
<LockedFile
loading={loading}
@ -82,8 +82,12 @@ export default function SharePreload() {
share={share}
/>
)}
{share&&!share.locked&&!share.is_dir&&<SharedFile share={share}/>}
{share&&!share.locked&&share.is_dir&&<SharedFolder share={share}/>}
{share && !share.locked && !share.is_dir && (
<SharedFile share={share} />
)}
{share && !share.locked && share.is_dir && (
<SharedFolder share={share} />
)}
</Suspense>
);
}

View File

@ -2,7 +2,8 @@ import React, { Component } from "react";
import { connect } from "react-redux";
import { sizeToString, vhCheck } from "../../utils";
import {
openMusicDialog, openResaveDialog,
openMusicDialog,
openResaveDialog,
setSelectedTarget,
showImgPreivew,
toggleSnackbar
@ -17,8 +18,7 @@ import { withRouter } from "react-router-dom";
import Creator from "./Creator";
import pathHelper from "../../utils/page";
vhCheck()
vhCheck();
const styles = theme => ({
layout: {
width: "auto",
@ -113,9 +113,9 @@ const mapDispatchToProps = dispatch => {
showImgPreivew: first => {
dispatch(showImgPreivew(first));
},
openResave: (key) => {
openResave: key => {
dispatch(openResaveDialog(key));
},
}
};
};
@ -156,8 +156,8 @@ class SharedFileCompoment extends Component {
case "msDoc":
this.props.history.push(
this.props.share.key +
"/doc?name=" +
encodeURIComponent(this.props.share.source.name)
"/doc?name=" +
encodeURIComponent(this.props.share.source.name)
);
return;
case "audio":
@ -177,20 +177,26 @@ class SharedFileCompoment extends Component {
);
return;
case "edit":
this.props.history.push(this.props.share.key +
"/text?name=" +
encodeURIComponent(this.props.share.source.name));
return
this.props.history.push(
this.props.share.key +
"/text?name=" +
encodeURIComponent(this.props.share.source.name)
);
return;
case "pdf":
this.props.history.push(this.props.share.key +
"/pdf?name=" +
encodeURIComponent(this.props.share.source.name));
return
this.props.history.push(
this.props.share.key +
"/pdf?name=" +
encodeURIComponent(this.props.share.source.name)
);
return;
case "code":
this.props.history.push(this.props.share.key +
"/code?name=" +
encodeURIComponent(this.props.share.source.name));
return
this.props.history.push(
this.props.share.key +
"/code?name=" +
encodeURIComponent(this.props.share.source.name)
);
return;
default:
this.props.toggleSnackbar(
"top",
@ -237,7 +243,7 @@ class SharedFileCompoment extends Component {
<Modals />
<ImgPreview />
<div className={classes.box}>
<Creator share={this.props.share}/>
<Creator share={this.props.share} />
<Divider />
<div className={classes.boxContent}>
<TypeIcon
@ -258,18 +264,17 @@ class SharedFileCompoment extends Component {
<div className={classes.boxFooter}>
<div className={classes.actionLeft}>
{this.props.share.preview && (
<Button
variant="outlined"
color="secondary"
onClick={this.scoreHandle(this.preview)}
disabled={this.state.loading}
>
预览
</Button>
)}
<Button
variant="outlined"
color="secondary"
onClick={this.scoreHandle(this.preview)}
disabled={this.state.loading}
>
预览
</Button>
)}
</div>
<div className={classes.actions}>
<Button
variant="contained"
color="secondary"

View File

@ -71,8 +71,8 @@ const styles = theme => ({
left: 0,
top: 0
},
fileName:{
wordBreak: "break-all",
fileName: {
wordBreak: "break-all"
}
});
class FileList extends Component {

View File

@ -1 +1 @@
// import React from 'react'
// import React from 'react'

View File

@ -40,10 +40,10 @@ const useStyles = makeStyles(theme => ({
}
},
formControl: {
margin: "8px 16px 8px 16px",
margin: "8px 16px 8px 16px"
},
toobar:{
textAlign:"right",
toobar: {
textAlign: "right"
}
}));
@ -145,9 +145,11 @@ export default function CodeViewer() {
onChange={e => setSuffix(e.target.value)}
>
{Array.from(
new Set(Object.keys(codePreviewSuffix).map(k=>{
return codePreviewSuffix[k]
}))
new Set(
Object.keys(codePreviewSuffix).map(k => {
return codePreviewSuffix[k];
})
)
).map((extension, index) => (
<MenuItem value={extension} key={index}>
{extension}

Some files were not shown because too many files have changed in this diff Show More