diff --git a/frontend/src/css/system-departments-v2.css b/frontend/src/css/system-departments-v2.css index b7992e04fe..1d767a454b 100644 --- a/frontend/src/css/system-departments-v2.css +++ b/frontend/src/css/system-departments-v2.css @@ -128,4 +128,6 @@ color: #21bc2e; } -/* 顶部的样式:背景色和边界去掉,和 seatable 保持一致 */ +.departments-members-item { + height: 58px; +} diff --git a/frontend/src/pages/sys-admin/departments-v2/departments-node-dropdown-menu.js b/frontend/src/pages/sys-admin/departments-v2/departments-node-dropdown-menu.js new file mode 100644 index 0000000000..3606a9123b --- /dev/null +++ b/frontend/src/pages/sys-admin/departments-v2/departments-node-dropdown-menu.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { DropdownItem, DropdownMenu } from 'reactstrap'; +import { gettext } from '../../../utils/constants'; + +function DepartmentNodeMenu({ node, toggleDelete, toggleRename, toggleAddMembers, toggleAddDepartment, toggleAddLibrary }) { + return ( + + toggleAddDepartment(node)}> + {gettext('Add sub-department')} + + toggleAddLibrary(node)}> + {gettext('Add Library')} + + toggleAddMembers(node)}> + {gettext('Add members')} + + toggleRename(node)}> + {gettext('Rename')} + + toggleDelete(node)}> + {gettext('Delete')} + + + {`${gettext('Department ID')} : ${node.id}`} + + + ); +} + +DepartmentNodeMenu.propTypes = { + node: PropTypes.object.isRequired, + toggleDelete: PropTypes.func.isRequired, + toggleRename: PropTypes.func.isRequired, + toggleAddMembers: PropTypes.func.isRequired, + toggleAddDepartment: PropTypes.func.isRequired, + toggleAddLibrary: PropTypes.func.isRequired, +}; + +export default DepartmentNodeMenu; diff --git a/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-item.js b/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-item.js index 26ad635575..cdade86271 100644 --- a/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-item.js +++ b/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-item.js @@ -78,7 +78,7 @@ class DepartmentsV2MembersItem extends React.Component { const option = options.find(item => item.value === currentRole) || {}; return ( - + {member.name} @@ -103,12 +103,12 @@ class DepartmentsV2MembersItem extends React.Component { - + {gettext('Delete')} diff --git a/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-list.js b/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-list.js index 42331187ad..bfd242b66e 100644 --- a/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-list.js +++ b/frontend/src/pages/sys-admin/departments-v2/departments-v2-members-list.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Table } from 'reactstrap'; +import { Table, Dropdown, DropdownToggle } from 'reactstrap'; import Loading from '../../../components/loading'; import EmptyTip from '../../../components/empty-tip'; import { gettext } from '../../../utils/constants'; @@ -8,6 +8,7 @@ import DepartmentsV2MembersItem from './departments-v2-members-item'; import RepoItem from '../departments/repo-item'; import ModalPortal from '../../../components/modal-portal'; import DeleteRepoDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-repo-dialog'; +import DepartmentNodeMenu from './departments-node-dropdown-menu'; const propTypes = { rootNodes: PropTypes.array, @@ -32,6 +33,7 @@ class DepartmentsV2MembersList extends React.Component { repos: [], deletedRepo: {}, showDeleteRepoDialog: false, + dropdownOpen: false, }; } @@ -75,20 +77,20 @@ class DepartmentsV2MembersList extends React.Component { this.setState({ isItemFreezed: !this.state.isItemFreezed }); }; - getDepartmentName = () => { + getCurrentDepartment = () => { const { rootNodes, checkedDepartmentId } = this.props; - if (!rootNodes) return ''; - let name = ''; + if (!rootNodes) return {}; + let currentDepartment = {}; let arr = [...rootNodes]; - while (!name && arr.length > 0) { + while (!currentDepartment.id && arr.length > 0) { let curr = arr.shift(); if (curr.id === checkedDepartmentId) { - name = curr.name; + currentDepartment = curr; } else if (curr.children && curr.children.length > 0) { arr.push(...curr.children); } } - return name; + return currentDepartment; }; sortByName = (e) => { @@ -117,16 +119,47 @@ class DepartmentsV2MembersList extends React.Component { }); }; + dropdownToggle = (e) => { + e.stopPropagation(); + this.setState({ dropdownOpen: !this.state.dropdownOpen }); + }; + render() { const { activeNav, repos } = this.state; const { membersList, isMembersListLoading, sortBy, sortOrder } = this.props; const sortByName = sortBy === 'name'; const sortByRole = sortBy === 'role'; const sortIcon = ; + const currentDepartment = this.getCurrentDepartment(); return (
-
{this.getDepartmentName()}
+
+ {currentDepartment.name} + this.dropdownToggle(e)} + direction="down" + className="department-dropdown-menu" + > + + + +
    diff --git a/frontend/src/pages/sys-admin/departments-v2/departments-v2-tree-node.js b/frontend/src/pages/sys-admin/departments-v2/departments-v2-tree-node.js index 62144758a7..efa29d16b7 100644 --- a/frontend/src/pages/sys-admin/departments-v2/departments-v2-tree-node.js +++ b/frontend/src/pages/sys-admin/departments-v2/departments-v2-tree-node.js @@ -1,8 +1,9 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap'; +import { Dropdown, DropdownToggle } from 'reactstrap'; import { gettext } from '../../../utils/constants'; +import DepartmentNodeMenu from './departments-node-dropdown-menu'; const departmentsV2TreeNodePropTypes = { node: PropTypes.object, @@ -105,22 +106,6 @@ class DepartmentsV2TreeNode extends Component { } }; - toggleAddDepartment = (node) => { - this.props.toggleAddDepartment(node); - }; - - toggleAddMembers = (node) => { - this.props.toggleAddMembers(node); - }; - - toggleRename = (node) => { - this.props.toggleRename(node); - }; - - toggleDelete = (node) => { - this.props.toggleDelete(node); - }; - render() { const { node, checkedDepartmentId } = this.props; const { isChildrenShow, dropdownOpen, active } = this.state; @@ -144,7 +129,7 @@ class DepartmentsV2TreeNode extends Component { } {node.name} - {active && node.id !== 'other_users' && + {active && this.dropdownToggle(e)} @@ -159,55 +144,16 @@ class DepartmentsV2TreeNode extends Component { aria-label={gettext('More operations')} data-toggle="dropdown" > - + - - - {gettext('Add sub-department')} - - - {gettext('Add Library')} - - {node.id !== -1 && ( - - - {gettext('Add members')} - - - {gettext('Rename')} - - - {gettext('Delete')} - - - {`${gettext('Department ID')} : ${node.id}`} - - - )} - + }
