Bladeren bron

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.
CalciumIon 1 jaar geleden
bovenliggende
commit
be0b2f6a64
5 gewijzigde bestanden met toevoegingen van 95 en 48 verwijderingen
  1. 15 5
      controller/user.go
  2. 32 3
      model/user.go
  3. 6 0
      web/src/components/LogsTable.js
  4. 37 39
      web/src/components/UsersTable.js
  5. 5 1
      web/src/pages/User/AddUser.js

+ 15 - 5
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
 }

+ 32 - 3
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) {

+ 6 - 0
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,

+ 37 - 39
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 (
     <>
       <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}

+ 5 - 1
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) {