mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-01 20:27:45 +00:00
v4.6.9-alpha (#918)
Co-authored-by: Mufei <327958099@qq.com> Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import { IMG_BLOCK_KEY } from '@fastgpt/global/core/chat/constants';
|
||||
import { addDays } from 'date-fns';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
|
||||
enum FileTypeEnum {
|
||||
@@ -35,8 +36,12 @@ const MessageInput = ({
|
||||
isChatting,
|
||||
TextareaDom,
|
||||
showFileSelector = false,
|
||||
resetInputVal
|
||||
}: {
|
||||
resetInputVal,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
}: OutLinkChatAuthProps & {
|
||||
onChange?: (e: string) => void;
|
||||
onSendMessage: (e: string) => void;
|
||||
onStop: () => void;
|
||||
@@ -47,7 +52,6 @@ const MessageInput = ({
|
||||
}) => {
|
||||
const [, startSts] = useTransition();
|
||||
|
||||
const { shareId } = useRouter().query as { shareId?: string };
|
||||
const {
|
||||
isSpeaking,
|
||||
isTransCription,
|
||||
@@ -56,7 +60,7 @@ const MessageInput = ({
|
||||
speakingTimeString,
|
||||
renderAudioGraph,
|
||||
stream
|
||||
} = useSpeech({ shareId });
|
||||
} = useSpeech({ shareId, outLinkUid, teamId, teamToken });
|
||||
const { isPc } = useSystemStore();
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const { t } = useTranslation();
|
||||
@@ -82,7 +86,10 @@ const MessageInput = ({
|
||||
maxSize: 1024 * 1024 * 5,
|
||||
// 30 day expired.
|
||||
expiredTime: addDays(new Date(), 7),
|
||||
shareId
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
setFileList((state) =>
|
||||
state.map((item) =>
|
||||
@@ -320,7 +327,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
|
||||
rows={1}
|
||||
height={'22px'}
|
||||
lineHeight={'22px'}
|
||||
maxHeight={'150px'}
|
||||
maxHeight={'50vh'}
|
||||
maxLength={-1}
|
||||
overflowY={'auto'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
|
||||
|
||||
import MyModal from '../MyModal';
|
||||
@@ -10,12 +10,12 @@ import RawSourceBox from '../core/dataset/RawSourceBox';
|
||||
const QuoteModal = ({
|
||||
rawSearch = [],
|
||||
onClose,
|
||||
isShare,
|
||||
showDetail,
|
||||
metadata
|
||||
}: {
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
onClose: () => void;
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
metadata?: {
|
||||
collectionId: string;
|
||||
sourceId?: string;
|
||||
@@ -57,7 +57,7 @@ const QuoteModal = ({
|
||||
}
|
||||
>
|
||||
<ModalBody>
|
||||
<QuoteList rawSearch={filterResults} isShare={isShare} />
|
||||
<QuoteList rawSearch={filterResults} showDetail={showDetail} />
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
</>
|
||||
@@ -68,10 +68,10 @@ export default QuoteModal;
|
||||
|
||||
export const QuoteList = React.memo(function QuoteList({
|
||||
rawSearch = [],
|
||||
isShare
|
||||
showDetail
|
||||
}: {
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -88,7 +88,7 @@ export const QuoteList = React.memo(function QuoteList({
|
||||
_hover={{ '& .hover-data': { display: 'flex' } }}
|
||||
bg={i % 2 === 0 ? 'white' : 'myWhite.500'}
|
||||
>
|
||||
<QuoteItem quoteItem={item} canViewSource={!isShare} linkToDataset={!isShare} />
|
||||
<QuoteItem quoteItem={item} canViewSource={showDetail} linkToDataset={showDetail} />
|
||||
</Box>
|
||||
))}
|
||||
</>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { Flex, BoxProps, useDisclosure, Image, useTheme, Box } from '@chakra-ui/react';
|
||||
import { Flex, BoxProps, useDisclosure, useTheme, Box } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
@@ -20,10 +20,10 @@ const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr:
|
||||
|
||||
const ResponseTags = ({
|
||||
responseData = [],
|
||||
isShare
|
||||
showDetail
|
||||
}: {
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { isPc } = useSystemStore();
|
||||
@@ -76,13 +76,13 @@ const ResponseTags = ({
|
||||
sourceName: item.sourceName,
|
||||
sourceId: item.sourceId,
|
||||
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName }),
|
||||
canReadQuote: !isShare || strIsLink(item.sourceId),
|
||||
canReadQuote: showDetail || strIsLink(item.sourceId),
|
||||
collectionId: item.collectionId
|
||||
})),
|
||||
historyPreview: chatData?.historyPreview,
|
||||
runningTime: +responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
|
||||
};
|
||||
}, [isShare, responseData]);
|
||||
}, [showDetail, responseData]);
|
||||
|
||||
const TagStyles: BoxProps = {
|
||||
mr: 2,
|
||||
@@ -134,7 +134,7 @@ const ResponseTags = ({
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{!isShare && (
|
||||
{showDetail && (
|
||||
<Flex alignItems={'center'} mt={3} flexWrap={'wrap'}>
|
||||
{quoteList.length > 0 && (
|
||||
<MyTooltip label="查看引用">
|
||||
@@ -187,7 +187,7 @@ const ResponseTags = ({
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal
|
||||
{...quoteModalData}
|
||||
isShare={isShare}
|
||||
showDetail={showDetail}
|
||||
onClose={() => setQuoteModalData(undefined)}
|
||||
/>
|
||||
)}
|
||||
@@ -195,7 +195,11 @@ const ResponseTags = ({
|
||||
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
|
||||
)}
|
||||
{isOpenWholeModal && (
|
||||
<WholeResponseModal response={responseData} isShare={isShare} onClose={onCloseWholeModal} />
|
||||
<WholeResponseModal
|
||||
response={responseData}
|
||||
showDetail={showDetail}
|
||||
onClose={onCloseWholeModal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@@ -51,11 +51,11 @@ function Row({
|
||||
|
||||
const WholeResponseModal = ({
|
||||
response,
|
||||
isShare,
|
||||
showDetail,
|
||||
onClose
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -78,7 +78,7 @@ const WholeResponseModal = ({
|
||||
}
|
||||
>
|
||||
<Flex h={'100%'} flexDirection={'column'}>
|
||||
<ResponseBox response={response} isShare={isShare} />
|
||||
<ResponseBox response={response} showDetail={showDetail} />
|
||||
</Flex>
|
||||
</MyModal>
|
||||
);
|
||||
@@ -88,10 +88,10 @@ export default WholeResponseModal;
|
||||
|
||||
const ResponseBox = React.memo(function ResponseBox({
|
||||
response,
|
||||
isShare
|
||||
showDetail
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
@@ -142,10 +142,7 @@ const ResponseBox = React.memo(function ResponseBox({
|
||||
value={`${activeModule?.runningTime || 0}s`}
|
||||
/>
|
||||
<Row label={t('core.chat.response.module model')} value={activeModule?.model} />
|
||||
<Row
|
||||
label={t('support.wallet.usage.Chars length')}
|
||||
value={`${activeModule?.charsLength}`}
|
||||
/>
|
||||
<Row label={t('core.chat.response.module tokens')} value={`${activeModule?.tokens}`} />
|
||||
<Row label={t('core.chat.response.module query')} value={activeModule?.query} />
|
||||
<Row
|
||||
label={t('core.chat.response.context total length')}
|
||||
@@ -188,7 +185,7 @@ const ResponseBox = React.memo(function ResponseBox({
|
||||
{activeModule.quoteList && activeModule.quoteList.length > 0 && (
|
||||
<Row
|
||||
label={t('core.chat.response.module quoteList')}
|
||||
rawDom={<QuoteList isShare={isShare} rawSearch={activeModule.quoteList} />}
|
||||
rawDom={<QuoteList showDetail={showDetail} rawSearch={activeModule.quoteList} />}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -280,7 +277,7 @@ const ResponseBox = React.memo(function ResponseBox({
|
||||
{activeModule?.pluginDetail && activeModule?.pluginDetail.length > 0 && (
|
||||
<Row
|
||||
label={t('core.chat.response.Plugin Resonse Detail')}
|
||||
rawDom={<ResponseBox response={activeModule.pluginDetail} isShare={isShare} />}
|
||||
rawDom={<ResponseBox response={activeModule.pluginDetail} showDetail={showDetail} />}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@@ -68,6 +68,7 @@ import type { AppTTSConfigType, VariableItemType } from '@fastgpt/global/core/mo
|
||||
import MessageInput from './MessageInput';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import ChatBoxDivider from '../core/chat/Divider';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
|
||||
|
||||
@@ -106,7 +107,7 @@ const MessageCardStyle: BoxProps = {
|
||||
maxW: ['calc(100% - 25px)', 'calc(100% - 40px)']
|
||||
};
|
||||
|
||||
type Props = {
|
||||
type Props = OutLinkChatAuthProps & {
|
||||
feedbackType?: `${FeedbackTypeEnum}`;
|
||||
showMarkIcon?: boolean; // admin mark dataset
|
||||
showVoiceIcon?: boolean;
|
||||
@@ -120,9 +121,6 @@ type Props = {
|
||||
// not chat test params
|
||||
appId?: string;
|
||||
chatId?: string;
|
||||
shareId?: string;
|
||||
shareTeamId?: string;
|
||||
outLinkUid?: string;
|
||||
|
||||
onUpdateVariable?: (e: Record<string, any>) => void;
|
||||
onStartChat?: (e: StartChatFnProps) => Promise<{
|
||||
@@ -147,8 +145,9 @@ const ChatBox = (
|
||||
appId,
|
||||
chatId,
|
||||
shareId,
|
||||
shareTeamId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
onUpdateVariable,
|
||||
onStartChat,
|
||||
onDelMessage
|
||||
@@ -288,7 +287,10 @@ const ChatBox = (
|
||||
const result = await postQuestionGuide(
|
||||
{
|
||||
messages: adaptChat2GptMessages({ messages: history, reserveId: false }).slice(-6),
|
||||
shareId
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
},
|
||||
abortSignal
|
||||
);
|
||||
@@ -300,7 +302,7 @@ const ChatBox = (
|
||||
}
|
||||
} catch (error) {}
|
||||
},
|
||||
[questionGuide, shareId]
|
||||
[questionGuide, shareId, outLinkUid, teamId, teamToken]
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -398,22 +400,20 @@ const ChatBox = (
|
||||
};
|
||||
})
|
||||
);
|
||||
if (!shareTeamId) {
|
||||
setTimeout(() => {
|
||||
createQuestionGuide({
|
||||
history: newChatList.map((item, i) =>
|
||||
i === newChatList.length - 1
|
||||
? {
|
||||
...item,
|
||||
value: responseText
|
||||
}
|
||||
: item
|
||||
)
|
||||
});
|
||||
generatingScroll();
|
||||
isPc && TextareaDom.current?.focus();
|
||||
}, 100);
|
||||
}
|
||||
setTimeout(() => {
|
||||
createQuestionGuide({
|
||||
history: newChatList.map((item, i) =>
|
||||
i === newChatList.length - 1
|
||||
? {
|
||||
...item,
|
||||
value: responseText
|
||||
}
|
||||
: item
|
||||
)
|
||||
});
|
||||
generatingScroll();
|
||||
isPc && TextareaDom.current?.focus();
|
||||
}, 100);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: t(getErrText(err, 'core.chat.error.Chat error')),
|
||||
@@ -622,6 +622,7 @@ const ChatBox = (
|
||||
{/* control icon */}
|
||||
<Flex w={'100%'} alignItems={'center'} justifyContent={'flex-end'}>
|
||||
<ChatControllerComponent
|
||||
isChatting={isChatting}
|
||||
chat={item}
|
||||
onDelete={
|
||||
onDelMessage
|
||||
@@ -654,12 +655,17 @@ const ChatBox = (
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* control icon */}
|
||||
<ChatControllerComponent
|
||||
isChatting={isChatting}
|
||||
ml={2}
|
||||
chat={item}
|
||||
setChatHistory={setChatHistory}
|
||||
display={index === chatHistory.length - 1 && isChatting ? 'none' : 'flex'}
|
||||
showVoiceIcon={showVoiceIcon}
|
||||
ttsConfig={ttsConfig}
|
||||
shareId={shareId}
|
||||
outLinkUid={outLinkUid}
|
||||
teamId={teamId}
|
||||
teamToken={teamToken}
|
||||
onDelete={
|
||||
onDelMessage
|
||||
? () => {
|
||||
@@ -829,7 +835,10 @@ const ChatBox = (
|
||||
isChatting={index === chatHistory.length - 1 && isChatting}
|
||||
/>
|
||||
|
||||
<ResponseTags responseData={item.responseData} isShare={!!shareId} />
|
||||
<ResponseTags
|
||||
responseData={item.responseData}
|
||||
showDetail={!shareId && !teamId}
|
||||
/>
|
||||
|
||||
{/* custom feedback */}
|
||||
{item.customFeedbacks && item.customFeedbacks.length > 0 && (
|
||||
@@ -909,6 +918,10 @@ const ChatBox = (
|
||||
TextareaDom={TextareaDom}
|
||||
resetInputVal={resetInputVal}
|
||||
showFileSelector={showFileSelector}
|
||||
shareId={shareId}
|
||||
outLinkUid={outLinkUid}
|
||||
teamId={teamId}
|
||||
teamToken={teamToken}
|
||||
/>
|
||||
)}
|
||||
{/* user feedback modal */}
|
||||
@@ -1236,6 +1249,7 @@ function Empty() {
|
||||
}
|
||||
|
||||
const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
isChatting,
|
||||
chat,
|
||||
setChatHistory,
|
||||
display,
|
||||
@@ -1249,8 +1263,13 @@ const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
onAddUserDislike,
|
||||
onAddUserLike,
|
||||
ml,
|
||||
mr
|
||||
}: {
|
||||
mr,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
}: OutLinkChatAuthProps & {
|
||||
isChatting: boolean;
|
||||
chat: ChatSiteItemType;
|
||||
setChatHistory?: React.Dispatch<React.SetStateAction<ChatSiteItemType[]>>;
|
||||
showVoiceIcon?: boolean;
|
||||
@@ -1267,7 +1286,11 @@ const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
const { t } = useTranslation();
|
||||
const { copyData } = useCopyData();
|
||||
const { audioLoading, audioPlaying, hasAudio, playAudio, cancelAudio } = useAudioPlay({
|
||||
ttsConfig
|
||||
ttsConfig,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
const controlIconStyle = {
|
||||
w: '14px',
|
||||
@@ -1296,7 +1319,7 @@ const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
onClick={() => copyData(chat.value)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
{!!onDelete && (
|
||||
{!!onDelete && !isChatting && (
|
||||
<>
|
||||
{onRetry && (
|
||||
<MyTooltip label={t('core.chat.retry')}>
|
||||
|
@@ -1,103 +0,0 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItemOption,
|
||||
MenuOptionGroup,
|
||||
Flex,
|
||||
TagLabel,
|
||||
TagCloseButton,
|
||||
HStack,
|
||||
Tag,
|
||||
Input
|
||||
} from '@chakra-ui/react';
|
||||
import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
|
||||
const TagEdit = ({
|
||||
defaultValues,
|
||||
teamsTags,
|
||||
setSelectedTags
|
||||
}: {
|
||||
defaultValues: [];
|
||||
teamsTags: Array<TeamTagsSchema>;
|
||||
setSelectedTags: (item: Array<string>) => void;
|
||||
}) => {
|
||||
const [teamTagsOptions, setTeamTagsOptions] = useState(teamsTags);
|
||||
const setSelectTeamsTags = (item: any) => {
|
||||
setSelectedTags(item);
|
||||
};
|
||||
useMemo(() => {
|
||||
setTeamTagsOptions(teamsTags);
|
||||
}, [teamsTags]);
|
||||
return (
|
||||
<>
|
||||
<Menu closeOnSelect={false}>
|
||||
<MenuButton className="menu-btn" maxHeight={'250'} minWidth={'80%'}>
|
||||
<HStack
|
||||
style={{
|
||||
border: 'solid 2px #f3f3f3',
|
||||
borderRadius: '5px',
|
||||
padding: '3px',
|
||||
|
||||
flexWrap: 'wrap',
|
||||
minHeight: '40px'
|
||||
}}
|
||||
>
|
||||
{teamsTags.map((item: TeamTagsSchema, index: number) => {
|
||||
const key: string = item?.key;
|
||||
if (defaultValues.indexOf(key as never) > -1) {
|
||||
return (
|
||||
<Tag
|
||||
key={index}
|
||||
size={'md'}
|
||||
colorScheme="red"
|
||||
// maxWidth={"100px"}
|
||||
borderRadius="full"
|
||||
>
|
||||
<TagLabel> {item.label}</TagLabel>
|
||||
<TagCloseButton />
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</HStack>
|
||||
</MenuButton>
|
||||
<MenuList style={{ height: '300px', overflow: 'scroll' }}>
|
||||
<Input
|
||||
style={{ border: 'none', borderBottom: 'solid 1px #f6f6f6' }}
|
||||
placeholder="pleace "
|
||||
onChange={(e: any) => {
|
||||
// 对用户输入的搜索文本进行小写转换,以实现不区分大小写的搜索
|
||||
const searchLower: string = e?.nativeEvent?.data || '';
|
||||
// 使用filter方法来过滤列表,只返回包含搜索文本的项
|
||||
const resultList = teamsTags.filter((item) => {
|
||||
const searchValue = item.label || '';
|
||||
// 对列表中的每一项也进行小写转换
|
||||
return searchValue.includes(searchLower);
|
||||
});
|
||||
!searchLower ? setTeamTagsOptions(teamsTags) : setTeamTagsOptions(resultList);
|
||||
}}
|
||||
/>
|
||||
<MenuOptionGroup
|
||||
defaultValue={defaultValues}
|
||||
type="checkbox"
|
||||
style={{ height: '300px', overflow: 'scroll' }}
|
||||
onChange={(e) => {
|
||||
setSelectTeamsTags(e);
|
||||
}}
|
||||
>
|
||||
{teamTagsOptions.map((item, index) => {
|
||||
return (
|
||||
<MenuItemOption key={index} value={item.key}>
|
||||
{item?.label}
|
||||
</MenuItemOption>
|
||||
);
|
||||
})}
|
||||
</MenuOptionGroup>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagEdit;
|
@@ -8,7 +8,7 @@ import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & FlexProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const item = DatasetTypeMap[type];
|
||||
const item = DatasetTypeMap[type] || DatasetTypeMap['dataset'];
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
@@ -301,10 +301,22 @@ function RenderHttpProps({
|
||||
headers &&
|
||||
jsonBody &&
|
||||
{
|
||||
[TabEnum.params]: <RenderForm moduleId={moduleId} input={params} variables={variables} />,
|
||||
[TabEnum.params]: (
|
||||
<RenderForm
|
||||
moduleId={moduleId}
|
||||
input={params}
|
||||
variables={variables}
|
||||
tabType={TabEnum.params}
|
||||
/>
|
||||
),
|
||||
[TabEnum.body]: <RenderJson moduleId={moduleId} variables={variables} input={jsonBody} />,
|
||||
[TabEnum.headers]: (
|
||||
<RenderForm moduleId={moduleId} input={headers} variables={variables} />
|
||||
<RenderForm
|
||||
moduleId={moduleId}
|
||||
input={headers}
|
||||
variables={variables}
|
||||
tabType={TabEnum.headers}
|
||||
/>
|
||||
)
|
||||
}[selectedTab]}
|
||||
</Box>
|
||||
@@ -313,11 +325,13 @@ function RenderHttpProps({
|
||||
const RenderForm = ({
|
||||
moduleId,
|
||||
input,
|
||||
variables
|
||||
variables,
|
||||
tabType
|
||||
}: {
|
||||
moduleId: string;
|
||||
input: FlowNodeInputItemType;
|
||||
variables: EditorVariablePickerType[];
|
||||
tabType?: TabEnum;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
@@ -327,11 +341,52 @@ const RenderForm = ({
|
||||
const [shouldUpdateNode, setShouldUpdateNode] = useState(false);
|
||||
|
||||
const leftVariables = useMemo(() => {
|
||||
return variables.filter((variable) => {
|
||||
const HttpHeaders = [
|
||||
{ key: 'A-IM', label: 'A-IM' },
|
||||
{ key: 'Accept', label: 'Accept' },
|
||||
{ key: 'Accept-Charset', label: 'Accept-Charset' },
|
||||
{ key: 'Accept-Encoding', label: 'Accept-Encoding' },
|
||||
{ key: 'Accept-Language', label: 'Accept-Language' },
|
||||
{ key: 'Accept-Datetime', label: 'Accept-Datetime' },
|
||||
{ key: 'Access-Control-Request-Method', label: 'Access-Control-Request-Method' },
|
||||
{ key: 'Access-Control-Request-Headers', label: 'Access-Control-Request-Headers' },
|
||||
{ key: 'Authorization', label: 'Authorization' },
|
||||
{ key: 'Cache-Control', label: 'Cache-Control' },
|
||||
{ key: 'Connection', label: 'Connection' },
|
||||
{ key: 'Content-Length', label: 'Content-Length' },
|
||||
{ key: 'Content-Type', label: 'Content-Type' },
|
||||
{ key: 'Cookie', label: 'Cookie' },
|
||||
{ key: 'Date', label: 'Date' },
|
||||
{ key: 'Expect', label: 'Expect' },
|
||||
{ key: 'Forwarded', label: 'Forwarded' },
|
||||
{ key: 'From', label: 'From' },
|
||||
{ key: 'Host', label: 'Host' },
|
||||
{ key: 'If-Match', label: 'If-Match' },
|
||||
{ key: 'If-Modified-Since', label: 'If-Modified-Since' },
|
||||
{ key: 'If-None-Match', label: 'If-None-Match' },
|
||||
{ key: 'If-Range', label: 'If-Range' },
|
||||
{ key: 'If-Unmodified-Since', label: 'If-Unmodified-Since' },
|
||||
{ key: 'Max-Forwards', label: 'Max-Forwards' },
|
||||
{ key: 'Origin', label: 'Origin' },
|
||||
{ key: 'Pragma', label: 'Pragma' },
|
||||
{ key: 'Proxy-Authorization', label: 'Proxy-Authorization' },
|
||||
{ key: 'Range', label: 'Range' },
|
||||
{ key: 'Referer', label: 'Referer' },
|
||||
{ key: 'TE', label: 'TE' },
|
||||
{ key: 'User-Agent', label: 'User-Agent' },
|
||||
{ key: 'Upgrade', label: 'Upgrade' },
|
||||
{ key: 'Via', label: 'Via' },
|
||||
{ key: 'Warning', label: 'Warning' },
|
||||
{ key: 'Dnt', label: 'Dnt' },
|
||||
{ key: 'X-Requested-With', label: 'X-Requested-With' },
|
||||
{ key: 'X-CSRF-Token', label: 'X-CSRF-Token' }
|
||||
];
|
||||
|
||||
return (tabType === TabEnum.headers ? HttpHeaders : variables).filter((variable) => {
|
||||
const existVariables = list.map((item) => item.key);
|
||||
return !existVariables.includes(variable.key);
|
||||
});
|
||||
}, [list, variables]);
|
||||
}, [list, tabType, variables]);
|
||||
|
||||
useEffect(() => {
|
||||
setList(input.value || []);
|
||||
@@ -378,16 +433,23 @@ const RenderForm = ({
|
||||
};
|
||||
|
||||
const handleAddNewProps = (key: string, value: string = '') => {
|
||||
const checkExist = list.find((item) => item.key === key);
|
||||
if (checkExist) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('core.module.http.Key already exists')
|
||||
});
|
||||
}
|
||||
if (!key) return;
|
||||
setList((prevList) => {
|
||||
if (!key) {
|
||||
return prevList;
|
||||
}
|
||||
|
||||
const checkExist = prevList.find((item) => item.key === key);
|
||||
if (checkExist) {
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('core.module.http.Key already exists')
|
||||
});
|
||||
return prevList;
|
||||
}
|
||||
return [...prevList, { key, type: 'string', value }];
|
||||
});
|
||||
|
||||
setList((prevList) => [...prevList, { key, type: 'string', value }]);
|
||||
setShouldUpdateNode(true);
|
||||
};
|
||||
|
||||
@@ -406,7 +468,7 @@ const RenderForm = ({
|
||||
<Td p={0} w={'150px'}>
|
||||
<HttpInput
|
||||
hasVariablePlugin={false}
|
||||
hasDropDownPlugin={true}
|
||||
hasDropDownPlugin={tabType === TabEnum.headers}
|
||||
setDropdownValue={(value) => {
|
||||
handleKeyChange(index, value);
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
@@ -450,16 +512,19 @@ const RenderForm = ({
|
||||
<Tr>
|
||||
<Td p={0} w={'150px'}>
|
||||
<HttpInput
|
||||
hasDropDownPlugin={true}
|
||||
hasVariablePlugin={false}
|
||||
hasDropDownPlugin={tabType === TabEnum.headers}
|
||||
setDropdownValue={(val) => {
|
||||
handleAddNewProps(val);
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
}}
|
||||
placeholder={t('core.module.http.Add props')}
|
||||
value={''}
|
||||
h={40}
|
||||
variables={leftVariables}
|
||||
updateTrigger={updateTrigger}
|
||||
onBlur={(val) => {
|
||||
handleAddNewProps(val);
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
@@ -490,7 +555,7 @@ const RenderJson = ({
|
||||
<Box mt={1}>
|
||||
<JSONEditor
|
||||
bg={'myGray.50'}
|
||||
height={200}
|
||||
defaultHeight={200}
|
||||
resize
|
||||
value={input.value}
|
||||
placeholder={t('core.module.template.http body placeholder')}
|
||||
|
@@ -9,9 +9,7 @@ import {
|
||||
putSwitchTeam,
|
||||
putUpdateMember,
|
||||
delRemoveMember,
|
||||
delLeaveTeam,
|
||||
getTeamsTags,
|
||||
insertTeamsTags
|
||||
delLeaveTeam
|
||||
} from '@/web/support/user/team/api';
|
||||
import {
|
||||
Box,
|
||||
@@ -49,7 +47,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
const EditModal = dynamic(() => import('./EditModal'));
|
||||
const InviteModal = dynamic(() => import('./InviteModal'));
|
||||
const TeamTagsAsync = dynamic(() => import('../TeamTagsAsync'));
|
||||
const TeamTagModal = dynamic(() => import('../TeamTagModal'));
|
||||
|
||||
const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -57,7 +55,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { toast } = useToast();
|
||||
const { teamPlanStatus } = useUserStore();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const [teamsTags, setTeamTags] = useState<any>();
|
||||
|
||||
const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm();
|
||||
const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({
|
||||
@@ -87,8 +84,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
mutationFn: async (teamId: string) => {
|
||||
const token = await putSwitchTeam(teamId);
|
||||
token && setToken(token);
|
||||
// get team tags
|
||||
await getTeamsTags(teamId);
|
||||
return initUserInfo();
|
||||
},
|
||||
errorToast: t('user.team.Switch Team Failed')
|
||||
@@ -99,11 +94,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
['getMembers', userInfo?.team?.teamId],
|
||||
() => {
|
||||
if (!userInfo?.team?.teamId) return [];
|
||||
// get team tags
|
||||
getTeamsTags(userInfo.team.teamId).then((res: any) => {
|
||||
setTeamTags(res);
|
||||
});
|
||||
|
||||
return getTeamMembers(userInfo.team.teamId);
|
||||
}
|
||||
);
|
||||
@@ -217,17 +207,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
: {})}
|
||||
>
|
||||
{team.teamName}
|
||||
{/* {userInfo?.team?.teamId === team.teamId && (
|
||||
<HStack spacing={1}>
|
||||
{teamsTags.slice(0, 3).map((item: any, index) => {
|
||||
return (
|
||||
<Tag key={index} size={'sm'} variant="outline" colorScheme="blue">
|
||||
{item.label}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
)} */}
|
||||
</Box>
|
||||
{userInfo?.team?.teamId === team.teamId ? (
|
||||
<MyIcon name={'common/tickFill'} w={'16px'} color={'primary.500'} />
|
||||
@@ -290,31 +269,32 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
<Box ml={2} bg={'myGray.100'} borderRadius={'20px'} px={3} fontSize={'xs'}>
|
||||
{members.length}
|
||||
</Box>
|
||||
{userInfo.team.role === TeamMemberRoleEnum.owner &&
|
||||
teamPlanStatus?.standardConstants &&
|
||||
teamPlanStatus.standardConstants.maxTeamMember > members.length && (
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size="sm"
|
||||
borderRadius={'md'}
|
||||
ml={3}
|
||||
leftIcon={
|
||||
<MyIcon name={'common/inviteLight'} w={'14px'} color={'primary.500'} />
|
||||
{userInfo.team.role === TeamMemberRoleEnum.owner && (
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size="sm"
|
||||
borderRadius={'md'}
|
||||
ml={3}
|
||||
leftIcon={<MyIcon name={'common/inviteLight'} w={'14px'} color={'primary.500'} />}
|
||||
onClick={() => {
|
||||
if (
|
||||
teamPlanStatus?.standardConstants?.maxTeamMember &&
|
||||
teamPlanStatus.standardConstants.maxTeamMember <= members.length
|
||||
) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('user.team.Over Max Member Tip', {
|
||||
max: teamPlanStatus.standardConstants.maxTeamMember
|
||||
})
|
||||
});
|
||||
} else {
|
||||
onOpenInvite();
|
||||
}
|
||||
onClick={() => {
|
||||
if (userInfo.team.maxSize <= members.length) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('user.team.Over Max Member Tip', { max: userInfo.team.maxSize })
|
||||
});
|
||||
} else {
|
||||
onOpenInvite();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('user.team.Invite Member')}
|
||||
</Button>
|
||||
)}
|
||||
}}
|
||||
>
|
||||
{t('user.team.Invite Member')}
|
||||
</Button>
|
||||
)}
|
||||
{userInfo.team.role === TeamMemberRoleEnum.owner && feConfigs?.show_team_chat && (
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
@@ -323,14 +303,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
ml={3}
|
||||
leftIcon={<DragHandleIcon w={'14px'} color={'primary.500'} />}
|
||||
onClick={() => {
|
||||
if (userInfo.team.maxSize <= members.length) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('user.team.Team Tags Async', { max: userInfo.team.maxSize })
|
||||
});
|
||||
} else {
|
||||
onOpenTeamTagsAsync();
|
||||
}
|
||||
onOpenTeamTagsAsync();
|
||||
}}
|
||||
>
|
||||
{t('user.team.Team Tags Async')}
|
||||
@@ -492,13 +465,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
onSuccess={refetchMembers}
|
||||
/>
|
||||
)}
|
||||
{isOpenTeamTagsAsync && (
|
||||
<TeamTagsAsync
|
||||
teamInfo={teamsTags?.tagsUrl}
|
||||
teamsTags={teamsTags?.list || []}
|
||||
onClose={onCloseTeamTagsAsync}
|
||||
/>
|
||||
)}
|
||||
{isOpenTeamTagsAsync && <TeamTagModal onClose={onCloseTeamTagsAsync} />}
|
||||
<ConfirmRemoveMemberModal />
|
||||
<ConfirmLeaveTeamModal />
|
||||
</>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import {
|
||||
Box,
|
||||
@@ -11,61 +11,74 @@ import {
|
||||
HStack,
|
||||
Avatar
|
||||
} from '@chakra-ui/react';
|
||||
import { AttachmentIcon, CopyIcon, DragHandleIcon } from '@chakra-ui/icons';
|
||||
import { putUpdateTeamTags, updateTags } from '@/web/support/user/team/api';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { putUpdateTeam } from '@/web/support/user/team/api';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import type { TeamTagItemType } from '@fastgpt/global/support/user/team/type';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { RepeatIcon } from '@chakra-ui/icons';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getTeamsTags, loadTeamTagsByDomain } from '@/web/support/user/team/api';
|
||||
|
||||
const TeamTagsAsync = ({
|
||||
teamsTags,
|
||||
teamInfo,
|
||||
onClose
|
||||
}: {
|
||||
teamsTags: Array<TeamTagsSchema>;
|
||||
teamInfo: any;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
type FormType = {
|
||||
teamDomain: string;
|
||||
tags: TeamTagItemType[];
|
||||
};
|
||||
|
||||
const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const [_teamsTags, setTeamTags] = useState<Array<TeamTagsSchema>>(teamsTags);
|
||||
|
||||
const { register, setValue, getValues, handleSubmit } = useForm<any>({
|
||||
defaultValues: { ...teamInfo }
|
||||
});
|
||||
const { userInfo, initUserInfo } = useUserStore();
|
||||
const { copyData } = useCopyData();
|
||||
|
||||
const teamInfo = userInfo?.team;
|
||||
|
||||
if (!teamInfo) {
|
||||
onClose();
|
||||
return null;
|
||||
}
|
||||
|
||||
const { register, control, handleSubmit } = useForm<FormType>({
|
||||
defaultValues: {
|
||||
teamDomain: teamInfo.teamDomain,
|
||||
tags: []
|
||||
}
|
||||
});
|
||||
const { fields: teamTags, replace: replaceTeamTags } = useFieldArray({
|
||||
control,
|
||||
name: 'tags'
|
||||
});
|
||||
|
||||
const baseUrl = global.feConfigs?.customSharePageDomain || location?.origin;
|
||||
const linkUrl = `${baseUrl}/chat/team?shareTeamId=${teamInfo?._id}${
|
||||
getValues('showHistory') ? '' : '&showHistory=0'
|
||||
}`;
|
||||
const linkUrl = `${baseUrl}/chat/team?teamId=${teamInfo.teamId}&teamToken=`;
|
||||
|
||||
// tags Async
|
||||
const { mutate: onclickAsync, isLoading: creating } = useRequest({
|
||||
mutationFn: async (data: any) => {
|
||||
return putUpdateTeamTags({ tagsUrl: data.tagsUrl, teamId: teamInfo?._id });
|
||||
const { mutate: onclickUpdate, isLoading: isUpdating } = useRequest({
|
||||
mutationFn: async (data: FormType) => {
|
||||
return putUpdateTeam({ teamDomain: data.teamDomain, teamId: teamInfo?.teamId });
|
||||
},
|
||||
onSuccess(id: string) {
|
||||
onSuccess() {
|
||||
initUserInfo();
|
||||
onClose();
|
||||
},
|
||||
successToast: t('user.team.Team Tags Async Success'),
|
||||
errorToast: t('common.Create Failed')
|
||||
});
|
||||
const asyncTags = async () => {
|
||||
console.log('getValues', getValues());
|
||||
const res: Array<TeamTagsSchema> = await updateTags(teamInfo?._id, getValues().tagsUrl);
|
||||
setTeamTags(res);
|
||||
toast({ status: 'success', title: '团队标签同步成功' });
|
||||
};
|
||||
useEffect(() => {
|
||||
console.log('teamInfo', teamInfo);
|
||||
}, []);
|
||||
const { mutate: onclickTagAsync, isLoading: isSyncing } = useRequest({
|
||||
mutationFn: (data: FormType) => loadTeamTagsByDomain(data.teamDomain),
|
||||
onSuccess(res) {
|
||||
replaceTeamTags(res);
|
||||
},
|
||||
successToast: t('support.user.team.Team Tags Async Success')
|
||||
});
|
||||
|
||||
useQuery(['getTeamsTags'], getTeamsTags, {
|
||||
onSuccess: (data) => {
|
||||
replaceTeamTags(data);
|
||||
}
|
||||
});
|
||||
|
||||
// 获取
|
||||
return (
|
||||
<>
|
||||
<MyModal
|
||||
@@ -80,7 +93,7 @@ const TeamTagsAsync = ({
|
||||
overflow={'hidden'}
|
||||
title={
|
||||
<Box>
|
||||
<Box>{teamInfo?.name}</Box>
|
||||
<Box>{teamInfo?.teamName}</Box>
|
||||
<Box color={'myGray.500'} fontSize={'xs'} fontWeight={'normal'}>
|
||||
{'填写标签同步链接,点击同步按钮即可同步'}
|
||||
</Box>
|
||||
@@ -98,8 +111,8 @@ const TeamTagsAsync = ({
|
||||
autoFocus
|
||||
bg={'myWhite.600'}
|
||||
placeholder="请输入同步标签"
|
||||
{...register('tagsUrl', {
|
||||
required: t('core.app.error.App name can not be empty')
|
||||
{...register('teamDomain', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
@@ -146,7 +159,7 @@ const TeamTagsAsync = ({
|
||||
}}
|
||||
spacing={1}
|
||||
>
|
||||
{_teamsTags.map((item, index) => {
|
||||
{teamTags.map((item, index) => {
|
||||
return (
|
||||
<Tag key={index} mt={2} size={'md'} colorScheme="red" borderRadius="full">
|
||||
<Avatar
|
||||
@@ -161,7 +174,13 @@ const TeamTagsAsync = ({
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
<Button ml={4} size="md" leftIcon={<RepeatIcon />} onClick={asyncTags}>
|
||||
<Button
|
||||
isLoading={isSyncing}
|
||||
ml={4}
|
||||
size="md"
|
||||
leftIcon={<RepeatIcon />}
|
||||
onClick={handleSubmit((data) => onclickTagAsync(data))}
|
||||
>
|
||||
立即同步
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -170,7 +189,7 @@ const TeamTagsAsync = ({
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button isLoading={creating} onClick={handleSubmit((data) => onclickAsync(data))}>
|
||||
<Button isLoading={isUpdating} onClick={handleSubmit((data) => onclickUpdate(data))}>
|
||||
{t('user.team.Tags Async')}
|
||||
</Button>
|
||||
</ModalFooter>
|
Reference in New Issue
Block a user