feat: enhance user management and pagination features #518

- Updated GetAllUsers function to return total user count along with paginated results, improving data handling in user retrieval.
- Modified GetAllUsers API endpoint to accept page size as a parameter, allowing for dynamic pagination.
- Enhanced UsersTable component to support customizable page sizes and improved pagination logic.
- Added error handling for empty username and password in AddUser component.
- Updated LogsTable component to display pagination information in a user-friendly format.
This commit is contained in:
CalciumIon
2024-12-31 14:52:20 +08:00
parent a7e5f1e509
commit be0b2f6a64
5 changed files with 95 additions and 48 deletions

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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 (
<>
<AddUser
@@ -492,7 +486,7 @@ const UsersTable = () => {
<Table
columns={columns}
dataSource={pageData}
dataSource={users}
pagination={{
formatPageText: (page) =>
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}

View File

@@ -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) {