mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-17 15:53:28 +00:00
Change department UI (#7063)
* 01 change department member more icon
* 02 when no contact email return None
* 03 delete menu
* 04 move into member high change
* 05 current department support more operations
* 06 change comment
* add username_as_email is true
* update-get-group-members-info
* Revert "06 change comment"
This reverts commit 14800df3fd
.
---------
Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
This commit is contained in:
@@ -128,4 +128,6 @@
|
|||||||
color: #21bc2e;
|
color: #21bc2e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶部的样式:背景色和边界去掉,和 seatable 保持一致 */
|
.departments-members-item {
|
||||||
|
height: 58px;
|
||||||
|
}
|
||||||
|
@@ -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 (
|
||||||
|
<DropdownMenu
|
||||||
|
right={true}
|
||||||
|
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
|
||||||
|
positionFixed={true}
|
||||||
|
>
|
||||||
|
<DropdownItem key={`${node.id}-add-department`} onClick={() => toggleAddDepartment(node)}>
|
||||||
|
{gettext('Add sub-department')}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem key={`${node.id}-add-repo`} onClick={() => toggleAddLibrary(node)}>
|
||||||
|
{gettext('Add Library')}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem key={`${node.id}-add-members`} onClick={() => toggleAddMembers(node)}>
|
||||||
|
{gettext('Add members')}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem key={`${node.id}-rename`} onClick={() => toggleRename(node)}>
|
||||||
|
{gettext('Rename')}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem key={`${node.id}-delete`} onClick={() => toggleDelete(node)}>
|
||||||
|
{gettext('Delete')}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem key={`${node.id}-id`} disabled={true}>
|
||||||
|
{`${gettext('Department ID')} : ${node.id}`}
|
||||||
|
</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
@@ -78,7 +78,7 @@ class DepartmentsV2MembersItem extends React.Component {
|
|||||||
const option = options.find(item => item.value === currentRole) || {};
|
const option = options.find(item => item.value === currentRole) || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={member.email} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
<tr className="departments-members-item" key={member.email} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
<td><img className="avatar" src={member.avatar_url} alt=""></img></td>
|
<td><img className="avatar" src={member.avatar_url} alt=""></img></td>
|
||||||
<td className='text-truncate'>
|
<td className='text-truncate'>
|
||||||
<Link to={`${siteRoot}sys/users/${encodeURIComponent(member.email)}/`}>{member.name}</Link>
|
<Link to={`${siteRoot}sys/users/${encodeURIComponent(member.email)}/`}>{member.name}</Link>
|
||||||
@@ -103,12 +103,12 @@ class DepartmentsV2MembersItem extends React.Component {
|
|||||||
<DropdownToggle
|
<DropdownToggle
|
||||||
tag='a'
|
tag='a'
|
||||||
role="button"
|
role="button"
|
||||||
className='attr-action-icon sf3-font sf3-font-more'
|
className='attr-action-icon sf3-font sf3-font-more-vertical'
|
||||||
title={gettext('More operations')}
|
title={gettext('More operations')}
|
||||||
aria-label={gettext('More operations')}
|
aria-label={gettext('More operations')}
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
/>
|
/>
|
||||||
<DropdownMenu className="dtable-dropdown-menu dropdown-menu mt-2 mr-2" right={true}>
|
<DropdownMenu right={true}>
|
||||||
<DropdownItem key='delete' onClick={this.deleteMember}>{gettext('Delete')}</DropdownItem>
|
<DropdownItem key='delete' onClick={this.deleteMember}>{gettext('Delete')}</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Table } from 'reactstrap';
|
import { Table, Dropdown, DropdownToggle } from 'reactstrap';
|
||||||
import Loading from '../../../components/loading';
|
import Loading from '../../../components/loading';
|
||||||
import EmptyTip from '../../../components/empty-tip';
|
import EmptyTip from '../../../components/empty-tip';
|
||||||
import { gettext } from '../../../utils/constants';
|
import { gettext } from '../../../utils/constants';
|
||||||
@@ -8,6 +8,7 @@ import DepartmentsV2MembersItem from './departments-v2-members-item';
|
|||||||
import RepoItem from '../departments/repo-item';
|
import RepoItem from '../departments/repo-item';
|
||||||
import ModalPortal from '../../../components/modal-portal';
|
import ModalPortal from '../../../components/modal-portal';
|
||||||
import DeleteRepoDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-repo-dialog';
|
import DeleteRepoDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-repo-dialog';
|
||||||
|
import DepartmentNodeMenu from './departments-node-dropdown-menu';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
rootNodes: PropTypes.array,
|
rootNodes: PropTypes.array,
|
||||||
@@ -32,6 +33,7 @@ class DepartmentsV2MembersList extends React.Component {
|
|||||||
repos: [],
|
repos: [],
|
||||||
deletedRepo: {},
|
deletedRepo: {},
|
||||||
showDeleteRepoDialog: false,
|
showDeleteRepoDialog: false,
|
||||||
|
dropdownOpen: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,20 +77,20 @@ class DepartmentsV2MembersList extends React.Component {
|
|||||||
this.setState({ isItemFreezed: !this.state.isItemFreezed });
|
this.setState({ isItemFreezed: !this.state.isItemFreezed });
|
||||||
};
|
};
|
||||||
|
|
||||||
getDepartmentName = () => {
|
getCurrentDepartment = () => {
|
||||||
const { rootNodes, checkedDepartmentId } = this.props;
|
const { rootNodes, checkedDepartmentId } = this.props;
|
||||||
if (!rootNodes) return '';
|
if (!rootNodes) return {};
|
||||||
let name = '';
|
let currentDepartment = {};
|
||||||
let arr = [...rootNodes];
|
let arr = [...rootNodes];
|
||||||
while (!name && arr.length > 0) {
|
while (!currentDepartment.id && arr.length > 0) {
|
||||||
let curr = arr.shift();
|
let curr = arr.shift();
|
||||||
if (curr.id === checkedDepartmentId) {
|
if (curr.id === checkedDepartmentId) {
|
||||||
name = curr.name;
|
currentDepartment = curr;
|
||||||
} else if (curr.children && curr.children.length > 0) {
|
} else if (curr.children && curr.children.length > 0) {
|
||||||
arr.push(...curr.children);
|
arr.push(...curr.children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return name;
|
return currentDepartment;
|
||||||
};
|
};
|
||||||
|
|
||||||
sortByName = (e) => {
|
sortByName = (e) => {
|
||||||
@@ -117,16 +119,47 @@ class DepartmentsV2MembersList extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dropdownToggle = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.setState({ dropdownOpen: !this.state.dropdownOpen });
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { activeNav, repos } = this.state;
|
const { activeNav, repos } = this.state;
|
||||||
const { membersList, isMembersListLoading, sortBy, sortOrder } = this.props;
|
const { membersList, isMembersListLoading, sortBy, sortOrder } = this.props;
|
||||||
const sortByName = sortBy === 'name';
|
const sortByName = sortBy === 'name';
|
||||||
const sortByRole = sortBy === 'role';
|
const sortByRole = sortBy === 'role';
|
||||||
const sortIcon = <span className={`sort-dirent sf3-font sf3-font-down ${sortOrder === 'asc' ? 'rotate-180' : ''}`}></span>;
|
const sortIcon = <span className={`sort-dirent sf3-font sf3-font-down ${sortOrder === 'asc' ? 'rotate-180' : ''}`}></span>;
|
||||||
|
const currentDepartment = this.getCurrentDepartment();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="department-content-main">
|
<div className="department-content-main">
|
||||||
<div className="department-content-main-name">{this.getDepartmentName()}</div>
|
<div className="department-content-main-name">
|
||||||
|
{currentDepartment.name}
|
||||||
|
<Dropdown
|
||||||
|
isOpen={this.state.dropdownOpen}
|
||||||
|
toggle={(e) => this.dropdownToggle(e)}
|
||||||
|
direction="down"
|
||||||
|
className="department-dropdown-menu"
|
||||||
|
>
|
||||||
|
<DropdownToggle
|
||||||
|
tag='span'
|
||||||
|
role="button"
|
||||||
|
className='sf3-font-down sf3-font ml-1 sf-dropdown-toggle'
|
||||||
|
title={gettext('More operations')}
|
||||||
|
aria-label={gettext('More operations')}
|
||||||
|
data-toggle="dropdown"
|
||||||
|
/>
|
||||||
|
<DepartmentNodeMenu
|
||||||
|
node={currentDepartment}
|
||||||
|
toggleAddDepartment={this.props.toggleAddDepartment}
|
||||||
|
toggleAddLibrary={this.props.toggleAddLibrary}
|
||||||
|
toggleAddMembers={this.props.toggleAddMembers}
|
||||||
|
toggleRename={this.props.toggleRename}
|
||||||
|
toggleDelete={this.props.toggleDelete}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="cur-view-path tab-nav-container">
|
<div className="cur-view-path tab-nav-container">
|
||||||
<ul className="nav">
|
<ul className="nav">
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
|
import { Dropdown, DropdownToggle } from 'reactstrap';
|
||||||
import { gettext } from '../../../utils/constants';
|
import { gettext } from '../../../utils/constants';
|
||||||
|
import DepartmentNodeMenu from './departments-node-dropdown-menu';
|
||||||
|
|
||||||
const departmentsV2TreeNodePropTypes = {
|
const departmentsV2TreeNodePropTypes = {
|
||||||
node: PropTypes.object,
|
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() {
|
render() {
|
||||||
const { node, checkedDepartmentId } = this.props;
|
const { node, checkedDepartmentId } = this.props;
|
||||||
const { isChildrenShow, dropdownOpen, active } = this.state;
|
const { isChildrenShow, dropdownOpen, active } = this.state;
|
||||||
@@ -144,7 +129,7 @@ class DepartmentsV2TreeNode extends Component {
|
|||||||
<span style={{ width: 24 }}></span>
|
<span style={{ width: 24 }}></span>
|
||||||
}
|
}
|
||||||
<span className="departments-v2-tree-node-text text-truncate">{node.name}</span>
|
<span className="departments-v2-tree-node-text text-truncate">{node.name}</span>
|
||||||
{active && node.id !== 'other_users' &&
|
{active &&
|
||||||
<Dropdown
|
<Dropdown
|
||||||
isOpen={dropdownOpen}
|
isOpen={dropdownOpen}
|
||||||
toggle={(e) => this.dropdownToggle(e)}
|
toggle={(e) => this.dropdownToggle(e)}
|
||||||
@@ -159,55 +144,16 @@ class DepartmentsV2TreeNode extends Component {
|
|||||||
aria-label={gettext('More operations')}
|
aria-label={gettext('More operations')}
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
>
|
>
|
||||||
<i className="sf3-font sf3-font-more"></i>
|
<i className="sf3-font sf3-font-more mr-1"></i>
|
||||||
</DropdownToggle>
|
</DropdownToggle>
|
||||||
<DropdownMenu
|
<DepartmentNodeMenu
|
||||||
className="dtable-dropdown-menu dropdown-menu drop-list"
|
node={node}
|
||||||
right={true}
|
toggleAddDepartment={this.props.toggleAddDepartment}
|
||||||
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
|
toggleAddLibrary={this.props.toggleAddLibrary}
|
||||||
positionFixed={true}
|
toggleAddMembers={this.props.toggleAddMembers}
|
||||||
>
|
toggleRename={this.props.toggleRename}
|
||||||
<DropdownItem
|
toggleDelete={this.props.toggleDelete}
|
||||||
key={`${node.id}-add-department`}
|
/>
|
||||||
onClick={this.toggleAddDepartment.bind(this, node)}
|
|
||||||
>
|
|
||||||
{gettext('Add sub-department')}
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
key={`${node.id}-add-repo`}
|
|
||||||
onClick={this.props.toggleAddLibrary.bind(this, node)}
|
|
||||||
>
|
|
||||||
{gettext('Add Library')}
|
|
||||||
</DropdownItem>
|
|
||||||
{node.id !== -1 && (
|
|
||||||
<Fragment>
|
|
||||||
<DropdownItem
|
|
||||||
key={`${node.id}-add-members`}
|
|
||||||
onClick={this.toggleAddMembers.bind(this, node)}
|
|
||||||
>
|
|
||||||
{gettext('Add members')}
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
key={`${node.id}-rename`}
|
|
||||||
onClick={this.toggleRename.bind(this, node)}
|
|
||||||
>
|
|
||||||
{gettext('Rename')}
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
key={`${node.id}-delete`}
|
|
||||||
onClick={this.toggleDelete.bind(this, node)}
|
|
||||||
>
|
|
||||||
{gettext('Delete')}
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
key={`${node.id}-id`}
|
|
||||||
disabled={true}
|
|
||||||
>
|
|
||||||
{`${gettext('Department ID')} : ${node.id}`}
|
|
||||||
</DropdownItem>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -336,6 +336,11 @@ class DepartmentsV2 extends React.Component {
|
|||||||
getRepos={this.getRepos}
|
getRepos={this.getRepos}
|
||||||
deleteGroup={this.deleteGroup}
|
deleteGroup={this.deleteGroup}
|
||||||
createGroup={this.createGroup}
|
createGroup={this.createGroup}
|
||||||
|
toggleAddDepartment={this.toggleAddDepartment}
|
||||||
|
toggleAddLibrary={this.toggleAddLibrary}
|
||||||
|
toggleAddMembers={this.toggleAddMembers}
|
||||||
|
toggleRename={this.toggleRename}
|
||||||
|
toggleDelete={this.toggleDelete}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ from rest_framework import status
|
|||||||
|
|
||||||
from seaserv import seafile_api, ccnet_api
|
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.group.signals import add_user_to_group
|
||||||
from seahub.avatar.settings import AVATAR_DEFAULT_SIZE
|
from seahub.avatar.settings import AVATAR_DEFAULT_SIZE
|
||||||
from seahub.base.accounts import User
|
from seahub.base.accounts import User
|
||||||
@@ -67,15 +67,13 @@ class AdminGroupMembers(APIView):
|
|||||||
else:
|
else:
|
||||||
has_next_page = False
|
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_members = {
|
||||||
'group_id': group_id,
|
'group_id': group_id,
|
||||||
'group_name': group.group_name,
|
'group_name': group.group_name,
|
||||||
'members': group_members_info,
|
'members': members_info,
|
||||||
'page_info': {
|
'page_info': {
|
||||||
'has_next_page': has_next_page,
|
'has_next_page': has_next_page,
|
||||||
'current_page': page
|
'current_page': page
|
||||||
|
@@ -118,6 +118,42 @@ def get_group_member_info(request, group_id, email):
|
|||||||
|
|
||||||
return member_info
|
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_PREFIX = "GROUP_ID_"
|
||||||
GROUP_ID_CACHE_TIMEOUT = 24 * 60 * 60
|
GROUP_ID_CACHE_TIMEOUT = 24 * 60 * 60
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user