4.8.18 test (#3543)

* perf: login check

* doc

* perf: llm model config

* perf: team clb config
This commit is contained in:
Archer
2025-01-07 14:21:05 +08:00
committed by GitHub
parent 07cc849877
commit f556fbf0d5
49 changed files with 1644 additions and 1535 deletions

View File

@@ -7,7 +7,6 @@ import MyDivider from '@fastgpt/web/components/common/MyDivider';
import { useTranslation } from 'next-i18next';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import DefaultPermissionList from '@/components/support/permission/DefaultPerList';
import CollaboratorContextProvider, {
MemberManagerInputPropsType
} from '../../support/permission/MemberManager/context';
@@ -24,7 +23,6 @@ const FolderSlideCard = ({
deleteTip,
onDelete,
defaultPer,
managePer,
isInheritPermission,
resumeInheritPermission,
@@ -39,11 +37,6 @@ const FolderSlideCard = ({
deleteTip: string;
onDelete: () => void;
defaultPer?: {
value: PermissionValueType;
defaultValue: PermissionValueType;
onChange: (v: PermissionValueType) => Promise<any>;
};
managePer: MemberManagerInputPropsType;
isInheritPermission?: boolean;

View File

@@ -1,11 +0,0 @@
import { useContextSelector } from 'use-context-selector';
import { CollaboratorContext } from './context';
import { AddModalPropsType } from './MemberModal';
import MemberModal from './MemberModal';
function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
const context = useContextSelector(CollaboratorContext, (v) => v);
return <MemberModal onClose={onClose} mode={mode} collaboratorContext={context} />;
}
export default AddMemberModal;

View File

@@ -19,35 +19,31 @@ 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, useState } from 'react';
import { useContextSelector } from 'use-context-selector';
import { useMemo, useRef, useState } from 'react';
import PermissionSelect from './PermissionSelect';
import PermissionTags from './PermissionTags';
import { MemberManagerPropsType } from './context';
import { DEFAULT_ORG_AVATAR, DEFAULT_TEAM_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 { OrgType } from '@fastgpt/global/support/user/team/org/type';
import { createMemberPermission } from '@/web/support/user/team/api';
import { useContextSelector } from 'use-context-selector';
import { CollaboratorContext } from './context';
export type AddModalPropsType = {
onClose: () => void;
mode?: 'member' | 'all';
const HoverBoxStyle = {
bgColor: 'myGray.50',
cursor: 'pointer'
};
function MemberModal({
onClose,
mode = 'member',
collaboratorContext: context
}: AddModalPropsType & { collaboratorContext?: MemberManagerPropsType }) {
function MemberModal({ onClose }: { onClose: () => void }) {
const { t } = useTranslation();
const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups, loadAndGetOrgs } =
useUserStore();
const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList);
const [searchText, setSearchText] = useState<string>('');
const [filterClass, setFilterClass] = useState<'member' | 'org' | 'group'>();
const [parentPath, setParentPath] = useState('');
const { data: [members = [], groups = [], orgs = []] = [], loading: loadingMembersAndGroups } =
useRequest2(
@@ -65,13 +61,7 @@ function MemberModal({
}
);
const currentOrg = useMemo(() => {
const splitPath = parentPath.split('/');
const currentOrgId = splitPath[splitPath.length - 1];
if (!currentOrgId) return;
return orgs.find((org) => org.pathId === currentOrgId);
}, [orgs, parentPath]);
const [parentPath, setParentPath] = useState('');
const paths = useMemo(() => {
const splitPath = parentPath.split('/').filter(Boolean);
return splitPath
@@ -88,30 +78,17 @@ function MemberModal({
.filter(Boolean) as ParentTreePathItemType[];
}, [parentPath, orgs]);
const filterMembers = useMemo(() => {
if (!searchText && filterClass !== 'member' && filterClass !== 'org') return [];
if (searchText) return members.filter((item) => item.memberName.includes(searchText));
if (filterClass === 'org') {
if (!currentOrg) return [];
return members.filter((item) => currentOrg.members.find((v) => v.tmbId === item.tmbId));
}
return members;
}, [members, searchText, filterClass, currentOrg]);
const filterGroups = useMemo(() => {
if (mode !== 'all') return [];
if (!searchText && filterClass !== 'group') return [];
if (searchText) return groups.filter((item) => item.name.includes(searchText));
return groups.filter((item) => {
if (context === undefined || context.permission.isOwner) return true; // owner can see all groups
return !myGroups.find((i) => String(i._id) === String(item._id));
});
}, [groups, searchText, filterClass, myGroups, mode, context]);
const [selectedOrgIdList, setSelectedOrgIdList] = useState<string[]>([]);
const currentOrg = useMemo(() => {
const splitPath = parentPath.split('/');
const currentOrgId = splitPath[splitPath.length - 1];
if (!currentOrgId) return;
return orgs.find((org) => org.pathId === currentOrgId);
}, [orgs, parentPath]);
const filterOrgs: (OrgType & { count?: number })[] = useMemo(() => {
if (mode !== 'all') return [];
if (!searchText && filterClass !== 'org') return [];
if (searchText) return orgs.filter((item) => item.name.includes(searchText));
if (!searchText && filterClass !== 'org') return [];
if (parentPath === '') {
setParentPath(`/${orgs[0].pathId}`);
return [];
@@ -123,44 +100,97 @@ function MemberModal({
count:
item.members.length + orgs.filter((org) => org.path === getOrgChildrenPath(item)).length
}));
}, [orgs, searchText, filterClass, mode, parentPath]);
}, [orgs, searchText, filterClass, parentPath]);
const [selectedMemberIdList, setSelectedMembers] = useState<string[]>([]);
const [selectedGroupIdList, setSelectedGroupIdList] = useState<string[]>([]);
const [selectedOrgIdList, setSelectedOrgIdList] = useState<string[]>([]);
const [selectedPermission, setSelectedPermission] = useState(
context?.permissionList['read'].value
);
const perLabel = useMemo(() => {
if (context) return context.getPerLabelList(selectedPermission!).join('、');
}, [context, selectedPermission]);
const filterMembers = useMemo(() => {
if (searchText) return members.filter((item) => item.memberName.includes(searchText));
if (!searchText && filterClass !== 'member' && filterClass !== 'org') return [];
if (filterClass === 'org') {
if (!currentOrg) return [];
return members.filter((item) => currentOrg.members.find((v) => v.tmbId === item.tmbId));
}
return members;
}, [members, searchText, filterClass, currentOrg]);
const [selectedGroupIdList, setSelectedGroupIdList] = useState<string[]>([]);
const filterGroups = useMemo(() => {
if (searchText) return groups.filter((item) => item.name.includes(searchText));
if (!searchText && filterClass !== 'group') return [];
return groups.filter((item) => {
return !myGroups.find((i) => String(i._id) === String(item._id));
});
}, [groups, searchText, filterClass, myGroups]);
const permissionList = useContextSelector(CollaboratorContext, (v) => v.permissionList);
const getPerLabelList = useContextSelector(CollaboratorContext, (v) => v.getPerLabelList);
const [selectedPermission, setSelectedPermission] = useState<number>();
const perLabel = useMemo(() => {
if (selectedPermission === undefined) return '';
return getPerLabelList(selectedPermission!).join('、');
}, [getPerLabelList, selectedPermission]);
const onUpdateCollaborators = useContextSelector(
CollaboratorContext,
(v) => v.onUpdateCollaborators
);
const { runAsync: onConfirm, loading: isUpdating } = useRequest2(
() => {
if (context) {
return context.onUpdateCollaborators({
members: selectedMemberIdList,
groups: selectedGroupIdList,
orgs: selectedOrgIdList,
permission: selectedPermission!
});
} else {
return createMemberPermission({
tmbId: selectedMemberIdList,
groupId: selectedGroupIdList,
orgId: selectedOrgIdList
});
}
},
() =>
onUpdateCollaborators({
members: selectedMemberIdList,
groups: selectedGroupIdList,
orgs: selectedOrgIdList,
permission: selectedPermission!
}),
{
successToast: t('common:common.Add Success'),
errorToast: 'Error',
onSuccess() {
onClose();
}
}
);
const entryList = useRef([
{ label: t('user:team.group.members'), icon: '/imgs/avatar/BlueAvatar.svg', value: 'member' },
{ label: t('user:team.org.org'), icon: DEFAULT_ORG_AVATAR, value: 'org' },
{ label: t('user:team.group.group'), icon: DEFAULT_TEAM_AVATAR, value: 'group' }
]);
const selectedList = useMemo(() => {
const selectedOrgs = orgs.filter((org) => selectedOrgIdList.includes(org._id));
const selectedGroups = groups.filter((group) => selectedGroupIdList.includes(group._id));
const selectedMembers = members.filter((member) => selectedMemberIdList.includes(member.tmbId));
return [
...selectedOrgs.map((item) => ({
id: `org-${item._id}`,
avatar: item.avatar,
name: item.name,
onDelete: () => setSelectedOrgIdList(selectedOrgIdList.filter((v) => v !== item._id))
})),
...selectedGroups.map((item) => ({
id: `group-${item._id}`,
avatar: item.avatar,
name: item.name === DefaultGroupName ? userInfo?.team.teamName : item.name,
onDelete: () => setSelectedGroupIdList(selectedGroupIdList.filter((v) => v !== item._id))
})),
...selectedMembers.map((item) => ({
id: `member-${item.tmbId}`,
avatar: item.avatar,
name: item.memberName,
onDelete: () => setSelectedMembers(selectedMemberIdList.filter((v) => v !== item.tmbId))
}))
];
}, [
orgs,
groups,
members,
selectedOrgIdList,
selectedGroupIdList,
selectedMemberIdList,
userInfo?.team.teamName
]);
return (
<MyModal
isOpen
@@ -169,7 +199,7 @@ function MemberModal({
title={t('user:team.add_collaborator')}
minW="800px"
h={'100%'}
maxH={'800px'}
maxH={'90vh'}
isCentered
isLoading={loadingMembersAndGroups}
>
@@ -181,73 +211,49 @@ function MemberModal({
gridTemplateColumns="1fr 1fr"
h={'100%'}
>
<Flex flexDirection="column" borderRight="1px solid" borderColor="myGray.200" p="4">
<Flex
h={'100%'}
flexDirection="column"
borderRight="1px solid"
borderColor="myGray.200"
p="4"
>
<SearchInput
placeholder={t('user:search_user')}
bgColor="myGray.50"
onChange={(e) => setSearchText(e.target.value)}
/>
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
{!searchText &&
(filterClass === undefined ? (
<>
<HStack
justifyContent="space-between"
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer'
}}
onClick={() => setFilterClass('member')}
>
<MyAvatar src="/imgs/avatar/BlueAvatar.svg" w="1.5rem" borderRadius={'50%'} />
<Box ml="2" w="full">
{t('user:team.group.members')}
</Box>
<MyIcon name="core/chat/chevronRight" w="16px" />
</HStack>
<HStack
justifyContent="space-between"
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer'
}}
onClick={() => setFilterClass('org')}
>
<MyAvatar src={DEFAULT_ORG_AVATAR} w="1.5rem" borderRadius={'50%'} />
<Box ml="2" w="full">
{t('user:team.org.org')}
</Box>
<MyIcon name="core/chat/chevronRight" w="16px" />
</HStack>
<HStack
justifyContent="space-between"
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer'
}}
onClick={() => setFilterClass('group')}
>
<MyAvatar src={DEFAULT_TEAM_AVATAR} w="1.5rem" borderRadius={'50%'} />
<Box ml="2" w="full">
{t('user:team.group.group')}
</Box>
<MyIcon name="core/chat/chevronRight" w="16px" />
</HStack>
</>
) : (
<Flex flexDirection="column" mt="3" overflow={'auto'} flex={'1 0 0'} h={0}>
{!searchText && !filterClass && (
<>
{entryList.current.map((item) => {
return (
<HStack
key={item.value}
justifyContent="space-between"
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={HoverBoxStyle}
_notLast={{ mb: 1 }}
onClick={() => setFilterClass(item.value as any)}
>
<MyAvatar src={item.icon} w="1.5rem" borderRadius={'50%'} />
<Box ml="2" w="full">
{item.label}
</Box>
<MyIcon name="core/chat/chevronRight" w="16px" />
</HStack>
);
})}
</>
)}
{/* Path */}
{!searchText && filterClass && (
<Box mb={1}>
<Path
paths={[
{
@@ -278,267 +284,173 @@ function MemberModal({
}}
rootName={t('common:common.Team')}
/>
))}
{filterOrgs.map((org) => {
const onChange = () => {
setSelectedOrgIdList((state) => {
if (state.includes(org._id)) {
return state.filter((v) => v !== org._id);
}
return [...state, org._id];
});
};
const collaborator = context?.collaboratorList?.find((v) => v.orgId === org._id);
return (
<HStack
justifyContent="space-between"
key={org._id}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer'
}}
onClick={onChange}
>
<Checkbox
isChecked={selectedOrgIdList.includes(org._id)}
pointerEvents="none"
/>
<MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} />
<HStack ml="2" w="full" gap="5px">
<Text>{org.name}</Text>
</Box>
)}
<Flex flexDirection={'column'} gap={1} userSelect={'none'}>
{filterMembers.map((member) => {
const onChange = () => {
setSelectedMembers((state) => {
if (state.includes(member.tmbId)) {
return state.filter((v) => v !== member.tmbId);
}
return [...state, member.tmbId];
});
};
const collaborator = collaboratorList?.find((v) => v.tmbId === member.tmbId);
return (
<HStack
justifyContent="space-between"
key={member.tmbId}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={HoverBoxStyle}
onClick={onChange}
>
<Checkbox
isChecked={selectedMemberIdList.includes(member.tmbId)}
pointerEvents="none"
/>
<MyAvatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
<Box w="full" ml="2">
{member.memberName}
</Box>
<PermissionTags permission={collaborator?.permission.value} />
</HStack>
);
})}
{filterOrgs.map((org) => {
const onChange = () => {
setSelectedOrgIdList((state) => {
if (state.includes(org._id)) {
return state.filter((v) => v !== org._id);
}
return [...state, org._id];
});
};
const collaborator = collaboratorList?.find((v) => v.orgId === org._id);
return (
<HStack
justifyContent="space-between"
key={org._id}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={HoverBoxStyle}
onClick={onChange}
>
<Checkbox
isChecked={selectedOrgIdList.includes(org._id)}
pointerEvents="none"
/>
<MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} />
<HStack ml="2" w="full" gap="5px">
<Text>{org.name}</Text>
{org.count && (
<>
<Tag size="sm" my="auto">
{org.count}
</Tag>
</>
)}
</HStack>
<PermissionTags permission={collaborator?.permission.value} />
{org.count && (
<>
<Tag size="sm" my="auto">
{org.count}
</Tag>
</>
<MyIcon
name="core/chat/chevronRight"
w="16px"
p="4px"
rounded={'6px'}
_hover={{
bgColor: 'myGray.200'
}}
onClick={() => {
setParentPath(getOrgChildrenPath(org));
}}
/>
)}
</HStack>
{!!collaborator && (
<PermissionTags permission={collaborator.permission.value} />
)}
{org.count && (
<MyIcon
name="core/chat/chevronRight"
w="16px"
p="4px"
rounded={'6px'}
_hover={{
bgColor: 'myGray.200'
}}
onClick={() => {
setParentPath(getOrgChildrenPath(org));
}}
);
})}
{filterGroups.map((group) => {
const onChange = () => {
setSelectedGroupIdList((state) => {
if (state.includes(group._id)) {
return state.filter((v) => v !== group._id);
}
return [...state, group._id];
});
};
const collaborator = collaboratorList?.find((v) => v.groupId === group._id);
return (
<HStack
justifyContent="space-between"
key={group._id}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={HoverBoxStyle}
onClick={onChange}
>
<Checkbox
isChecked={selectedGroupIdList.includes(group._id)}
pointerEvents="none"
/>
)}
</HStack>
);
})}
{filterGroups.map((group) => {
const onChange = () => {
setSelectedGroupIdList((state) => {
if (state.includes(group._id)) {
return state.filter((v) => v !== group._id);
}
return [...state, group._id];
});
};
const collaborator = context?.collaboratorList?.find(
(v) => v.groupId === group._id
);
return (
<HStack
justifyContent="space-between"
key={group._id}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer'
}}
onClick={onChange}
>
<Checkbox
isChecked={selectedGroupIdList.includes(group._id)}
pointerEvents="none"
/>
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
<Box ml="2" w="full">
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
</Box>
{!!collaborator && (
<PermissionTags permission={collaborator.permission.value} />
)}
</HStack>
);
})}
{filterMembers.map((member) => {
const onChange = () => {
setSelectedMembers((state) => {
if (state.includes(member.tmbId)) {
return state.filter((v) => v !== member.tmbId);
}
return [...state, member.tmbId];
});
};
const collaborator = context?.collaboratorList?.find(
(v) => v.tmbId === member.tmbId
);
return (
<HStack
justifyContent="space-between"
key={member.tmbId}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer'
}}
onClick={userInfo?.team?.tmbId === member.tmbId ? undefined : onChange}
>
<Checkbox
isChecked={selectedMemberIdList.includes(member.tmbId)}
pointerEvents="none"
isDisabled={userInfo?.team?.tmbId === member.tmbId}
/>
<MyAvatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
<Box w="full" ml="2">
{member.memberName}
</Box>
{!!collaborator && (
<PermissionTags permission={collaborator.permission.value} />
)}
</HStack>
);
})}
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
<Box ml="2" w="full">
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
</Box>
<PermissionTags permission={collaborator?.permission.value} />
</HStack>
);
})}
</Flex>
</Flex>
</Flex>
<Flex p="4" flexDirection="column">
<Flex h={'100%'} p="4" flexDirection="column">
<Box>
{`${t('user:has_chosen')}: `}
{selectedMemberIdList.length + selectedGroupIdList.length + selectedOrgIdList.length}
</Box>
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
{selectedOrgIdList.map((orgId) => {
const org = orgs.find((v) => String(v._id) === orgId);
<Flex flexDirection="column" mt="2" gap={1} overflow={'auto'} flex={'1 0 0'} h={0}>
{selectedList.map((item) => {
return (
<HStack
justifyContent="space-between"
key={orgId}
key={item.id}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer',
...(!selectedOrgIdList.includes(orgId) ? { svg: { color: 'myGray.50' } } : {})
}}
onClick={() =>
setSelectedOrgIdList(selectedOrgIdList.filter((v) => v !== orgId))
}
_hover={HoverBoxStyle}
>
<MyAvatar src={org?.avatar} w="1.5rem" borderRadius={'50%'} />
<MyAvatar src={item.avatar} w="1.5rem" borderRadius={'50%'} />
<Box w="full" ml="2">
{org?.name}
{item.name}
</Box>
<MyIcon
name="common/closeLight"
w="16px"
w="1rem"
cursor={'pointer'}
_hover={{
color: 'red.600'
}}
onClick={item.onDelete}
/>
</HStack>
);
})}
{selectedGroupIdList.map((groupId) => {
const onChange = () => {
setSelectedGroupIdList((state) => {
if (state.includes(groupId)) {
return state.filter((v) => v !== groupId);
}
return [...state, groupId];
});
};
const group = groups.find((v) => String(v._id) === groupId);
return (
<HStack
justifyContent="space-between"
key={groupId}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={{
bgColor: 'myGray.50',
cursor: 'pointer',
...(!selectedGroupIdList.includes(groupId)
? { svg: { color: 'myGray.50' } }
: {})
}}
onClick={onChange}
>
<MyAvatar src={group?.avatar} w="1.5rem" borderRadius={'50%'} />
<Box w="full" ml="2">
{group?.name === DefaultGroupName ? userInfo?.team.teamName : group?.name}
</Box>
<MyIcon
name="common/closeLight"
w="16px"
cursor={'pointer'}
_hover={{
color: 'red.600'
}}
/>
</HStack>
);
})}
{selectedMemberIdList.map((tmbId) => {
const member = members.find((v) => v.tmbId === tmbId);
return member ? (
<HStack
justifyContent="space-between"
key={tmbId}
alignItems="center"
py="2"
px={3}
borderRadius={'md'}
_hover={{ bg: 'myGray.50' }}
onClick={() =>
setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId))
}
>
<MyAvatar src={member.avatar} w="1.5rem" borderRadius="50%" />
<Box w="full" ml={2}>
{member.memberName}
</Box>
<MyIcon
name="common/closeLight"
w="16px"
cursor={'pointer'}
_hover={{
color: 'red.600'
}}
/>
</HStack>
) : null;
})}
</Flex>
</Flex>
</Grid>
</ModalBody>
<ModalFooter>
{selectedPermission && (
{!!permissionList && (
<PermissionSelect
value={selectedPermission}
Button={

View File

@@ -49,13 +49,16 @@ function PermissionSelect({
onDelete
}: PermissionSelectProps) {
const { t } = useTranslation();
const { permission, permissionList } = useContextSelector(CollaboratorContext, (v) => v);
const ref = useRef<HTMLButtonElement>(null);
const closeTimer = useRef<NodeJS.Timeout>();
const { permission, permissionList } = useContextSelector(CollaboratorContext, (v) => v);
const [isOpen, setIsOpen] = useState(false);
const permissionSelectList = useMemo(() => {
if (!permissionList) return { singleCheckBoxList: [], multipleCheckBoxList: [] };
const list = Object.entries(permissionList).map(([_, value]) => {
return {
name: value.name,
@@ -77,6 +80,8 @@ function PermissionSelect({
};
}, [permission.isOwner, permissionList]);
const selectedSingleValue = useMemo(() => {
if (!permissionList) return undefined;
const per = new Permission({ per: value });
if (per.hasManagePer) return permissionList['manage'].value;
@@ -107,7 +112,7 @@ function PermissionSelect({
}
});
return (
return selectedSingleValue !== undefined ? (
<Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'}>
<Box
w="fit-content"
@@ -241,7 +246,7 @@ function PermissionSelect({
</MenuList>
</Box>
</Menu>
);
) : null;
}
export default React.memo(PermissionSelect);

View File

@@ -7,12 +7,15 @@ import { CollaboratorContext } from './context';
import { useTranslation } from 'next-i18next';
export type PermissionTagsProp = {
permission: PermissionValueType;
permission?: PermissionValueType;
};
function PermissionTags({ permission }: PermissionTagsProp) {
const { getPerLabelList } = useContextSelector(CollaboratorContext, (v) => v);
const { t } = useTranslation();
if (permission === undefined) return null;
const perTagList = getPerLabelList(permission);
return (

View File

@@ -19,19 +19,19 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useI18n } from '@/web/context/I18n';
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
const AddMemberModal = dynamic(() => import('./AddMemberModal'));
const MemberModal = dynamic(() => import('./MemberModal'));
const ManageModal = dynamic(() => import('./ManageModal'));
export type MemberManagerInputPropsType = {
permission: Permission;
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
permissionList: PermissionListType;
permissionList?: PermissionListType;
onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise<any>;
onDelOneCollaborator: (
props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }>
) => Promise<any>;
refreshDeps?: any[];
mode?: 'member' | 'all';
};
export type MemberManagerPropsType = MemberManagerInputPropsType & {
@@ -80,8 +80,7 @@ const CollaboratorContextProvider = ({
refetchResource,
refreshDeps = [],
isInheritPermission,
hasParent,
mode = 'member'
hasParent
}: MemberManagerInputPropsType & {
children: (props: ChildrenProps) => ReactNode;
refetchResource?: () => void;
@@ -121,6 +120,8 @@ const CollaboratorContextProvider = ({
const getPerLabelList = useCallback(
(per: PermissionValueType) => {
if (!permissionList) return [];
const Per = new Permission({ per });
const labels: string[] = [];
@@ -128,7 +129,7 @@ const CollaboratorContextProvider = ({
labels.push(permissionList['manage'].name);
} else if (Per.hasWritePer) {
labels.push(permissionList['write'].name);
} else {
} else if (Per.hasReadPer) {
labels.push(permissionList['read'].name);
}
@@ -203,12 +204,11 @@ const CollaboratorContextProvider = ({
MemberListCard
})}
{isOpenAddMember && (
<AddMemberModal
<MemberModal
onClose={() => {
onCloseAddMember();
refetchResource?.();
}}
mode={mode}
/>
)}
{isOpenManageModal && (

View File

@@ -0,0 +1,84 @@
import React, { useMemo, useState } from 'react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { Box, Flex, ModalBody } from '@chakra-ui/react';
import { MultipleRowArraySelect } from '@fastgpt/web/components/common/MySelect/MultipleRowSelect';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { ModelProviderList } from '@fastgpt/global/core/ai/provider';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { HUGGING_FACE_ICON } from '@fastgpt/global/common/system/constants';
import { getModelFromList } from '@fastgpt/global/core/ai/model';
const DefaultModal = ({ onClose }: { onClose: () => void }) => {
const { t } = useTranslation();
const { llmModelList, vectorModelList, whisperModel, audioSpeechModelList, reRankModelList } =
useSystemStore();
const [value, setValue] = useState<string[]>([]);
const modelList = useMemo(() => {
return [
...llmModelList,
...vectorModelList,
...audioSpeechModelList,
...reRankModelList,
whisperModel
].map((item) => ({
provider: item.provider,
name: item.name,
model: item.model
}));
}, [llmModelList, vectorModelList, whisperModel, audioSpeechModelList, reRankModelList]);
const selectorList = useMemo(() => {
const renderList = ModelProviderList.map<{
label: React.JSX.Element;
value: string;
children: { label: string | React.ReactNode; value: string }[];
}>((provider) => ({
label: (
<Flex alignItems={'center'} py={1}>
<Avatar
borderRadius={'0'}
mr={2}
src={provider?.avatar || HUGGING_FACE_ICON}
fallbackSrc={HUGGING_FACE_ICON}
w={'1rem'}
/>
<Box>{t(provider.name as any)}</Box>
</Flex>
),
value: provider.id,
children: []
}));
for (const item of modelList) {
const modelData = getModelFromList(modelList, item.model);
const provider =
renderList.find((item) => item.value === (modelData?.provider || 'Other')) ??
renderList[renderList.length - 1];
provider.children.push({
label: modelData.name,
value: modelData.model
});
}
return renderList.filter((item) => item.children.length > 0);
}, [modelList, t]);
console.log(selectorList);
return (
<MyModal
isOpen
title={t('account:add_default_model')}
iconSrc="common/model"
iconColor="primary.600"
onClose={onClose}
>
<ModalBody>11</ModalBody>
</MyModal>
);
};
export default DefaultModal;

View File

@@ -1,15 +1,72 @@
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
import React from 'react';
import React, { useState } from 'react';
import AccountContainer from '../components/AccountContainer';
import { Box } from '@chakra-ui/react';
import { Box, Button, Flex, useDisclosure } from '@chakra-ui/react';
import ModelTable from '@/components/core/ai/ModelTable';
import { useUserStore } from '@/web/support/user/useUserStore';
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
import { useTranslation } from 'next-i18next';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import dynamic from 'next/dynamic';
const DefaultModal = dynamic(() => import('./components/DefaultModal'), {
ssr: false
});
const ModelProvider = () => {
const { t } = useTranslation();
const { userInfo } = useUserStore();
const isRoot = userInfo?.username === 'root';
const [tab, setTab] = useState<'model' | 'channel'>('model');
const { isOpen: isOpenDefault, onOpen: onOpenDefault, onClose: onCloseDefault } = useDisclosure();
return (
<AccountContainer>
<Box h={'100%'} py={4} px={6}>
<ModelTable />
</Box>
<Flex h={'100%'} flexDirection={'column'} gap={4} py={4} px={6}>
{/* Header */}
{/* <Flex justifyContent={'space-between'}>
<FillRowTabs<'model' | 'channel'>
list={[
{ label: t('account:active_model'), value: 'model' },
{ label: t('account:channel'), value: 'channel' }
]}
value={tab}
px={8}
py={1}
onChange={setTab}
/>
{tab === 'model' && (
<MyMenu
trigger="hover"
size="mini"
Button={<Button>{t('account:create_model')}</Button>}
menuList={[
{
children: [
{
label: t('account:default_model'),
onClick: onOpenDefault
},
{
label: t('account:custom_model')
}
]
}
]}
/>
)}
{tab === 'channel' && <Button>{t('account:create_channel')}</Button>}
</Flex> */}
<Box flex={'1 0 0'}>
{tab === 'model' && <ModelTable />}
{/* {tab === 'channel' && <ChannelTable />} */}
</Box>
</Flex>
{isOpenDefault && <DefaultModal onClose={onCloseDefault} />}
</AccountContainer>
);
};

View File

@@ -1,6 +1,8 @@
import AvatarGroup from '@fastgpt/web/components/common/Avatar/AvatarGroup';
import {
Box,
Button,
Flex,
HStack,
Table,
TableContainer,
@@ -29,17 +31,32 @@ import { useState } from 'react';
import IconButton from '../OrgManage/IconButton';
const ChangeOwnerModal = dynamic(() => import('./GroupTransferOwnerModal'));
const GroupInfoModal = dynamic(() => import('./GroupInfoModal'));
const ManageGroupMemberModal = dynamic(() => import('./GroupManageMember'));
function MemberTable({
onEditGroup,
onManageMember
}: {
onEditGroup: (groupId: string) => void;
onManageMember: (groupId: string) => void;
}) {
function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
const { t } = useTranslation();
const { userInfo } = useUserStore();
const [editGroupId, setEditGroupId] = useState<string>();
const {
isOpen: isOpenGroupInfo,
onOpen: onOpenGroupInfo,
onClose: onCloseGroupInfo
} = useDisclosure();
const {
isOpen: isOpenManageGroupMember,
onOpen: onOpenManageGroupMember,
onClose: onCloseManageGroupMember
} = useDisclosure();
const onEditGroup = (groupId: string) => {
setEditGroupId(groupId);
onOpenGroupInfo();
};
const onManageMember = (groupId: string) => {
setEditGroupId(groupId);
onOpenManageGroupMember();
};
const { ConfirmModal: ConfirmDeleteGroupModal, openConfirm: openDeleteGroupModal } = useConfirm({
type: 'delete',
@@ -73,145 +90,184 @@ function MemberTable({
onOpen: onOpenChangeOwner,
onClose: onCloseChangeOwner
} = useDisclosure();
const onChangeOwner = (groupId: string) => {
setEditGroupId(groupId);
onOpenChangeOwner();
};
return (
<MyBox>
<TableContainer overflow={'unset'} fontSize={'sm'}>
<Table overflow={'unset'}>
<Thead>
<Tr bg={'white !important'}>
<Th bg="myGray.100" borderLeftRadius="6px">
{t('account_team:group_name')}
</Th>
<Th bg="myGray.100">{t('account_team:owner')}</Th>
<Th bg="myGray.100">{t('account_team:member')}</Th>
<Th bg="myGray.100" borderRightRadius="6px">
{t('common:common.Action')}
</Th>
</Tr>
</Thead>
<Tbody>
{groups?.map((group) => (
<Tr key={group._id} overflow={'unset'}>
<Td>
<HStack>
<>
<Flex justify={'space-between'} align={'center'} pb={'1rem'}>
{Tabs}
{userInfo?.team.permission.hasManagePer && (
<Button
variant={'primary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="support/permission/collaborator" w={'14px'} />}
onClick={onOpenGroupInfo}
>
{t('user:team.group.create')}
</Button>
)}
</Flex>
<MyBox flex={'1 0 0'} overflow={'auto'}>
<TableContainer overflow={'unset'} fontSize={'sm'}>
<Table overflow={'unset'}>
<Thead>
<Tr bg={'white !important'}>
<Th bg="myGray.100" borderLeftRadius="6px">
{t('account_team:group_name')}
</Th>
<Th bg="myGray.100">{t('account_team:owner')}</Th>
<Th bg="myGray.100">{t('account_team:member')}</Th>
<Th bg="myGray.100" borderRightRadius="6px">
{t('common:common.Action')}
</Th>
</Tr>
</Thead>
<Tbody>
{groups?.map((group) => (
<Tr key={group._id} overflow={'unset'}>
<Td>
<HStack>
<MemberTag
name={
group.name === DefaultGroupName
? userInfo?.team.teamName ?? ''
: group.name
}
avatar={group.avatar}
/>
<Box>
({group.name === DefaultGroupName ? members.length : group.members.length})
</Box>
</HStack>
</Td>
<Td>
<MemberTag
name={
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
group.name === DefaultGroupName
? members.find((item) => item.role === 'owner')?.memberName ?? ''
: members.find(
(item) =>
item.tmbId ===
group.members.find((item) => item.role === 'owner')?.tmbId
)?.memberName ?? ''
}
avatar={
group.name === DefaultGroupName
? members.find((item) => item.role === 'owner')?.avatar ?? ''
: members.find(
(i) =>
i.tmbId ===
group.members.find((item) => item.role === 'owner')?.tmbId
)?.avatar ?? ''
}
avatar={group.avatar}
/>
<Box>
({group.name === DefaultGroupName ? members.length : group.members.length})
</Box>
</HStack>
</Td>
<Td>
<MemberTag
name={
group.name === DefaultGroupName
? members.find((item) => item.role === 'owner')?.memberName ?? ''
: members.find(
(item) =>
item.tmbId ===
group.members.find((item) => item.role === 'owner')?.tmbId
)?.memberName ?? ''
}
avatar={
group.name === DefaultGroupName
? members.find((item) => item.role === 'owner')?.avatar ?? ''
: members.find(
(i) =>
i.tmbId === group.members.find((item) => item.role === 'owner')?.tmbId
)?.avatar ?? ''
}
/>
</Td>
<Td>
{group.name === DefaultGroupName ? (
<AvatarGroup avatars={members.map((v) => v.avatar)} groupId={group._id} />
) : hasGroupManagePer(group) ? (
<MyTooltip label={t('account_team:manage_member')}>
<Box cursor="pointer" onClick={() => onManageMember(group._id)}>
<AvatarGroup
avatars={group.members.map(
(v) => members.find((m) => m.tmbId === v.tmbId)?.avatar ?? ''
)}
groupId={group._id}
/>
</Box>
</MyTooltip>
) : (
<AvatarGroup
avatars={group.members.map(
(v) => members.find((m) => m.tmbId === v.tmbId)?.avatar ?? ''
)}
groupId={group._id}
/>
)}
</Td>
<Td>
{hasGroupManagePer(group) && group.name !== DefaultGroupName && (
<MyMenu
Button={<IconButton name={'more'} />}
menuList={[
{
children: [
{
label: t('account_team:edit_info'),
icon: 'edit',
onClick: () => {
onEditGroup(group._id);
}
},
{
label: t('account_team:manage_member'),
icon: 'support/team/group',
onClick: () => {
onManageMember(group._id);
}
},
...(isGroupOwner(group)
? [
{
label: t('account_team:transfer_ownership'),
icon: 'modal/changePer',
onClick: () => {
onChangeOwner(group._id);
</Td>
<Td>
{group.name === DefaultGroupName ? (
<AvatarGroup avatars={members.map((v) => v.avatar)} groupId={group._id} />
) : hasGroupManagePer(group) ? (
<MyTooltip label={t('account_team:manage_member')}>
<Box cursor="pointer" onClick={() => onManageMember(group._id)}>
<AvatarGroup
avatars={group.members.map(
(v) => members.find((m) => m.tmbId === v.tmbId)?.avatar ?? ''
)}
groupId={group._id}
/>
</Box>
</MyTooltip>
) : (
<AvatarGroup
avatars={group.members.map(
(v) => members.find((m) => m.tmbId === v.tmbId)?.avatar ?? ''
)}
groupId={group._id}
/>
)}
</Td>
<Td>
{hasGroupManagePer(group) && group.name !== DefaultGroupName && (
<MyMenu
Button={<IconButton name={'more'} />}
menuList={[
{
children: [
{
label: t('account_team:edit_info'),
icon: 'edit',
onClick: () => {
onEditGroup(group._id);
}
},
{
label: t('account_team:manage_member'),
icon: 'support/team/group',
onClick: () => {
onManageMember(group._id);
}
},
...(isGroupOwner(group)
? [
{
label: t('account_team:transfer_ownership'),
icon: 'modal/changePer',
onClick: () => {
onChangeOwner(group._id);
},
type: 'primary' as MenuItemType
},
type: 'primary' as MenuItemType
},
{
label: t('common:common.Delete'),
icon: 'delete',
onClick: () => {
openDeleteGroupModal(() => delDeleteGroup(group._id))();
},
type: 'danger' as MenuItemType
}
]
: [])
]
}
]}
/>
)}
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
{
label: t('common:common.Delete'),
icon: 'delete',
onClick: () => {
openDeleteGroupModal(() => delDeleteGroup(group._id))();
},
type: 'danger' as MenuItemType
}
]
: [])
]
}
]}
/>
)}
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</MyBox>
<ConfirmDeleteGroupModal />
{isOpenChangeOwner && editGroupId && (
<ChangeOwnerModal groupId={editGroupId} onClose={onCloseChangeOwner} />
)}
</MyBox>
{isOpenGroupInfo && (
<GroupInfoModal
onClose={() => {
onCloseGroupInfo();
setEditGroupId(undefined);
}}
editGroupId={editGroupId}
/>
)}
{isOpenManageGroupMember && (
<ManageGroupMemberModal
onClose={() => {
onCloseManageGroupMember();
setEditGroupId(undefined);
}}
editGroupId={editGroupId}
/>
)}
</>
);
}

View File

@@ -1,106 +1,224 @@
import Avatar from '@fastgpt/web/components/common/Avatar';
import { Box, HStack, Table, TableContainer, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react';
import {
Box,
Button,
Flex,
HStack,
Table,
TableContainer,
Tbody,
Td,
Th,
Thead,
Tr,
useDisclosure
} from '@chakra-ui/react';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { useTranslation } from 'next-i18next';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { delRemoveMember } from '@/web/support/user/team/api';
import Tag from '@fastgpt/web/components/common/Tag';
import Icon from '@fastgpt/web/components/common/Icon';
import GroupTags from '@/components/support/permission/Group/GroupTags';
import { useContextSelector } from 'use-context-selector';
import { TeamContext } from './context';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import dynamic from 'next/dynamic';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { delLeaveTeam } from '@/web/support/user/team/api';
function MemberTable() {
const { userInfo } = useUserStore();
const InviteModal = dynamic(() => import('./InviteModal'));
const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal'));
function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
const { t } = useTranslation();
const { toast } = useToast();
const { userInfo, teamPlanStatus } = useUserStore();
const { feConfigs, setNotSufficientModalType } = useSystemStore();
const { groups, refetchGroups, myTeams, refetchTeams, members, refetchMembers, onSwitchTeam } =
useContextSelector(TeamContext, (v) => v);
const {
isOpen: isOpenTeamTagsAsync,
onOpen: onOpenTeamTagsAsync,
onClose: onCloseTeamTagsAsync
} = useDisclosure();
const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure();
const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm({
type: 'delete'
});
const { members, groups, refetchMembers, refetchGroups } = useContextSelector(
TeamContext,
(v) => v
const { runAsync: onLeaveTeam } = useRequest2(
async () => {
const defaultTeam = myTeams.find((item) => item.defaultTeam) || myTeams[0];
// change to personal team
onSwitchTeam(defaultTeam.teamId);
return delLeaveTeam();
},
{
onSuccess() {
refetchTeams();
},
errorToast: t('account_team:user_team_leave_team_failed')
}
);
const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({
content: t('account_team:confirm_leave_team')
});
return (
<MyBox>
<TableContainer overflow={'unset'} fontSize={'sm'}>
<Table overflow={'unset'}>
<Thead>
<Tr bgColor={'white !important'}>
<Th borderLeftRadius="6px" bgColor="myGray.100">
{t('account_team:user_name')}
</Th>
<Th bgColor="myGray.100">{t('account_team:member_group')}</Th>
<Th borderRightRadius="6px" bgColor="myGray.100">
{t('common:common.Action')}
</Th>
</Tr>
</Thead>
<Tbody>
{members?.map((item) => (
<Tr key={item.userId} overflow={'unset'}>
<Td>
<HStack>
<Avatar src={item.avatar} w={['18px', '22px']} borderRadius={'50%'} />
<Box className={'textEllipsis'}>
{item.memberName}
{item.status === 'waiting' && (
<Tag ml="2" colorSchema="yellow">
{t('account_team:waiting')}
</Tag>
)}
</Box>
</HStack>
</Td>
<Td maxW={'300px'}>
<GroupTags
names={groups
?.filter((group) => group.members.map((m) => m.tmbId).includes(item.tmbId))
.map((g) => g.name)}
max={3}
/>
</Td>
<Td>
{userInfo?.team.permission.hasManagePer &&
item.role !== TeamMemberRoleEnum.owner &&
item.tmbId !== userInfo?.team.tmbId && (
<Icon
name={'common/trash'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'red.600',
bgColor: 'myGray.100'
}}
onClick={() => {
openRemoveMember(
() =>
delRemoveMember(item.tmbId).then(() =>
Promise.all([refetchGroups(), refetchMembers()])
),
undefined,
t('account_team:remove_tip', {
username: item.memberName
})
)();
}}
/>
)}
</Td>
</Tr>
))}
</Tbody>
</Table>
<>
<Flex justify={'space-between'} align={'center'} pb={'1rem'}>
{Tabs}
<HStack>
{userInfo?.team.permission.hasManagePer && feConfigs?.show_team_chat && (
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="core/dataset/tag" w={'16px'} />}
onClick={() => {
onOpenTeamTagsAsync();
}}
>
{t('account_team:label_sync')}
</Button>
)}
{userInfo?.team.permission.hasManagePer && (
<Button
variant={'primary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="common/inviteLight" w={'16px'} color={'white'} />}
onClick={() => {
if (
teamPlanStatus?.standardConstants?.maxTeamMember &&
teamPlanStatus.standardConstants.maxTeamMember <= members.length
) {
toast({
status: 'warning',
title: t('common:user.team.Over Max Member Tip', {
max: teamPlanStatus.standardConstants.maxTeamMember
})
});
setNotSufficientModalType(TeamErrEnum.teamMemberOverSize);
} else {
onOpenInvite();
}
}}
>
{t('account_team:user_team_invite_member')}
</Button>
)}
{!userInfo?.team.permission.isOwner && (
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'support/account/loginoutLight'} w={'14px'} />}
onClick={() => openLeaveConfirm(onLeaveTeam)()}
>
{t('account_team:user_team_leave_team')}
</Button>
)}
</HStack>
</Flex>
<ConfirmRemoveMemberModal />
</TableContainer>
</MyBox>
<Box flex={'1 0 0'} overflow={'auto'}>
<TableContainer overflow={'unset'} fontSize={'sm'}>
<Table overflow={'unset'}>
<Thead>
<Tr bgColor={'white !important'}>
<Th borderLeftRadius="6px" bgColor="myGray.100">
{t('account_team:user_name')}
</Th>
<Th bgColor="myGray.100">{t('account_team:member_group')}</Th>
<Th borderRightRadius="6px" bgColor="myGray.100">
{t('common:common.Action')}
</Th>
</Tr>
</Thead>
<Tbody>
{members?.map((item) => (
<Tr key={item.userId} overflow={'unset'}>
<Td>
<HStack>
<Avatar src={item.avatar} w={['18px', '22px']} borderRadius={'50%'} />
<Box className={'textEllipsis'}>
{item.memberName}
{item.status === 'waiting' && (
<Tag ml="2" colorSchema="yellow">
{t('account_team:waiting')}
</Tag>
)}
</Box>
</HStack>
</Td>
<Td maxW={'300px'}>
<GroupTags
names={groups
?.filter((group) => group.members.map((m) => m.tmbId).includes(item.tmbId))
.map((g) => g.name)}
max={3}
/>
</Td>
<Td>
{userInfo?.team.permission.hasManagePer &&
item.role !== TeamMemberRoleEnum.owner &&
item.tmbId !== userInfo?.team.tmbId && (
<Icon
name={'common/trash'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'red.600',
bgColor: 'myGray.100'
}}
onClick={() => {
openRemoveMember(
() =>
delRemoveMember(item.tmbId).then(() =>
Promise.all([refetchGroups(), refetchMembers()])
),
undefined,
t('account_team:remove_tip', {
username: item.memberName
})
)();
}}
/>
)}
</Td>
</Tr>
))}
</Tbody>
</Table>
<ConfirmRemoveMemberModal />
</TableContainer>
</Box>
<ConfirmLeaveTeamModal />
{isOpenInvite && userInfo?.team?.teamId && (
<InviteModal
teamId={userInfo.team.teamId}
onClose={onCloseInvite}
onSuccess={refetchMembers}
/>
)}
{isOpenTeamTagsAsync && <TeamTagModal onClose={onCloseTeamTagsAsync} />}
</>
);
}

View File

@@ -71,7 +71,7 @@ function ActionButton({
);
}
function OrgTable() {
function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
const { t } = useTranslation();
const { userInfo, isTeamAdmin } = useUserStore();
@@ -157,99 +157,69 @@ function OrgTable() {
});
return (
<MyBox isLoading={isLoadingOrgs}>
<Box mb={3}>
<Path paths={paths} rootName={userInfo?.team?.teamName} onClick={setParentPath} />
</Box>
<Flex w={'100%'} gap={'4'}>
{/* Table */}
<TableContainer overflow={'unset'} fontSize={'sm'} flexGrow={1}>
<Table overflow={'unset'}>
<Thead>
<Tr bg={'white !important'}>
<Th bg="myGray.100" borderLeftRadius="6px">
{t('common:Name')}
</Th>
<Th bg="myGray.100" borderRightRadius="6px">
{t('common:common.Action')}
</Th>
</Tr>
</Thead>
<Tbody>
{currentOrgs.map((org) => (
<Tr key={org._id} overflow={'unset'}>
<Td>
<HStack
cursor={'pointer'}
onClick={() => setParentPath(getOrgChildrenPath(org))}
>
<MemberTag name={org.name} avatar={org.avatar} />
<Tag size="sm">{org.count}</Tag>
<MyIcon
name="core/chat/chevronRight"
w={'1rem'}
h={'1rem'}
color={'myGray.500'}
/>
</HStack>
</Td>
<Td w={'6rem'}>
{isTeamAdmin && (
<MyMenu
trigger="hover"
Button={<IconButton name="more" />}
menuList={[
{
children: [
{
icon: 'edit',
label: t('account_team:edit_info'),
onClick: () => setEditOrg(org)
},
{
icon: 'common/file/move',
label: t('common:Move'),
onClick: () => setMovingOrg(org)
},
{
icon: 'delete',
label: t('account_team:delete'),
type: 'danger',
onClick: () => deleteOrgHandler(org._id)
}
]
}
]}
/>
)}
</Td>
<>
<Flex justify={'space-between'} align={'center'} pb={'1rem'}>
{Tabs}
</Flex>
<MyBox flex={'1 0 0'} overflow={'auto'} isLoading={isLoadingOrgs}>
<Box mb={3}>
<Path paths={paths} rootName={userInfo?.team?.teamName} onClick={setParentPath} />
</Box>
<Flex w={'100%'} gap={'4'}>
{/* Table */}
<TableContainer overflow={'unset'} fontSize={'sm'} flexGrow={1}>
<Table overflow={'unset'}>
<Thead>
<Tr bg={'white !important'}>
<Th bg="myGray.100" borderLeftRadius="6px">
{t('common:Name')}
</Th>
<Th bg="myGray.100" borderRightRadius="6px">
{t('common:common.Action')}
</Th>
</Tr>
))}
{currentOrg?.members.map((member) => {
const memberInfo = members.find((m) => m.tmbId === member.tmbId);
if (!memberInfo) return null;
return (
<Tr key={member.tmbId}>
</Thead>
<Tbody>
{currentOrgs.map((org) => (
<Tr key={org._id} overflow={'unset'}>
<Td>
<MemberTag name={memberInfo.memberName} avatar={memberInfo.avatar} />
<HStack
cursor={'pointer'}
onClick={() => setParentPath(getOrgChildrenPath(org))}
>
<MemberTag name={org.name} avatar={org.avatar} />
<Tag size="sm">{org.count}</Tag>
<MyIcon
name="core/chat/chevronRight"
w={'1rem'}
h={'1rem'}
color={'myGray.500'}
/>
</HStack>
</Td>
<Td w={'6rem'}>
{isTeamAdmin && (
<MyMenu
trigger={'hover'}
trigger="hover"
Button={<IconButton name="more" />}
menuList={[
{
children: [
{
icon: 'edit',
label: t('account_team:edit_info'),
onClick: () => setEditOrg(org)
},
{
icon: 'common/file/move',
label: t('common:Move'),
onClick: () => setMovingOrg(org)
},
{
icon: 'delete',
label: t('account_team:delete'),
type: 'danger',
onClick: () =>
openDeleteMemberModal(() =>
deleteMemberReq(currentOrg._id, member.tmbId)
)()
onClick: () => deleteOrgHandler(org._id)
}
]
}
@@ -258,91 +228,126 @@ function OrgTable() {
)}
</Td>
</Tr>
);
})}
</Tbody>
</Table>
</TableContainer>
{/* Slider */}
<VStack w={'180px'} alignItems={'start'}>
<HStack gap={'6px'}>
<Avatar src={currentOrg?.avatar} w={'1rem'} h={'1rem'} rounded={'xs'} />
<Box fontWeight={500} color={'myGray.900'}>
{currentOrg?.name}
</Box>
{currentOrg?.path !== '' && (
<IconButton name="edit" onClick={() => setEditOrg(currentOrg)} />
)}
</HStack>
<Box fontSize={'xs'}>{currentOrg?.description || t('common:common.no_intro')}</Box>
))}
{currentOrg?.members.map((member) => {
const memberInfo = members.find((m) => m.tmbId === member.tmbId);
if (!memberInfo) return null;
<Divider my={'20px'} />
<Box fontWeight={500} fontSize="sm" color="myGray.900">
{t('common:common.Action')}
</Box>
{currentOrg && isTeamAdmin && (
<VStack gap="13px" w="100%">
<ActionButton
icon="common/add2"
text={t('account_team:create_sub_org')}
onClick={() => {
setEditOrg({
...defaultOrgForm,
parentId: currentOrg?._id
});
}}
/>
<ActionButton
icon="common/administrator"
text={t('account_team:manage_member')}
onClick={() => setManageMemberOrg(currentOrg)}
/>
return (
<Tr key={member.tmbId}>
<Td>
<MemberTag name={memberInfo.memberName} avatar={memberInfo.avatar} />
</Td>
<Td w={'6rem'}>
{isTeamAdmin && (
<MyMenu
trigger={'hover'}
Button={<IconButton name="more" />}
menuList={[
{
children: [
{
icon: 'delete',
label: t('account_team:delete'),
type: 'danger',
onClick: () =>
openDeleteMemberModal(() =>
deleteMemberReq(currentOrg._id, member.tmbId)
)()
}
]
}
]}
/>
)}
</Td>
</Tr>
);
})}
</Tbody>
</Table>
</TableContainer>
{/* Slider */}
<VStack w={'180px'} alignItems={'start'}>
<HStack gap={'6px'}>
<Avatar src={currentOrg?.avatar} w={'1rem'} h={'1rem'} rounded={'xs'} />
<Box fontWeight={500} color={'myGray.900'}>
{currentOrg?.name}
</Box>
{currentOrg?.path !== '' && (
<>
<ActionButton
icon="common/file/move"
text={t('account_team:move_org')}
onClick={() => setMovingOrg(currentOrg)}
/>
<ActionButton
icon="delete"
text={t('account_team:delete_org')}
onClick={() => deleteOrgHandler(currentOrg._id)}
/>
</>
<IconButton name="edit" onClick={() => setEditOrg(currentOrg)} />
)}
</VStack>
)}
</VStack>
</Flex>
</HStack>
<Box fontSize={'xs'}>{currentOrg?.description || t('common:common.no_intro')}</Box>
{!!editOrg && (
<OrgInfoModal
editOrg={editOrg}
onClose={() => setEditOrg(undefined)}
onSuccess={refetchOrgs}
/>
)}
{!!movingOrg && (
<OrgMoveModal
orgs={orgs}
movingOrg={movingOrg}
onClose={() => setMovingOrg(undefined)}
onSuccess={refetchOrgs}
/>
)}
{!!manageMemberOrg && (
<OrgMemberManageModal
currentOrg={manageMemberOrg}
refetchOrgs={refetchOrgs}
onClose={() => setManageMemberOrg(undefined)}
/>
)}
<Divider my={'20px'} />
<ConfirmDeleteOrgModal />
<ConfirmDeleteMember />
</MyBox>
<Box fontWeight={500} fontSize="sm" color="myGray.900">
{t('common:common.Action')}
</Box>
{currentOrg && isTeamAdmin && (
<VStack gap="13px" w="100%">
<ActionButton
icon="common/add2"
text={t('account_team:create_sub_org')}
onClick={() => {
setEditOrg({
...defaultOrgForm,
parentId: currentOrg?._id
});
}}
/>
<ActionButton
icon="common/administrator"
text={t('account_team:manage_member')}
onClick={() => setManageMemberOrg(currentOrg)}
/>
{currentOrg?.path !== '' && (
<>
<ActionButton
icon="common/file/move"
text={t('account_team:move_org')}
onClick={() => setMovingOrg(currentOrg)}
/>
<ActionButton
icon="delete"
text={t('account_team:delete_org')}
onClick={() => deleteOrgHandler(currentOrg._id)}
/>
</>
)}
</VStack>
)}
</VStack>
</Flex>
{!!editOrg && (
<OrgInfoModal
editOrg={editOrg}
onClose={() => setEditOrg(undefined)}
onSuccess={refetchOrgs}
/>
)}
{!!movingOrg && (
<OrgMoveModal
orgs={orgs}
movingOrg={movingOrg}
onClose={() => setMovingOrg(undefined)}
onSuccess={refetchOrgs}
/>
)}
{!!manageMemberOrg && (
<OrgMemberManageModal
currentOrg={manageMemberOrg}
refetchOrgs={refetchOrgs}
onClose={() => setManageMemberOrg(undefined)}
/>
)}
<ConfirmDeleteOrgModal />
<ConfirmDeleteMember />
</MyBox>
</>
);
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo, useState } from 'react';
import {
Box,
Checkbox,
@@ -10,7 +10,9 @@ import {
Th,
Thead,
Text,
Tr
Tr,
Flex,
Button
} from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
@@ -26,442 +28,400 @@ import MemberTag from '../../../../../components/support/user/team/Info/MemberTa
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import {
TeamManagePermissionVal,
TeamPermissionList,
TeamWritePermissionVal
} from '@fastgpt/global/support/permission/user/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { useCreation, useToggle } from 'ahooks';
import { useToggle } from 'ahooks';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import MemberModal from '@/components/support/permission/MemberManager/MemberModal';
import MyBox from '@fastgpt/web/components/common/MyBox';
import CollaboratorContextProvider, {
CollaboratorContext
} from '@/components/support/permission/MemberManager/context';
import MyIcon from '@fastgpt/web/components/common/Icon';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { useContextSelector } from 'use-context-selector';
import { CollaboratorItemType } from '@fastgpt/global/support/permission/collaborator';
function PermissionManage({
isOpenAddPermission,
onCloseAddPermission
Tabs,
onOpenAddMember
}: {
isOpenAddPermission: boolean;
onCloseAddPermission: () => void;
Tabs: React.ReactNode;
onOpenAddMember: () => void;
}) {
const { t } = useTranslation();
const { userInfo } = useUserStore();
const [searchKey, setSearchKey] = useState('');
const { runAsync: refetchClbs, data: clbs = { tmb: [], group: [], org: [] } } = useRequest2(
getTeamClbs,
{
manual: false,
refreshDeps: [userInfo?.team?.teamId]
}
const collaboratorList = useContextSelector(
CollaboratorContext,
(state) => state.collaboratorList
);
const onUpdateCollaborators = useContextSelector(
CollaboratorContext,
(state) => state.onUpdateCollaborators
);
const onDelOneCollaborator = useContextSelector(
CollaboratorContext,
(state) => state.onDelOneCollaborator
);
const [isExpandMember, setExpandMember] = useToggle(true);
const [isExpandGroup, setExpandGroup] = useToggle(true);
const [isExpandOrg, setExpandOrg] = useToggle(true);
const members = useCreation(
() =>
clbs.tmb.map((item) => ({
...item,
permission: new TeamPermission({ per: item.permission })
})),
[clbs]
);
const { tmbList, groupList, orgList } = useMemo(() => {
const tmbList: CollaboratorItemType[] = [];
const groupList: CollaboratorItemType[] = [];
const orgList: CollaboratorItemType[] = [];
const groups = useCreation(
() =>
clbs.group.map((item) => ({
...item,
permission: new TeamPermission({ per: item.permission })
})),
[clbs]
);
const orgs = useCreation(
() =>
clbs.org.map((item) => ({
...item,
permission: new TeamPermission({ per: item.permission })
})),
[clbs]
);
const { runAsync: onUpdateMemberPermission } = useRequest2(updateMemberPermission, {
onSuccess: () => {
refetchClbs();
}
});
const { runAsync: onAddPermission, loading: addLoading } = useRequest2(
async ({
orgId,
groupId,
memberId,
per
}: {
orgId?: string;
groupId?: string;
memberId?: string;
per: 'write' | 'manage';
}) => {
if (groupId) {
const group = groups?.find((group) => group.groupId === groupId);
if (group) {
const permission = new TeamPermission({ per: group.permission.value });
switch (per) {
case 'write':
permission.addPer(TeamWritePermissionVal);
return onUpdateMemberPermission({
groupId: group.groupId,
permission: permission.value
});
case 'manage':
permission.addPer(TeamManagePermissionVal);
return onUpdateMemberPermission({
groupId: group.groupId,
permission: permission.value
});
}
}
collaboratorList.forEach((item) => {
if (item.tmbId) {
tmbList.push(item);
} else if (item.groupId) {
groupList.push(item);
} else if (item.orgId) {
orgList.push(item);
}
if (orgId) {
const org = orgs.find((org) => String(org.orgId) === orgId);
if (org) {
const permission = new TeamPermission({ per: org.permission.value });
switch (per) {
case 'write':
permission.addPer(TeamWritePermissionVal);
return onUpdateMemberPermission({
orgId: org.orgId,
permission: permission.value
});
case 'manage':
permission.addPer(TeamManagePermissionVal);
return onUpdateMemberPermission({
orgId: org.orgId,
permission: permission.value
});
}
}
}
if (memberId) {
const member = members?.find((member) => member.tmbId === memberId);
if (member) {
const permission = new TeamPermission({ per: member.permission.value });
switch (per) {
case 'write':
permission.addPer(TeamWritePermissionVal);
return onUpdateMemberPermission({
memberId: member.tmbId,
permission: permission.value
});
case 'manage':
permission.addPer(TeamManagePermissionVal);
return onUpdateMemberPermission({
memberId: member.tmbId,
permission: permission.value
});
}
}
});
return {
tmbList,
groupList,
orgList
};
}, [collaboratorList]);
const { runAsync: onUpdatePermission, loading: addLoading } = useRequest2(
async ({ id, type, per }: { id: string; type: 'add' | 'remove'; per: 'write' | 'manage' }) => {
const clb = collaboratorList.find(
(clb) => clb.tmbId === id || clb.groupId === id || clb.orgId === id
);
if (!clb) return;
const updatePer = per === 'write' ? TeamWritePermissionVal : TeamManagePermissionVal;
const permission = new TeamPermission({ per: clb.permission.value });
if (type === 'add') {
permission.addPer(updatePer);
} else {
permission.removePer(updatePer);
}
return onUpdateCollaborators({
...(clb.tmbId && { members: [clb.tmbId] }),
...(clb.groupId && { groups: [clb.groupId] }),
...(clb.orgId && { orgs: [clb.orgId] }),
permission: permission.value
});
}
);
const { runAsync: onRemovePermission, loading: removeLoading } = useRequest2(
async ({
orgId,
groupId,
memberId,
per
}: {
orgId?: string;
groupId?: string;
memberId?: string;
per: 'write' | 'manage';
}) => {
if (groupId) {
const group = groups?.find((group) => group.groupId === groupId);
if (group) {
const permission = new TeamPermission({ per: group.permission.value });
switch (per) {
case 'write':
permission.removePer(TeamWritePermissionVal);
return onUpdateMemberPermission({
groupId: group.groupId,
permission: permission.value
});
case 'manage':
permission.removePer(TeamManagePermissionVal);
return onUpdateMemberPermission({
groupId: group.groupId,
permission: permission.value
});
}
}
}
if (orgId) {
const org = orgs.find((org) => String(org.orgId) === orgId);
if (org) {
const permission = new TeamPermission({ per: org.permission.value });
switch (per) {
case 'write':
permission.removePer(TeamWritePermissionVal);
return onUpdateMemberPermission({
orgId: org.orgId,
permission: permission.value
});
case 'manage':
permission.removePer(TeamManagePermissionVal);
return onUpdateMemberPermission({
orgId: org.orgId,
permission: permission.value
});
}
}
}
if (memberId) {
const member = members?.find((member) => String(member.tmbId) === memberId);
if (member) {
const permission = new TeamPermission({ per: member.permission.value }); // Hint: member.permission is read-only
switch (per) {
case 'write':
permission.removePer(TeamWritePermissionVal);
return onUpdateMemberPermission({
memberId: String(member.tmbId),
permission: permission.value
});
case 'manage':
permission.removePer(TeamManagePermissionVal);
return onUpdateMemberPermission({
memberId: String(member.tmbId),
permission: permission.value
});
}
}
}
}
);
const { runAsync: onDeleteMemberPermission } = useRequest2(deleteMemberPermission, {
onSuccess: () => {
refetchClbs();
}
});
const { runAsync: onDeleteMemberPermission, loading: deleteLoading } =
useRequest2(onDelOneCollaborator);
const userManage = userInfo?.permission.hasManagePer;
return (
<TableContainer fontSize={'sm'}>
<Table>
<Thead>
<Tr bg={'white !important'}>
<Th bg="myGray.100" borderLeftRadius="md" maxW={'150px'}>
{`${t('user:team.group.members')} / ${t('user:team.org.org')} / ${t('user:team.group.group')}`}
<QuestionTip ml="1" label={t('user:team.group.permission_tip')} />
</Th>
<Th bg="myGray.100">
<Box mx="auto" w="fit-content">
{t('user:team.group.permission.write')}
</Box>
</Th>
<Th bg="myGray.100">
<Box mx="auto" w="fit-content">
{t('user:team.group.permission.manage')}
<QuestionTip ml="1" label={t('user:team.group.manage_tip')} />
</Box>
</Th>
<Th bg="myGray.100" borderRightRadius="md">
<Box mx="auto" w="fit-content">
{t('common:common.Action')}
</Box>
</Th>
</Tr>
</Thead>
<Tbody>
<Tr overflow={'unset'} border="none">
<HStack paddingX={'8px'} paddingY={'4px'}>
<MyIconButton
icon={isExpandMember ? 'common/downArrowFill' : 'common/rightArrowFill'}
onClick={setExpandMember.toggle}
/>
<Text>{t('user:team.group.members')}</Text>
</HStack>
</Tr>
{isExpandMember &&
members.map((member) => (
<Tr key={member.tmbId} overflow={'unset'} border="none">
<Td border="none">
<HStack>
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
<Box>{member.name}</Box>
<>
<Flex justify={'space-between'} align={'center'} pb={'1rem'}>
{Tabs}
<Box ml="auto">
{/* <SearchInput
placeholder={t('user:team.group.search_placeholder')}
w="200px"
value={searchKey}
onChange={(e) => setSearchKey(e.target.value)}
/> */}
</Box>
{userInfo?.team.permission.hasManagePer && (
<Button
variant={'primary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="common/add2" w={'14px'} />}
onClick={onOpenAddMember}
>
{t('common:common.Add')}
</Button>
)}
</Flex>
<MyBox isLoading={addLoading || deleteLoading}>
<TableContainer fontSize={'sm'}>
<Table>
<Thead>
<Tr bg={'white !important'}>
<Th bg="myGray.100" borderLeftRadius="md" maxW={'150px'}>
{`${t('user:team.group.members')} / ${t('user:team.org.org')} / ${t('user:team.group.group')}`}
<QuestionTip ml="1" label={t('user:team.group.permission_tip')} />
</Th>
<Th bg="myGray.100">
<Box mx="auto" w="fit-content">
{t('user:team.group.permission.write')}
</Box>
</Th>
<Th bg="myGray.100">
<Box mx="auto" w="fit-content">
{t('user:team.group.permission.manage')}
<QuestionTip ml="1" label={t('user:team.group.manage_tip')} />
</Box>
</Th>
<Th bg="myGray.100" borderRightRadius="md">
<Box mx="auto" w="fit-content">
{t('common:common.Action')}
</Box>
</Th>
</Tr>
</Thead>
<Tbody>
<>
<Tr userSelect={'none'}>
<HStack pl={3} pt={3} pb={isExpandMember && !!tmbList.length ? 0 : 3}>
<MyIconButton
icon={isExpandMember ? 'common/downArrowFill' : 'common/rightArrowFill'}
onClick={setExpandMember.toggle}
/>
<Box color={'myGray.900'}>{t('user:team.group.members')}</Box>
</HStack>
</Td>
<Td border="none">
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={member.permission.isOwner || !userManage}
isChecked={member.permission.hasWritePer}
onChange={(e) =>
e.target.checked
? onAddPermission({ memberId: String(member.tmbId), per: 'write' })
: onRemovePermission({ memberId: String(member.tmbId), per: 'write' })
}
</Tr>
{isExpandMember &&
tmbList.map((member) => (
<Tr key={member.tmbId}>
<Td pl={10}>
<HStack>
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
<Box>{member.name}</Box>
</HStack>
</Td>
<Td>
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={member.permission.isOwner || !userManage}
isChecked={member.permission.hasWritePer}
onChange={(e) =>
e.target.checked
? onUpdatePermission({
id: member.tmbId!,
type: 'add',
per: 'write'
})
: onUpdatePermission({
id: member.tmbId!,
type: 'remove',
per: 'write'
})
}
/>
</Box>
</Td>
<Td>
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={member.permission.isOwner || !userInfo?.permission.isOwner}
isChecked={member.permission.hasManagePer}
onChange={(e) =>
e.target.checked
? onUpdatePermission({
id: member.tmbId!,
type: 'add',
per: 'manage'
})
: onUpdatePermission({
id: member.tmbId!,
type: 'remove',
per: 'manage'
})
}
/>
</Box>
</Td>
{userManage &&
!member.permission.isOwner &&
userInfo?.team.tmbId !== member.tmbId && (
<Td>
<Box mx="auto" w="fit-content">
<MyIconButton
icon="common/trash"
onClick={() =>
onDeleteMemberPermission({ tmbId: String(member.tmbId) })
}
/>
</Box>
</Td>
)}
</Tr>
))}
</>
<>
<Tr borderBottom={'1px solid'} borderColor={'myGray.200'} />
<Tr userSelect={'none'}>
<HStack pl={3} pt={3} pb={isExpandOrg && !!orgList.length ? 0 : 3}>
<MyIconButton
icon={isExpandOrg ? 'common/downArrowFill' : 'common/rightArrowFill'}
onClick={setExpandOrg.toggle}
/>
</Box>
</Td>
<Td border="none">
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={member.permission.isOwner || !userInfo?.permission.isOwner}
isChecked={member.permission.hasManagePer}
onChange={(e) =>
e.target.checked
? onAddPermission({ memberId: String(member.tmbId), per: 'manage' })
: onRemovePermission({ memberId: String(member.tmbId), per: 'manage' })
}
<Text>{t('user:team.org.org')}</Text>
</HStack>
</Tr>
{isExpandOrg &&
orgList.map((org) => (
<Tr key={org.orgId}>
<Td pl={10}>
<MemberTag name={org.name} avatar={org.avatar} />
</Td>
<Td>
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userManage}
isChecked={org.permission.hasWritePer}
onChange={(e) =>
e.target.checked
? onUpdatePermission({ id: org.orgId!, type: 'add', per: 'write' })
: onUpdatePermission({
id: org.orgId!,
type: 'remove',
per: 'write'
})
}
/>
</Box>
</Td>
<Td>
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userInfo?.permission.isOwner}
isChecked={org.permission.hasManagePer}
onChange={(e) =>
e.target.checked
? onUpdatePermission({ id: org.orgId!, type: 'add', per: 'manage' })
: onUpdatePermission({
id: org.orgId!,
type: 'remove',
per: 'manage'
})
}
/>
</Box>
</Td>
{userInfo?.permission.isOwner && (
<Td>
<Box mx="auto" w="fit-content">
<MyIconButton
icon="common/trash"
onClick={() => onDeleteMemberPermission({ orgId: org.orgId! })}
/>
</Box>
</Td>
)}
</Tr>
))}
</>
<>
<Tr borderBottom={'1px solid'} borderColor={'myGray.200'} />
<Tr userSelect={'none'}>
<HStack pl={3} pt={3} pb={isExpandGroup && !!groupList.length ? 0 : 3}>
<MyIconButton
icon={isExpandGroup ? 'common/downArrowFill' : 'common/rightArrowFill'}
onClick={setExpandGroup.toggle}
/>
</Box>
</Td>
{userManage &&
!member.permission.isOwner &&
userInfo?.team.tmbId !== member.tmbId && (
<Td border="none">
<Box mx="auto" w="fit-content">
<MyIconButton
icon="common/trash"
onClick={() => onDeleteMemberPermission({ tmbId: String(member.tmbId) })}
<Text>{t('user:team.group.group')}</Text>
</HStack>
</Tr>
{isExpandGroup &&
groupList.map((group) => (
<Tr key={group.groupId}>
<Td pl={10}>
<MemberTag
name={
group.name === DefaultGroupName
? userInfo?.team.teamName ?? ''
: group.name
}
avatar={group.avatar}
/>
</Box>
</Td>
)}
</Tr>
))}
<Tr borderBottom={'1px solid'} borderColor={'myGray.200'} />
<Tr overflow={'unset'} border="none">
<HStack paddingX={'8px'} paddingY={'4px'}>
<MyIconButton
icon={isExpandOrg ? 'common/downArrowFill' : 'common/rightArrowFill'}
onClick={setExpandOrg.toggle}
/>
<Text>{t('user:team.org.org')}</Text>
</HStack>
</Tr>
{isExpandOrg &&
orgs.map((org) => (
<Tr key={org.orgId} overflow={'unset'} border="none">
<Td border="none">
<MemberTag name={org.name} avatar={org.avatar} />
</Td>
<Td border="none">
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userManage}
isChecked={org.permission.hasWritePer}
onChange={(e) =>
e.target.checked
? onAddPermission({ orgId: org.orgId, per: 'write' })
: onRemovePermission({ orgId: org.orgId, per: 'write' })
}
/>
</Box>
</Td>
<Td border="none">
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userInfo?.permission.isOwner}
isChecked={org.permission.hasManagePer}
onChange={(e) =>
e.target.checked
? onAddPermission({ orgId: org.orgId, per: 'manage' })
: onRemovePermission({ orgId: org.orgId, per: 'manage' })
}
/>
</Box>
</Td>
{userInfo?.permission.isOwner && (
<Td border="none">
<Box mx="auto" w="fit-content">
<MyIconButton
icon="common/trash"
onClick={() => onDeleteMemberPermission({ orgId: org.orgId })}
/>
</Box>
</Td>
)}
</Tr>
))}
<Tr borderBottom={'1px solid'} borderColor={'myGray.200'} />
<Tr overflow={'unset'} border="none">
<HStack paddingX={'8px'} paddingY={'4px'}>
<MyIconButton
icon={isExpandGroup ? 'common/downArrowFill' : 'common/rightArrowFill'}
onClick={setExpandGroup.toggle}
/>
<Text>{t('user:team.group.group')}</Text>
</HStack>
</Tr>
{isExpandGroup &&
groups.map((group) => (
<Tr key={group.groupId} overflow={'unset'} border="none">
<Td border="none">
<MemberTag
name={
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
}
avatar={group.avatar}
/>
</Td>
<Td border="none">
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userManage}
isChecked={group.permission.hasWritePer}
onChange={(e) =>
e.target.checked
? onAddPermission({ groupId: group.groupId, per: 'write' })
: onRemovePermission({ groupId: group.groupId, per: 'write' })
}
/>
</Box>
</Td>
<Td border="none">
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userInfo?.permission.isOwner}
isChecked={group.permission.hasManagePer}
onChange={(e) =>
e.target.checked
? onAddPermission({ groupId: group.groupId, per: 'manage' })
: onRemovePermission({ groupId: group.groupId, per: 'manage' })
}
/>
</Box>
</Td>
{userInfo?.permission.isOwner && (
<Td border="none">
<Box mx="auto" w="fit-content">
<MyIconButton
icon="common/trash"
onClick={() => onDeleteMemberPermission({ groupId: group.groupId })}
/>
</Box>
</Td>
)}
</Tr>
))}
</Tbody>
</Table>
{isOpenAddPermission && (
<MemberModal
onClose={() => {
refetchClbs();
onCloseAddPermission();
}}
mode="all"
/>
)}
</TableContainer>
</Td>
<Td>
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userManage}
isChecked={group.permission.hasWritePer}
onChange={(e) =>
e.target.checked
? onUpdatePermission({
id: group.groupId!,
type: 'add',
per: 'write'
})
: onUpdatePermission({
id: group.groupId!,
type: 'remove',
per: 'write'
})
}
/>
</Box>
</Td>
<Td>
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={!userInfo?.permission.isOwner}
isChecked={group.permission.hasManagePer}
onChange={(e) =>
e.target.checked
? onUpdatePermission({
id: group.groupId!,
type: 'add',
per: 'manage'
})
: onUpdatePermission({
id: group.groupId!,
type: 'remove',
per: 'manage'
})
}
/>
</Box>
</Td>
{userInfo?.permission.isOwner && (
<Td>
<Box mx="auto" w="fit-content">
<MyIconButton
icon="common/trash"
onClick={() => onDeleteMemberPermission({ groupId: group.groupId! })}
/>
</Box>
</Td>
)}
</Tr>
))}
</>
</Tbody>
</Table>
</TableContainer>
</MyBox>
</>
);
}
export default PermissionManage;
export const Render = ({ Tabs }: { Tabs: React.ReactNode }) => {
const { userInfo } = useUserStore();
return userInfo?.team ? (
<CollaboratorContextProvider
permission={userInfo?.team.permission}
permissionList={TeamPermissionList}
onGetCollaboratorList={getTeamClbs}
onUpdateCollaborators={updateMemberPermission}
onDelOneCollaborator={deleteMemberPermission}
refreshDeps={[userInfo?.team.teamId]}
>
{({ onOpenAddMember }) => <PermissionManage Tabs={Tabs} onOpenAddMember={onOpenAddMember} />}
</CollaboratorContextProvider>
) : null;
};
export default Render;

View File

@@ -25,8 +25,6 @@ type TeamModalContextType = {
refetchMembers: () => void;
refetchTeams: () => void;
refetchGroups: () => void;
searchKey: string;
setSearchKey: React.Dispatch<React.SetStateAction<string>>;
teamSize: number;
};
@@ -51,10 +49,6 @@ export const TeamContext = createContext<TeamModalContextType>({
throw new Error('Function not implemented.');
},
searchKey: '',
setSearchKey: function (_value: React.SetStateAction<string>): void {
throw new Error('Function not implemented.');
},
teamSize: 0
});
@@ -62,7 +56,6 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode })
const { t } = useTranslation();
const [editTeamData, setEditTeamData] = useState<EditTeamFormDataType>();
const { userInfo, initUserInfo, loadAndGetTeamMembers } = useUserStore();
const [searchKey, setSearchKey] = useState('');
const {
data: myTeams = [],
@@ -115,8 +108,6 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode })
refetchTeams,
isLoading,
onSwitchTeam,
searchKey,
setSearchKey,
// create | update team
setEditTeamData,

View File

@@ -1,33 +1,25 @@
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
import AccountContainer from '../components/AccountContainer';
import { Box, Button, Flex, useDisclosure } from '@chakra-ui/react';
import { Box, Flex, useDisclosure } from '@chakra-ui/react';
import Icon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import TeamSelector from '../components/TeamSelector';
import { useUserStore } from '@/web/support/user/useUserStore';
import React, { useState } from 'react';
import React, { useMemo } from 'react';
import { useContextSelector } from 'use-context-selector';
import { useRouter } from 'next/router';
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { delLeaveTeam } from '@/web/support/user/team/api';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { TeamContext, TeamModalContextProvider } from './components/context';
import dynamic from 'next/dynamic';
import TeamTagModal from '@/components/support/user/team/TeamTagModal';
import MemberTable from './components/MemberTable';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
const InviteModal = dynamic(() => import('./components/InviteModal'));
const PermissionManage = dynamic(() => import('./components/PermissionManage/index'));
const GroupManage = dynamic(() => import('./components/GroupManage/index'));
const GroupInfoModal = dynamic(() => import('./components/GroupManage/GroupInfoModal'));
const ManageGroupMemberModal = dynamic(() => import('./components/GroupManage/GroupManageMember'));
const OrgManage = dynamic(() => import('./components/OrgManage/index'));
export enum TeamTabEnum {
@@ -39,79 +31,37 @@ export enum TeamTabEnum {
const Team = () => {
const router = useRouter();
const { toast } = useToast();
const { t } = useTranslation();
const { userInfo, teamPlanStatus } = useUserStore();
const { feConfigs, setNotSufficientModalType } = useSystemStore();
const {
myTeams,
refetchTeams,
members,
refetchMembers,
setEditTeamData,
onSwitchTeam,
searchKey,
setSearchKey,
teamSize,
isLoading
} = useContextSelector(TeamContext, (v) => v);
const { teamTab = TeamTabEnum.member } = router.query as { teamTab: `${TeamTabEnum}` };
const {
isOpen: isOpenTeamTagsAsync,
onOpen: onOpenTeamTagsAsync,
onClose: onCloseTeamTagsAsync
} = useDisclosure();
const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure();
const {
isOpen: isOpenGroupInfo,
onOpen: onOpenGroupInfo,
onClose: onCloseGroupInfo
} = useDisclosure();
const {
isOpen: isOpenManageGroupMember,
onOpen: onOpenManageGroupMember,
onClose: onCloseManageGroupMember
} = useDisclosure();
const {
isOpen: isOpenAddPermission,
onOpen: onOpenAddPermission,
onClose: onCloseAddPermission
} = useDisclosure();
const { t } = useTranslation();
const { userInfo } = useUserStore();
const { runAsync: onLeaveTeam } = useRequest2(
async () => {
const defaultTeam = myTeams.find((item) => item.defaultTeam) || myTeams[0];
// change to personal team
// get members
onSwitchTeam(defaultTeam.teamId);
return delLeaveTeam();
},
{
onSuccess() {
refetchTeams();
},
errorToast: t('account_team:user_team_leave_team_failed')
}
const { setEditTeamData, teamSize, isLoading } = useContextSelector(TeamContext, (v) => v);
const Tabs = useMemo(
() => (
<FillRowTabs
list={[
{ label: t('account_team:member'), value: TeamTabEnum.member },
{ label: t('account_team:org'), value: TeamTabEnum.org },
{ label: t('account_team:group'), value: TeamTabEnum.group },
{ label: t('account_team:permission'), value: TeamTabEnum.permission }
]}
px={'1rem'}
value={teamTab}
onChange={(e) => {
router.replace({
query: {
...router.query,
teamTab: e
}
});
}}
/>
),
[router, t, teamTab]
);
const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({
content: t('account_team:confirm_leave_team')
});
const [editGroupId, setEditGroupId] = useState<string>();
const onEditGroup = (groupId: string) => {
setEditGroupId(groupId);
onOpenGroupInfo();
};
const onManageMember = (groupId: string) => {
setEditGroupId(groupId);
onOpenManageGroupMember();
};
return (
<AccountContainer isLoading={isLoading}>
{/* header */}
@@ -175,159 +125,11 @@ const Team = () => {
{/* table */}
<Box py={'1.5rem'} px={'2rem'}>
<Flex justify={'space-between'} align={'center'} pb={'1rem'}>
<FillRowTabs
list={[
{ label: t('account_team:member'), value: TeamTabEnum.member },
{ label: t('account_team:org'), value: TeamTabEnum.org },
{ label: t('account_team:group'), value: TeamTabEnum.group },
{ label: t('account_team:permission'), value: TeamTabEnum.permission }
]}
px={'1rem'}
value={teamTab}
onChange={(e) => {
router.replace({
query: {
...router.query,
teamTab: e
}
});
}}
/>
<Flex alignItems={'center'}>
{teamTab === TeamTabEnum.member &&
userInfo?.team.permission.hasManagePer &&
feConfigs?.show_team_chat && (
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="core/dataset/tag" w={'16px'} />}
onClick={() => {
onOpenTeamTagsAsync();
}}
>
{t('account_team:label_sync')}
</Button>
)}
{teamTab === TeamTabEnum.member && userInfo?.team.permission.hasManagePer && (
<Button
variant={'primary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="common/inviteLight" w={'16px'} color={'white'} />}
onClick={() => {
if (
teamPlanStatus?.standardConstants?.maxTeamMember &&
teamPlanStatus.standardConstants.maxTeamMember <= members.length
) {
toast({
status: 'warning',
title: t('common:user.team.Over Max Member Tip', {
max: teamPlanStatus.standardConstants.maxTeamMember
})
});
setNotSufficientModalType(TeamErrEnum.teamMemberOverSize);
} else {
onOpenInvite();
}
}}
>
{t('account_team:user_team_invite_member')}
</Button>
)}
{teamTab === TeamTabEnum.member && !userInfo?.team.permission.isOwner && (
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'support/account/loginoutLight'} w={'14px'} />}
onClick={() => openLeaveConfirm(onLeaveTeam)()}
>
{t('account_team:user_team_leave_team')}
</Button>
)}
{teamTab === TeamTabEnum.group && userInfo?.team.permission.hasManagePer && (
<Button
variant={'primary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="support/permission/collaborator" w={'14px'} />}
onClick={onOpenGroupInfo}
>
{t('user:team.group.create')}
</Button>
)}
{teamTab === TeamTabEnum.permission && (
<Box ml="auto">
<SearchInput
placeholder={t('user:team.group.search_placeholder')}
w="200px"
value={searchKey}
onChange={(e) => setSearchKey(e.target.value)}
/>
</Box>
)}
{teamTab === TeamTabEnum.permission && userInfo?.team.permission.hasManagePer && (
<Button
variant={'primary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="common/add2" w={'14px'} />}
onClick={onOpenAddPermission}
>
{t('common:common.Add')}
</Button>
)}
</Flex>
</Flex>
<Box flex={'1 0 0'} overflow={'auto'}>
{teamTab === TeamTabEnum.member && <MemberTable />}
{teamTab === TeamTabEnum.group && (
<GroupManage onEditGroup={onEditGroup} onManageMember={onManageMember} />
)}
{teamTab === TeamTabEnum.org && <OrgManage />}
{teamTab === TeamTabEnum.permission && (
<PermissionManage
isOpenAddPermission={isOpenAddPermission}
onCloseAddPermission={onCloseAddPermission}
/>
)}
</Box>
{teamTab === TeamTabEnum.member && <MemberTable Tabs={Tabs} />}
{teamTab === TeamTabEnum.org && <OrgManage Tabs={Tabs} />}
{teamTab === TeamTabEnum.group && <GroupManage Tabs={Tabs} />}
{teamTab === TeamTabEnum.permission && <PermissionManage Tabs={Tabs} />}
</Box>
{isOpenInvite && userInfo?.team?.teamId && (
<InviteModal
teamId={userInfo.team.teamId}
onClose={onCloseInvite}
onSuccess={refetchMembers}
/>
)}
{isOpenTeamTagsAsync && <TeamTagModal onClose={onCloseTeamTagsAsync} />}
{isOpenGroupInfo && (
<GroupInfoModal
onClose={() => {
onCloseGroupInfo();
setEditGroupId(undefined);
}}
editGroupId={editGroupId}
/>
)}
{isOpenManageGroupMember && (
<ManageGroupMemberModal
onClose={() => {
onCloseManageGroupMember();
setEditGroupId(undefined);
}}
editGroupId={editGroupId}
/>
)}
<ConfirmLeaveTeamModal />
</AccountContainer>
);
};

View File

@@ -0,0 +1,25 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import * as fs from 'fs';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
export type writefileQuery = {};
export type writefileBody = {
name: string;
content: string;
};
export type writefileResponse = {};
async function handler(
req: ApiRequestProps<writefileBody, writefileQuery>,
res: ApiResponseType<any>
): Promise<writefileResponse> {
await authCert({ req, authRoot: true });
const { name, content } = req.body;
await fs.promises.writeFile(`public/${name}`, content);
return {};
}
export default NextAPI(handler);

View File

@@ -187,7 +187,6 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
)}
<Box mt={6}>
<CollaboratorContextProvider
mode="all"
permission={appDetail.permission}
onGetCollaboratorList={() => getCollaboratorList(appDetail._id)}
permissionList={AppPermissionList}

View File

@@ -57,7 +57,7 @@ const NodeCard = (props: Props) => {
name = t('common:core.module.template.UnKnow Module'),
intro,
minW = '300px',
maxW = '600px',
maxW = '666px',
minH = 0,
w = 'full',
h = 'full',

View File

@@ -431,7 +431,6 @@ const ListItem = () => {
avatar={editPerApp.avatar}
name={editPerApp.name}
managePer={{
mode: 'all',
permission: editPerApp.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerApp._id),
permissionList: AppPermissionList,

View File

@@ -301,7 +301,6 @@ const MyApps = () => {
deleteTip={t('app:confirm_delete_folder_tip')}
onDelete={() => onDeleFolder(folderDetail._id)}
managePer={{
mode: 'all',
permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: AppPermissionList,

View File

@@ -1,4 +1,4 @@
import { Box, Button, Flex, FormLabel } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import CollaboratorContextProvider, {
MemberManagerInputPropsType

View File

@@ -354,7 +354,6 @@ const Info = ({ datasetId }: { datasetId: string }) => {
<Box>
<MemberManager
managePer={{
mode: 'all',
permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList,

View File

@@ -440,7 +440,6 @@ function List() {
avatar={editPerDataset.avatar}
name={editPerDataset.name}
managePer={{
mode: 'all',
permission: editPerDataset.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerDataset._id),
permissionList: DatasetPermissionList,

View File

@@ -238,7 +238,6 @@ const Dataset = () => {
})
}
managePer={{
mode: 'all',
permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: DatasetPermissionList,

View File

@@ -0,0 +1,5 @@
export type CreateModelParams = {
name: string;
description: string;
prompt: string;
};

View File

@@ -0,0 +1,66 @@
import { addLog } from '@fastgpt/service/common/system/log';
import axios, { Method } from 'axios';
const url = process.env.API_PROXY_URL;
const token = process.env.API_PROXY_TOKEN;
const instance = axios.create({
baseURL: url,
timeout: 60000, // 超时时间
headers: {
Authorization: `Bearer ${token}`
}
});
/**
* 响应数据检查
*/
const checkRes = (data: any) => {
if (data === undefined) {
addLog.info('api proxy data is empty');
return Promise.reject('服务器异常');
}
return data.data;
};
const responseError = (err: any) => {
console.log('error->', '请求错误', err);
if (!err) {
return Promise.reject({ message: '未知错误' });
}
if (typeof err === 'string') {
return Promise.reject({ message: err });
}
if (typeof err.message === 'string') {
return Promise.reject({ message: err.message });
}
if (typeof err.data === 'string') {
return Promise.reject({ message: err.data });
}
if (err?.response?.data) {
return Promise.reject(err?.response?.data);
}
return Promise.reject(err);
};
const request = <T>(url: string, data: any, method: Method): Promise<T> => {
/* 去空 */
for (const key in data) {
if (data[key] === undefined) {
delete data[key];
}
}
return instance
.request({
url,
method,
data: ['POST', 'PUT'].includes(method) ? data : undefined,
params: !['POST', 'PUT'].includes(method) ? data : undefined
})
.then((res) => checkRes(res.data))
.catch((err) => responseError(err));
};
// TODO: channel crud
export const ApiProxy = {};

View File

@@ -9,6 +9,7 @@ import { TOKEN_ERROR_CODE } from '@fastgpt/global/common/error/errorCode';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { useSystemStore } from '../system/useSystemStore';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import { i18nT } from '@fastgpt/web/i18n/utils';
interface ConfigType {
headers?: { [key: string]: string };
@@ -108,17 +109,15 @@ function responseError(err: any) {
return Promise.reject({ message: err });
}
// 有报错响应
if (err?.code in TOKEN_ERROR_CODE) {
if (
!(window.location.pathname === '/chat/share' || window.location.pathname === '/chat/team')
) {
if (err?.code in TOKEN_ERROR_CODE || err?.response?.data?.code in TOKEN_ERROR_CODE) {
if (!['/chat/share', '/chat/team', '/login'].includes(window.location.pathname)) {
clearToken();
window.location.replace(
getWebReqUrl(`/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}`)
);
}
return Promise.reject({ message: '无权操作' });
return Promise.reject({ message: i18nT('common:unauth_token') });
}
if (
err?.statusText === TeamErrEnum.aiPointsNotEnough ||

View File

@@ -1,9 +1,8 @@
import { GET, POST, PUT, DELETE } from '@/web/common/api/request';
import {
CreatePermissionBody,
CollaboratorItemType,
DeletePermissionQuery,
ListPermissionResponse,
UpdatePermissionBody
UpdateClbPermissionProps
} from '@fastgpt/global/support/permission/collaborator';
import {
CreateTeamProps,
@@ -43,16 +42,11 @@ export const updateInviteResult = (data: UpdateInviteProps) =>
PUT('/proApi/support/user/team/member/updateInvite', data);
export const delLeaveTeam = () => DELETE('/proApi/support/user/team/member/leave');
export const getTeamClbs = () =>
GET<ListPermissionResponse>(`/proApi/support/user/team/collaborator/list`);
/* -------------- team collaborator -------------------- */
export const updateMemberPermission = (data: UpdatePermissionBody) =>
PUT('/proApi/support/user/team/collaborator/updatePermission', data);
export const createMemberPermission = (data: CreatePermissionBody) =>
POST('/proApi/support/user/team/collaborator/create', data);
export const getTeamClbs = () =>
GET<CollaboratorItemType[]>(`/proApi/support/user/team/collaborator/list`);
export const updateMemberPermission = (data: UpdateClbPermissionProps) =>
PUT('/proApi/support/user/team/collaborator/update', data);
export const deleteMemberPermission = (id: DeletePermissionQuery) =>
DELETE('/proApi/support/user/team/collaborator/delete', id);