Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docSite/content/zh-cn/docs/development/upgrading/492.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ weight: 799
2. 知识库分块增加自定义分隔符预设值,同时支持自定义换行符分割。
3. 外部变量改名:自定义变量。 并且支持在测试时调试,在分享链接中,该变量直接隐藏。
4. 集合同步时,支持同步修改标题。
5. 团队成员管理重构,抽离主流 IM SSO(企微、飞书、钉钉),并支持通过自定义 SSO 接入 FastGPT。同时完善与外部系统的成员同步。

## ⚙️ 优化

Expand Down
13 changes: 9 additions & 4 deletions packages/global/support/user/team/org/type.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { TeamPermission } from 'support/permission/user/controller';
import type { TeamPermission } from '../../../permission/user/controller';
import { ResourcePermissionType } from '../type';
import { SourceMemberType } from 'support/user/type';
import { SourceMemberType } from '../../type';

type OrgSchemaType = {
_id: string;
teamId: string;
pathId: string;
path: string;
name: string;
avatar?: string;
avatar: string;
description?: string;
updateTime: Date;
};
Expand All @@ -20,7 +20,12 @@ type OrgMemberSchemaType = {
tmbId: string;
};

type OrgType = Omit<OrgSchemaType, 'avatar'> & {
export type OrgListItemType = OrgSchemaType & {
permission: TeamPermission;
total: number; // members + children orgs
};

export type OrgType = Omit<OrgSchemaType, 'avatar'> & {
avatar: string;
permission: TeamPermission;
members: OrgMemberSchemaType[];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { connectionMongo, getMongoModel } from '../../../common/mongo';
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
import { GroupMemberCollectionName } from './groupMemberSchema';
const { Schema } = connectionMongo;

export const MemberGroupCollectionName = 'team_member_groups';
Expand Down
2 changes: 1 addition & 1 deletion projects/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.9.1",
"version": "4.9.2",
"private": false,
"scripts": {
"dev": "next dev",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ 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 } from '@fastgpt/global/support/permission/memberGroup/type';
import { useMount } from 'ahooks';

export type GroupFormType = {
members: {
Expand All @@ -46,26 +48,20 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
return groups.find((item) => item._id === editGroupId);
}, [editGroupId, groups]);

const { data: groupMembers } = useRequest2(
() => {
if (editGroupId) return getGroupMembers(editGroupId);
return Promise.resolve(undefined);
},
{
manual: false,
onSuccess: (data) => {
setMembers(data ?? []);
}
}
);

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<string>();

const selectedMembersRef = useRef<HTMLDivElement>(null);
const [members, setMembers] = useState(groupMembers || []);
const [members, setMembers] = useState<GroupMemberItemType[]>([]);

useMount(async () => {
if (editGroupId) {
const data = await getGroupMembers(editGroupId);
setMembers(data);
}
});

const [searchKey, setSearchKey] = useState('');
const filtered = useMemo(() => {
Expand All @@ -80,7 +76,7 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
const { runAsync: onUpdate, loading: isLoadingUpdate } = useRequest2(
async () => {
if (!editGroupId || !members.length) return;
console.log(members);

return putUpdateGroup({
groupId: editGroupId,
memberList: members
Expand Down Expand Up @@ -209,7 +205,7 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
<Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4" h={'100%'}>
<Box mt={2}>{t('common:chosen') + ': ' + members.length}</Box>
<MemberScrollData ScrollContainerRef={selectedMembersRef} mt={3} flex={'1 0 0'} h={0}>
{members?.map((member) => {
{members.map((member) => {
return (
<HStack
onMouseEnter={() => setHoveredMemberId(member.tmbId)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
<Box className={'textEllipsis'}>
{member.memberName}
{member.status !== 'active' && (
<Tag ml="2" colorSchema="gray">
<Tag ml="2" colorSchema="gray" bg={'myGray.100'} color={'myGray.700'}>
{t('account_team:leave')}
</Tag>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import dynamic from 'next/dynamic';
import { useForm } from 'react-hook-form';

export type OrgFormType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type React from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useContextSelector } from 'use-context-selector';
import { TeamContext } from '../context';
import { OrgType } from '@fastgpt/global/support/user/team/org/type';
import { OrgListItemType, OrgType } from '@fastgpt/global/support/user/team/org/type';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';

export type GroupFormType = {
Expand All @@ -46,7 +46,7 @@ function OrgMemberManageModal({
refetchOrgs,
onClose
}: {
currentOrg: OrgType;
currentOrg: OrgListItemType;
refetchOrgs: () => void;
onClose: () => void;
}) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { putMoveOrg } from '@/web/support/user/team/org/api';
import { Button, ModalBody, ModalFooter } from '@chakra-ui/react';
import type { OrgType } from '@fastgpt/global/support/user/team/org/type';
import type { OrgListItemType, OrgType } from '@fastgpt/global/support/user/team/org/type';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
Expand All @@ -15,13 +15,13 @@ function OrgMoveModal({
onClose,
onSuccess
}: {
movingOrg: OrgType;
orgs: OrgType[];
movingOrg: OrgListItemType;
orgs: OrgListItemType[];
onClose: () => void;
onSuccess: () => void;
}) {
const { t } = useTranslation();
const [selectedOrg, setSelectedOrg] = useState<OrgType>();
const [selectedOrg, setSelectedOrg] = useState<OrgListItemType>();
const { userInfo } = useUserStore();
const team = userInfo?.team!;

Expand Down
74 changes: 30 additions & 44 deletions projects/app/src/pageComponents/account/team/OrgManage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
Tr,
VStack
} from '@chakra-ui/react';
import type { OrgType } from '@fastgpt/global/support/user/team/org/type';
import type { OrgListItemType, OrgType } 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';
Expand All @@ -23,9 +23,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { useMemo, useState } from 'react';
import { useContextSelector } from 'use-context-selector';
import MemberTag from '@/components/support/user/team/Info/MemberTag';
import { TeamContext } from '../context';
import {
deleteOrg,
deleteOrgMember,
Expand All @@ -39,10 +37,10 @@ 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 { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
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, getTeamMembers } from '@/web/support/user/team/api';
import { delRemoveMember } from '@/web/support/user/team/api';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';

Expand Down Expand Up @@ -82,68 +80,52 @@ function ActionButton({
function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
const { t } = useTranslation();
const { userInfo, isTeamAdmin } = useUserStore();
const [searchOrg, setSearchOrg] = useState('');
const [orgStack, setOrgStack] = useState<OrgType[]>([]);
const currentOrg = useMemo(() => orgStack[orgStack.length - 1], [orgStack]);

const [rootOrg, setRootOrg] = useState<OrgType>();
const { feConfigs } = useSystemStore();
const isSyncMember = feConfigs.register_method?.includes('sync');

const { data: members = [], ScrollData: MemberScrollData } = useScrollPagination(getOrgMembers, {
pageSize: 20,
params: {
orgId: currentOrg?._id ?? rootOrg?._id
},
refreshDeps: [currentOrg?._id, rootOrg?._id]
});
const [searchOrg, setSearchOrg] = useState('');

const { feConfigs } = useSystemStore();
const [parentId, setParentId] = useState<ParentIdType>();
const [currentOrg, setCurrentOrg] = useState<OrgListItemType>();

const isSyncMember = feConfigs.register_method?.includes('sync');
const [path, setPath] = useState('');
// 用于 org 层级
const [orgStack, setOrgStack] = useState<OrgListItemType[]>([]);

const {
data: orgs = [],
loading: isLoadingOrgs,
refresh: refetchOrgs
} = useRequest2(
() => {
// sync path to orgStack
const splitPath = path.split('/').filter(Boolean);
const orgs = orgStack.filter((o) => splitPath.includes(o.pathId));
setOrgStack(orgs);
return getOrgList(path);
return getOrgList(parentId);
},
{
manual: false,
refreshDeps: [userInfo?.team?.teamId, path],
onSuccess: (data) => {
if (!rootOrg) {
setRootOrg(data[0]);
}
}
refreshDeps: [userInfo?.team?.teamId, parentId]
}
);

const paths = useMemo(() => {
if (!currentOrg) return [];
return orgStack
.map((org) => {
if (org?.path === '') return;
return {
parentId: getOrgChildrenPath(org),
parentName: org.name
};
})
.filter(Boolean) as ParentTreePathItemType[];
}, [orgStack]);
}, [currentOrg, orgStack]);

const onClickOrg = (org: OrgType) => {
const onClickOrg = (org: OrgListItemType) => {
setParentId(currentOrg?._id);
setOrgStack([...orgStack, org]);
setPath(getOrgChildrenPath(org));
setCurrentOrg(org);
};

const [editOrg, setEditOrg] = useState<OrgFormType>();
const [manageMemberOrg, setManageMemberOrg] = useState<OrgType>();
const [movingOrg, setMovingOrg] = useState<OrgType>();
const [manageMemberOrg, setManageMemberOrg] = useState<OrgListItemType>();
const [movingOrg, setMovingOrg] = useState<OrgListItemType>();

// Delete org
const { ConfirmModal: ConfirmDeleteOrgModal, openConfirm: openDeleteOrgModal } = useConfirm({
Expand All @@ -157,6 +139,14 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
}
});

const { data: members = [], ScrollData: MemberScrollData } = useScrollPagination(getOrgMembers, {
pageSize: 20,
params: {
orgId: currentOrg?._id
},
refreshDeps: [currentOrg?._id]
});

// Delete member
const { ConfirmModal: ConfirmDeleteMemberFromOrg, openConfirm: openDeleteMemberFromOrgModal } =
useConfirm({
Expand All @@ -183,11 +173,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
const searchedOrgs = useMemo(() => {
if (!searchOrg) return [];

return orgs
.filter((org) => org.name.includes(searchOrg))
.map((org) => ({
...org
}));
return orgs.filter((org) => org.name.includes(searchOrg));
}, [orgs, searchOrg]);

return (
Expand Down Expand Up @@ -231,7 +217,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
<Tr key={org._id} overflow={'unset'} onClick={() => onClickOrg(org)}>
<Td>
<HStack cursor={'pointer'} onClick={() => onClickOrg(org)}>
<MemberTag name={org.name} avatar={org.avatar} />
<MemberTag name={org.name} avatar={org.avatar!} />
<Tag size="sm">{org.total}</Tag>
<MyIcon
name="core/chat/chevronRight"
Expand Down Expand Up @@ -427,7 +413,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
onClick={() => {
setEditOrg({
...defaultOrgForm,
parentId: currentOrg?._id ?? rootOrg?._id
parentId: currentOrg?._id
});
}}
/>
Expand Down
Loading
Loading