diff --git a/frontend/src/pages/sys-admin/departments-v2/departments-v2.js b/frontend/src/pages/sys-admin/departments-v2/departments-v2.js index 9355dc4b59..ea5b74a9ce 100644 --- a/frontend/src/pages/sys-admin/departments-v2/departments-v2.js +++ b/frontend/src/pages/sys-admin/departments-v2/departments-v2.js @@ -336,6 +336,11 @@ class DepartmentsV2 extends React.Component { getRepos={this.getRepos} deleteGroup={this.deleteGroup} createGroup={this.createGroup} + toggleAddDepartment={this.toggleAddDepartment} + toggleAddLibrary={this.toggleAddLibrary} + toggleAddMembers={this.toggleAddMembers} + toggleRename={this.toggleRename} + toggleDelete={this.toggleDelete} /> } diff --git a/seahub/api2/endpoints/admin/group_members.py b/seahub/api2/endpoints/admin/group_members.py index 6753f30991..9cd0c21f8c 100644 --- a/seahub/api2/endpoints/admin/group_members.py +++ b/seahub/api2/endpoints/admin/group_members.py @@ -9,7 +9,7 @@ from rest_framework import status from seaserv import seafile_api, ccnet_api -from seahub.group.utils import get_group_member_info, is_group_member +from seahub.group.utils import get_group_member_info, is_group_member, get_group_members_info from seahub.group.signals import add_user_to_group from seahub.avatar.settings import AVATAR_DEFAULT_SIZE from seahub.base.accounts import User @@ -67,15 +67,13 @@ class AdminGroupMembers(APIView): else: has_next_page = False - group_members_info = [] - for m in members: - member_info = get_group_member_info(request, group_id, m.user_name) - group_members_info.append(member_info) - + + member_usernames = [m.user_name for m in members] + members_info = get_group_members_info(group_id, member_usernames) group_members = { 'group_id': group_id, 'group_name': group.group_name, - 'members': group_members_info, + 'members': members_info, 'page_info': { 'has_next_page': has_next_page, 'current_page': page diff --git a/seahub/group/utils.py b/seahub/group/utils.py index d80881098f..17ac4d0b35 100644 --- a/seahub/group/utils.py +++ b/seahub/group/utils.py @@ -118,6 +118,42 @@ def get_group_member_info(request, group_id, email): return member_info +def get_group_members_info(group_id, emails): + member_profiles = Profile.objects.filter(user__in=emails) + username_profile_map = {p.user : p for p in member_profiles} + members_info_list = [] + for email in emails: + p = username_profile_map.get(email) or None + if p: + login_id = p.login_id if p.login_id else '' + contact_email = p.contact_email + else: + login_id = '' + contact_email = '' + + avatar_url, _, _ = api_avatar_url(email) + + role = 'Member' + group = ccnet_api.get_group(int(group_id)) + is_admin = bool(ccnet_api.check_group_staff(int(group_id), email)) + if email == group.creator_name: + role = 'Owner' + elif is_admin: + role = 'Admin' + + member_info = { + 'group_id': group_id, + "name": email2nickname(email), + 'email': email, + "contact_email": contact_email, + "login_id": login_id, + "avatar_url": avatar_url, + "is_admin": is_admin, + "role": role, + } + members_info_list.append(member_info) + return members_info_list + GROUP_ID_CACHE_PREFIX = "GROUP_ID_" GROUP_ID_CACHE_TIMEOUT = 24 * 60 * 60