From 95ffd710aaff6591657954b68645958a066a6597 Mon Sep 17 00:00:00 2001 From: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Date: Wed, 26 Mar 2025 22:10:03 +0800 Subject: [PATCH] pref: member list (#4344) * chore: search member new api * chore: permission * fix: ts error * fix: member modal --- .../global/support/user/team/org/api.d.ts | 3 +- packages/global/support/user/team/type.d.ts | 26 ++- .../permission/MemberManager/MemberModal.tsx | 159 +++++++------- .../team/GroupManage/GroupManageMember.tsx | 170 +++++++-------- .../GroupManage/GroupTransferOwnerModal.tsx | 80 +++---- .../account/team/MemberTable.tsx | 197 ++++++++++-------- .../account/team/OrgManage/OrgInfoModal.tsx | 7 +- .../team/OrgManage/OrgMemberManageModal.tsx | 78 ++----- .../account/team/OrgManage/OrgMoveModal.tsx | 4 +- .../account/team/OrgManage/OrgTree.tsx | 15 +- .../account/team/OrgManage/index.tsx | 159 +++++--------- .../pageComponents/account/team/context.tsx | 43 +--- projects/app/src/web/support/user/team/api.ts | 12 +- .../app/src/web/support/user/team/org/api.ts | 7 +- .../support/user/team/org/hooks/useOrg.tsx | 36 +++- 15 files changed, 479 insertions(+), 517 deletions(-) diff --git a/packages/global/support/user/team/org/api.d.ts b/packages/global/support/user/team/org/api.d.ts index feaded6f1..dee9bf4d8 100644 --- a/packages/global/support/user/team/org/api.d.ts +++ b/packages/global/support/user/team/org/api.d.ts @@ -1,9 +1,8 @@ -// orgId, pathid, path === null ===> root org export type postCreateOrgData = { name: string; description?: string; avatar?: string; - path?: string; + orgId?: string; }; export type putUpdateOrgMembersData = { diff --git a/packages/global/support/user/team/type.d.ts b/packages/global/support/user/team/type.d.ts index c715d93b5..597eaf662 100644 --- a/packages/global/support/user/team/type.d.ts +++ b/packages/global/support/user/team/type.d.ts @@ -70,7 +70,13 @@ export type TeamTmbItemType = { permission: TeamPermission; } & ThirdPartyAccountType; -export type TeamMemberItemType = { +export type TeamMemberItemType< + Options extends { + withPermission?: boolean; + withOrgs?: boolean; + withGroupRole?: boolean; + } = { withPermission: true; withOrgs: true; withGroupRole: false } +> = { userId: string; tmbId: string; teamId: string; @@ -78,12 +84,24 @@ export type TeamMemberItemType = { avatar: string; role: `${TeamMemberRoleEnum}`; status: `${TeamMemberStatusEnum}`; - permission: TeamPermission; contact?: string; createTime: Date; updateTime?: Date; - orgs?: string[]; // full path name, pattern: /teamName/orgname1/orgname2 -}; +} & (Options extends { withPermission: true } + ? { + permission: TeamPermission; + } + : {}) & + (Options extends { withOrgs: true } + ? { + orgs?: string[]; // full path name, pattern: /teamName/orgname1/orgname2 + } + : {}) & + (Options extends { withGroupRole: true } + ? { + groupRole?: `${GroupMemberRole}`; + } + : {}); export type TeamTagItemType = { label: string; diff --git a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx index 02b762893..11f70e921 100644 --- a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx @@ -19,7 +19,7 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; -import { useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import PermissionSelect from './PermissionSelect'; import PermissionTags from './PermissionTags'; import { @@ -39,6 +39,7 @@ import { GetSearchUserGroupOrg } from '@/web/support/user/api'; import useOrg from '@/web/support/user/team/org/hooks/useOrg'; import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; import { MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; +import _ from 'lodash'; const HoverBoxStyle = { bgColor: 'myGray.50', @@ -55,7 +56,6 @@ function MemberModal({ const { t } = useTranslation(); const { userInfo } = useUserStore(); const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList); - const [searchText, setSearchText] = useState(''); const [filterClass, setFilterClass] = useState<'member' | 'org' | 'group'>(); const { paths, @@ -63,18 +63,35 @@ function MemberModal({ members: orgMembers, MemberScrollData: OrgMemberScrollData, onPathClick, - orgs - } = useOrg({ getPermission: false }); + orgs, + searchKey, + setSearchKey + } = useOrg({ withPermission: false }); - const { data: members, ScrollData: TeamMemberScrollData } = useScrollPagination(getTeamMembers, { - pageSize: 15 + const { + data: members, + ScrollData: TeamMemberScrollData, + refreshList + } = useScrollPagination(getTeamMembers, { + pageSize: 15, + params: { + withPermission: true, + withOrgs: true, + status: 'active', + searchKey + } }); - const { data: groups = [], loading: loadingGroupsAndOrgs } = useRequest2( + const { + data: groups = [], + loading: loadingGroupsAndOrgs, + runAsync: refreshGroups + } = useRequest2( async () => { if (!userInfo?.team?.teamId) return []; return getGroupList({ - withMembers: false + withMembers: false, + searchKey }); }, { @@ -83,53 +100,20 @@ function MemberModal({ } ); - const { data: searchedData } = useRequest2(() => GetSearchUserGroupOrg(searchText), { - manual: false, - throttleWait: 500, - debounceWait: 200, - refreshDeps: [searchText] - }); + const search = _.debounce(() => { + refreshList(); + refreshGroups(); + }, 200); + + useEffect(search, [searchKey]); const [selectedOrgList, setSelectedOrgIdList] = useState([]); - const filterOrgs: (OrgListItemType & { count?: number })[] = useMemo(() => { - if (searchText && searchedData) { - const orgids = searchedData.orgs.map((item) => item._id); - return orgs.filter((org) => orgids.includes(String(org._id))); - } - return orgs - .filter((org) => org.path !== '') - .map((org) => ({ - ...org, - count: org.total - })); - }, [searchText, orgs, searchedData]); - const [selectedMemberList, setSelectedMemberList] = useState< Omit[] >([]); - const filterMembers = useMemo(() => { - if (searchText) { - return searchedData?.members || []; - } - - return members; - }, [searchText, members, searchedData?.members]); const [selectedGroupList, setSelectedGroupList] = useState[]>([]); - const filterGroups = useMemo(() => { - if (searchText) { - return searchedData?.groups.map((item) => ({ - groupName: item.name, - _id: item.id, - ...item - })); - } - if (!searchText && filterClass !== 'group') return []; - - return groups; - }, [searchText, filterClass, groups, searchedData]); - const permissionList = useContextSelector(CollaboratorContext, (v) => v.permissionList); const getPerLabelList = useContextSelector(CollaboratorContext, (v) => v.getPerLabelList); const [selectedPermission, setSelectedPermission] = useState( @@ -225,12 +209,12 @@ function MemberModal({ setSearchText(e.target.value)} + onChange={(e) => setSearchKey(e.target.value)} /> {/* Entry */} - {!searchText && !filterClass && ( + {!searchKey && !filterClass && ( <> {entryList.current.map((item) => { return ( @@ -257,7 +241,7 @@ function MemberModal({ )} {/* Path */} - {!searchText && filterClass && ( + {!searchKey && filterClass && ( )} - {(filterClass === 'member' || (searchText && filterMembers.length > 0)) && + {(filterClass === 'member' || searchKey) && (() => { - const members = filterMembers?.map((member) => { + const Members = members?.map((member) => { const onChange = () => { setSelectedMemberList((state) => { if (state.find((v) => v.tmbId === member.tmbId)) { @@ -315,8 +299,8 @@ function MemberModal({ /> ); }); - return searchText ? ( - members + return searchKey ? ( + Members ) : ( - {members} + {Members} ); })()} - {(filterClass === 'org' || searchText) && + {(filterClass === 'org' || searchKey) && (() => { - const orgs = filterOrgs?.map((org) => { + const Orgs = orgs?.map((org) => { const onChange = () => { setSelectedOrgIdList((state) => { if (state.find((v) => v._id === org._id)) { @@ -356,18 +340,18 @@ function MemberModal({ pointerEvents="none" /> - + {org.name} - {org.count && ( + {org.total && ( <> - {org.count} + {org.total} )} - {org.count && ( + {org.total && ( ); }); - return searchText ? ( - orgs + return searchKey ? ( + Orgs ) : ( - {orgs} + {Orgs} {orgMembers.map((member) => { return ( ); })()} - {filterGroups?.map((group) => { - const onChange = () => { - setSelectedGroupList((state) => { - if (state.find((v) => v._id === group._id)) { - return state.filter((v) => v._id !== group._id); - } - return [...state, group]; - }); - }; - const collaborator = collaboratorList?.find((v) => v.groupId === group._id); - return ( - v._id === group._id)} - /> - ); - })} + {(filterClass === 'group' || searchKey) && + groups?.map((group) => { + const onChange = () => { + setSelectedGroupList((state) => { + if (state.find((v) => v._id === group._id)) { + return state.filter((v) => v._id !== group._id); + } + return [...state, group]; + }); + }; + const collaborator = collaboratorList?.find((v) => v.groupId === group._id); + return ( + v._id === group._id)} + /> + ); + })} diff --git a/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx index 2b89afd6b..2ce0f2c23 100644 --- a/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx @@ -1,34 +1,25 @@ -import { - Box, - ModalBody, - Flex, - Button, - ModalFooter, - Checkbox, - Grid, - HStack -} from '@chakra-ui/react'; +import { Box, ModalBody, Flex, Button, ModalFooter, Grid, HStack } from '@chakra-ui/react'; import MyModal from '@fastgpt/web/components/common/MyModal'; import MyIcon from '@fastgpt/web/components/common/Icon'; import Avatar from '@fastgpt/web/components/common/Avatar'; import Tag from '@fastgpt/web/components/common/Tag'; import { useTranslation } from 'next-i18next'; -import React, { useMemo, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { useContextSelector } from 'use-context-selector'; -import { TeamContext } from '../context'; -import { getGroupMembers, putUpdateGroup } from '@/web/support/user/team/group/api'; +import { putUpdateGroup } from '@/web/support/user/team/group/api'; import { GroupMemberRole } from '@fastgpt/global/support/permission/memberGroup/constant'; import { useUserStore } from '@/web/support/user/useUserStore'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; -import { - GroupMemberItemType, - MemberGroupListItemType -} from '@fastgpt/global/support/permission/memberGroup/type'; -import { useMount } from 'ahooks'; +import { MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; +import { getTeamMembers } from '@/web/support/user/team/api'; +import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; +import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; +import _ from 'lodash'; +import MemberItemCard from '@/components/support/permission/MemberManager/MemberItemCard'; export type GroupFormType = { members: { @@ -53,42 +44,65 @@ function GroupEditModal({ const { userInfo } = useUserStore(); const { toast } = useToast(); - const allMembers = useContextSelector(TeamContext, (v) => v.members); - const MemberScrollData = useContextSelector(TeamContext, (v) => v.MemberScrollData); - const [hoveredMemberId, setHoveredMemberId] = useState(); + const [searchKey, setSearchKey] = useState(''); + const [selected, setSelected] = useState< + { name: string; tmbId: string; avatar: string; role: `${GroupMemberRole}` }[] + >([]); - const selectedMembersRef = useRef(null); - const [members, setMembers] = useState([]); - const editGroupId = useMemo(() => { - return group?._id; - }, [group]); + const { + data: allMembers = [], + ScrollData: MemberScrollData, + refreshList + } = useScrollPagination< + any, + PaginationResponse> + >(getTeamMembers, { + pageSize: 20, + params: { + status: 'active', + withOrgs: true, + searchKey + } + }); + const refetchMemberList = _.debounce(refreshList, 200); - useMount(async () => { - console.log('aaaaaa'); - if (editGroupId) { - const data = await getGroupMembers(editGroupId); - console.log(data); - setMembers(data); + useEffect(() => refetchMemberList, [searchKey]); + + const groupId = useMemo(() => String(group._id), [group._id]); + + const { data: groupMembers = [], ScrollData: GroupScrollData } = useScrollPagination< + any, + PaginationResponse< + TeamMemberItemType<{ withOrgs: true; withPermission: true; withGroupRole: true }> + > + >(getTeamMembers, { + pageSize: 100000, + params: { + groupId: groupId } }); - const [searchKey, setSearchKey] = useState(''); - const filtered = useMemo(() => { - return [ - ...allMembers.filter((member) => { - if (member.memberName.toLowerCase().includes(searchKey.toLowerCase())) return true; - return false; - }) - ]; - }, [searchKey, allMembers]); + useEffect(() => { + if (!groupId) return; + setSelected( + groupMembers.map((item) => ({ + name: item.memberName, + tmbId: item.tmbId, + avatar: item.avatar, + role: (item.groupRole ?? 'member') as `${GroupMemberRole}` + })) + ); + }, [groupId, groupMembers]); + + const [hoveredMemberId, setHoveredMemberId] = useState(); const { runAsync: onUpdate, loading: isLoadingUpdate } = useRequest2( async () => { - if (!editGroupId || !members.length) return; + if (!group._id || !groupMembers.length) return; return putUpdateGroup({ - groupId: editGroupId, - memberList: members + groupId: group._id, + memberList: selected }); }, { @@ -97,18 +111,21 @@ function GroupEditModal({ ); const isSelected = (memberId: string) => { - return members.find((item) => item.tmbId === memberId); + return selected.find((item) => item.tmbId === memberId); }; const myRole = useMemo(() => { if (userInfo?.team.permission.hasManagePer) { return 'owner'; } - return members.find((item) => item.tmbId === userInfo?.team.tmbId)?.role ?? 'member'; - }, [members, userInfo]); + return groupMembers.find((item) => item.tmbId === userInfo?.team.tmbId)?.groupRole ?? 'member'; + }, [groupMembers, userInfo]); const handleToggleSelect = (memberId: string) => { - if (myRole === 'owner' && memberId === members.find((item) => item.role === 'owner')?.tmbId) { + if ( + myRole === 'owner' && + memberId === groupMembers.find((item) => item.role === 'owner')?.tmbId + ) { toast({ title: t('user:team.group.toast.can_not_delete_owner'), status: 'error' @@ -118,18 +135,18 @@ function GroupEditModal({ if ( myRole === 'admin' && - members.find((item) => String(item.tmbId) === memberId)?.role !== 'member' + selected.find((item) => String(item.tmbId) === memberId)?.role !== 'member' ) { return; } if (isSelected(memberId)) { - setMembers(members.filter((item) => item.tmbId !== memberId)); + setSelected(selected.filter((item) => item.tmbId !== memberId)); } else { const member = allMembers.find((m) => m.tmbId === memberId); if (!member) return; - setMembers([ - ...members, + setSelected([ + ...selected, { name: member.memberName, avatar: member.avatar, @@ -142,14 +159,14 @@ function GroupEditModal({ const handleToggleAdmin = (memberId: string) => { if (myRole === 'owner' && isSelected(memberId)) { - const oldRole = members.find((item) => item.tmbId === memberId)?.role; + const oldRole = groupMembers.find((item) => item.tmbId === memberId)?.groupRole; if (oldRole === 'admin') { - setMembers( - members.map((item) => (item.tmbId === memberId ? { ...item, role: 'member' } : item)) + setSelected( + selected.map((item) => (item.tmbId === memberId ? { ...item, role: 'member' } : item)) ); } else { - setMembers( - members.map((item) => (item.tmbId === memberId ? { ...item, role: 'admin' } : item)) + setSelected( + selected.map((item) => (item.tmbId === memberId ? { ...item, role: 'admin' } : item)) ); } } @@ -184,37 +201,24 @@ function GroupEditModal({ }} /> - {filtered.map((member) => { + {allMembers.map((member) => { return ( - handleToggleSelect(member.tmbId)} - > - } - /> - - {member.memberName} - + name={member.memberName} + onChange={() => handleToggleSelect(member.tmbId)} + isChecked={!!isSelected(member.tmbId)} + orgs={member.orgs} + /> ); })} - {t('common:chosen') + ': ' + members.length} - - {members.map((member) => { + {t('common:chosen') + ': ' + selected.length} + + {selected.map((member) => { return ( setHoveredMemberId(member.tmbId)} @@ -284,7 +288,7 @@ function GroupEditModal({ ); })} - + diff --git a/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx index 48741fc91..9915b6afc 100644 --- a/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx @@ -15,12 +15,16 @@ import Avatar from '@fastgpt/web/components/common/Avatar'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { TeamContext } from '../context'; import { useContextSelector } from 'use-context-selector'; import { MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; import { GetSearchUserGroupOrg } from '@/web/support/user/api'; import { Omit } from '@fastgpt/web/components/common/DndDrag'; +import { getTeamMembers } from '@/web/support/user/team/api'; +import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; +import _ from 'lodash'; export function ChangeOwnerModal({ group, @@ -33,22 +37,24 @@ export function ChangeOwnerModal({ }) { const { t } = useTranslation(); - const [inputValue, setInputValue] = React.useState(''); - const { data: searchedData } = useRequest2( - async () => { - if (!inputValue) return; - return GetSearchUserGroupOrg(inputValue); - }, + const [searchKey, setSearchKey] = React.useState(''); + + const { + data: members = [], + ScrollData: MemberScrollData, + refreshList + } = useScrollPagination>>( + getTeamMembers, { - manual: false, - refreshDeps: [inputValue], - throttleWait: 500, - debounceWait: 200 + pageSize: 20, + params: { + searchKey: searchKey + } } ); - const { members: allMembers } = useContextSelector(TeamContext, (v) => v); - const memberList = searchedData ? searchedData.members : allMembers; + const search = _.debounce(refreshList, 500); + useEffect(() => search, [searchKey]); const { isOpen: isOpenMemberListMenu, @@ -108,9 +114,9 @@ export function ChangeOwnerModal({ )} { - setInputValue(e.target.value); + setSearchKey(e.target.value); setSelectedMember(null); }} onFocus={() => { @@ -120,7 +126,7 @@ export function ChangeOwnerModal({ {...(selectedMember && { pl: '10' })} /> - {isOpenMemberListMenu && memberList.length > 0 && ( + {isOpenMemberListMenu && members.length > 0 && ( - {memberList.map((item) => ( - { - setInputValue(item.memberName); - setSelectedMember(item); - onCloseMemberListMenu(); - }} - > - - - {item.memberName} - - - ))} + + {members.map((item) => ( + { + setSearchKey(item.memberName); + setSelectedMember(item); + onCloseMemberListMenu(); + }} + > + + + {item.memberName} + + + ))} + )} diff --git a/projects/app/src/pageComponents/account/team/MemberTable.tsx b/projects/app/src/pageComponents/account/team/MemberTable.tsx index a47701654..596eb1202 100644 --- a/projects/app/src/pageComponents/account/team/MemberTable.tsx +++ b/projects/app/src/pageComponents/account/team/MemberTable.tsx @@ -35,21 +35,40 @@ import { import format from 'date-fns/format'; import OrgTags from '@/components/support/user/team/OrgTags'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { downloadFetch } from '@/web/common/system/utils'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; +import { PaginationResponse, PaginationProps } from '@fastgpt/web/common/fetch/type'; +import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; +import _ from 'lodash'; +import MySelect from '@fastgpt/web/components/common/MySelect'; const InviteModal = dynamic(() => import('./Invite/InviteModal')); const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal')); function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { const { t } = useTranslation(); + const statusOptions = [ + { + label: t('common:common.All'), + value: undefined + }, + { + label: t('common:user.team.member.active'), + value: 'active' + }, + { + label: t('account_team:leave'), + value: 'inactive' + } + ]; const { userInfo } = useUserStore(); const { feConfigs } = useSystemStore(); const isSyncMember = feConfigs?.register_method?.includes('sync'); const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v); + const [status, setStatus] = useState(); const { isOpen: isOpenTeamTagsAsync, @@ -58,35 +77,36 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { } = useDisclosure(); // member action + const [searchKey, setSearchKey] = useState(''); const { data: members = [], isLoading: loadingMembers, refreshList: refetchMemberList, ScrollData: MemberScrollData - } = useScrollPagination(getTeamMembers, { + } = useScrollPagination< + any, + PaginationResponse> + >(getTeamMembers, { pageSize: 20, params: { - withLeaved: true + status, + withPermission: true, + withOrgs: true, + searchKey } }); - const [searchText, setSearchText] = useState(''); - const { data: searchMembersData, run: refreshSearchMembers } = useRequest2( - async () => { - if (!searchText) return Promise.resolve(); - return GetSearchUserGroupOrg(searchText, { members: true, orgs: false, groups: false }); - }, - { - manual: false, - throttleWait: 500, - refreshDeps: [searchText] - } - ); + const refreshList = _.debounce(() => { + refetchMemberList(); + }, 200); + + useEffect(() => { + refreshList(); + }, [searchKey, status]); const onRefreshMembers = useCallback(() => { refetchMemberList(); - refreshSearchMembers(); - }, [refetchMemberList, refreshSearchMembers]); + }, [refetchMemberList]); const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure(); @@ -133,10 +153,13 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { {Tabs} + + setStatus(v)} /> + setSearchText(e.target.value)} + onChange={(e) => setSearchKey(e.target.value)} /> {userInfo?.team.permission.hasManagePer && feConfigs?.show_team_chat && ( @@ -229,91 +252,89 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { - {(searchText && searchMembersData ? searchMembersData.members : members).map( - (member) => ( - - - - - - {member.memberName} - {member.status !== 'active' && ( - - {t('account_team:leave')} - - )} - - - - {member.contact || '-'} - - {(() => { - return ; - })()} - - - - {format(new Date(member.createTime), 'yyyy-MM-dd HH:mm:ss')} - - {member.updateTime - ? format(new Date(member.updateTime), 'yyyy-MM-dd HH:mm:ss') - : '-'} - - - - - {userInfo?.team.permission.hasManagePer && - member.role !== TeamMemberRoleEnum.owner && - member.tmbId !== userInfo?.team.tmbId && - (member.status === TeamMemberStatusEnum.active ? ( + {members.map((member) => ( + + + + + + {member.memberName} + {member.status !== 'active' && ( + + {t('account_team:leave')} + + )} + + + + {member.contact || '-'} + + {(() => { + return ; + })()} + + + + {format(new Date(member.createTime), 'yyyy-MM-dd HH:mm:ss')} + + {member.updateTime + ? format(new Date(member.updateTime), 'yyyy-MM-dd HH:mm:ss') + : '-'} + + + + + {userInfo?.team.permission.hasManagePer && + member.role !== TeamMemberRoleEnum.owner && + member.tmbId !== userInfo?.team.tmbId && + (member.status === TeamMemberStatusEnum.active ? ( + { + openRemoveMember( + () => onRemoveMember(member.tmbId), + undefined, + t('account_team:remove_tip', { + username: member.memberName + }) + )(); + }} + /> + ) : ( + member.status === TeamMemberStatusEnum.forbidden && ( { - openRemoveMember( - () => onRemoveMember(member.tmbId), + openRestoreMember( + () => onRestore(member.tmbId), undefined, - t('account_team:remove_tip', { + t('account_team:restore_tip', { username: member.memberName }) )(); }} /> - ) : ( - member.status === TeamMemberStatusEnum.forbidden && ( - { - openRestoreMember( - () => onRestore(member.tmbId), - undefined, - t('account_team:restore_tip', { - username: member.memberName - }) - )(); - }} - /> - ) - ))} - - - ) - )} + ) + ))} + + + ))} diff --git a/projects/app/src/pageComponents/account/team/OrgManage/OrgInfoModal.tsx b/projects/app/src/pageComponents/account/team/OrgManage/OrgInfoModal.tsx index 01d81362d..47614cbea 100644 --- a/projects/app/src/pageComponents/account/team/OrgManage/OrgInfoModal.tsx +++ b/projects/app/src/pageComponents/account/team/OrgManage/OrgInfoModal.tsx @@ -29,12 +29,14 @@ function OrgInfoModal({ editOrg, onClose, onSuccess, - updateCurrentOrg + updateCurrentOrg, + parentId }: { editOrg: OrgFormType; onClose: () => void; onSuccess: () => void; updateCurrentOrg: (data: { name?: string; avatar?: string; description?: string }) => void; + parentId?: string; }) { const { t } = useTranslation(); @@ -51,10 +53,11 @@ function OrgInfoModal({ const { run: onCreate, loading: isLoadingCreate } = useRequest2( async (data: OrgFormType) => { + if (parentId === undefined) return; return postCreateOrg({ name: data.name, avatar: data.avatar, - path: editOrg.path, + orgId: parentId, description: data.description }); }, diff --git a/projects/app/src/pageComponents/account/team/OrgManage/OrgMemberManageModal.tsx b/projects/app/src/pageComponents/account/team/OrgManage/OrgMemberManageModal.tsx index 0bb789c86..f67263933 100644 --- a/projects/app/src/pageComponents/account/team/OrgManage/OrgMemberManageModal.tsx +++ b/projects/app/src/pageComponents/account/team/OrgManage/OrgMemberManageModal.tsx @@ -1,14 +1,5 @@ -import { getOrgMembers, putUpdateOrgMembers } from '@/web/support/user/team/org/api'; -import { - Box, - Button, - Checkbox, - Flex, - Grid, - HStack, - ModalBody, - ModalFooter -} from '@chakra-ui/react'; +import { putUpdateOrgMembers } from '@/web/support/user/team/org/api'; +import { Box, Button, Flex, Grid, HStack, ModalBody, ModalFooter } from '@chakra-ui/react'; import type { GroupMemberRole } from '@fastgpt/global/support/permission/memberGroup/constant'; import Avatar from '@fastgpt/web/components/common/Avatar'; import MyIcon from '@fastgpt/web/components/common/Icon'; @@ -17,11 +8,11 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useState } from 'react'; import { OrgListItemType } from '@fastgpt/global/support/user/team/org/type'; import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; -import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; import { getTeamMembers } from '@/web/support/user/team/api'; +import MemberItemCard from '@/components/support/permission/MemberManager/MemberItemCard'; export type GroupFormType = { members: { @@ -30,16 +21,6 @@ export type GroupFormType = { }[]; }; -function CheckboxIcon({ - name -}: { - isChecked?: boolean; - isIndeterminate?: boolean; - name: IconNameType; -}) { - return ; -} - function OrgMemberManageModal({ currentOrg, refetchOrgs, @@ -56,17 +37,24 @@ function OrgMemberManageModal({ ScrollData: MemberScrollData, isLoading: isLoadingMembers } = useScrollPagination(getTeamMembers, { - pageSize: 20 + pageSize: 20, + params: { + withOrgs: true, + withPermission: false, + status: 'active' + } }); const { data: orgMembers, ScrollData: OrgMemberScrollData, isLoading: isLoadingOrgMembers - } = useScrollPagination(getOrgMembers, { - pageSize: 20, + } = useScrollPagination(getTeamMembers, { + pageSize: 100000, params: { - orgPath: getOrgChildrenPath(currentOrg) + orgId: currentOrg._id, + withOrgs: false, + withPermission: false } }); @@ -83,11 +71,6 @@ function OrgMemberManageModal({ }, [orgMembers]); const [searchKey, setSearchKey] = useState(''); - const filterMembers = useMemo(() => { - if (!searchKey) return allMembers; - const regx = new RegExp(searchKey, 'i'); - return allMembers.filter((member) => regx.test(member.memberName)); - }, [searchKey, allMembers]); const { run: onUpdate, loading: isLoadingUpdate } = useRequest2( () => { @@ -165,35 +148,20 @@ function OrgMemberManageModal({ }} /> - {filterMembers.map((member) => { + {allMembers.map((member) => { return ( - handleToggleSelect(member.tmbId)} - > - } - pointerEvents="none" - /> - - {member.memberName} - + name={member.memberName} + onChange={() => handleToggleSelect(member.tmbId)} + isChecked={!!isSelected(member.tmbId)} + orgs={member.orgs} + /> ); })} - {/* */} (); - const { userInfo } = useUserStore(); - const team = userInfo?.team!; const { runAsync: onMoveOrg, loading } = useRequest2(putMoveOrg, { onSuccess: () => { @@ -39,7 +37,7 @@ function OrgMoveModal({ iconColor="primary.600" > - +