From af960070259231d87da7fa1d4c9089de9114fb4c Mon Sep 17 00:00:00 2001 From: JustSong Date: Sat, 22 Apr 2023 21:41:16 +0800 Subject: [PATCH] Remove useless page --- controller/{file.go => channel.go} | 12 +- model/{file.go => channel.go} | 14 +- model/main.go | 2 +- router/api-router.go | 4 +- web/src/App.js | 6 +- web/src/components/ChannelsTable.js | 300 ++++++++++++++++++++++ web/src/components/FilesTable.js | 303 ----------------------- web/src/components/Header.js | 6 +- web/src/pages/{File => Channel}/index.js | 6 +- 9 files changed, 325 insertions(+), 328 deletions(-) rename controller/{file.go => channel.go} (92%) rename model/{file.go => channel.go} (76%) create mode 100644 web/src/components/ChannelsTable.js delete mode 100644 web/src/components/FilesTable.js rename web/src/pages/{File => Channel}/index.js (56%) diff --git a/controller/file.go b/controller/channel.go similarity index 92% rename from controller/file.go rename to controller/channel.go index 64523622..1e85e970 100644 --- a/controller/file.go +++ b/controller/channel.go @@ -12,12 +12,12 @@ import ( "time" ) -func GetAllFiles(c *gin.Context) { +func GetAllChannels(c *gin.Context) { p, _ := strconv.Atoi(c.Query("p")) if p < 0 { p = 0 } - files, err := model.GetAllFiles(p*common.ItemsPerPage, common.ItemsPerPage) + files, err := model.GetAllChannels(p*common.ItemsPerPage, common.ItemsPerPage) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, @@ -33,9 +33,9 @@ func GetAllFiles(c *gin.Context) { return } -func SearchFiles(c *gin.Context) { +func SearchChannels(c *gin.Context) { keyword := c.Query("keyword") - files, err := model.SearchFiles(keyword) + files, err := model.SearchChannels(keyword) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, @@ -85,7 +85,7 @@ func UploadFile(c *gin.Context) { return } // save to database - fileObj := &model.File{ + fileObj := &model.Channel{ Description: description, Uploader: uploader, UploadTime: currentTime, @@ -116,7 +116,7 @@ func DeleteFile(c *gin.Context) { return } - fileObj := &model.File{ + fileObj := &model.Channel{ Id: fileId, } model.DB.Where("id = ?", fileId).First(&fileObj) diff --git a/model/file.go b/model/channel.go similarity index 76% rename from model/file.go rename to model/channel.go index 390b2e2e..eaaba0e8 100644 --- a/model/file.go +++ b/model/channel.go @@ -8,7 +8,7 @@ import ( "path" ) -type File struct { +type Channel struct { Id int `json:"id"` Filename string `json:"filename" gorm:"index"` Description string `json:"description"` @@ -19,27 +19,27 @@ type File struct { DownloadCounter int `json:"download_counter"` } -func GetAllFiles(startIdx int, num int) ([]*File, error) { - var files []*File +func GetAllChannels(startIdx int, num int) ([]*Channel, error) { + var files []*Channel var err error err = DB.Order("id desc").Limit(num).Offset(startIdx).Find(&files).Error return files, err } -func SearchFiles(keyword string) (files []*File, err error) { +func SearchChannels(keyword string) (files []*Channel, err error) { 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 return files, err } -func (file *File) Insert() error { +func (file *Channel) Insert() error { var err error err = DB.Create(file).Error return err } // 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 err = DB.Delete(file).Error err = os.Remove(path.Join(common.UploadPath, file.Link)) @@ -47,5 +47,5 @@ func (file *File) Delete() error { } 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")) } diff --git a/model/main.go b/model/main.go index d8f7ac3a..563835cb 100644 --- a/model/main.go +++ b/model/main.go @@ -52,7 +52,7 @@ func InitDB() (err error) { } if err == nil { DB = db - err := db.AutoMigrate(&File{}) + err := db.AutoMigrate(&Channel{}) if err != nil { return err } diff --git a/router/api-router.go b/router/api-router.go index 57775431..cc319075 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -57,8 +57,8 @@ func SetApiRouter(router *gin.Engine) { fileRoute := apiRouter.Group("/file") fileRoute.Use(middleware.AdminAuth()) { - fileRoute.GET("/", controller.GetAllFiles) - fileRoute.GET("/search", controller.SearchFiles) + fileRoute.GET("/", controller.GetAllChannels) + fileRoute.GET("/search", controller.SearchChannels) fileRoute.POST("/", middleware.UploadRateLimit(), controller.UploadFile) fileRoute.DELETE("/:id", controller.DeleteFile) } diff --git a/web/src/App.js b/web/src/App.js index 72cf98d0..a1a8a898 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -14,7 +14,7 @@ import PasswordResetForm from './components/PasswordResetForm'; import GitHubOAuth from './components/GitHubOAuth'; import PasswordResetConfirm from './components/PasswordResetConfirm'; import { UserContext } from './context/User'; -import File from './pages/File'; +import Channel from './pages/Channel'; const Home = lazy(() => import('./pages/Home')); const About = lazy(() => import('./pages/About')); @@ -65,10 +65,10 @@ function App() { } /> - + } /> diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js new file mode 100644 index 00000000..744e44ba --- /dev/null +++ b/web/src/components/ChannelsTable.js @@ -0,0 +1,300 @@ +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 ; + case 10: + return ; + case 100: + return ; + default: + return ; + } +} + +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 ; + case 2: + return ( + + ); + default: + return ( + + ); + } + }; + + 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 ( + <> +
+ + + + + + + { + sortUser('username'); + }} + > + 用户名 + + { + sortUser('display_name'); + }} + > + 显示名称 + + { + sortUser('email'); + }} + > + 邮箱地址 + + { + sortUser('role'); + }} + > + 用户角色 + + { + sortUser('status'); + }} + > + 状态 + + 操作 + + + + + {users + .slice( + (activePage - 1) * ITEMS_PER_PAGE, + activePage * ITEMS_PER_PAGE + ) + .map((user, idx) => { + if (user.deleted) return <>; + return ( + + {user.username} + {user.display_name} + {user.email ? user.email : '无'} + {renderRole(user.role)} + {renderStatus(user.status)} + +
+ + + + + +
+
+
+ ); + })} +
+ + + + + + + + + +
+ + ); +}; + +export default ChannelsTable; diff --git a/web/src/components/FilesTable.js b/web/src/components/FilesTable.js deleted file mode 100644 index 79b290bb..00000000 --- a/web/src/components/FilesTable.js +++ /dev/null @@ -1,303 +0,0 @@ -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 ( - <> - -
- - 拖拽上传或点击上传 - -
-
- {uploading ? ( - - ) : ( - <> - )} -
- - - - - - - { - sortFile('filename'); - }} - > - 文件名 - - { - sortFile('uploader_id'); - }} - > - 上传者 - - { - sortFile('email'); - }} - > - 上传时间 - - 操作 - - - - - {files - .slice( - (activePage - 1) * ITEMS_PER_PAGE, - activePage * ITEMS_PER_PAGE - ) - .map((file, idx) => { - if (file.deleted) return <>; - return ( - - - - {file.filename} - - - {file.uploader}} - /> - {file.upload_time} - -
- - - -
-
-
- ); - })} -
- - - - - - - - -
- - ); -}; - -export default FilesTable; diff --git a/web/src/components/Header.js b/web/src/components/Header.js index 0b9707bd..78e8f2b7 100644 --- a/web/src/components/Header.js +++ b/web/src/components/Header.js @@ -14,9 +14,9 @@ const headerButtons = [ icon: 'home', }, { - name: '文件', - to: '/file', - icon: 'file', + name: '渠道', + to: '/channel', + icon: 'sitemap', admin: true, }, { diff --git a/web/src/pages/File/index.js b/web/src/pages/Channel/index.js similarity index 56% rename from web/src/pages/File/index.js rename to web/src/pages/Channel/index.js index d61f6bab..57b64893 100644 --- a/web/src/pages/File/index.js +++ b/web/src/pages/Channel/index.js @@ -1,12 +1,12 @@ import React from 'react'; import { Header, Segment } from 'semantic-ui-react'; -import FilesTable from '../../components/FilesTable'; +import ChannelsTable from '../../components/ChannelsTable'; const File = () => ( <> -
管理文件
- +
管理渠道
+
);