From ac7366f9390f19ce49b200972f359b1ec0d96c2e Mon Sep 17 00:00:00 2001 From: WJH <40563566+loveclever@users.noreply.github.com> Date: Sat, 4 Nov 2023 17:44:26 +0800 Subject: [PATCH] improve admin search repos (#5726) --- .../src/pages/sys-admin/repos/all-repos.js | 21 ++++++ frontend/src/pages/sys-admin/repos/repos.js | 4 +- .../src/pages/sys-admin/repos/search-repos.js | 66 ++++++++++++++++--- seahub/api2/endpoints/admin/libraries.py | 38 ++++++++++- 4 files changed, 114 insertions(+), 15 deletions(-) diff --git a/frontend/src/pages/sys-admin/repos/all-repos.js b/frontend/src/pages/sys-admin/repos/all-repos.js index 590393c9845..b6b15eea60a 100644 --- a/frontend/src/pages/sys-admin/repos/all-repos.js +++ b/frontend/src/pages/sys-admin/repos/all-repos.js @@ -120,9 +120,30 @@ class AllRepos extends Component { }; searchRepos = (repoNameOrID) => { + if (this.getValueLength(repoNameOrID) < 3) { + toaster.notify(gettext('Required at least three letters.')); + return; + } navigate(`${siteRoot}sys/search-libraries/?name_or_id=${encodeURIComponent(repoNameOrID)}`); }; + getValueLength(str) { + let code, len = 0; + for (let i = 0, length = str.length; i < length; i++) { + code = str.charCodeAt(i); + if (code === 10) { //solve enter problem + len += 2; + } else if (code < 0x007f) { + len += 1; + } else if (code >= 0x0080 && code <= 0x07ff) { + len += 2; + } else if (code >= 0x0800 && code <= 0xffff) { + len += 3; + } + } + return len; + } + render() { let { isCreateRepoDialogOpen } = this.state; return ( diff --git a/frontend/src/pages/sys-admin/repos/repos.js b/frontend/src/pages/sys-admin/repos/repos.js index 7089f90d1ae..b567d03e74b 100644 --- a/frontend/src/pages/sys-admin/repos/repos.js +++ b/frontend/src/pages/sys-admin/repos/repos.js @@ -132,8 +132,8 @@ Content.propTypes = { resetPerPage: PropTypes.func, pageInfo: PropTypes.object, curPerPage: PropTypes.number, - sortItems: PropTypes.func.isRequired, - sortBy: PropTypes.string.isRequired, + sortItems: PropTypes.func, + sortBy: PropTypes.string, onTransferRepo: PropTypes.func.isRequired, }; diff --git a/frontend/src/pages/sys-admin/repos/search-repos.js b/frontend/src/pages/sys-admin/repos/search-repos.js index b8c360c43d9..c2e2bf13ce1 100644 --- a/frontend/src/pages/sys-admin/repos/search-repos.js +++ b/frontend/src/pages/sys-admin/repos/search-repos.js @@ -2,6 +2,7 @@ import React, { Component, Fragment } from 'react'; import { Form, FormGroup, Input, Label, Col } from 'reactstrap'; import { seafileAPI } from '../../../utils/seafile-api'; import { gettext } from '../../../utils/constants'; +import toaster from '../../../components/toast'; import { Utils } from '../../../utils/utils'; import MainPanelTopbar from '../main-panel-topbar'; import Content from './repos'; @@ -13,27 +14,36 @@ class SearchRepos extends Component { super(props); this.state = { name: '', + currentPage: 1, + perPage: 25, isSubmitBtnActive: false, loading: true, errorMsg: '', - repos: [] + repos: [], + pageInfo: {}, }; } componentDidMount() { let params = (new URL(document.location)).searchParams; + const { currentPage, perPage } = this.state; this.setState({ - name: params.get('name_or_id') || '' - }, this.getRepos); + name: params.get('name_or_id') || '', + perPage: parseInt(params.get('per_page') || perPage), + currentPage: parseInt(params.get('page') || currentPage), + }, () => { + this.getRepos(this.state.currentPage); + }); } - getRepos = () => { - const { name } = this.state; - seafileAPI.sysAdminSearchRepos(name).then((res) => { + getRepos = (page) => { + const { name, perPage } = this.state; + seafileAPI.sysAdminSearchRepos(name, page, perPage).then((res) => { this.setState({ loading: false, errorMsg: '', - repos: res.data.repo_list + repos: res.data.repo_list, + pageInfo: res.data.page_info, }); }).catch((error) => { this.setState({ @@ -44,7 +54,8 @@ class SearchRepos extends Component { }; searchRepos = () => { - this.getRepos(); + const { currentPage } = this.state; + this.getRepos(currentPage); }; onDeleteRepo = (targetRepo) => { @@ -67,7 +78,8 @@ class SearchRepos extends Component { handleNameInputChange = (e) => { this.setState({ - name: e.target.value + name: e.target.value, + currentPage: 1, }, this.checkSubmitBtnActive); }; @@ -80,13 +92,43 @@ class SearchRepos extends Component { handleKeyDown = (e) => { if (e.keyCode === 13) { - const { isSubmitBtnActive } = this.state; + const { isSubmitBtnActive, name } = this.state; if (isSubmitBtnActive) { + if (this.getValueLength(name) < 3) { + toaster.notify(gettext('Required at least three letters.')); + return; + } this.searchRepos(); } } }; + resetPerPage = (perPage) => { + this.setState({ + perPage: perPage, + currentPage: 1, + }, () => { + this.searchRepos(); + }); + }; + + getValueLength(str) { + let code, len = 0; + for (let i = 0, length = str.length; i < length; i++) { + code = str.charCodeAt(i); + if (code === 10) { //solve enter problem + len += 2; + } else if (code < 0x007f) { + len += 1; + } else if (code >= 0x0080 && code <= 0x07ff) { + len += 2; + } else if (code >= 0x0800 && code <= 0xffff) { + len += 3; + } + } + return len; + } + render() { const { name, isSubmitBtnActive } = this.state; return ( @@ -121,6 +163,10 @@ class SearchRepos extends Component { loading={this.state.loading} errorMsg={this.state.errorMsg} items={this.state.repos} + pageInfo={this.state.pageInfo} + curPerPage={this.state.perPage} + getListByPage={this.getRepos} + resetPerPage={this.resetPerPage} onDeleteRepo={this.onDeleteRepo} onTransferRepo={this.onTransferRepo} /> diff --git a/seahub/api2/endpoints/admin/libraries.py b/seahub/api2/endpoints/admin/libraries.py index 1e226b8b30e..a372323e2f9 100644 --- a/seahub/api2/endpoints/admin/libraries.py +++ b/seahub/api2/endpoints/admin/libraries.py @@ -496,8 +496,36 @@ def get(self, request, format=None): error_msg = 'query invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + try: + current_page = int(request.GET.get('page', '1')) + per_page = int(request.GET.get('per_page', '25')) + except ValueError: + current_page = 1 + per_page = 25 + + start = (current_page - 1) * per_page + end = current_page * per_page + limit = per_page + 1 + repos = seafile_api.search_repos_by_name(query_str) - repos += seafile_api.get_repos_by_id_prefix(query_str) + repos_count = len(repos) + if repos_count > end: + repos = repos[start: end] + has_next_page = True + else: + if start - repos_count > 0: + repos = list() + start = start - repos_count + else: + repos = repos[start: end] + start = 0 + + repos += seafile_api.get_repos_by_id_prefix(query_str, start, limit) + if len(repos) > per_page: + repos = repos[:per_page] + has_next_page = True + else: + has_next_page = False default_repo_id = get_system_default_repo_id() repos = [r for r in repos if not r.is_virtual] @@ -546,7 +574,7 @@ def get(self, request, format=None): result = [] for repo in repos: - info = {} + info = dict() info['id'] = repo.repo_id info['name'] = repo.repo_name @@ -561,4 +589,8 @@ def get(self, request, format=None): result.append(info) - return Response({"repo_list": result}) + page_info = { + 'has_next_page': has_next_page, + 'current_page': current_page + } + return Response({"repo_list": result, "page_info": page_info})