From 4871a6980f395e12e897bd8f0d8e0390f86cedf6 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Wed, 26 Mar 2025 00:02:14 +0800 Subject: [PATCH] perf: member group (#4324) * sync collection * remove lock * perf: member group --- .../support/permission/memberGroup/api.d.ts | 4 + .../support/permission/memberGroup/type.d.ts | 33 ++-- packages/global/support/user/api.d.ts | 5 +- .../service/support/user/team/controller.ts | 8 + .../permission/MemberManager/MemberModal.tsx | 11 +- .../support/user/team/Info/MemberTag.tsx | 8 +- .../team/GroupManage/GroupInfoModal.tsx | 42 ++--- .../team/GroupManage/GroupManageMember.tsx | 17 +- .../GroupManage/GroupTransferOwnerModal.tsx | 47 +++--- .../account/team/GroupManage/index.tsx | 120 ++++++-------- .../account/team/MemberTable.tsx | 149 ++++++++---------- .../account/team/OrgManage/index.tsx | 5 +- .../pageComponents/account/team/context.tsx | 10 +- .../src/web/support/user/team/group/api.ts | 9 +- 14 files changed, 213 insertions(+), 255 deletions(-) create mode 100644 packages/global/support/permission/memberGroup/api.d.ts diff --git a/packages/global/support/permission/memberGroup/api.d.ts b/packages/global/support/permission/memberGroup/api.d.ts new file mode 100644 index 000000000..0e81cc1d4 --- /dev/null +++ b/packages/global/support/permission/memberGroup/api.d.ts @@ -0,0 +1,4 @@ +export type GetGroupListBody = { + searchKey?: string; + withMembers?: boolean; +}; diff --git a/packages/global/support/permission/memberGroup/type.d.ts b/packages/global/support/permission/memberGroup/type.d.ts index f70e9a89e..5011d74f6 100644 --- a/packages/global/support/permission/memberGroup/type.d.ts +++ b/packages/global/support/permission/memberGroup/type.d.ts @@ -1,6 +1,7 @@ import { TeamMemberItemType } from 'support/user/team/type'; import { TeamPermission } from '../user/controller'; import { GroupMemberRole } from './constant'; +import { Permission } from '../controller'; type MemberGroupSchemaType = { _id: string; @@ -16,23 +17,25 @@ type GroupMemberSchemaType = { role: `${GroupMemberRole}`; }; -type MemberGroupType = MemberGroupSchemaType & { - members: { - tmbId: string; - name: string; - avatar: string; - }[]; - count: number; - owner: { - tmbId: string; - name: string; - avatar: string; - }; - canEdit: boolean; +type MemberGroupListItemType = MemberGroupSchemaType & { + members: T extends true + ? { + tmbId: string; + name: string; + avatar: string; + }[] + : undefined; + count: T extends true ? number : undefined; + owner?: T extends true + ? { + tmbId: string; + name: string; + avatar: string; + } + : undefined; + permission: T extends true ? Permission : undefined; }; -type MemberGroupListType = MemberGroupType[]; - type GroupMemberItemType = { tmbId: string; name: string; diff --git a/packages/global/support/user/api.d.ts b/packages/global/support/user/api.d.ts index 2f8b4f5bd..8c5036d4a 100644 --- a/packages/global/support/user/api.d.ts +++ b/packages/global/support/user/api.d.ts @@ -1,4 +1,7 @@ -import { MemberGroupSchemaType, MemberGroupType } from 'support/permission/memberGroup/type'; +import { + MemberGroupSchemaType, + MemberGroupListItemType +} from 'support/permission/memberGroup/type'; import { OAuthEnum } from './constant'; import { TrackRegisterParams } from './login/api'; import { TeamMemberStatusEnum } from './team/constant'; diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index b90dff737..8c3c4cf10 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -55,6 +55,14 @@ async function getTeamMember(match: Record): Promise { + const tmb = await MongoTeamMember.findOne({ + teamId, + role: TeamMemberRoleEnum.owner + }).lean(); + return tmb; +}; + export async function getTmbInfoByTmbId({ tmbId }: { tmbId: string }) { if (!tmbId) { return Promise.reject('tmbId or userId is required'); diff --git a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx index 42534f59c..02b762893 100644 --- a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx @@ -28,20 +28,17 @@ import { DEFAULT_USER_AVATAR } from '@fastgpt/global/common/system/constants'; import Path from '@/components/common/folder/Path'; -import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; -import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type'; import { OrgListItemType, OrgType } from '@fastgpt/global/support/user/team/org/type'; import { useContextSelector } from 'use-context-selector'; import { CollaboratorContext } from './context'; import { getTeamMembers } from '@/web/support/user/team/api'; import { getGroupList } from '@/web/support/user/team/group/api'; -import { getOrgList, getOrgMembers } from '@/web/support/user/team/org/api'; import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; import MemberItemCard from './MemberItemCard'; import { GetSearchUserGroupOrg } from '@/web/support/user/api'; import useOrg from '@/web/support/user/team/org/hooks/useOrg'; -import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; +import { MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; const HoverBoxStyle = { bgColor: 'myGray.50', @@ -76,7 +73,9 @@ function MemberModal({ const { data: groups = [], loading: loadingGroupsAndOrgs } = useRequest2( async () => { if (!userInfo?.team?.teamId) return []; - return getGroupList(); + return getGroupList({ + withMembers: false + }); }, { manual: false, @@ -117,7 +116,7 @@ function MemberModal({ return members; }, [searchText, members, searchedData?.members]); - const [selectedGroupList, setSelectedGroupList] = useState([]); + const [selectedGroupList, setSelectedGroupList] = useState[]>([]); const filterGroups = useMemo(() => { if (searchText) { return searchedData?.groups.map((item) => ({ diff --git a/projects/app/src/components/support/user/team/Info/MemberTag.tsx b/projects/app/src/components/support/user/team/Info/MemberTag.tsx index c62f0f1cc..3868f37d4 100644 --- a/projects/app/src/components/support/user/team/Info/MemberTag.tsx +++ b/projects/app/src/components/support/user/team/Info/MemberTag.tsx @@ -3,16 +3,16 @@ import Avatar from '@fastgpt/web/components/common/Avatar'; import React from 'react'; type Props = { - name: string; - avatar: string; + name?: string; + avatar?: string; }; function MemberTag({ name, avatar }: Props) { return ( - + {avatar && } - {name} + {name || '-'} ); diff --git a/projects/app/src/pageComponents/account/team/GroupManage/GroupInfoModal.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupInfoModal.tsx index c86134bfe..d97b87590 100644 --- a/projects/app/src/pageComponents/account/team/GroupManage/GroupInfoModal.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/GroupInfoModal.tsx @@ -4,15 +4,13 @@ import Avatar from '@fastgpt/web/components/common/Avatar'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import { useTranslation } from 'next-i18next'; -import React, { useMemo } from 'react'; +import React from 'react'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useForm } from 'react-hook-form'; -import { useContextSelector } from 'use-context-selector'; -import { TeamContext } from '../context'; import { postCreateGroup, putUpdateGroup } from '@/web/support/user/team/group/api'; import { DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants'; -import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; +import { MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; export type GroupFormType = { avatar: string; @@ -21,17 +19,15 @@ export type GroupFormType = { function GroupInfoModal({ onClose, - editGroupId, - groups, - refetchGroups + editGroup, + onSuccess }: { onClose: () => void; - editGroupId?: string; - groups: MemberGroupListType; - refetchGroups: () => void; + editGroup?: MemberGroupListItemType; + onSuccess: () => void; }) { - const { refetchMembers } = useContextSelector(TeamContext, (v) => v); const { t } = useTranslation(); + const { File: AvatarSelect, onOpen: onOpenSelectAvatar, @@ -41,14 +37,10 @@ function GroupInfoModal({ multiple: false }); - const group = useMemo(() => { - return groups.find((item) => item._id === editGroupId); - }, [editGroupId, groups]); - const { register, handleSubmit, getValues, setValue } = useForm({ defaultValues: { - name: group?.name || '', - avatar: group?.avatar || DEFAULT_TEAM_AVATAR + name: editGroup?.name || '', + avatar: editGroup?.avatar || DEFAULT_TEAM_AVATAR } }); @@ -74,21 +66,21 @@ function GroupInfoModal({ }); }, { - onSuccess: () => Promise.all([onClose(), refetchGroups(), refetchMembers()]) + onSuccess: () => Promise.all([onClose(), onSuccess()]) } ); const { runAsync: onUpdate, loading: isLoadingUpdate } = useRequest2( async (data: GroupFormType) => { - if (!editGroupId) return; + if (!editGroup) return; return putUpdateGroup({ - groupId: editGroupId, + groupId: editGroup._id, name: data.name, avatar: data.avatar }); }, { - onSuccess: () => Promise.all([onClose(), refetchGroups(), refetchMembers()]) + onSuccess: () => Promise.all([onClose(), onSuccess()]) } ); @@ -97,8 +89,8 @@ function GroupInfoModal({ return ( {t('user:team.avatar_and_name')} @@ -120,14 +112,14 @@ function GroupInfoModal({ diff --git a/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx index 7b13fc301..24341ba32 100644 --- a/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx @@ -26,7 +26,7 @@ import { DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import { GroupMemberItemType, - MemberGroupListType + MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; import { useMount } from 'ahooks'; @@ -43,24 +43,19 @@ export type GroupFormType = { function GroupEditModal({ onClose, editGroupId, - groups, - refetchGroups + group, + onSuccess }: { onClose: () => void; editGroupId?: string; - groups: MemberGroupListType; - refetchGroups: () => void; + group: MemberGroupListItemType; + onSuccess: () => void; }) { const { t } = useTranslation(); const { userInfo } = useUserStore(); const { toast } = useToast(); - const group = useMemo(() => { - return groups.find((item) => item._id === editGroupId); - }, [editGroupId, groups]); - const allMembers = useContextSelector(TeamContext, (v) => v.members); - const refetchMembers = useContextSelector(TeamContext, (v) => v.refetchMembers); const MemberScrollData = useContextSelector(TeamContext, (v) => v.MemberScrollData); const [hoveredMemberId, setHoveredMemberId] = useState(); @@ -94,7 +89,7 @@ function GroupEditModal({ }); }, { - onSuccess: () => Promise.all([onClose(), refetchGroups(), refetchMembers()]) + onSuccess: () => Promise.all([onClose(), onSuccess()]) } ); diff --git a/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx index 93b0bc7cc..48741fc91 100644 --- a/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx @@ -1,4 +1,4 @@ -import { putGroupChangeOwner, putUpdateGroup } from '@/web/support/user/team/group/api'; +import { putGroupChangeOwner } from '@/web/support/user/team/group/api'; import { Box, Flex, @@ -15,26 +15,24 @@ 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, { useMemo, useState } from 'react'; +import React, { useState } from 'react'; import { TeamContext } from '../context'; import { useContextSelector } from 'use-context-selector'; -import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; +import { MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; import { GetSearchUserGroupOrg } from '@/web/support/user/api'; import { Omit } from '@fastgpt/web/components/common/DndDrag'; -export type ChangeOwnerModalProps = { - groupId: string; - groups: MemberGroupListType; - refetchGroups: () => void; -}; - export function ChangeOwnerModal({ - onClose, - groupId, - groups, - refetchGroups -}: ChangeOwnerModalProps & { onClose: () => void }) { + group, + onSuccess, + onClose +}: { + group: MemberGroupListItemType; + onSuccess: () => void; + onClose: () => void; +}) { const { t } = useTranslation(); + const [inputValue, setInputValue] = React.useState(''); const { data: searchedData } = useRequest2( async () => { @@ -50,14 +48,8 @@ export function ChangeOwnerModal({ ); const { members: allMembers } = useContextSelector(TeamContext, (v) => v); - const group = useMemo(() => { - return groups.find((item) => item._id === groupId); - }, [groupId, groups]); - const memberList = searchedData ? searchedData.members : allMembers; - const [keepAdmin, setKeepAdmin] = useState(true); - const { isOpen: isOpenMemberListMenu, onClose: onCloseMemberListMenu, @@ -69,10 +61,12 @@ export function ChangeOwnerModal({ 'permission' | 'teamId' > | null>(null); - const { runAsync, loading } = useRequest2( - (tmbId: string) => putGroupChangeOwner(groupId, tmbId), + const [keepAdmin, setKeepAdmin] = useState(true); + + const { runAsync: onTransfer, loading } = useRequest2( + (tmbId: string) => putGroupChangeOwner(group._id, tmbId), { - onSuccess: () => Promise.all([onClose(), refetchGroups()]), + onSuccess: () => Promise.all([onClose(), onSuccess()]), successToast: t('common:permission.change_owner_success'), errorToast: t('common:permission.change_owner_failed') } @@ -82,7 +76,7 @@ export function ChangeOwnerModal({ if (!selectedMember) { return; } - await runAsync(selectedMember.tmbId); + await onTransfer(selectedMember.tmbId); }; return ( @@ -92,7 +86,6 @@ export function ChangeOwnerModal({ iconColor="primary.600" onClose={onClose} title={t('common:permission.change_owner')} - isLoading={loading} > @@ -181,7 +174,9 @@ export function ChangeOwnerModal({ - + diff --git a/projects/app/src/pageComponents/account/team/GroupManage/index.tsx b/projects/app/src/pageComponents/account/team/GroupManage/index.tsx index 574e52117..78bb0d60c 100644 --- a/projects/app/src/pageComponents/account/team/GroupManage/index.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/index.tsx @@ -3,7 +3,6 @@ import { Box, Button, Flex, - HStack, Table, TableContainer, Tbody, @@ -16,20 +15,18 @@ import { import { useTranslation } from 'next-i18next'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import MyBox from '@fastgpt/web/components/common/MyBox'; -import { useContextSelector } from 'use-context-selector'; -import { TeamContext } from '../context'; import MyMenu, { MenuItemType } from '@fastgpt/web/components/common/MyMenu'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useUserStore } from '@/web/support/user/useUserStore'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { deleteGroup, getGroupList, getGroupMembers } from '@/web/support/user/team/group/api'; +import { deleteGroup, getGroupList } from '@/web/support/user/team/group/api'; import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; import MemberTag from '../../../../components/support/user/team/Info/MemberTag'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import dynamic from 'next/dynamic'; import { useState } from 'react'; import IconButton from '../OrgManage/IconButton'; -import { MemberGroupType } from '@fastgpt/global/support/permission/memberGroup/type'; +import { MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; const ChangeOwnerModal = dynamic(() => import('./GroupTransferOwnerModal')); const GroupInfoModal = dynamic(() => import('./GroupInfoModal')); @@ -39,26 +36,23 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { const { t } = useTranslation(); const { userInfo } = useUserStore(); - const { members, teamSize } = useContextSelector(TeamContext, (v) => v); - const { data: groups = [], loading: isLoadingGroups, refresh: refetchGroups - } = useRequest2(getGroupList, { + } = useRequest2(() => getGroupList({ withMembers: true }), { manual: false, refreshDeps: [userInfo?.team?.teamId] }); - const [editGroup, setEditGroup] = useState(); + const [editGroup, setEditGroup] = useState>(); const { isOpen: isOpenGroupInfo, onOpen: onOpenGroupInfo, onClose: onCloseGroupInfo } = useDisclosure(); - - const onEditGroupInfo = (e: MemberGroupType) => { + const onEditGroupInfo = (e: MemberGroupListItemType) => { setEditGroup(e); onOpenGroupInfo(); }; @@ -67,7 +61,6 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { type: 'delete', content: t('account_team:confirm_delete_group') }); - const { runAsync: delDeleteGroup } = useRequest2(deleteGroup, { onSuccess: () => { refetchGroups(); @@ -79,21 +72,17 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { onOpen: onOpenManageGroupMember, onClose: onCloseManageGroupMember } = useDisclosure(); - const onManageMember = (e: MemberGroupType) => { + const onManageMember = (e: MemberGroupListItemType) => { setEditGroup(e); onOpenManageGroupMember(); }; - const hasGroupManagePer = (group: (typeof groups)[0]) => userInfo?.team.permission.hasManagePer; - - const isGroupOwner = (group: (typeof groups)[0]) => userInfo?.team.permission.hasManagePer; - const { isOpen: isOpenChangeOwner, onOpen: onOpenChangeOwner, onClose: onCloseChangeOwner } = useDisclosure(); - const onChangeOwner = (e: MemberGroupType) => { + const onChangeOwner = (e: MemberGroupListItemType) => { setEditGroup(e); onOpenChangeOwner(); }; @@ -134,58 +123,38 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { {groups?.map((group) => ( - - - - ({group.name === DefaultGroupName ? teamSize : group.count}) - - item.role === 'owner')?.memberName ?? '' - : group.owner.name - } - avatar={ - group.name === DefaultGroupName - ? members.find((item) => item.role === 'owner')?.avatar ?? '' - : group.owner.avatar + group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name } + avatar={group.avatar} /> - {group.name === DefaultGroupName ? ( - v.avatar)} total={teamSize} /> - ) : hasGroupManagePer(group) ? ( - - onManageMember(group)}> - members.find((m) => m.tmbId === v.tmbId)?.avatar ?? '' - )} - total={group.count} - /> - - - ) : ( - members.find((m) => m.tmbId === v.tmbId)?.avatar ?? '' - )} - total={group.count} - /> - )} + - {hasGroupManagePer(group) && group.name !== DefaultGroupName && ( + + onManageMember(group) + } + : {})} + > + v.avatar)} + total={group.count} + /> + + + + + {group.permission?.hasManagePer && ( } menuList={[ @@ -205,7 +174,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { onManageMember(group); } }, - ...(isGroupOwner(group) + ...(group.permission?.isOwner ? [ { label: t('account_team:transfer_ownership'), @@ -239,34 +208,33 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { - {isOpenChangeOwner && editGroup && ( - - )} + {isOpenGroupInfo && ( { onCloseGroupInfo(); setEditGroup(undefined); }} - editGroupId={editGroup?._id} /> )} + {isOpenChangeOwner && editGroup && ( + + )} + {isOpenManageGroupMember && editGroup && ( { onCloseManageGroupMember(); setEditGroup(undefined); }} - editGroupId={editGroup._id} + onSuccess={refetchGroups} /> )} diff --git a/projects/app/src/pageComponents/account/team/MemberTable.tsx b/projects/app/src/pageComponents/account/team/MemberTable.tsx index edc1eedf4..cb8c5e4ea 100644 --- a/projects/app/src/pageComponents/account/team/MemberTable.tsx +++ b/projects/app/src/pageComponents/account/team/MemberTable.tsx @@ -20,8 +20,9 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea'; import { delRemoveMember, - postRestoreMember, - putUpdateMemberNameByManager + getTeamMembers, + putUpdateMemberNameByManager, + postRestoreMember } from '@/web/support/user/team/api'; import Tag from '@fastgpt/web/components/common/Tag'; import Icon from '@fastgpt/web/components/common/Icon'; @@ -33,7 +34,6 @@ import dynamic from 'next/dynamic'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { delLeaveTeam } from '@/web/support/user/team/api'; import { GetSearchUserGroupOrg, postSyncMembers } from '@/web/support/user/api'; -import MyLoading from '@fastgpt/web/components/common/MyLoading'; import { TeamMemberRoleEnum, TeamMemberStatusEnum @@ -41,10 +41,12 @@ 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 { useState } from 'react'; +import { useCallback, useState } from 'react'; import { downloadFetch } from '@/web/common/system/utils'; import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; import { useToast } from '@fastgpt/web/hooks/useToast'; +import MyBox from '@fastgpt/web/components/common/MyBox'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; const InviteModal = dynamic(() => import('./Invite/InviteModal')); const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal')); @@ -55,20 +57,72 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { const { userInfo } = useUserStore(); const { feConfigs } = useSystemStore(); + const isSyncMember = feConfigs?.register_method?.includes('sync'); - const { myTeams, refetchTeams, members, refetchMembers, onSwitchTeam, MemberScrollData } = - useContextSelector(TeamContext, (v) => v); + const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v); const { isOpen: isOpenTeamTagsAsync, onOpen: onOpenTeamTagsAsync, onClose: onCloseTeamTagsAsync } = useDisclosure(); + + // member action + const { + data: members = [], + isLoading: loadingMembers, + refreshList: refetchMemberList, + ScrollData: MemberScrollData + } = useScrollPagination(getTeamMembers, { + pageSize: 20, + params: { + withLeaved: true + } + }); + + 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 onRefreshMembers = useCallback(() => { + refetchMemberList(); + refreshSearchMembers(); + }, [refetchMemberList, refreshSearchMembers]); + const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure(); + const { runAsync: onSyncMember, loading: isSyncing } = useRequest2(postSyncMembers, { + onSuccess: onRefreshMembers, + successToast: t('account_team:sync_member_success'), + errorToast: t('account_team:sync_member_failed') + }); + + const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({ + content: t('account_team:confirm_leave_team') + }); + const { runAsync: onLeaveTeam } = useRequest2(delLeaveTeam, { + onSuccess() { + const defaultTeam = myTeams[0]; + onSwitchTeam(defaultTeam.teamId); + }, + errorToast: t('account_team:user_team_leave_team_failed') + }); + const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm({ type: 'delete' }); + const { runAsync: onRemoveMember } = useRequest2(delRemoveMember, { + onSuccess: onRefreshMembers + }); const { ConfirmModal: ConfirmRestoreMemberModal, openConfirm: openRestoreMember } = useConfirm({ type: 'common', @@ -76,59 +130,13 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { iconSrc: 'common/confirm/restoreTip', iconColor: 'primary.500' }); - - const [searchText, setSearchText] = useState(''); - const isSyncMember = feConfigs.register_method?.includes('sync'); - - const { data: searchMembersData } = useRequest2( - async () => { - if (!searchText) return Promise.resolve(); - return GetSearchUserGroupOrg(searchText, { members: true, orgs: false, groups: false }); - }, - { - manual: false, - throttleWait: 500, - debounceWait: 200, - refreshDeps: [searchText] - } - ); - - const { runAsync: onLeaveTeam } = useRequest2( - async () => { - const defaultTeam = myTeams[0]; - // change to personal team - onSwitchTeam(defaultTeam.teamId); - return delLeaveTeam(); - }, - { - onSuccess() { - refetchTeams(); - refetchMembers(); - }, - errorToast: t('account_team:user_team_leave_team_failed') - } - ); - const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({ - content: t('account_team:confirm_leave_team') - }); - - const { runAsync: onSyncMember, loading: isSyncing } = useRequest2(postSyncMembers, { - onSuccess() { - refetchMembers(); - }, - successToast: t('account_team:sync_member_success'), - errorToast: t('account_team:sync_member_failed') - }); - - const { runAsync: onRestore, loading: isUpdateInvite } = useRequest2(postRestoreMember, { - onSuccess() { - refetchMembers(); - }, - successToast: t('common:user.team.invite.Accepted'), + const { runAsync: onRestore } = useRequest2(postRestoreMember, { + onSuccess: onRefreshMembers, + successToast: t('common:common.Success'), errorToast: t('common:user.team.invite.Reject') }); - const isLoading = isUpdateInvite || isSyncing; + const isLoading = loadingMembers || isSyncing; const { EditModal: EditMemberNameModal, onOpenModal: openEditMemberName } = useEditTextarea({ title: t('account_team:edit_member'), @@ -141,7 +149,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { defaultVal: memberName, onSuccess: (newName: string) => { return putUpdateMemberNameByManager(tmbId, newName).then(() => { - refetchMembers(); + onRefreshMembers(); }); }, onError: (err) => { @@ -155,7 +163,6 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { return ( <> - {isLoading && } {Tabs} @@ -237,7 +244,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { - + @@ -321,27 +328,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { }} onClick={() => { openRemoveMember( - () => delRemoveMember(member.tmbId).then(refetchMembers), - undefined, - t('account_team:remove_tip', { - username: member.memberName - }) - )(); - }} - /> - { - openRemoveMember( - () => delRemoveMember(member.tmbId).then(refetchMembers), + () => onRemoveMember(member.tmbId), undefined, t('account_team:remove_tip', { username: member.memberName @@ -385,7 +372,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { - + {isOpenInvite && userInfo?.team?.teamId && } diff --git a/projects/app/src/pageComponents/account/team/OrgManage/index.tsx b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx index 1249bde30..15bd7dc78 100644 --- a/projects/app/src/pageComponents/account/team/OrgManage/index.tsx +++ b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx @@ -14,7 +14,7 @@ import { Tr, VStack } from '@chakra-ui/react'; -import type { OrgListItemType, OrgType } from '@fastgpt/global/support/user/team/org/type'; +import type { OrgListItemType } from '@fastgpt/global/support/user/team/org/type'; import Avatar from '@fastgpt/web/components/common/Avatar'; import MyIcon from '@fastgpt/web/components/common/Icon'; import type { IconNameType } from '@fastgpt/web/components/common/Icon/type'; @@ -32,12 +32,9 @@ import { defaultOrgForm, type OrgFormType } from './OrgInfoModal'; import dynamic from 'next/dynamic'; import MyBox from '@fastgpt/web/components/common/MyBox'; import Path from '@/components/common/folder/Path'; -import { ParentIdType, ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type'; -import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { delRemoveMember } from '@/web/support/user/team/api'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; -import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; import useOrg from '@/web/support/user/team/org/hooks/useOrg'; const OrgInfoModal = dynamic(() => import('./OrgInfoModal')); diff --git a/projects/app/src/pageComponents/account/team/context.tsx b/projects/app/src/pageComponents/account/team/context.tsx index 0c29585b6..e5a30b16f 100644 --- a/projects/app/src/pageComponents/account/team/context.tsx +++ b/projects/app/src/pageComponents/account/team/context.tsx @@ -13,10 +13,8 @@ import { useUserStore } from '@/web/support/user/useUserStore'; import type { TeamTmbItemType, TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; -import { getGroupList } from '@/web/support/user/team/group/api'; -import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; -import { OrgType } from '@fastgpt/global/support/user/team/org/type'; +import { useRouter } from 'next/router'; const EditInfoModal = dynamic(() => import('./EditInfoModal')); @@ -55,6 +53,8 @@ export const TeamContext = createContext({ export const TeamModalContextProvider = ({ children }: { children: ReactNode }) => { const { t } = useTranslation(); + const router = useRouter(); + const [editTeamData, setEditTeamData] = useState(); const { userInfo, initUserInfo } = useUserStore(); @@ -96,10 +96,12 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode }) const { runAsync: onSwitchTeam, loading: isSwitchingTeam } = useRequest2( async (teamId: string) => { await putSwitchTeam(teamId); - refetchMembers(); return initUserInfo(); }, { + onSuccess: () => { + router.reload(); + }, errorToast: t('common:user.team.Switch Team Failed') } ); diff --git a/projects/app/src/web/support/user/team/group/api.ts b/projects/app/src/web/support/user/team/group/api.ts index 7de009cfa..146510735 100644 --- a/projects/app/src/web/support/user/team/group/api.ts +++ b/projects/app/src/web/support/user/team/group/api.ts @@ -1,14 +1,19 @@ import { DELETE, GET, POST, PUT } from '@/web/common/api/request'; +import { GetGroupListBody } from '@fastgpt/global/support/permission/memberGroup/api'; import type { GroupMemberItemType, - MemberGroupListType + MemberGroupListItemType } from '@fastgpt/global/support/permission/memberGroup/type'; import type { postCreateGroupData, putUpdateGroupData } from '@fastgpt/global/support/user/team/group/api'; -export const getGroupList = () => GET('/proApi/support/user/team/group/list'); +export const getGroupList = (data: GetGroupListBody) => + POST[]>('/proApi/support/user/team/group/list', data).then((res) => { + console.log(res); + return res; + }); export const postCreateGroup = (data: postCreateGroupData) => POST('/proApi/support/user/team/group/create', data);