fix: ui
This commit is contained in:
parent
3b0f0a8108
commit
e74ab643fe
@ -50,6 +50,7 @@ export type SelectProps<T = any> = Omit<ButtonProps, 'onChange'> & {
|
||||
showBorder?: boolean;
|
||||
}[];
|
||||
isLoading?: boolean;
|
||||
showAvatar?: boolean;
|
||||
onChange?: (val: T) => any | Promise<any>;
|
||||
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
|
||||
customOnOpen?: () => void;
|
||||
@ -79,6 +80,7 @@ const MySelect = <T = any,>(
|
||||
list = [],
|
||||
onChange,
|
||||
isLoading = false,
|
||||
showAvatar = true,
|
||||
ScrollData,
|
||||
customOnOpen,
|
||||
customOnClose,
|
||||
@ -255,7 +257,7 @@ const MySelect = <T = any,>(
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{selectItem?.icon && (
|
||||
{selectItem?.icon && showAvatar && (
|
||||
<Avatar
|
||||
mr={2}
|
||||
src={selectItem.icon as any}
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
"image_upload": "Image upload",
|
||||
"no_gate_available": "No portal available",
|
||||
"no_gate_to_delete": "There is no gate to delete",
|
||||
"quick_app": "Quick Application",
|
||||
"slogan": "slogan",
|
||||
"status": "state",
|
||||
"suggestion_ratio_1_1": "Suggested ratio 1:1",
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"image_upload": "图片上传",
|
||||
"no_gate_available": "没有可用门户",
|
||||
"no_gate_to_delete": "没有可以删除的门户了",
|
||||
"quick_app": "快捷应用",
|
||||
"slogan": "标语",
|
||||
"status": "状态",
|
||||
"suggestion_ratio_1_1": "建议比例 1:1",
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
"image_upload": "圖片上傳",
|
||||
"no_gate_available": "沒有可用門戶",
|
||||
"no_gate_to_delete": "沒有可以刪除的門戶了",
|
||||
"quick_app": "快捷應用",
|
||||
"slogan": "標語",
|
||||
"status": "狀態",
|
||||
"suggestion_ratio_1_1": "建議比例 1:1",
|
||||
|
||||
@ -110,7 +110,14 @@ const OneRowSelector = ({ list, onChange, disableTip, ...props }: Props) => {
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props }: Props) => {
|
||||
const MultipleRowSelector = ({
|
||||
list,
|
||||
onChange,
|
||||
disableTip,
|
||||
placeholder,
|
||||
showAvatar = true,
|
||||
...props
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
|
||||
useSystemStore();
|
||||
@ -193,17 +200,20 @@ const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props
|
||||
|
||||
return (
|
||||
<HStack spacing={1}>
|
||||
<Avatar
|
||||
borderRadius={'0'}
|
||||
mr={2}
|
||||
src={modelData?.avatar}
|
||||
fallbackSrc={HUGGING_FACE_ICON}
|
||||
w={avatarSize}
|
||||
/>
|
||||
{showAvatar && (
|
||||
<Avatar
|
||||
borderRadius={'0'}
|
||||
mr={2}
|
||||
src={modelData?.avatar}
|
||||
fallbackSrc={HUGGING_FACE_ICON}
|
||||
w={avatarSize}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Box>{modelData?.name}</Box>
|
||||
</HStack>
|
||||
);
|
||||
}, [modelList, props.value, t, avatarSize]);
|
||||
}, [modelList, props.value, t, showAvatar, avatarSize]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useRef, useCallback, useMemo, useState, useEffect, useContext } from 'react';
|
||||
import { Box, Flex, Textarea, IconButton, useBreakpointValue, Button } from '@chakra-ui/react';
|
||||
import { Box, Flex, Textarea, IconButton, useBreakpointValue } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { getWebDefaultLLMModel } from '@/web/common/system/utils';
|
||||
@ -23,7 +23,8 @@ import dynamic from 'next/dynamic';
|
||||
import { AppContext } from '@/pageComponents/app/detail/context';
|
||||
import { AppFormContext } from '@/pages/chat/gate/index';
|
||||
import Icon from '@fastgpt/web/components/common/Icon';
|
||||
import GateSelect from '@fastgpt/web/components/common/MySelect/GateSelect';
|
||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
|
||||
const GateToolSelect = dynamic(
|
||||
() => import('@/pageComponents/app/detail/Gate/components/GateToolSelect'),
|
||||
@ -46,8 +47,8 @@ type Props = {
|
||||
resetInputVal: (val: ChatBoxInputType) => void;
|
||||
chatForm: UseFormReturn<ChatBoxInputFormType>;
|
||||
placeholder?: string;
|
||||
selectedToolIds?: string[];
|
||||
onSelectedToolIdsChange?: (toolIds: string[]) => void;
|
||||
selectedTools?: FlowNodeTemplateType[];
|
||||
onSelectTools?: (toolIds: FlowNodeTemplateType[]) => void;
|
||||
};
|
||||
|
||||
const GateChatInput = ({
|
||||
@ -57,8 +58,8 @@ const GateChatInput = ({
|
||||
resetInputVal,
|
||||
chatForm,
|
||||
placeholder,
|
||||
selectedToolIds: externalSelectedToolIds,
|
||||
onSelectedToolIdsChange
|
||||
selectedTools: externalSelectedToolIds,
|
||||
onSelectTools
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystem();
|
||||
@ -78,10 +79,9 @@ const GateChatInput = ({
|
||||
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
|
||||
const fileSelectConfig = useContextSelector(ChatBoxContext, (v) => v.fileSelectConfig);
|
||||
|
||||
// 如果有外部传入的工具选择,使用外部的;否则使用内部状态
|
||||
const [internalSelectedToolIds, setInternalSelectedToolIds] = useState<string[]>([]);
|
||||
const selectedToolIds = externalSelectedToolIds ?? internalSelectedToolIds;
|
||||
const setSelectedToolIds = onSelectedToolIdsChange ?? setInternalSelectedToolIds;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const selectedTools = externalSelectedToolIds ?? [];
|
||||
const setSelectedToolIds = onSelectTools!;
|
||||
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const { llmModelList } = useSystemStore();
|
||||
@ -93,10 +93,7 @@ const GateChatInput = ({
|
||||
const [selectedModel, setSelectedModel] = useState(defaultModel);
|
||||
|
||||
const showModelSelector = useMemo(() => {
|
||||
return (
|
||||
router.pathname.startsWith('/chat/gate') &&
|
||||
!router.pathname.includes('/chat/gate/application')
|
||||
);
|
||||
return router.pathname === '/chat/gate';
|
||||
}, [router.pathname]);
|
||||
|
||||
// 是否显示工具选择器
|
||||
@ -188,7 +185,7 @@ const GateChatInput = ({
|
||||
text: textareaValue.trim(),
|
||||
files: fileList,
|
||||
gateModel: showModelSelector ? selectedModel : undefined,
|
||||
selectedTool: selectedToolIds.length > 0 ? selectedToolIds.join(',') : null // 将工具ID数组转换为逗号分隔的字符串
|
||||
selectedTool: selectedTools.length > 0 ? selectedTools.join(',') : null // 将工具ID数组转换为逗号分隔的字符串
|
||||
});
|
||||
replaceFiles([]);
|
||||
},
|
||||
@ -200,7 +197,7 @@ const GateChatInput = ({
|
||||
replaceFiles,
|
||||
showModelSelector,
|
||||
selectedModel,
|
||||
selectedToolIds
|
||||
selectedTools
|
||||
]
|
||||
);
|
||||
|
||||
@ -214,8 +211,9 @@ const GateChatInput = ({
|
||||
boxShadow="0px 5px 16px -4px rgba(19, 51, 107, 0.08)"
|
||||
borderRadius="20px"
|
||||
position="relative"
|
||||
p={4}
|
||||
pb="56px"
|
||||
px={4}
|
||||
pb={'62px'}
|
||||
pt={fileList.length > 0 ? 0 : 4}
|
||||
overflow="hidden"
|
||||
transition="all 0.2s ease"
|
||||
_hover={{
|
||||
@ -335,24 +333,18 @@ const GateChatInput = ({
|
||||
>
|
||||
<Flex align="center" gap={2} overflow="hidden" maxW="65%" flexShrink={1} flexWrap="nowrap">
|
||||
{showModelSelector && (
|
||||
<GateSelect
|
||||
value={selectedModel}
|
||||
<AIModelSelector
|
||||
list={modelList}
|
||||
value={selectedModel}
|
||||
showAvatar={false}
|
||||
onChange={setSelectedModel}
|
||||
minW="128px"
|
||||
maxW="180px"
|
||||
w="auto"
|
||||
bg="#F9F9F9"
|
||||
border="0.5px solid #E0E0E0"
|
||||
borderRadius="10px"
|
||||
color="#485264"
|
||||
h="36px"
|
||||
fontSize="14px"
|
||||
bg={'myGray.50'}
|
||||
borderRadius={'lg'}
|
||||
/>
|
||||
)}
|
||||
{showTools && (
|
||||
<GateToolSelect
|
||||
selectedToolIds={selectedToolIds}
|
||||
selectedTools={selectedTools}
|
||||
onToolsChange={setSelectedToolIds}
|
||||
buttonSize={buttonSize}
|
||||
/>
|
||||
|
||||
@ -14,7 +14,7 @@ import type {
|
||||
} from '@fastgpt/global/core/chat/type.d';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { Box, Checkbox, Flex, Text } from '@chakra-ui/react';
|
||||
import { Box, Checkbox, Flex, HStack, Text } from '@chakra-ui/react';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
|
||||
import { useForm } from 'react-hook-form';
|
||||
@ -74,6 +74,9 @@ import { useRouter } from 'next/router';
|
||||
import { getTeamGateConfig, getTeamGateConfigCopyRight } from '@/web/support/user/team/gate/api';
|
||||
import type { getGateConfigCopyRightResponse } from '@fastgpt/global/support/user/team/gate/api';
|
||||
import type { GateSchemaType } from '@fastgpt/global/support/user/team/gate/type';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import type { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
|
||||
const FeedbackModal = dynamic(() => import('./components/FeedbackModal'));
|
||||
const ReadFeedbackModal = dynamic(() => import('./components/ReadFeedbackModal'));
|
||||
@ -96,8 +99,9 @@ type Props = OutLinkChatAuthProps &
|
||||
showVoiceIcon?: boolean;
|
||||
showEmptyIntro?: boolean;
|
||||
active?: boolean; // can use
|
||||
selectedToolIds?: string[];
|
||||
onSelectedToolIdsChange?: (toolIds: string[]) => void;
|
||||
selectedTools?: FlowNodeTemplateType[];
|
||||
onSelectTools?: (toolIds: FlowNodeTemplateType[]) => void;
|
||||
recommendApps?: AppListItemType[];
|
||||
|
||||
onStartChat?: (e: StartChatFnProps) => Promise<
|
||||
StreamResponseType & {
|
||||
@ -115,8 +119,9 @@ const ChatBox = ({
|
||||
active = true,
|
||||
onStartChat,
|
||||
chatType,
|
||||
selectedToolIds,
|
||||
onSelectedToolIdsChange
|
||||
selectedTools,
|
||||
onSelectTools,
|
||||
recommendApps = []
|
||||
}: Props) => {
|
||||
const ScrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const { t } = useTranslation();
|
||||
@ -421,7 +426,7 @@ const ChatBox = ({
|
||||
|
||||
const router = useRouter();
|
||||
const inGateRoute = useMemo(() => {
|
||||
return router.pathname.startsWith('/chat/gate');
|
||||
return router.pathname === '/chat/gate';
|
||||
}, [router.pathname]);
|
||||
/**
|
||||
* user confirm send prompt
|
||||
@ -1124,11 +1129,7 @@ const ChatBox = ({
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
const showWelcome = useMemo(() => {
|
||||
return (
|
||||
router.pathname.startsWith('/chat/gate') &&
|
||||
!router.pathname.includes('/chat/gate/application') &&
|
||||
chatRecords.length === 0
|
||||
);
|
||||
return router.pathname === '/chat/gate' && chatRecords.length === 0;
|
||||
}, [router.pathname, chatRecords.length]);
|
||||
|
||||
return (
|
||||
@ -1169,6 +1170,29 @@ const ChatBox = ({
|
||||
{/* message input */}
|
||||
{onStartChat && chatStarted && active && !isInteractive && (
|
||||
<Box w={{ base: 'calc(100% - 48px)', md: '700px' }} maxH="132px" h="100%" px={0}>
|
||||
<HStack mb={3}>
|
||||
{recommendApps.map((item) => (
|
||||
<HStack
|
||||
gap={1}
|
||||
key={item._id}
|
||||
border={'base'}
|
||||
px={2}
|
||||
py={1}
|
||||
borderRadius={'sm'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
bg: 'primary.50',
|
||||
borderColor: 'primary.400'
|
||||
}}
|
||||
onClick={() => {
|
||||
router.push(`/chat/gate/application?appId=${item._id}`);
|
||||
}}
|
||||
>
|
||||
<Avatar src={item.avatar} w="1rem" h="1rem" borderRadius={'sm'} />
|
||||
<Box fontSize={'sm'}>{item.name}</Box>
|
||||
</HStack>
|
||||
))}
|
||||
</HStack>
|
||||
<GateChatInput
|
||||
onSendMessage={sendPrompt}
|
||||
onStop={() => chatController.current?.abort('stop')}
|
||||
@ -1176,8 +1200,8 @@ const ChatBox = ({
|
||||
resetInputVal={resetInputVal}
|
||||
chatForm={chatForm}
|
||||
placeholder={gateConfig?.placeholderText || '你可以问我任何问题'}
|
||||
selectedToolIds={selectedToolIds}
|
||||
onSelectedToolIdsChange={onSelectedToolIdsChange}
|
||||
selectedTools={selectedTools}
|
||||
onSelectTools={onSelectTools}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
@ -1205,7 +1229,7 @@ const ChatBox = ({
|
||||
{RenderRecords}
|
||||
|
||||
{/* 移动端下的输入框和版权信息容器 */}
|
||||
<Flex direction="column" w="100%" mb={{ base: '12px', sm: 0 }} gap="12px">
|
||||
<Flex direction="column" w="100%" mb={{ base: '12px', sm: 0 }}>
|
||||
{/* message input */}
|
||||
{onStartChat && chatStarted && active && !isInteractive && (
|
||||
<Box
|
||||
@ -1225,8 +1249,8 @@ const ChatBox = ({
|
||||
resetInputVal={resetInputVal}
|
||||
chatForm={chatForm}
|
||||
placeholder={gateConfig?.placeholderText || t('common:gate.placeholder')}
|
||||
selectedToolIds={selectedToolIds}
|
||||
onSelectedToolIdsChange={onSelectedToolIdsChange}
|
||||
selectedTools={selectedTools}
|
||||
onSelectTools={onSelectTools}
|
||||
/>
|
||||
)}
|
||||
{!inGateRoute && (
|
||||
|
||||
@ -7,7 +7,6 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import ShareGateModal from './ShareModol';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { getMyAppsGate, postCreateApp, putAppById } from '@/web/core/app/api';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { emptyTemplates } from '@/web/core/app/templates';
|
||||
import { saveGateConfig } from './HomeTable';
|
||||
import type { GateSchemaType } from '@fastgpt/global/support/user/team/gate/type';
|
||||
@ -32,7 +31,10 @@ const ConfigButtons = ({ tab, appForm, gateConfig, copyRightConfig }: Props) =>
|
||||
const { runAsync: saveHomeConfig, loading: savingHome } = useRequest2(
|
||||
async () => {
|
||||
if (!!gateConfig) {
|
||||
await saveGateConfig(gateConfig);
|
||||
await saveGateConfig({
|
||||
...gateConfig,
|
||||
status: true
|
||||
});
|
||||
toast({
|
||||
title: t('common:save_success'),
|
||||
status: 'success'
|
||||
@ -89,7 +91,6 @@ const ConfigButtons = ({ tab, appForm, gateConfig, copyRightConfig }: Props) =>
|
||||
const gateApp = apps.find((app) => app.type === AppTypeEnum.gate);
|
||||
const currentTeamAvatar = copyRightConfig?.logo;
|
||||
const currentSlogan = gateConfig?.slogan;
|
||||
console.log('gateApp', gateApp, currentTeamAvatar, currentSlogan, nodes, edges);
|
||||
if (gateApp) {
|
||||
if (
|
||||
gateApp.avatar !== currentTeamAvatar ||
|
||||
@ -104,25 +105,17 @@ const ConfigButtons = ({ tab, appForm, gateConfig, copyRightConfig }: Props) =>
|
||||
nodes,
|
||||
edges
|
||||
});
|
||||
toast({
|
||||
title: t('common:update_success'),
|
||||
status: 'success'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await postCreateApp({
|
||||
avatar: gateConfig?.logo,
|
||||
name: gateConfig?.name,
|
||||
name: 'App',
|
||||
intro: gateConfig?.slogan,
|
||||
type: AppTypeEnum.gate,
|
||||
modules: emptyTemplates[AppTypeEnum.gate].nodes,
|
||||
edges: emptyTemplates[AppTypeEnum.gate].edges,
|
||||
chatConfig: emptyTemplates[AppTypeEnum.gate].chatConfig
|
||||
});
|
||||
toast({
|
||||
title: t('common:create_success'),
|
||||
status: 'success'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
toast({
|
||||
|
||||
@ -22,7 +22,6 @@ import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
|
||||
import { useMount } from 'ahooks';
|
||||
import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import { useSimpleAppSnapshots } from '@/pageComponents/app/detail/Gate/useSnapshots';
|
||||
import { Dropdown } from 'react-day-picker';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import AddQuickAppModal from './AddQuickAppModal';
|
||||
import { listQuickApps } from '@/web/support/user/team/gate/quickApp';
|
||||
@ -43,7 +42,6 @@ type Props = {
|
||||
tools: string[];
|
||||
slogan: string;
|
||||
placeholderText: string;
|
||||
onStatusChange?: (status: boolean) => void;
|
||||
onSloganChange?: (slogan: string) => void;
|
||||
onPlaceholderChange?: (text: string) => void;
|
||||
onToolsChange?: (tools: string[]) => void;
|
||||
@ -54,7 +52,6 @@ const HomeTable = ({
|
||||
appDetail,
|
||||
slogan,
|
||||
placeholderText,
|
||||
onStatusChange,
|
||||
onSloganChange,
|
||||
onPlaceholderChange,
|
||||
onAppFormChange
|
||||
@ -165,12 +162,6 @@ const HomeTable = ({
|
||||
letterSpacing: '0.25px'
|
||||
};
|
||||
|
||||
// 响应式工具布局
|
||||
|
||||
const handleStatusChange = (val: string) => {
|
||||
onStatusChange?.(val === 'enabled');
|
||||
};
|
||||
|
||||
const handleSloganChange = (val: string) => {
|
||||
onSloganChange?.(val);
|
||||
};
|
||||
@ -287,75 +278,6 @@ const HomeTable = ({
|
||||
pb={6}
|
||||
pt={{ base: 4, md: 6 }}
|
||||
>
|
||||
{/* 状态选择 */}
|
||||
<FormControl display="flex" flexDirection="column" gap={spacing.sm} w="full">
|
||||
<FormLabel
|
||||
fontWeight={formStyles.fontWeight}
|
||||
fontSize={formStyles.fontSize}
|
||||
lineHeight={formStyles.lineHeight}
|
||||
letterSpacing={formStyles.letterSpacing}
|
||||
color="myGray.700"
|
||||
mb="0"
|
||||
>
|
||||
{t('account_gate:status')}
|
||||
</FormLabel>
|
||||
<RadioGroup value={status ? 'enabled' : 'disabled'} onChange={handleStatusChange}>
|
||||
<Stack direction={{ base: 'column', sm: 'row' }} spacing={spacing.md}>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
p={`${spacing.sm} ${spacing.lg} ${spacing.sm} ${spacing.md}`}
|
||||
borderWidth="1px"
|
||||
borderColor={status ? 'primary.500' : 'myGray.200'}
|
||||
borderRadius="7px"
|
||||
bg={status ? 'blue.50' : 'white'}
|
||||
transition="all 0.2s ease-in-out"
|
||||
_hover={{
|
||||
bg: status ? 'blue.100' : 'myGray.50',
|
||||
borderColor: status ? 'primary.600' : 'myGray.300',
|
||||
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.05)',
|
||||
transform: 'translateY(-1px)'
|
||||
}}
|
||||
>
|
||||
<Radio value="enabled" colorScheme="blue">
|
||||
<Text
|
||||
fontSize={formStyles.fontSize}
|
||||
lineHeight={formStyles.lineHeight}
|
||||
fontWeight={formStyles.fontWeight}
|
||||
letterSpacing={formStyles.letterSpacing}
|
||||
>
|
||||
{t('account_gate:enabled')}
|
||||
</Text>
|
||||
</Radio>
|
||||
</Flex>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
p={`${spacing.sm} ${spacing.lg} ${spacing.sm} ${spacing.md}`}
|
||||
borderWidth="1px"
|
||||
borderColor={!status ? 'primary.500' : 'myGray.200'}
|
||||
borderRadius="7px"
|
||||
bg={!status ? 'blue.50' : 'white'}
|
||||
transition="all 0.2s ease-in-out"
|
||||
_hover={{
|
||||
bg: !status ? 'blue.100' : 'myGray.50',
|
||||
borderColor: !status ? 'primary.600' : 'myGray.300',
|
||||
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.05)',
|
||||
transform: 'translateY(-1px)'
|
||||
}}
|
||||
>
|
||||
<Radio value="disabled" colorScheme="blue">
|
||||
<Text
|
||||
fontSize={formStyles.fontSize}
|
||||
lineHeight={formStyles.lineHeight}
|
||||
fontWeight={formStyles.fontWeight}
|
||||
letterSpacing={formStyles.letterSpacing}
|
||||
>
|
||||
{t('account_gate:disabled')}
|
||||
</Text>
|
||||
</Radio>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
{/* 快捷应用 */}
|
||||
<FormControl
|
||||
display={'flex'}
|
||||
@ -365,7 +287,7 @@ const HomeTable = ({
|
||||
gap={'8px'}
|
||||
>
|
||||
{/* 标题行 */}
|
||||
<Flex alignItems={'center'} gap={'4px'}>
|
||||
<Flex alignItems={'center'} gap={'1'}>
|
||||
<Text
|
||||
color={'var(--Gray-Modern-600, #485264)'}
|
||||
fontFamily={'PingFang SC'}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { Box, Button, Flex, Grid, useDisclosure, Text } from '@chakra-ui/react';
|
||||
import React, { useMemo, useState, useCallback, useEffect, useRef } from 'react';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { theme } from '@fastgpt/web/styles/theme';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
@ -113,9 +112,8 @@ const ToolSelect = ({
|
||||
<>
|
||||
{/* 标题区域 */}
|
||||
<Flex alignItems="center" justifyContent="space-between" width="100%">
|
||||
<Flex alignItems="center" gap={spacing.xs}>
|
||||
<Flex alignItems="center" gap={1}>
|
||||
<Text
|
||||
ml={2}
|
||||
fontWeight={formStyles.fontWeight}
|
||||
fontSize={formStyles.fontSize}
|
||||
lineHeight={formStyles.lineHeight}
|
||||
@ -124,7 +122,7 @@ const ToolSelect = ({
|
||||
>
|
||||
{t('common:core.app.Tool call')}
|
||||
</Text>
|
||||
<QuestionTip ml={1} label={t('app:plugin_dispatch_tip')} />
|
||||
<QuestionTip label={t('app:plugin_dispatch_tip')} />
|
||||
</Flex>
|
||||
|
||||
{/* 已有工具时显示新增按钮 */}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { Box, Flex, HStack } from '@chakra-ui/react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { useSafeState } from 'ahooks';
|
||||
import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { useChatGate } from '../useChatGate';
|
||||
@ -11,6 +10,9 @@ import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { cardStyles } from '../constants';
|
||||
import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { getQuickApps, listQuickApps } from '@/web/support/user/team/gate/quickApp';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
|
||||
type Props = {
|
||||
appForm: AppSimpleEditFormType;
|
||||
@ -18,31 +20,17 @@ type Props = {
|
||||
appDetail: AppDetailType; // 添加 appDetail prop
|
||||
};
|
||||
const ChatGate = ({ appForm, setRenderEdit, appDetail }: Props) => {
|
||||
console.log('appDetai', appDetail);
|
||||
console.log('appform', appForm);
|
||||
|
||||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
|
||||
// 添加 selectedToolIds 状态管理
|
||||
const [selectedToolIds, setSelectedToolIds] = useState<string[]>([]);
|
||||
|
||||
const [workflowData] = useSafeState({
|
||||
nodes: appDetail.modules || [],
|
||||
edges: appDetail.edges || []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setRenderEdit(!datasetCiteData);
|
||||
}, [datasetCiteData, setRenderEdit]);
|
||||
|
||||
const { ChatContainer, restartChat, loading } = useChatGate({
|
||||
...workflowData,
|
||||
chatConfig: appForm.chatConfig,
|
||||
const { ChatContainer } = useChatGate({
|
||||
appForm,
|
||||
isReady: true,
|
||||
appDetail,
|
||||
selectedToolIds, // 传递 selectedToolIds
|
||||
onSelectedToolIdsChange: setSelectedToolIds // 传递更新函数
|
||||
appDetail
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@ -12,25 +12,31 @@ import {
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
useDisclosure
|
||||
useDisclosure,
|
||||
HStack
|
||||
} from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { getTeamGateConfig } from '@/web/support/user/team/gate/api';
|
||||
import { getSystemPlugTemplates, getTeamPlugTemplates } from '@/web/core/app/api/plugin';
|
||||
import type { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
import {
|
||||
getPreviewPluginNode,
|
||||
getSystemPlugTemplates,
|
||||
getTeamPlugTemplates
|
||||
} from '@/web/core/app/api/plugin';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
|
||||
type GateToolSelectProps = {
|
||||
selectedToolIds: string[];
|
||||
onToolsChange: (toolIds: string[]) => void;
|
||||
selectedTools: FlowNodeTemplateType[];
|
||||
onToolsChange: (tools: FlowNodeTemplateType[]) => void;
|
||||
buttonSize?: string;
|
||||
};
|
||||
|
||||
const GateToolSelect = ({
|
||||
selectedToolIds,
|
||||
selectedTools,
|
||||
onToolsChange,
|
||||
buttonSize = 'md'
|
||||
}: GateToolSelectProps) => {
|
||||
@ -77,60 +83,83 @@ const GateToolSelect = ({
|
||||
}, [gateConfig?.tools, allAvailableTools]);
|
||||
|
||||
// 处理单个工具的选择/取消选择
|
||||
const handleToolSelect = useCallback(
|
||||
(toolId: string, checked: boolean) => {
|
||||
const newSelectedIds = checked
|
||||
? [...selectedToolIds, toolId]
|
||||
: selectedToolIds.filter((id) => id !== toolId);
|
||||
onToolsChange(newSelectedIds);
|
||||
const { runAsync: handleToolSelect, loading: loadingToolSelect } = useRequest2(
|
||||
async (toolId: string, checked: boolean) => {
|
||||
const tool = allAvailableTools.find((item) => item.id === toolId);
|
||||
if (!tool) return;
|
||||
|
||||
if (checked) {
|
||||
const res = await getPreviewPluginNode({ appId: tool.id });
|
||||
onToolsChange([...selectedTools, res]);
|
||||
} else {
|
||||
onToolsChange(selectedTools.filter((item) => item.pluginId !== toolId));
|
||||
}
|
||||
},
|
||||
[selectedToolIds, onToolsChange]
|
||||
{
|
||||
refreshDeps: [allAvailableTools, selectedTools, onToolsChange]
|
||||
}
|
||||
);
|
||||
|
||||
const selectedCount = selectedToolIds.length;
|
||||
const selectedCount = selectedTools.length;
|
||||
const loading = loadingGateConfig || loadingSystemPlugins || loadingTeamPlugins;
|
||||
|
||||
// 调试信息
|
||||
console.log('GateToolSelect Debug:', {
|
||||
isOpen,
|
||||
loading,
|
||||
availableTools: availableTools.length,
|
||||
gateConfigTools: gateConfig?.tools?.length || 0,
|
||||
systemPlugins: systemPlugins.length,
|
||||
teamPlugins: teamPlugins.length,
|
||||
allAvailableTools: allAvailableTools.length
|
||||
});
|
||||
|
||||
return (
|
||||
return availableTools.length > 0 ? (
|
||||
<>
|
||||
<Button
|
||||
leftIcon={
|
||||
<MyIcon name={'support/gate/chat/toolkitLine'} w={'18px'} h={'18px'} color="blue.500" />
|
||||
}
|
||||
size={buttonSize}
|
||||
display="flex"
|
||||
padding="8px 12px"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap="4px"
|
||||
iconSpacing="4px"
|
||||
borderRadius="9999px"
|
||||
border="0.5px solid var(--Royal-Blue-200, #C5D7FF)"
|
||||
background="var(--light-fastgpt-primary-container-low, #F0F4FF)"
|
||||
color="blue.500"
|
||||
fontWeight="500"
|
||||
onClick={() => {
|
||||
console.log('Button clicked, opening modal');
|
||||
onOpen();
|
||||
}}
|
||||
flexShrink={0}
|
||||
_hover={{
|
||||
background: 'var(--light-fastgpt-primary-container-low, #E6EDFF)'
|
||||
}}
|
||||
>
|
||||
<Box display={{ base: 'none', md: 'block' }}>{t('common:tool_select')}: </Box>
|
||||
{selectedCount}
|
||||
</Button>
|
||||
{availableTools.length > 1 ? (
|
||||
<Button
|
||||
leftIcon={
|
||||
<MyIcon name={'support/gate/chat/toolkitLine'} w={'18px'} h={'18px'} color="blue.500" />
|
||||
}
|
||||
size={buttonSize}
|
||||
display="flex"
|
||||
padding="8px 12px"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap="4px"
|
||||
iconSpacing="4px"
|
||||
borderRadius="9999px"
|
||||
border="0.5px solid var(--Royal-Blue-200, #C5D7FF)"
|
||||
background="var(--light-fastgpt-primary-container-low, #F0F4FF)"
|
||||
color="blue.500"
|
||||
fontWeight="500"
|
||||
onClick={onOpen}
|
||||
flexShrink={0}
|
||||
_hover={{
|
||||
background: 'var(--light-fastgpt-primary-container-low, #E6EDFF)'
|
||||
}}
|
||||
>
|
||||
<Box display={{ base: 'none', md: 'block' }}>{t('common:tool_select')}: </Box>
|
||||
{selectedCount}
|
||||
</Button>
|
||||
) : (
|
||||
<MyBox
|
||||
isLoading={loadingToolSelect}
|
||||
display={'flex'}
|
||||
alignItems={'center'}
|
||||
gap={2}
|
||||
h={'40px'}
|
||||
borderRadius={'lg'}
|
||||
border={'base'}
|
||||
px={3}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
_hover={{
|
||||
borderColor: 'primary.300'
|
||||
}}
|
||||
{...(selectedTools.length > 0 && {
|
||||
borderColor: 'primary.400 !important',
|
||||
bg: 'primary.50'
|
||||
})}
|
||||
onClick={() => {
|
||||
handleToolSelect(availableTools[0].id, selectedTools.length === 0);
|
||||
}}
|
||||
>
|
||||
<Avatar src={availableTools[0].avatar} w="1.2rem" h="1.2rem" borderRadius={'sm'} />
|
||||
<Text display={['none', 'none', 'block']} fontSize="md" fontWeight="medium">
|
||||
{availableTools[0].name}
|
||||
</Text>
|
||||
</MyBox>
|
||||
)}
|
||||
|
||||
<Modal isOpen={isOpen} onClose={onClose} size="md">
|
||||
<ModalOverlay />
|
||||
@ -152,76 +181,83 @@ const GateToolSelect = ({
|
||||
<ModalCloseButton />
|
||||
|
||||
<ModalBody pb={6}>
|
||||
{loading ? (
|
||||
<Flex justify="center" py={8}>
|
||||
<Text fontSize="sm" color="myGray.500">
|
||||
{t('common:Loading')}
|
||||
</Text>
|
||||
</Flex>
|
||||
) : availableTools.length === 0 ? (
|
||||
<Box py={8} textAlign="center">
|
||||
<EmptyTip text="暂无可用工具" />
|
||||
<Text fontSize="sm" color="myGray.500" mt={3}>
|
||||
请先在门户管理中配置工具,或检查是否有可用的插件
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<VStack align="stretch" spacing={2}>
|
||||
{availableTools.map((tool) => (
|
||||
<Box
|
||||
key={tool.id}
|
||||
p={4}
|
||||
borderRadius="md"
|
||||
cursor="pointer"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
transition="all 0.2s"
|
||||
_hover={{
|
||||
bg: 'blue.50',
|
||||
borderColor: 'blue.300'
|
||||
}}
|
||||
onClick={() => handleToolSelect(tool.id, !selectedToolIds.includes(tool.id))}
|
||||
>
|
||||
<Flex align="center">
|
||||
<Checkbox
|
||||
size="md"
|
||||
isChecked={selectedToolIds.includes(tool.id)}
|
||||
onChange={(e) => {
|
||||
e.stopPropagation();
|
||||
handleToolSelect(tool.id, e.target.checked);
|
||||
}}
|
||||
mr={4}
|
||||
colorScheme="blue"
|
||||
/>
|
||||
<Avatar src={tool.avatar} w="32px" h="32px" mr={3} />
|
||||
<Box flex={1}>
|
||||
<Text fontSize="md" fontWeight="medium" color="myGray.900">
|
||||
{tool.name}
|
||||
</Text>
|
||||
{tool.intro && (
|
||||
<Text fontSize="sm" color="myGray.600" mt={1} noOfLines={2}>
|
||||
{tool.intro}
|
||||
<MyBox isLoading={loadingToolSelect}>
|
||||
{loading ? (
|
||||
<Flex justify="center" py={8}>
|
||||
<Text fontSize="sm" color="myGray.500">
|
||||
{t('common:Loading')}
|
||||
</Text>
|
||||
</Flex>
|
||||
) : availableTools.length === 0 ? (
|
||||
<Box py={8} textAlign="center">
|
||||
<EmptyTip text="暂无可用工具" />
|
||||
<Text fontSize="sm" color="myGray.500" mt={3}>
|
||||
请先在门户管理中配置工具,或检查是否有可用的插件
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<VStack align="stretch" spacing={2}>
|
||||
{availableTools.map((tool) => (
|
||||
<Box
|
||||
key={tool.id}
|
||||
p={4}
|
||||
borderRadius="md"
|
||||
cursor="pointer"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
transition="all 0.2s"
|
||||
_hover={{
|
||||
bg: 'blue.50',
|
||||
borderColor: 'blue.300'
|
||||
}}
|
||||
onClick={() =>
|
||||
handleToolSelect(
|
||||
tool.id,
|
||||
!selectedTools.some((item) => item.pluginId === tool.id)
|
||||
)
|
||||
}
|
||||
>
|
||||
<Flex align="center">
|
||||
<Checkbox
|
||||
size="md"
|
||||
isChecked={selectedTools.some((item) => item.pluginId === tool.id)}
|
||||
onChange={(e) => {
|
||||
e.stopPropagation();
|
||||
handleToolSelect(tool.id, e.target.checked);
|
||||
}}
|
||||
mr={4}
|
||||
colorScheme="blue"
|
||||
/>
|
||||
<Avatar src={tool.avatar} w="32px" h="32px" mr={3} />
|
||||
<Box flex={1}>
|
||||
<Text fontSize="md" fontWeight="medium" color="myGray.900">
|
||||
{tool.name}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
))}
|
||||
</VStack>
|
||||
)}
|
||||
{tool.intro && (
|
||||
<Text fontSize="sm" color="myGray.600" mt={1} noOfLines={2}>
|
||||
{tool.intro}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
))}
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
{selectedToolIds.length > 0 && (
|
||||
<Box mt={4} p={3} bg="blue.50" borderRadius="md">
|
||||
<Text fontSize="sm" color="blue.700">
|
||||
已选择 {selectedToolIds.length} 个工具
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
{selectedTools.length > 0 && (
|
||||
<Box mt={4} p={3} bg="blue.50" borderRadius="md">
|
||||
<Text fontSize="sm" color="blue.700">
|
||||
已选择 {selectedTools.length} 个工具
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</MyBox>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default React.memo(GateToolSelect);
|
||||
|
||||
@ -1,16 +1,10 @@
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { useMemoizedFn, useSafeState } from 'ahooks';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import type { AppChatConfigType, AppDetailType } from '@fastgpt/global/core/app/type';
|
||||
import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
@ -20,29 +14,39 @@ import { useTranslation } from 'next-i18next';
|
||||
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
|
||||
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
|
||||
import { form2AppWorkflow } from '@/web/core/app/utils';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { listQuickApps } from '@/web/support/user/team/gate/quickApp';
|
||||
|
||||
export const useChatGate = ({
|
||||
selectedToolIds,
|
||||
onSelectedToolIdsChange,
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig,
|
||||
appForm,
|
||||
isReady,
|
||||
appDetail
|
||||
}: {
|
||||
selectedToolIds?: string[];
|
||||
onSelectedToolIdsChange?: (toolIds: string[]) => void;
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
appForm: AppSimpleEditFormType;
|
||||
isReady: boolean;
|
||||
appDetail: AppDetailType;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { setChatId, chatId, appId } = useChatStore();
|
||||
const [selectedTools, setSelectedTools] = useState<FlowNodeTemplateType[]>([]);
|
||||
|
||||
const [workflowData, setWorkflowData] = useSafeState({
|
||||
nodes: appDetail.modules || [],
|
||||
edges: appDetail.edges || []
|
||||
});
|
||||
useEffect(() => {
|
||||
const { nodes, edges } = form2AppWorkflow(
|
||||
{
|
||||
...appForm,
|
||||
selectedTools
|
||||
},
|
||||
t
|
||||
);
|
||||
setWorkflowData({ nodes, edges });
|
||||
}, [appForm, selectedTools, setWorkflowData, t]);
|
||||
|
||||
const onUpdateHistoryTitle = useContextSelector(ChatContext, (v) => v.onUpdateHistoryTitle);
|
||||
|
||||
const startChat = useMemoizedFn(
|
||||
@ -61,19 +65,18 @@ export const useChatGate = ({
|
||||
data: {
|
||||
// Send histories and user messages
|
||||
messages: histories,
|
||||
nodes,
|
||||
edges,
|
||||
nodes: workflowData.nodes,
|
||||
edges: workflowData.edges,
|
||||
variables,
|
||||
responseChatItemId,
|
||||
appId,
|
||||
appName: t('chat:chat_gate_app', { name: appDetail.name }),
|
||||
chatId,
|
||||
chatConfig,
|
||||
chatConfig: appForm.chatConfig,
|
||||
metadata: {
|
||||
source: 'web',
|
||||
userAgent: navigator.userAgent
|
||||
},
|
||||
selectedToolIds: selectedToolIds || []
|
||||
}
|
||||
},
|
||||
onMessage: generatingMessage,
|
||||
abortCtrl: controller
|
||||
@ -99,22 +102,18 @@ export const useChatGate = ({
|
||||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const clearChatRecords = useContextSelector(ChatItemContext, (v) => v.clearChatRecords);
|
||||
|
||||
const pluginInputs = useMemo(() => {
|
||||
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
|
||||
}, [nodes]);
|
||||
|
||||
// Set chat box data
|
||||
useEffect(() => {
|
||||
setChatBoxData({
|
||||
userAvatar: userInfo?.avatar,
|
||||
appId: appId,
|
||||
app: {
|
||||
chatConfig,
|
||||
chatConfig: appForm.chatConfig,
|
||||
name: appDetail.name,
|
||||
avatar: appDetail.avatar,
|
||||
intro: appDetail.intro,
|
||||
type: appDetail.type,
|
||||
pluginInputs
|
||||
pluginInputs: []
|
||||
}
|
||||
});
|
||||
}, [
|
||||
@ -122,9 +121,8 @@ export const useChatGate = ({
|
||||
appDetail.intro,
|
||||
appDetail.name,
|
||||
appDetail.type,
|
||||
appForm.chatConfig,
|
||||
appId,
|
||||
chatConfig,
|
||||
pluginInputs,
|
||||
setChatBoxData,
|
||||
userInfo?.avatar
|
||||
]);
|
||||
@ -151,29 +149,24 @@ export const useChatGate = ({
|
||||
setChatId();
|
||||
}, [clearChatRecords, setChatId]);
|
||||
|
||||
const CustomChatContainer = useMemoizedFn(() =>
|
||||
appDetail.type === AppTypeEnum.plugin ? (
|
||||
<Box p={5} pb={16}>
|
||||
<PluginRunBox
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
onNewChat={restartChat}
|
||||
onStartChat={startChat}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<ChatBox
|
||||
isReady={isReady}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
showMarkIcon
|
||||
chatType={'chat'}
|
||||
onStartChat={startChat}
|
||||
selectedToolIds={selectedToolIds}
|
||||
onSelectedToolIdsChange={onSelectedToolIdsChange}
|
||||
/>
|
||||
)
|
||||
);
|
||||
// 精选应用
|
||||
const { data: recommendApps = [] } = useRequest2(listQuickApps, {
|
||||
manual: false
|
||||
});
|
||||
|
||||
const CustomChatContainer = useMemoizedFn(() => (
|
||||
<ChatBox
|
||||
isReady={isReady}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
showMarkIcon
|
||||
chatType={'chat'}
|
||||
onStartChat={startChat}
|
||||
selectedTools={selectedTools}
|
||||
onSelectTools={setSelectedTools}
|
||||
recommendApps={recommendApps}
|
||||
/>
|
||||
));
|
||||
|
||||
return {
|
||||
ChatContainer: CustomChatContainer,
|
||||
|
||||
@ -29,9 +29,9 @@ type TabType = 'home' | 'copyright' | 'app' | 'logs';
|
||||
|
||||
const GatewayConfig = () => {
|
||||
const { t } = useTranslation();
|
||||
const [gateConfig, setGateConfig] = useState<GateSchemaType | undefined>(undefined);
|
||||
const [gateConfig, setGateConfig] = useState<GateSchemaType>();
|
||||
// 添加 appForm 状态
|
||||
const [appForm, setAppForm] = useState<AppSimpleEditFormType | undefined>(undefined);
|
||||
const [appForm, setAppForm] = useState<AppSimpleEditFormType>();
|
||||
//从 appForm 中获取 selectedTools的 id 组成 string 数组
|
||||
|
||||
//gateConfig?.tools 改成
|
||||
|
||||
@ -64,7 +64,6 @@ export type Props = {
|
||||
chatId: string;
|
||||
chatConfig: AppChatConfigType;
|
||||
metadata?: Record<string, any>;
|
||||
selectedToolIds?: string[];
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
@ -81,8 +80,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId,
|
||||
chatConfig,
|
||||
chatId,
|
||||
metadata = {},
|
||||
selectedToolIds = []
|
||||
metadata = {}
|
||||
} = req.body as Props;
|
||||
try {
|
||||
if (!Array.isArray(nodes)) {
|
||||
@ -91,47 +89,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!Array.isArray(edges)) {
|
||||
throw new Error('Edges is not array');
|
||||
}
|
||||
|
||||
//对边进行过滤,只保留selectedToolIds中的边
|
||||
console.log('selectedToolIds', selectedToolIds);
|
||||
|
||||
// 创建从 pluginId 到 nodeId 的映射
|
||||
const pluginIdToNodeIdMap = new Map<string, string>();
|
||||
nodes.forEach((node) => {
|
||||
if (node.pluginId) {
|
||||
pluginIdToNodeIdMap.set(node.pluginId, node.nodeId);
|
||||
}
|
||||
});
|
||||
console.log('pluginIdToNodeIdMap', Object.fromEntries(pluginIdToNodeIdMap));
|
||||
|
||||
// 获取选中工具对应的 nodeId 集合
|
||||
const selectedNodeIds = new Set<string>();
|
||||
selectedToolIds.forEach((pluginId) => {
|
||||
const nodeId = pluginIdToNodeIdMap.get(pluginId);
|
||||
if (nodeId) {
|
||||
selectedNodeIds.add(nodeId);
|
||||
}
|
||||
});
|
||||
console.log('selectedNodeIds', Array.from(selectedNodeIds));
|
||||
|
||||
// 过滤边:保留第一个边和目标节点在选中工具中的边
|
||||
const filteredEdges = edges.filter((edge, index) => {
|
||||
// 保留第一个边
|
||||
if (index === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 保留目标节点在选中工具中的边
|
||||
return selectedNodeIds.has(edge.target);
|
||||
});
|
||||
|
||||
console.log('Original edges count:', edges.length);
|
||||
console.log('Filtered edges count:', filteredEdges.length);
|
||||
console.log('Filtered edges:', filteredEdges);
|
||||
|
||||
// 使用过滤后的边
|
||||
edges = filteredEdges;
|
||||
|
||||
const chatMessages = GPTMessages2Chats(messages);
|
||||
// console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length);
|
||||
|
||||
|
||||
@ -10,9 +10,6 @@ import { useTranslation } from 'next-i18next';
|
||||
import FoldButton from '@/pageComponents/chat/gatechat/FoldButton';
|
||||
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
|
||||
import PageContainer from '@/components/PageContainer';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import ChatHistorySlider from '@/pageComponents/chat/ChatHistorySlider';
|
||||
import SliderApps from '@/pageComponents/chat/SliderApps';
|
||||
import ChatHeader from '@/pageComponents/chat/ChatHeader';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user