From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001 From: wwf <yearningwang@iqtogether.com> Date: 星期三, 04 六月 2025 15:17:49 +0800 Subject: [PATCH] 初始化 --- app/components/base/chat/embedded-chatbot/chat-wrapper.tsx | 177 +++++++++++++--------------------------------------------- 1 files changed, 41 insertions(+), 136 deletions(-) diff --git a/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx b/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx index a06930c..8d0af02 100644 --- a/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx +++ b/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo } from 'react' import Chat from '../chat' import type { ChatConfig, @@ -9,22 +9,16 @@ import { useChat } from '../chat/hooks' import { getLastAnswer, isValidGeneratedAnswer } from '../utils' import { useEmbeddedChatbotContext } from './context' +import ConfigPanel from './config-panel' import { isDify } from './utils' -import { InputVarType } from '@/app/components/workflow/types' -import { TransferMethod } from '@/types/app' -import InputsForm from '@/app/components/base/chat/embedded-chatbot/inputs-form' +import cn from '@/utils/classnames' import { fetchSuggestedQuestions, getUrl, stopChatMessageResponding, } from '@/service/share' -import AppIcon from '@/app/components/base/app-icon' import LogoAvatar from '@/app/components/base/logo/logo-embedded-chat-avatar' import AnswerIcon from '@/app/components/base/answer-icon' -import SuggestedQuestions from '@/app/components/base/chat/chat/answer/suggested-questions' -import { Markdown } from '@/app/components/base/markdown' -import cn from '@/utils/classnames' -import type { FileEntity } from '../../file-uploader/types' const ChatWrapper = () => { const { @@ -33,10 +27,8 @@ appPrevChatList, currentConversationId, currentConversationItem, - currentConversationInputs, inputsForms, newConversationInputs, - newConversationInputsRef, handleNewConversationCompleted, isMobile, isInstalledApp, @@ -45,9 +37,6 @@ handleFeedback, currentChatInstanceRef, themeBuilder, - clearChatList, - setClearChatList, - setIsResponding, } = useEmbeddedChatbotContext() const appConfig = useMemo(() => { const config = appParams || {} @@ -67,65 +56,28 @@ setTargetMessageId, handleSend, handleStop, - isResponding: respondingState, + isResponding, suggestedQuestions, } = useChat( appConfig, { - inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any, + inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any, inputsForm: inputsForms, }, appPrevChatList, taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId), - clearChatList, - setClearChatList, ) - const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current - const inputDisabled = useMemo(() => { - let hasEmptyInput = '' - let fileIsUploading = false - const requiredVars = inputsForms.filter(({ required }) => required) - if (requiredVars.length) { - requiredVars.forEach(({ variable, label, type }) => { - if (hasEmptyInput) - return - - if (fileIsUploading) - return - - if (!inputsFormValue?.[variable]) - hasEmptyInput = label as string - - if ((type === InputVarType.singleFile || type === InputVarType.multiFiles) && inputsFormValue?.[variable]) { - const files = inputsFormValue[variable] - if (Array.isArray(files)) - fileIsUploading = files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId) - else - fileIsUploading = files.transferMethod === TransferMethod.local_file && !files.uploadedId - } - }) - } - if (hasEmptyInput) - return true - - if (fileIsUploading) - return true - return false - }, [inputsFormValue, inputsForms]) useEffect(() => { if (currentChatInstanceRef.current) currentChatInstanceRef.current.handleStop = handleStop }, [currentChatInstanceRef, handleStop]) - useEffect(() => { - setIsResponding(respondingState) - }, [respondingState, setIsResponding]) const doSend: OnSend = useCallback((message, files, isRegenerate = false, parentAnswer: ChatItem | null = null) => { const data: any = { query: message, files, - inputs: currentConversationId ? currentConversationInputs : newConversationInputs, + inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs, conversation_id: currentConversationId, parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null, } @@ -139,83 +91,43 @@ isPublicAPI: !isInstalledApp, }, ) - }, [currentConversationId, currentConversationInputs, newConversationInputs, chatList, handleSend, isInstalledApp, appId, handleNewConversationCompleted]) + }, [ + chatList, + handleNewConversationCompleted, + handleSend, + currentConversationId, + currentConversationItem, + newConversationInputs, + isInstalledApp, + appId, + ]) - const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => { - const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)! + const doRegenerate = useCallback((chatItem: ChatItemInTree) => { + const question = chatList.find(item => item.id === chatItem.parentMessageId)! const parentAnswer = chatList.find(item => item.id === question.parentMessageId) - doSend(editedQuestion ? editedQuestion.message : question.content, - editedQuestion ? editedQuestion.files : question.message_files, - true, - isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null, - ) + doSend(question.content, question.message_files, true, isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null) }, [chatList, doSend]) - const messageList = useMemo(() => { - if (currentConversationId) - return chatList - return chatList.filter(item => !item.isOpeningStatement) - }, [chatList, currentConversationId]) - - const [collapsed, setCollapsed] = useState(!!currentConversationId) - const chatNode = useMemo(() => { - if (!inputsForms.length) - return null - if (isMobile) { - if (!currentConversationId) - return <InputsForm collapsed={collapsed} setCollapsed={setCollapsed} /> - return <div className='mb-4'></div> - } - else { - return <InputsForm collapsed={collapsed} setCollapsed={setCollapsed} /> - } - }, [inputsForms.length, isMobile, currentConversationId, collapsed]) - - const welcome = useMemo(() => { - const welcomeMessage = chatList.find(item => item.isOpeningStatement) - if (respondingState) - return null - if (currentConversationId) - return null - if (!welcomeMessage) - return null - if (!collapsed && inputsForms.length > 0) - return null - if (welcomeMessage.suggestedQuestions && welcomeMessage.suggestedQuestions?.length > 0) { + if (inputsForms.length) { return ( - <div className={cn('flex items-center justify-center px-4 py-12', isMobile ? 'min-h-[30vh] py-0' : 'h-[50vh]')}> - <div className='flex max-w-[720px] grow gap-4'> - <AppIcon - size='xl' - iconType={appData?.site.icon_type} - icon={appData?.site.icon} - background={appData?.site.icon_background} - imageUrl={appData?.site.icon_url} - /> - <div className='body-lg-regular grow rounded-2xl bg-chat-bubble-bg px-4 py-3 text-text-primary'> - <Markdown content={welcomeMessage.content} /> - <SuggestedQuestions item={welcomeMessage} /> + <> + {!currentConversationId && ( + <div className={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')}> + <div className='mb-6' /> + <ConfigPanel /> + <div + className='my-6 h-[1px]' + style={{ background: 'linear-gradient(90deg, rgba(242, 244, 247, 0.00) 0%, #F2F4F7 49.17%, rgba(242, 244, 247, 0.00) 100%)' }} + /> </div> - </div> - </div> + )} + </> ) } - return ( - <div className={cn('flex h-[50vh] flex-col items-center justify-center gap-3 py-12', isMobile ? 'min-h-[30vh] py-0' : 'h-[50vh]')}> - <AppIcon - size='xl' - iconType={appData?.site.icon_type} - icon={appData?.site.icon} - background={appData?.site.icon_background} - imageUrl={appData?.site.icon_url} - /> - <div className='max-w-[768px] px-4'> - <Markdown className='!body-2xl-regular !text-text-tertiary' content={welcomeMessage.content} /> - </div> - </div> - ) - }, [appData?.site.icon, appData?.site.icon_background, appData?.site.icon_type, appData?.site.icon_url, chatList, collapsed, currentConversationId, inputsForms.length, respondingState]) + + return null + }, [currentConversationId, inputsForms, isMobile]) const answerIcon = isDify() ? <LogoAvatar className='relative shrink-0' /> @@ -232,22 +144,17 @@ <Chat appData={appData} config={appConfig} - chatList={messageList} - isResponding={respondingState} - chatContainerInnerClassName={cn('mx-auto w-full max-w-full pt-4 tablet:px-4', isMobile && 'px-4')} - chatFooterClassName={cn('pb-4', !isMobile && 'rounded-b-2xl')} - chatFooterInnerClassName={cn('mx-auto w-full max-w-full px-4', isMobile && 'px-2')} + chatList={chatList} + isResponding={isResponding} + chatContainerInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')} + chatFooterClassName='pb-4' + chatFooterInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')} onSend={doSend} - inputs={currentConversationId ? currentConversationInputs as any : newConversationInputs} + inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs} inputsForm={inputsForms} onRegenerate={doRegenerate} onStopResponding={handleStop} - chatNode={ - <> - {chatNode} - {welcome} - </> - } + chatNode={chatNode} allToolIcons={appMeta?.tool_icons || {}} onFeedback={handleFeedback} suggestedQuestions={suggestedQuestions} @@ -255,8 +162,6 @@ hideProcessDetail themeBuilder={themeBuilder} switchSibling={siblingMessageId => setTargetMessageId(siblingMessageId)} - inputDisabled={inputDisabled} - isMobile={isMobile} /> ) } -- Gitblit v1.8.0