Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
074df0f1
authored
Apr 22, 2023
by
JustSong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove useless page
parent
483bd64d
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
325 additions
and
328 deletions
+325
-328
controller/channel.go
+6
-6
model/channel.go
+7
-7
model/main.go
+1
-1
router/api-router.go
+2
-2
web/src/App.js
+3
-3
web/src/components/ChannelsTable.js
+300
-0
web/src/components/FilesTable.js
+0
-303
web/src/components/Header.js
+3
-3
web/src/pages/Channel/index.js
+3
-3
No files found.
controller/
file
.go
→
controller/
channel
.go
View file @
074df0f1
...
@@ -12,12 +12,12 @@ import (
...
@@ -12,12 +12,12 @@ import (
"time"
"time"
)
)
func
GetAll
File
s
(
c
*
gin
.
Context
)
{
func
GetAll
Channel
s
(
c
*
gin
.
Context
)
{
p
,
_
:=
strconv
.
Atoi
(
c
.
Query
(
"p"
))
p
,
_
:=
strconv
.
Atoi
(
c
.
Query
(
"p"
))
if
p
<
0
{
if
p
<
0
{
p
=
0
p
=
0
}
}
files
,
err
:=
model
.
GetAll
File
s
(
p
*
common
.
ItemsPerPage
,
common
.
ItemsPerPage
)
files
,
err
:=
model
.
GetAll
Channel
s
(
p
*
common
.
ItemsPerPage
,
common
.
ItemsPerPage
)
if
err
!=
nil
{
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"success"
:
false
,
...
@@ -33,9 +33,9 @@ func GetAllFiles(c *gin.Context) {
...
@@ -33,9 +33,9 @@ func GetAllFiles(c *gin.Context) {
return
return
}
}
func
Search
File
s
(
c
*
gin
.
Context
)
{
func
Search
Channel
s
(
c
*
gin
.
Context
)
{
keyword
:=
c
.
Query
(
"keyword"
)
keyword
:=
c
.
Query
(
"keyword"
)
files
,
err
:=
model
.
Search
File
s
(
keyword
)
files
,
err
:=
model
.
Search
Channel
s
(
keyword
)
if
err
!=
nil
{
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"success"
:
false
,
...
@@ -85,7 +85,7 @@ func UploadFile(c *gin.Context) {
...
@@ -85,7 +85,7 @@ func UploadFile(c *gin.Context) {
return
return
}
}
// save to database
// save to database
fileObj
:=
&
model
.
File
{
fileObj
:=
&
model
.
Channel
{
Description
:
description
,
Description
:
description
,
Uploader
:
uploader
,
Uploader
:
uploader
,
UploadTime
:
currentTime
,
UploadTime
:
currentTime
,
...
@@ -116,7 +116,7 @@ func DeleteFile(c *gin.Context) {
...
@@ -116,7 +116,7 @@ func DeleteFile(c *gin.Context) {
return
return
}
}
fileObj
:=
&
model
.
File
{
fileObj
:=
&
model
.
Channel
{
Id
:
fileId
,
Id
:
fileId
,
}
}
model
.
DB
.
Where
(
"id = ?"
,
fileId
)
.
First
(
&
fileObj
)
model
.
DB
.
Where
(
"id = ?"
,
fileId
)
.
First
(
&
fileObj
)
...
...
model/
file
.go
→
model/
channel
.go
View file @
074df0f1
...
@@ -8,7 +8,7 @@ import (
...
@@ -8,7 +8,7 @@ import (
"path"
"path"
)
)
type
File
struct
{
type
Channel
struct
{
Id
int
`json:"id"`
Id
int
`json:"id"`
Filename
string
`json:"filename" gorm:"index"`
Filename
string
`json:"filename" gorm:"index"`
Description
string
`json:"description"`
Description
string
`json:"description"`
...
@@ -19,27 +19,27 @@ type File struct {
...
@@ -19,27 +19,27 @@ type File struct {
DownloadCounter
int
`json:"download_counter"`
DownloadCounter
int
`json:"download_counter"`
}
}
func
GetAll
Files
(
startIdx
int
,
num
int
)
([]
*
File
,
error
)
{
func
GetAll
Channels
(
startIdx
int
,
num
int
)
([]
*
Channel
,
error
)
{
var
files
[]
*
File
var
files
[]
*
Channel
var
err
error
var
err
error
err
=
DB
.
Order
(
"id desc"
)
.
Limit
(
num
)
.
Offset
(
startIdx
)
.
Find
(
&
files
)
.
Error
err
=
DB
.
Order
(
"id desc"
)
.
Limit
(
num
)
.
Offset
(
startIdx
)
.
Find
(
&
files
)
.
Error
return
files
,
err
return
files
,
err
}
}
func
Search
Files
(
keyword
string
)
(
files
[]
*
File
,
err
error
)
{
func
Search
Channels
(
keyword
string
)
(
files
[]
*
Channel
,
err
error
)
{
err
=
DB
.
Select
([]
string
{
"id"
,
"filename"
,
"description"
,
"uploader"
,
"uploader_id"
,
"link"
,
"upload_time"
,
"download_counter"
})
.
Where
(
err
=
DB
.
Select
([]
string
{
"id"
,
"filename"
,
"description"
,
"uploader"
,
"uploader_id"
,
"link"
,
"upload_time"
,
"download_counter"
})
.
Where
(
"filename LIKE ? or uploader LIKE ? or uploader_id = ?"
,
keyword
+
"%"
,
keyword
+
"%"
,
keyword
)
.
Find
(
&
files
)
.
Error
"filename LIKE ? or uploader LIKE ? or uploader_id = ?"
,
keyword
+
"%"
,
keyword
+
"%"
,
keyword
)
.
Find
(
&
files
)
.
Error
return
files
,
err
return
files
,
err
}
}
func
(
file
*
File
)
Insert
()
error
{
func
(
file
*
Channel
)
Insert
()
error
{
var
err
error
var
err
error
err
=
DB
.
Create
(
file
)
.
Error
err
=
DB
.
Create
(
file
)
.
Error
return
err
return
err
}
}
// Delete Make sure link is valid! Because we will use os.Remove to delete it!
// Delete Make sure link is valid! Because we will use os.Remove to delete it!
func
(
file
*
File
)
Delete
()
error
{
func
(
file
*
Channel
)
Delete
()
error
{
var
err
error
var
err
error
err
=
DB
.
Delete
(
file
)
.
Error
err
=
DB
.
Delete
(
file
)
.
Error
err
=
os
.
Remove
(
path
.
Join
(
common
.
UploadPath
,
file
.
Link
))
err
=
os
.
Remove
(
path
.
Join
(
common
.
UploadPath
,
file
.
Link
))
...
@@ -47,5 +47,5 @@ func (file *File) Delete() error {
...
@@ -47,5 +47,5 @@ func (file *File) Delete() error {
}
}
func
UpdateDownloadCounter
(
link
string
)
{
func
UpdateDownloadCounter
(
link
string
)
{
DB
.
Model
(
&
File
{})
.
Where
(
"link = ?"
,
link
)
.
UpdateColumn
(
"download_counter"
,
gorm
.
Expr
(
"download_counter + 1"
))
DB
.
Model
(
&
Channel
{})
.
Where
(
"link = ?"
,
link
)
.
UpdateColumn
(
"download_counter"
,
gorm
.
Expr
(
"download_counter + 1"
))
}
}
model/main.go
View file @
074df0f1
...
@@ -52,7 +52,7 @@ func InitDB() (err error) {
...
@@ -52,7 +52,7 @@ func InitDB() (err error) {
}
}
if
err
==
nil
{
if
err
==
nil
{
DB
=
db
DB
=
db
err
:=
db
.
AutoMigrate
(
&
File
{})
err
:=
db
.
AutoMigrate
(
&
Channel
{})
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
...
router/api-router.go
View file @
074df0f1
...
@@ -57,8 +57,8 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -57,8 +57,8 @@ func SetApiRouter(router *gin.Engine) {
fileRoute
:=
apiRouter
.
Group
(
"/file"
)
fileRoute
:=
apiRouter
.
Group
(
"/file"
)
fileRoute
.
Use
(
middleware
.
AdminAuth
())
fileRoute
.
Use
(
middleware
.
AdminAuth
())
{
{
fileRoute
.
GET
(
"/"
,
controller
.
GetAll
File
s
)
fileRoute
.
GET
(
"/"
,
controller
.
GetAll
Channel
s
)
fileRoute
.
GET
(
"/search"
,
controller
.
Search
File
s
)
fileRoute
.
GET
(
"/search"
,
controller
.
Search
Channel
s
)
fileRoute
.
POST
(
"/"
,
middleware
.
UploadRateLimit
(),
controller
.
UploadFile
)
fileRoute
.
POST
(
"/"
,
middleware
.
UploadRateLimit
(),
controller
.
UploadFile
)
fileRoute
.
DELETE
(
"/:id"
,
controller
.
DeleteFile
)
fileRoute
.
DELETE
(
"/:id"
,
controller
.
DeleteFile
)
}
}
...
...
web/src/App.js
View file @
074df0f1
...
@@ -14,7 +14,7 @@ import PasswordResetForm from './components/PasswordResetForm';
...
@@ -14,7 +14,7 @@ import PasswordResetForm from './components/PasswordResetForm';
import
GitHubOAuth
from
'./components/GitHubOAuth'
;
import
GitHubOAuth
from
'./components/GitHubOAuth'
;
import
PasswordResetConfirm
from
'./components/PasswordResetConfirm'
;
import
PasswordResetConfirm
from
'./components/PasswordResetConfirm'
;
import
{
UserContext
}
from
'./context/User'
;
import
{
UserContext
}
from
'./context/User'
;
import
File
from
'./pages/File
'
;
import
Channel
from
'./pages/Channel
'
;
const
Home
=
lazy
(()
=>
import
(
'./pages/Home'
));
const
Home
=
lazy
(()
=>
import
(
'./pages/Home'
));
const
About
=
lazy
(()
=>
import
(
'./pages/About'
));
const
About
=
lazy
(()
=>
import
(
'./pages/About'
));
...
@@ -65,10 +65,10 @@ function App() {
...
@@ -65,10 +65,10 @@ function App() {
}
}
/
>
/
>
<
Route
<
Route
path
=
'/
file
'
path
=
'/
channel
'
element
=
{
element
=
{
<
PrivateRoute
>
<
PrivateRoute
>
<
File
/>
<
Channel
/>
<
/PrivateRoute
>
<
/PrivateRoute
>
}
}
/
>
/
>
...
...
web/src/components/ChannelsTable.js
0 → 100644
View file @
074df0f1
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Button
,
Form
,
Label
,
Pagination
,
Table
}
from
'semantic-ui-react'
;
import
{
Link
}
from
'react-router-dom'
;
import
{
API
,
showError
,
showSuccess
}
from
'../helpers'
;
import
{
ITEMS_PER_PAGE
}
from
'../constants'
;
function
renderRole
(
role
)
{
switch
(
role
)
{
case
1
:
return
<
Label
>
普通用户
<
/Label>
;
case
10
:
return
<
Label
color
=
'yellow'
>
管理员
<
/Label>
;
case
100
:
return
<
Label
color
=
'orange'
>
超级管理员
<
/Label>
;
default
:
return
<
Label
color
=
'red'
>
未知身份
<
/Label>
;
}
}
const
ChannelsTable
=
()
=>
{
const
[
users
,
setUsers
]
=
useState
([]);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
activePage
,
setActivePage
]
=
useState
(
1
);
const
[
searchKeyword
,
setSearchKeyword
]
=
useState
(
''
);
const
[
searching
,
setSearching
]
=
useState
(
false
);
const
loadUsers
=
async
(
startIdx
)
=>
{
const
res
=
await
API
.
get
(
`/api/user/?p=
${
startIdx
}
`
);
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
if
(
startIdx
===
0
)
{
setUsers
(
data
);
}
else
{
let
newUsers
=
users
;
newUsers
.
push
(...
data
);
setUsers
(
newUsers
);
}
}
else
{
showError
(
message
);
}
setLoading
(
false
);
};
const
onPaginationChange
=
(
e
,
{
activePage
})
=>
{
(
async
()
=>
{
if
(
activePage
===
Math
.
ceil
(
users
.
length
/
ITEMS_PER_PAGE
)
+
1
)
{
// In this case we have to load more data and then append them.
await
loadUsers
(
activePage
-
1
);
}
setActivePage
(
activePage
);
})();
};
useEffect
(()
=>
{
loadUsers
(
0
)
.
then
()
.
catch
((
reason
)
=>
{
showError
(
reason
);
});
},
[]);
const
manageUser
=
(
username
,
action
,
idx
)
=>
{
(
async
()
=>
{
const
res
=
await
API
.
post
(
'/api/user/manage'
,
{
username
,
action
,
});
const
{
success
,
message
}
=
res
.
data
;
if
(
success
)
{
showSuccess
(
'操作成功完成!'
);
let
user
=
res
.
data
.
data
;
let
newUsers
=
[...
users
];
let
realIdx
=
(
activePage
-
1
)
*
ITEMS_PER_PAGE
+
idx
;
if
(
action
===
'delete'
)
{
newUsers
[
realIdx
].
deleted
=
true
;
}
else
{
newUsers
[
realIdx
].
status
=
user
.
status
;
newUsers
[
realIdx
].
role
=
user
.
role
;
}
setUsers
(
newUsers
);
}
else
{
showError
(
message
);
}
})();
};
const
renderStatus
=
(
status
)
=>
{
switch
(
status
)
{
case
1
:
return
<
Label
basic
>
已激活
<
/Label>
;
case
2
:
return
(
<
Label
basic
color
=
'red'
>
已封禁
<
/Label
>
);
default
:
return
(
<
Label
basic
color
=
'grey'
>
未知状态
<
/Label
>
);
}
};
const
searchUsers
=
async
()
=>
{
if
(
searchKeyword
===
''
)
{
// if keyword is blank, load files instead.
await
loadUsers
(
0
);
setActivePage
(
1
);
return
;
}
setSearching
(
true
);
const
res
=
await
API
.
get
(
`/api/user/search?keyword=
${
searchKeyword
}
`
);
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
setUsers
(
data
);
setActivePage
(
1
);
}
else
{
showError
(
message
);
}
setSearching
(
false
);
};
const
handleKeywordChange
=
async
(
e
,
{
value
})
=>
{
setSearchKeyword
(
value
.
trim
());
};
const
sortUser
=
(
key
)
=>
{
if
(
users
.
length
===
0
)
return
;
setLoading
(
true
);
let
sortedUsers
=
[...
users
];
sortedUsers
.
sort
((
a
,
b
)
=>
{
return
(
''
+
a
[
key
]).
localeCompare
(
b
[
key
]);
});
if
(
sortedUsers
[
0
].
id
===
users
[
0
].
id
)
{
sortedUsers
.
reverse
();
}
setUsers
(
sortedUsers
);
setLoading
(
false
);
};
return
(
<>
<
Form
onSubmit
=
{
searchUsers
}
>
<
Form
.
Input
icon
=
'search'
fluid
iconPosition
=
'left'
placeholder
=
'搜索用户的 ID,用户名,显示名称,以及邮箱地址 ...'
value
=
{
searchKeyword
}
loading
=
{
searching
}
onChange
=
{
handleKeywordChange
}
/
>
<
/Form
>
<
Table
basic
>
<
Table
.
Header
>
<
Table
.
Row
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'username'
);
}}
>
用户名
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'display_name'
);
}}
>
显示名称
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'email'
);
}}
>
邮箱地址
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'role'
);
}}
>
用户角色
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'status'
);
}}
>
状态
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
>
操作
<
/Table.HeaderCell
>
<
/Table.Row
>
<
/Table.Header
>
<
Table
.
Body
>
{
users
.
slice
(
(
activePage
-
1
)
*
ITEMS_PER_PAGE
,
activePage
*
ITEMS_PER_PAGE
)
.
map
((
user
,
idx
)
=>
{
if
(
user
.
deleted
)
return
<><
/>
;
return
(
<
Table
.
Row
key
=
{
user
.
id
}
>
<
Table
.
Cell
>
{
user
.
username
}
<
/Table.Cell
>
<
Table
.
Cell
>
{
user
.
display_name
}
<
/Table.Cell
>
<
Table
.
Cell
>
{
user
.
email
?
user
.
email
:
'无'
}
<
/Table.Cell
>
<
Table
.
Cell
>
{
renderRole
(
user
.
role
)}
<
/Table.Cell
>
<
Table
.
Cell
>
{
renderStatus
(
user
.
status
)}
<
/Table.Cell
>
<
Table
.
Cell
>
<
div
>
<
Button
size
=
{
'small'
}
positive
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
'promote'
,
idx
);
}}
>
提升
<
/Button
>
<
Button
size
=
{
'small'
}
color
=
{
'yellow'
}
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
'demote'
,
idx
);
}}
>
降级
<
/Button
>
<
Button
size
=
{
'small'
}
negative
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
'delete'
,
idx
);
}}
>
删除
<
/Button
>
<
Button
size
=
{
'small'
}
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
user
.
status
===
1
?
'disable'
:
'enable'
,
idx
);
}}
>
{
user
.
status
===
1
?
'禁用'
:
'启用'
}
<
/Button
>
<
Button
size
=
{
'small'
}
as
=
{
Link
}
to
=
{
'/user/edit/'
+
user
.
id
}
>
编辑
<
/Button
>
<
/div
>
<
/Table.Cell
>
<
/Table.Row
>
);
})}
<
/Table.Body
>
<
Table
.
Footer
>
<
Table
.
Row
>
<
Table
.
HeaderCell
colSpan
=
'6'
>
<
Button
size
=
'small'
as
=
{
Link
}
to
=
'/user/add'
loading
=
{
loading
}
>
添加新的用户
<
/Button
>
<
Pagination
floated
=
'right'
activePage
=
{
activePage
}
onPageChange
=
{
onPaginationChange
}
size
=
'small'
siblingRange
=
{
1
}
totalPages
=
{
Math
.
ceil
(
users
.
length
/
ITEMS_PER_PAGE
)
+
(
users
.
length
%
ITEMS_PER_PAGE
===
0
?
1
:
0
)
}
/
>
<
/Table.HeaderCell
>
<
/Table.Row
>
<
/Table.Footer
>
<
/Table
>
<
/
>
);
};
export
default
ChannelsTable
;
web/src/components/FilesTable.js
deleted
100644 → 0
View file @
483bd64d
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Button
,
Form
,
Header
,
Icon
,
Pagination
,
Popup
,
Progress
,
Segment
,
Table
,
}
from
'semantic-ui-react'
;
import
{
API
,
copy
,
showError
,
showSuccess
}
from
'../helpers'
;
import
{
useDropzone
}
from
'react-dropzone'
;
import
{
ITEMS_PER_PAGE
}
from
'../constants'
;
const
FilesTable
=
()
=>
{
const
[
files
,
setFiles
]
=
useState
([]);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
activePage
,
setActivePage
]
=
useState
(
1
);
const
[
searchKeyword
,
setSearchKeyword
]
=
useState
(
''
);
const
[
searching
,
setSearching
]
=
useState
(
false
);
const
{
acceptedFiles
,
getRootProps
,
getInputProps
}
=
useDropzone
();
const
[
uploading
,
setUploading
]
=
useState
(
false
);
const
[
uploadProgress
,
setUploadProgress
]
=
useState
(
'0'
);
const
loadFiles
=
async
(
startIdx
)
=>
{
const
res
=
await
API
.
get
(
`/api/file/?p=
${
startIdx
}
`
);
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
if
(
startIdx
===
0
)
{
setFiles
(
data
);
}
else
{
let
newFiles
=
files
;
newFiles
.
push
(...
data
);
setFiles
(
newFiles
);
}
}
else
{
showError
(
message
);
}
setLoading
(
false
);
};
const
onPaginationChange
=
(
e
,
{
activePage
})
=>
{
(
async
()
=>
{
if
(
activePage
===
Math
.
ceil
(
files
.
length
/
ITEMS_PER_PAGE
)
+
1
)
{
// In this case we have to load more data and then append them.
await
loadFiles
(
activePage
-
1
);
}
setActivePage
(
activePage
);
})();
};
useEffect
(()
=>
{
loadFiles
(
0
)
.
then
()
.
catch
((
reason
)
=>
{
showError
(
reason
);
});
},
[]);
const
downloadFile
=
(
link
,
filename
)
=>
{
let
linkElement
=
document
.
createElement
(
'a'
);
linkElement
.
download
=
filename
;
linkElement
.
href
=
'/upload/'
+
link
;
linkElement
.
click
();
};
const
copyLink
=
(
link
)
=>
{
let
url
=
window
.
location
.
origin
+
'/upload/'
+
link
;
copy
(
url
).
then
();
showSuccess
(
'链接已复制到剪贴板'
);
};
const
deleteFile
=
async
(
id
,
idx
)
=>
{
const
res
=
await
API
.
delete
(
`/api/file/
${
id
}
`
);
const
{
success
,
message
}
=
res
.
data
;
if
(
success
)
{
let
newFiles
=
[...
files
];
let
realIdx
=
(
activePage
-
1
)
*
ITEMS_PER_PAGE
+
idx
;
newFiles
[
realIdx
].
deleted
=
true
;
// newFiles.splice(idx, 1);
setFiles
(
newFiles
);
showSuccess
(
'文件已删除!'
);
}
else
{
showError
(
message
);
}
};
const
searchFiles
=
async
()
=>
{
if
(
searchKeyword
===
''
)
{
// if keyword is blank, load files instead.
await
loadFiles
(
0
);
setActivePage
(
1
);
return
;
}
setSearching
(
true
);
const
res
=
await
API
.
get
(
`/api/file/search?keyword=
${
searchKeyword
}
`
);
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
setFiles
(
data
);
setActivePage
(
1
);
}
else
{
showError
(
message
);
}
setSearching
(
false
);
};
const
handleKeywordChange
=
async
(
e
,
{
value
})
=>
{
setSearchKeyword
(
value
.
trim
());
};
const
sortFile
=
(
key
)
=>
{
if
(
files
.
length
===
0
)
return
;
setLoading
(
true
);
let
sortedUsers
=
[...
files
];
sortedUsers
.
sort
((
a
,
b
)
=>
{
return
(
''
+
a
[
key
]).
localeCompare
(
b
[
key
]);
});
if
(
sortedUsers
[
0
].
id
===
files
[
0
].
id
)
{
sortedUsers
.
reverse
();
}
setFiles
(
sortedUsers
);
setLoading
(
false
);
};
const
uploadFiles
=
async
()
=>
{
if
(
acceptedFiles
.
length
===
0
)
return
;
setUploading
(
true
);
let
formData
=
new
FormData
();
for
(
let
i
=
0
;
i
<
acceptedFiles
.
length
;
i
++
)
{
formData
.
append
(
'file'
,
acceptedFiles
[
i
]);
}
const
res
=
await
API
.
post
(
`/api/file`
,
formData
,
{
headers
:
{
'Content-Type'
:
'multipart/form-data'
,
},
onUploadProgress
:
(
e
)
=>
{
let
uploadProgress
=
((
e
.
loaded
/
e
.
total
)
*
100
).
toFixed
(
2
);
setUploadProgress
(
uploadProgress
);
},
});
const
{
success
,
message
}
=
res
.
data
;
if
(
success
)
{
showSuccess
(
`
${
acceptedFiles
.
length
}
个文件上传成功!`
);
}
else
{
showError
(
message
);
}
setUploading
(
false
);
setUploadProgress
(
'0'
);
setSearchKeyword
(
''
);
loadFiles
(
0
).
then
();
setActivePage
(
1
);
};
useEffect
(()
=>
{
uploadFiles
().
then
();
},
[
acceptedFiles
]);
return
(
<>
<
Segment
placeholder
{...
getRootProps
({
className
:
'dropzone'
})}
loading
=
{
uploading
||
loading
}
style
=
{{
cursor
:
'pointer'
}}
>
<
Header
icon
>
<
Icon
name
=
'file outline'
/>
拖拽上传或点击上传
<
input
{...
getInputProps
()}
/
>
<
/Header
>
<
/Segment
>
{
uploading
?
(
<
Progress
percent
=
{
uploadProgress
}
success
progress
=
'percent'
><
/Progress
>
)
:
(
<><
/
>
)}
<
Form
onSubmit
=
{
searchFiles
}
>
<
Form
.
Input
icon
=
'search'
fluid
iconPosition
=
'left'
placeholder
=
'搜索文件的名称,上传者以及描述信息 ...'
value
=
{
searchKeyword
}
loading
=
{
searching
}
onChange
=
{
handleKeywordChange
}
/
>
<
/Form
>
<
Table
basic
>
<
Table
.
Header
>
<
Table
.
Row
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortFile
(
'filename'
);
}}
>
文件名
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortFile
(
'uploader_id'
);
}}
>
上传者
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortFile
(
'email'
);
}}
>
上传时间
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
>
操作
<
/Table.HeaderCell
>
<
/Table.Row
>
<
/Table.Header
>
<
Table
.
Body
>
{
files
.
slice
(
(
activePage
-
1
)
*
ITEMS_PER_PAGE
,
activePage
*
ITEMS_PER_PAGE
)
.
map
((
file
,
idx
)
=>
{
if
(
file
.
deleted
)
return
<><
/>
;
return
(
<
Table
.
Row
key
=
{
file
.
id
}
>
<
Table
.
Cell
>
<
a
href
=
{
'/upload/'
+
file
.
link
}
target
=
'_blank'
>
{
file
.
filename
}
<
/a
>
<
/Table.Cell
>
<
Popup
content
=
{
'上传者 ID:'
+
file
.
uploader_id
}
trigger
=
{
<
Table
.
Cell
>
{
file
.
uploader
}
<
/Table.Cell>
}
/>
<
Table
.
Cell
>
{
file
.
upload_time
}
<
/Table.Cell
>
<
Table
.
Cell
>
<
div
>
<
Button
size
=
{
'small'
}
positive
onClick
=
{()
=>
{
downloadFile
(
file
.
link
,
file
.
filename
);
}}
>
下载
<
/Button
>
<
Button
size
=
{
'small'
}
negative
onClick
=
{()
=>
{
deleteFile
(
file
.
id
,
idx
).
then
();
}}
>
删除
<
/Button
>
<
Button
size
=
{
'small'
}
onClick
=
{()
=>
{
copyLink
(
file
.
link
);
}}
>
复制链接
<
/Button
>
<
/div
>
<
/Table.Cell
>
<
/Table.Row
>
);
})}
<
/Table.Body
>
<
Table
.
Footer
>
<
Table
.
Row
>
<
Table
.
HeaderCell
colSpan
=
'6'
>
<
Pagination
floated
=
'right'
activePage
=
{
activePage
}
onPageChange
=
{
onPaginationChange
}
size
=
'small'
siblingRange
=
{
1
}
totalPages
=
{
Math
.
ceil
(
files
.
length
/
ITEMS_PER_PAGE
)
+
(
files
.
length
%
ITEMS_PER_PAGE
===
0
?
1
:
0
)
}
/
>
<
/Table.HeaderCell
>
<
/Table.Row
>
<
/Table.Footer
>
<
/Table
>
<
/
>
);
};
export
default
FilesTable
;
web/src/components/Header.js
View file @
074df0f1
...
@@ -14,9 +14,9 @@ const headerButtons = [
...
@@ -14,9 +14,9 @@ const headerButtons = [
icon
:
'home'
,
icon
:
'home'
,
},
},
{
{
name
:
'
文件
'
,
name
:
'
渠道
'
,
to
:
'/
file
'
,
to
:
'/
channel
'
,
icon
:
'
file
'
,
icon
:
'
sitemap
'
,
admin
:
true
,
admin
:
true
,
},
},
{
{
...
...
web/src/pages/
File
/index.js
→
web/src/pages/
Channel
/index.js
View file @
074df0f1
import
React
from
'react'
;
import
React
from
'react'
;
import
{
Header
,
Segment
}
from
'semantic-ui-react'
;
import
{
Header
,
Segment
}
from
'semantic-ui-react'
;
import
FilesTable
from
'../../components/File
sTable'
;
import
ChannelsTable
from
'../../components/Channel
sTable'
;
const
File
=
()
=>
(
const
File
=
()
=>
(
<>
<>
<
Segment
>
<
Segment
>
<
Header
as
=
'h3'
>
管理
文件
<
/Header
>
<
Header
as
=
'h3'
>
管理
渠道
<
/Header
>
<
File
sTable
/>
<
Channel
sTable
/>
<
/Segment
>
<
/Segment
>
<
/
>
<
/
>
);
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment