diff --git a/controller/user.go b/controller/user.go index b9b0393f..4a5db14e 100644 --- a/controller/user.go +++ b/controller/user.go @@ -11,9 +11,10 @@ import ( "strings" "sync" + "one-api/constant" + "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" - "one-api/constant" ) type LoginRequest struct { @@ -242,10 +243,14 @@ func Register(c *gin.Context) { func GetAllUsers(c *gin.Context) { p, _ := strconv.Atoi(c.Query("p")) - if p < 0 { - p = 0 + pageSize, _ := strconv.Atoi(c.Query("page_size")) + if p < 1 { + p = 1 } - users, err := model.GetAllUsers(p*common.ItemsPerPage, common.ItemsPerPage) + if pageSize < 0 { + pageSize = common.ItemsPerPage + } + users, total, err := model.GetAllUsers((p-1)*pageSize, pageSize) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, @@ -256,7 +261,12 @@ func GetAllUsers(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", - "data": users, + "data": gin.H{ + "items": users, + "total": total, + "page": p, + "page_size": pageSize, + }, }) return } diff --git a/model/user.go b/model/user.go index 0171f3b6..08392c04 100644 --- a/model/user.go +++ b/model/user.go @@ -81,9 +81,38 @@ func GetMaxUserId() int { return user.Id } -func GetAllUsers(startIdx int, num int) (users []*User, err error) { - err = DB.Unscoped().Order("id desc").Limit(num).Offset(startIdx).Omit("password").Find(&users).Error - return users, err +func GetAllUsers(startIdx int, num int) (users []*User, total int64, err error) { + // Start transaction + tx := DB.Begin() + if tx.Error != nil { + return nil, 0, tx.Error + } + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // Get total count within transaction + err = tx.Unscoped().Model(&User{}).Count(&total).Error + if err != nil { + tx.Rollback() + return nil, 0, err + } + + // Get paginated users within same transaction + err = tx.Unscoped().Order("id desc").Limit(num).Offset(startIdx).Omit("password").Find(&users).Error + if err != nil { + tx.Rollback() + return nil, 0, err + } + + // Commit transaction + if err = tx.Commit().Error; err != nil { + return nil, 0, err + } + + return users, total, nil } func SearchUsers(keyword string, group string) ([]*User, error) { diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js index 438c4df4..b467089d 100644 --- a/web/src/components/LogsTable.js +++ b/web/src/components/LogsTable.js @@ -825,6 +825,12 @@ const LogsTable = () => { dataSource={logs} rowKey="key" pagination={{ + formatPageText: (page) => + t('第 {{start}} - {{end}} 条,共 {{total}} 条', { + start: page.currentStart, + end: page.currentEnd, + total: users.length + }), currentPage: activePage, pageSize: pageSize, total: logCount, diff --git a/web/src/components/UsersTable.js b/web/src/components/UsersTable.js index d53db796..24d6d873 100644 --- a/web/src/components/UsersTable.js +++ b/web/src/components/UsersTable.js @@ -231,6 +231,7 @@ const UsersTable = () => { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [activePage, setActivePage] = useState(1); + const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE); const [searchKeyword, setSearchKeyword] = useState(''); const [searching, setSearching] = useState(false); const [searchGroup, setSearchGroup] = useState(''); @@ -242,14 +243,6 @@ const UsersTable = () => { id: undefined, }); - const setCount = (data) => { - if (data.length >= activePage * ITEMS_PER_PAGE) { - setUserCount(data.length + 1); - } else { - setUserCount(data.length); - } - }; - const removeRecord = (key) => { let newDataSource = [...users]; if (key != null) { @@ -263,37 +256,30 @@ const UsersTable = () => { } }; - const loadUsers = async (startIdx) => { - const res = await API.get(`/api/user/?p=${startIdx}`); + const setUserFormat = (users) => { + for (let i = 0; i < users.length; i++) { + users[i].key = users[i].id; + } + setUsers(users); + } + + const loadUsers = async (startIdx, pageSize) => { + const res = await API.get(`/api/user/?p=${startIdx}&page_size=${pageSize}`); const { success, message, data } = res.data; if (success) { - if (startIdx === 0) { - setUsers(data); - setCount(data); - } else { - let newUsers = users; - newUsers.push(...data); - setUsers(newUsers); - setCount(newUsers); - } + const newPageData = data.items; + setActivePage(data.page); + setUserCount(data.total); + setUserFormat(newPageData); } 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) + loadUsers(0, pageSize) .then() .catch((reason) => { showError(reason); @@ -344,8 +330,7 @@ const UsersTable = () => { const searchUsers = async (searchKeyword, searchGroup) => { if (searchKeyword === '' && searchGroup === '') { // if keyword is blank, load files instead. - await loadUsers(0); - setActivePage(1); + await loadUsers(activePage, pageSize); return; } setSearching(true); @@ -380,10 +365,7 @@ const UsersTable = () => { const handlePageChange = (page) => { setActivePage(page); - if (page === Math.ceil(users.length / ITEMS_PER_PAGE) + 1) { - // In this case we have to load more data and then append them. - loadUsers(page - 1).then((r) => {}); - } + loadUsers(page, pageSize).then((r) => {}); }; const pageData = users.slice( @@ -403,8 +385,9 @@ const UsersTable = () => { }; const refresh = async () => { + setActivePage(1) if (searchKeyword === '') { - await loadUsers(activePage - 1); + await loadUsers(activePage, pageSize); } else { await searchUsers(searchKeyword, searchGroup); } @@ -429,6 +412,17 @@ const UsersTable = () => { } }; + const handlePageSizeChange = async (size) => { + localStorage.setItem('page-size', size + ''); + setPageSize(size); + setActivePage(1); + loadUsers(activePage, size) + .then() + .catch((reason) => { + showError(reason); + }); + }; + return ( <> { t('第 {{start}} - {{end}} 条,共 {{total}} 条', { @@ -501,9 +495,13 @@ const UsersTable = () => { total: users.length }), currentPage: activePage, - pageSize: ITEMS_PER_PAGE, + pageSize: pageSize, total: userCount, - pageSizeOpts: [10, 20, 50, 100], + pageSizeOpts: [2, 20, 50, 100], + showSizeChanger: true, + onPageSizeChange: (size) => { + handlePageSizeChange(size); + }, onPageChange: handlePageChange, }} loading={loading} diff --git a/web/src/pages/User/AddUser.js b/web/src/pages/User/AddUser.js index b56c94d9..13480206 100644 --- a/web/src/pages/User/AddUser.js +++ b/web/src/pages/User/AddUser.js @@ -19,7 +19,11 @@ const AddUser = (props) => { const submit = async () => { setLoading(true); - if (inputs.username === '' || inputs.password === '') return; + if (inputs.username === '' || inputs.password === '') { + setLoading(false); + showError('用户名和密码不能为空!'); + return; + } const res = await API.post(`/api/user/`, inputs); const { success, message } = res.data; if (success) {