Просмотр исходного кода

feat: enhance user search functionality with pagination support

- Updated SearchUsers function to include pagination parameters (startIdx and num) for improved user search results.
- Modified API response structure to return paginated data, including total user count and current page information.
- Adjusted UsersTable component to handle pagination and search parameters, ensuring a seamless user experience.
- Added internationalization support for new search functionality in the UI.
CalciumIon 1 год назад
Родитель
Сommit
014fb7edab
4 измененных файлов с 94 добавлено и 58 удалено
  1. 16 2
      controller/user.go
  2. 45 15
      model/user.go
  3. 30 40
      web/src/components/UsersTable.js
  4. 3 1
      web/src/i18n/locales/en.json

+ 16 - 2
controller/user.go

@@ -274,7 +274,16 @@ func GetAllUsers(c *gin.Context) {
 func SearchUsers(c *gin.Context) {
 	keyword := c.Query("keyword")
 	group := c.Query("group")
-	users, err := model.SearchUsers(keyword, group)
+	p, _ := strconv.Atoi(c.Query("p"))
+	pageSize, _ := strconv.Atoi(c.Query("page_size"))
+	if p < 1 {
+		p = 1
+	}
+	if pageSize < 0 {
+		pageSize = common.ItemsPerPage
+	}
+	startIdx := (p - 1) * pageSize
+	users, total, err := model.SearchUsers(keyword, group, startIdx, pageSize)
 	if err != nil {
 		c.JSON(http.StatusOK, gin.H{
 			"success": false,
@@ -285,7 +294,12 @@ func SearchUsers(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
 }

+ 45 - 15
model/user.go

@@ -115,36 +115,66 @@ func GetAllUsers(startIdx int, num int) (users []*User, total int64, err error)
 	return users, total, nil
 }
 
-func SearchUsers(keyword string, group string) ([]*User, error) {
+func SearchUsers(keyword string, group string, startIdx int, num int) ([]*User, int64, error) {
 	var users []*User
+	var total int64
 	var err error
 
+	// 开始事务
+	tx := DB.Begin()
+	if tx.Error != nil {
+		return nil, 0, tx.Error
+	}
+	defer func() {
+		if r := recover(); r != nil {
+			tx.Rollback()
+		}
+	}()
+
+	// 构建基础查询
+	query := tx.Unscoped().Model(&User{})
+
 	// 尝试将关键字转换为整数ID
 	keywordInt, err := strconv.Atoi(keyword)
 	if err == nil {
 		// 如果转换成功,按照ID和可选的组别搜索用户
-		query := DB.Unscoped().Omit("password").Where("id = ?", keywordInt)
 		if group != "" {
-			query = query.Where(groupCol+" = ?", group) // 使用反引号包围group
+			query = query.Where("id = ? AND "+groupCol+" = ?", keywordInt, group)
+		} else {
+			query = query.Where("id = ?", keywordInt)
 		}
-		err = query.Find(&users).Error
-		if err != nil || len(users) > 0 {
-			return users, err
+	} else {
+		// 如果不是ID搜索,则使用模糊匹配
+		likeCondition := "username LIKE ? OR email LIKE ? OR display_name LIKE ?"
+		if group != "" {
+			query = query.Where("("+likeCondition+") AND "+groupCol+" = ?",
+				"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", group)
+		} else {
+			query = query.Where(likeCondition,
+				"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
 		}
 	}
 
-	err = nil
+	// 获取总数
+	err = query.Count(&total).Error
+	if err != nil {
+		tx.Rollback()
+		return nil, 0, err
+	}
 
-	query := DB.Unscoped().Omit("password")
-	likeCondition := "username LIKE ? OR email LIKE ? OR display_name LIKE ?"
-	if group != "" {
-		query = query.Where("("+likeCondition+") AND "+groupCol+" = ?", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", group)
-	} else {
-		query = query.Where(likeCondition, "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
+	// 获取分页数据
+	err = query.Omit("password").Order("id desc").Limit(num).Offset(startIdx).Find(&users).Error
+	if err != nil {
+		tx.Rollback()
+		return nil, 0, err
 	}
-	err = query.Find(&users).Error
 
-	return users, err
+	// 提交事务
+	if err = tx.Commit().Error; err != nil {
+		return nil, 0, err
+	}
+
+	return users, total, nil
 }
 
 func GetUserById(id int, selectAll bool) (*User, error) {

+ 30 - 40
web/src/components/UsersTable.js

@@ -327,20 +327,22 @@ const UsersTable = () => {
     }
   };
 
-  const searchUsers = async (searchKeyword, searchGroup) => {
+  const searchUsers = async (startIdx, pageSize, searchKeyword, searchGroup) => {
     if (searchKeyword === '' && searchGroup === '') {
-      // if keyword is blank, load files instead.
-      await loadUsers(activePage, pageSize);
-      return;
+        // if keyword is blank, load files instead.
+        await loadUsers(startIdx, pageSize);
+        return;
     }
     setSearching(true);
-    const res = await API.get(`/api/user/search?keyword=${searchKeyword}&group=${searchGroup}`);
+    const res = await API.get(`/api/user/search?keyword=${searchKeyword}&group=${searchGroup}&p=${startIdx}&page_size=${pageSize}`);
     const { success, message, data } = res.data;
     if (success) {
-      setUsers(data);
-      setActivePage(1);
+        const newPageData = data.items;
+        setActivePage(data.page);
+        setUserCount(data.total);
+        setUserFormat(newPageData);
     } else {
-      showError(message);
+        showError(message);
     }
     setSearching(false);
   };
@@ -349,30 +351,15 @@ const UsersTable = () => {
     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);
-  };
-
   const handlePageChange = (page) => {
     setActivePage(page);
-    loadUsers(page, pageSize).then((r) => {});
+    if (searchKeyword === '' && searchGroup === '') {
+        loadUsers(page, pageSize).then();
+    } else {
+        searchUsers(page, pageSize, searchKeyword, searchGroup).then();
+    }
   };
 
-  const pageData = users.slice(
-    (activePage - 1) * ITEMS_PER_PAGE,
-    activePage * ITEMS_PER_PAGE,
-  );
-
   const closeAddUser = () => {
     setShowAddUser(false);
   };
@@ -438,29 +425,32 @@ const UsersTable = () => {
       ></EditUser>
       <Form
         onSubmit={() => {
-          searchUsers(searchKeyword, searchGroup);
+          searchUsers(activePage, pageSize, searchKeyword, searchGroup);
         }}
         labelPosition='left'
       >
         <div style={{ display: 'flex' }}>
           <Space>
-            <Form.Input
-              label={t('搜索关键字')}
-              icon='search'
-              field='keyword'
-              iconPosition='left'
-              placeholder={t('搜索用户的 ID,用户名,显示名称,以及邮箱地址 ...')}
-              value={searchKeyword}
-              loading={searching}
-              onChange={(value) => handleKeywordChange(value)}
-            />
+            <Tooltip content={t('支持搜索用户的 ID、用户名、显示名称和邮箱地址')}>
+              <Form.Input
+                label={t('搜索关键字')}
+                icon='search'
+                field='keyword'
+                iconPosition='left'
+                placeholder={t('搜索关键字')}
+                value={searchKeyword}
+                loading={searching}
+                onChange={(value) => handleKeywordChange(value)}
+              />
+            </Tooltip>
+            
             <Form.Select
               field='group'
               label={t('分组')}
               optionList={groupOptions}
               onChange={(value) => {
                 setSearchGroup(value);
-                searchUsers(searchKeyword, value);
+                searchUsers(activePage, pageSize, searchKeyword, value);
               }}
             />
             <Button

+ 3 - 1
web/src/i18n/locales/en.json

@@ -1240,5 +1240,7 @@
   "时间范围": "Time range",
   "批量设置标签": "Batch set tag",
   "请输入要设置的标签名称": "Please enter the tag name to be set",
-  "请输入标签名称": "Please enter the tag name"
+  "请输入标签名称": "Please enter the tag name",
+  "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "Support searching for user ID, username, display name, and email address",
+  "已注销": "Logged out"
 }