From 374f5171cf0366ae302ec083d8914d0542919737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=85=83=E5=9D=A4?= Date: Thu, 10 Jul 2025 09:37:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=A8=E6=88=B7=E5=8F=8D?= =?UTF-8?q?=E9=A6=88=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/chat-with-history/hooks.tsx | 784 +++++++++++++-------- 1 file changed, 489 insertions(+), 295 deletions(-) diff --git a/app/components/chat-with-history/hooks.tsx b/app/components/chat-with-history/hooks.tsx index 2d18f0f..d2df323 100644 --- a/app/components/chat-with-history/hooks.tsx +++ b/app/components/chat-with-history/hooks.tsx @@ -1,25 +1,15 @@ -import { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react' -import { useTranslation } from 'react-i18next' -import useSWR from 'swr' -import { useLocalStorageState } from 'ahooks' -import { produce } from 'immer' -import type { - Callback, - ChatConfig, - ChatItem, - Feedback, -} from '../types' -import { CONVERSATION_ID_INFO } from '../constants' -import { buildChatItemTree } from '../utils' -import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils' -import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' -// TODO mars +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import useSWR from "swr"; +import { useLocalStorageState } from "ahooks"; +import { produce } from "immer"; +import { message } from "antd"; +import { client, getInfo } from "@/app/api/utils/common"; +import type { Callback, ChatConfig, ChatItem, Feedback } from "../types"; +import { CONVERSATION_ID_INFO } from "../constants"; +import { buildChatItemTree } from "../utils"; +import { addFileInfos, sortAgentSorts } from "@/app/components/tools/utils"; +import { getProcessedFilesFromResponse } from "@/app/components/base/file-uploader/utils"; import { delConversation, fetchAppInfo, @@ -32,50 +22,66 @@ import { renameConversation, unpinConversation, updateFeedback, -} from '@/service/index' -import type { InstalledApp } from '@/models/explore' -import type { - AppData, - ConversationItem, -} from '@/models/share' -import { useToastContext } from '@/app/components/base/toast' -import { changeLanguage } from '@/i18n/i18next-config' -import { useAppFavicon } from '@/hooks/use-app-favicon' -import { InputVarType } from '@/app/components/workflow/types' -import { TransferMethod } from '@/types/app' +} from "@/service/index"; +import type { InstalledApp } from "@/models/explore"; +import type { AppData, ConversationItem } from "@/models/share"; +import { useToastContext } from "@/app/components/base/toast"; +import { changeLanguage } from "@/i18n/i18next-config"; +import { useAppFavicon } from "@/hooks/use-app-favicon"; +import { InputVarType } from "@/app/components/workflow/types"; +import { TransferMethod } from "@/types/app"; +import FeedbackModal from "@/app/components/chat-with-history/FeedbackModal"; // 引入 FeedbackModal 组件 function getFormattedChatList(messages: any[]) { - const newChatList: ChatItem[] = [] + const newChatList: ChatItem[] = []; messages.forEach((item) => { - const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || [] + const questionFiles = + item.message_files?.filter((file: any) => file.belongs_to === "user") || + []; newChatList.push({ id: `question-${item.id}`, content: item.query, isAnswer: false, - message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))), + message_files: getProcessedFilesFromResponse( + questionFiles.map((item: any) => ({ ...item, related_id: item.id })) + ), parentMessageId: item.parent_message_id || undefined, - }) - const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [] + }); + const answerFiles = + item.message_files?.filter( + (file: any) => file.belongs_to === "assistant" + ) || []; if (answerFiles.length > 0 && questionFiles.length > 0) // 获取本地文件名 - answerFiles[0].filename = questionFiles[0]?.filename + answerFiles[0].filename = questionFiles[0]?.filename; newChatList.push({ id: item.id, content: item.answer, - agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files), + agent_thoughts: addFileInfos( + item.agent_thoughts + ? sortAgentSorts(item.agent_thoughts) + : item.agent_thoughts, + item.message_files + ), feedback: item.feedback, isAnswer: true, citation: item.retriever_resources, - message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))), + message_files: getProcessedFilesFromResponse( + answerFiles.map((item: any) => ({ ...item, related_id: item.id })) + ), parentMessageId: `question-${item.id}`, - }) - }) - return newChatList + }); + }); + return newChatList; } export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { - const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]) - const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR(installedAppInfo ? null : 'appInfo', fetchAppInfo) + const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]); + const { + data: appInfo, + isLoading: appInfoLoading, + error: appInfoError, + } = useSWR(installedAppInfo ? null : "appInfo", fetchAppInfo); useAppFavicon({ enable: !installedAppInfo, @@ -83,11 +89,11 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { icon: appInfo?.site?.icon, icon_background: appInfo?.site?.icon_background, icon_url: appInfo?.site?.icon_url, - }) + }); const appData = useMemo(() => { if (isInstalledApp) { - const { id, app } = installedAppInfo! + const { id, app } = installedAppInfo!; return { app_id: id, site: { @@ -97,326 +103,507 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { icon_background: app.icon_background, icon_url: app.icon_url, prompt_public: false, - copyright: '', + copyright: "", show_workflow_steps: true, use_icon_as_answer_icon: app.use_icon_as_answer_icon, }, - plan: 'basic', - } as AppData + plan: "basic", + } as AppData; } - return appInfo - }, [isInstalledApp, installedAppInfo, appInfo]) - const appId = useMemo(() => appData?.app_id, [appData]) + return appInfo; + }, [isInstalledApp, installedAppInfo, appInfo]); + const appId = useMemo(() => appData?.app_id, [appData]); useEffect(() => { if (appData?.site?.default_language) - changeLanguage(appData.site?.default_language) - }, [appData]) + changeLanguage(appData.site?.default_language); + }, [appData]); - const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState>(CONVERSATION_ID_INFO, { + const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState< + Record + >(CONVERSATION_ID_INFO, { defaultValue: {}, - }) - const currentConversationId = useMemo(() => conversationIdInfo?.[appId || ''] || '', [appId, conversationIdInfo]) - const handleConversationIdInfoChange = useCallback((changeConversationId: string) => { - if (appId) { - setConversationIdInfo({ - ...conversationIdInfo, - [appId || '']: changeConversationId, - }) - } - }, [appId, conversationIdInfo, setConversationIdInfo]) - const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] = useState(true) - const [newConversationId, setNewConversationId] = useState('') + }); + const currentConversationId = useMemo( + () => conversationIdInfo?.[appId || ""] || "", + [appId, conversationIdInfo] + ); + const handleConversationIdInfoChange = useCallback( + (changeConversationId: string) => { + if (appId) { + setConversationIdInfo({ + ...conversationIdInfo, + [appId || ""]: changeConversationId, + }); + } + }, + [appId, conversationIdInfo, setConversationIdInfo] + ); + const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] = + useState(true); + const [newConversationId, setNewConversationId] = useState(""); const chatShouldReloadKey = useMemo(() => { - if (currentConversationId === newConversationId) - return '' + if (currentConversationId === newConversationId) return ""; - return currentConversationId - }, [currentConversationId, newConversationId]) - const { data: appParams } = useSWR(['appParams', isInstalledApp, appId], () => fetchAppParams()) - const { data: appMeta } = useSWR(['appMeta', isInstalledApp, appId], () => fetchAppMeta()) - const { data: appPinnedConversationData, mutate: mutateAppPinnedConversationData } = useSWR(['appConversationData', isInstalledApp, appId, true], () => fetchConversations()) - const { data: appConversationData, isLoading: appConversationDataLoading, mutate: mutateAppConversationData } = useSWR(['appConversationData', isInstalledApp, appId, false], () => fetchConversations()) - const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR(chatShouldReloadKey ? ['appChatList', chatShouldReloadKey, isInstalledApp, appId] : null, () => fetchChatList(chatShouldReloadKey)) + return currentConversationId; + }, [currentConversationId, newConversationId]); + const { data: appParams } = useSWR(["appParams", isInstalledApp, appId], () => + fetchAppParams() + ); + const { data: appMeta } = useSWR(["appMeta", isInstalledApp, appId], () => + fetchAppMeta() + ); + const { + data: appPinnedConversationData, + mutate: mutateAppPinnedConversationData, + } = useSWR(["appConversationData", isInstalledApp, appId, true], () => + fetchConversations() + ); + const { + data: appConversationData, + isLoading: appConversationDataLoading, + mutate: mutateAppConversationData, + } = useSWR(["appConversationData", isInstalledApp, appId, false], () => + fetchConversations() + ); + const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR( + chatShouldReloadKey + ? ["appChatList", chatShouldReloadKey, isInstalledApp, appId] + : null, + () => fetchChatList(chatShouldReloadKey) + ); const appPrevChatTree = useMemo( - () => (currentConversationId && appChatListData?.data.length) - ? buildChatItemTree(getFormattedChatList(appChatListData.data)) - : [], - [appChatListData, currentConversationId], - ) + () => + currentConversationId && appChatListData?.data.length + ? buildChatItemTree(getFormattedChatList(appChatListData.data)) + : [], + [appChatListData, currentConversationId] + ); - const [showNewConversationItemInList, setShowNewConversationItemInList] = useState(false) + const [showNewConversationItemInList, setShowNewConversationItemInList] = + useState(false); const pinnedConversationList = useMemo(() => { - return appPinnedConversationData?.data || [] - }, [appPinnedConversationData]) - const { t } = useTranslation() - const newConversationInputsRef = useRef>({}) - const [newConversationInputs, setNewConversationInputs] = useState>({}) - const handleNewConversationInputsChange = useCallback((newInputs: Record) => { - newConversationInputsRef.current = newInputs - setNewConversationInputs(newInputs) - }, []) + return appPinnedConversationData?.data || []; + }, [appPinnedConversationData]); + const { t } = useTranslation(); + const newConversationInputsRef = useRef>({}); + const [newConversationInputs, setNewConversationInputs] = useState< + Record + >({}); + const handleNewConversationInputsChange = useCallback( + (newInputs: Record) => { + newConversationInputsRef.current = newInputs; + setNewConversationInputs(newInputs); + }, + [] + ); const inputsForms = useMemo(() => { - return (appParams?.user_input_form || []).filter((item: any) => !item.external_data_tool).map((item: any) => { - if (item.paragraph) { - return { - ...item.paragraph, - type: 'paragraph', + return (appParams?.user_input_form || []) + .filter((item: any) => !item.external_data_tool) + .map((item: any) => { + if (item.paragraph) { + return { + ...item.paragraph, + type: "paragraph", + }; } - } - if (item.number) { - return { - ...item.number, - type: 'number', + if (item.number) { + return { + ...item.number, + type: "number", + }; } - } - if (item.select) { - return { - ...item.select, - type: 'select', + if (item.select) { + return { + ...item.select, + type: "select", + }; } - } - if (item['file-list']) { - return { - ...item['file-list'], - type: 'file-list', + if (item["file-list"]) { + return { + ...item["file-list"], + type: "file-list", + }; } - } - if (item.file) { - return { - ...item.file, - type: 'file', + if (item.file) { + return { + ...item.file, + type: "file", + }; } - } - return { - ...item['text-input'], - type: 'text-input', - } - }) - }, [appParams]) + return { + ...item["text-input"], + type: "text-input", + }; + }); + }, [appParams]); useEffect(() => { - const conversationInputs: Record = {} + const conversationInputs: Record = {}; inputsForms.forEach((item: any) => { - conversationInputs[item.variable] = item.default || null - }) - handleNewConversationInputsChange(conversationInputs) - }, [handleNewConversationInputsChange, inputsForms]) + conversationInputs[item.variable] = item.default || null; + }); + handleNewConversationInputsChange(conversationInputs); + }, [handleNewConversationInputsChange, inputsForms]); - const { data: newConversation } = useSWR(newConversationId ? [isInstalledApp, appId, newConversationId] : null, () => generationConversationName(newConversationId)) - const [originConversationList, setOriginConversationList] = useState([]) + const { data: newConversation } = useSWR( + newConversationId ? [isInstalledApp, appId, newConversationId] : null, + () => generationConversationName(newConversationId) + ); + const [originConversationList, setOriginConversationList] = useState< + ConversationItem[] + >([]); useEffect(() => { if (appConversationData?.data && !appConversationDataLoading) - setOriginConversationList(appConversationData?.data) - }, [appConversationData, appConversationDataLoading]) + setOriginConversationList(appConversationData?.data); + }, [appConversationData, appConversationDataLoading]); const conversationList = useMemo(() => { - const data = originConversationList.slice() + const data = originConversationList.slice(); - if (showNewConversationItemInList && data[0]?.id !== '') { + if (showNewConversationItemInList && data[0]?.id !== "") { data.unshift({ - id: '', - name: t('share.chat.newChatDefaultName'), + id: "", + name: t("share.chat.newChatDefaultName"), inputs: {}, - introduction: '', - }) + introduction: "", + }); } - return data - }, [originConversationList, showNewConversationItemInList, t]) + return data; + }, [originConversationList, showNewConversationItemInList, t]); useEffect(() => { if (newConversation) { - setOriginConversationList(produce((draft) => { - const index = draft.findIndex(item => item.id === newConversation.id) + setOriginConversationList( + produce((draft) => { + const index = draft.findIndex( + (item) => item.id === newConversation.id + ); - if (index > -1) - draft[index] = newConversation - else - draft.unshift(newConversation) - })) + if (index > -1) draft[index] = newConversation; + else draft.unshift(newConversation); + }) + ); } - }, [newConversation]) + }, [newConversation]); const currentConversationItem = useMemo(() => { - let conversationItem = conversationList.find(item => item.id === currentConversationId) + let conversationItem = conversationList.find( + (item) => item.id === currentConversationId + ); if (!conversationItem && pinnedConversationList.length) - conversationItem = pinnedConversationList.find(item => item.id === currentConversationId) + conversationItem = pinnedConversationList.find( + (item) => item.id === currentConversationId + ); - return conversationItem - }, [conversationList, currentConversationId, pinnedConversationList]) + return conversationItem; + }, [conversationList, currentConversationId, pinnedConversationList]); - const { notify } = useToastContext() - const checkInputsRequired = useCallback((silent?: boolean) => { - let hasEmptyInput = '' - let fileIsUploading = false - const requiredVars = inputsForms.filter(({ required }) => required) - if (requiredVars.length) { - requiredVars.forEach(({ variable, label, type }) => { - if (hasEmptyInput) - return + const { notify } = useToastContext(); + const checkInputsRequired = useCallback( + (silent?: boolean) => { + 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 (fileIsUploading) return; - if (!newConversationInputsRef.current[variable] && !silent) - hasEmptyInput = label as string + if (!newConversationInputsRef.current[variable] && !silent) + hasEmptyInput = label as string; - if ((type === InputVarType.singleFile || type === InputVarType.multiFiles) && newConversationInputsRef.current[variable] && !silent) { - const files = newConversationInputsRef.current[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 ( + (type === InputVarType.singleFile || + type === InputVarType.multiFiles) && + newConversationInputsRef.current[variable] && + !silent + ) { + const files = newConversationInputsRef.current[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) { - notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }) }) - return false - } + if (hasEmptyInput) { + notify({ + type: "error", + message: t("appDebug.errorMessage.valueOfVarRequired", { + key: hasEmptyInput, + }), + }); + return false; + } - if (fileIsUploading) { - notify({ type: 'info', message: t('appDebug.errorMessage.waitForFileUpload') }) - return - } + if (fileIsUploading) { + notify({ + type: "info", + message: t("appDebug.errorMessage.waitForFileUpload"), + }); + return; + } - return true - }, [inputsForms, notify, t]) + return true; + }, + [inputsForms, notify, t] + ); const handleStartChat = useCallback(() => { if (checkInputsRequired()) { - setShowConfigPanelBeforeChat(false) - setShowNewConversationItemInList(true) + setShowConfigPanelBeforeChat(false); + setShowNewConversationItemInList(true); } - }, [setShowConfigPanelBeforeChat, setShowNewConversationItemInList, checkInputsRequired]) - const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ handleStop: () => { } }) - const handleChangeConversation = useCallback((conversationId: string) => { - currentChatInstanceRef.current.handleStop() - setNewConversationId('') - handleConversationIdInfoChange(conversationId) + }, [ + setShowConfigPanelBeforeChat, + setShowNewConversationItemInList, + checkInputsRequired, + ]); + const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ + handleStop: () => {}, + }); + const handleChangeConversation = useCallback( + (conversationId: string) => { + currentChatInstanceRef.current.handleStop(); + setNewConversationId(""); + handleConversationIdInfoChange(conversationId); - if (conversationId === '' && !checkInputsRequired(true)) - setShowConfigPanelBeforeChat(true) - else - setShowConfigPanelBeforeChat(false) - }, [handleConversationIdInfoChange, setShowConfigPanelBeforeChat, checkInputsRequired]) + if (conversationId === "" && !checkInputsRequired(true)) + setShowConfigPanelBeforeChat(true); + else setShowConfigPanelBeforeChat(false); + }, + [ + handleConversationIdInfoChange, + setShowConfigPanelBeforeChat, + checkInputsRequired, + ] + ); const handleNewConversation = useCallback(() => { - currentChatInstanceRef.current.handleStop() - setNewConversationId('') + currentChatInstanceRef.current.handleStop(); + setNewConversationId(""); if (showNewConversationItemInList) { - handleChangeConversation('') + handleChangeConversation(""); + } else if (currentConversationId) { + handleConversationIdInfoChange(""); + setShowConfigPanelBeforeChat(true); + setShowNewConversationItemInList(true); + handleNewConversationInputsChange({}); } - else if (currentConversationId) { - handleConversationIdInfoChange('') - setShowConfigPanelBeforeChat(true) - setShowNewConversationItemInList(true) - handleNewConversationInputsChange({}) - } - }, [handleChangeConversation, currentConversationId, handleConversationIdInfoChange, setShowConfigPanelBeforeChat, setShowNewConversationItemInList, showNewConversationItemInList, handleNewConversationInputsChange]) + }, [ + handleChangeConversation, + currentConversationId, + handleConversationIdInfoChange, + setShowConfigPanelBeforeChat, + setShowNewConversationItemInList, + showNewConversationItemInList, + handleNewConversationInputsChange, + ]); const handleUpdateConversationList = useCallback(() => { - mutateAppConversationData() - mutateAppPinnedConversationData() - }, [mutateAppConversationData, mutateAppPinnedConversationData]) + mutateAppConversationData(); + mutateAppPinnedConversationData(); + }, [mutateAppConversationData, mutateAppPinnedConversationData]); - const handlePinConversation = useCallback(async (conversationId: string) => { - await pinConversation(isInstalledApp, appId, conversationId) - notify({ type: 'success', message: t('common.api.success') }) - handleUpdateConversationList() - }, [isInstalledApp, appId, notify, t, handleUpdateConversationList]) + const handlePinConversation = useCallback( + async (conversationId: string) => { + await pinConversation(isInstalledApp, appId, conversationId); + notify({ type: "success", message: t("common.api.success") }); + handleUpdateConversationList(); + }, + [isInstalledApp, appId, notify, t, handleUpdateConversationList] + ); - const handleUnpinConversation = useCallback(async (conversationId: string) => { - await unpinConversation(isInstalledApp, appId, conversationId) - notify({ type: 'success', message: t('common.api.success') }) - handleUpdateConversationList() - }, [isInstalledApp, appId, notify, t, handleUpdateConversationList]) + const handleUnpinConversation = useCallback( + async (conversationId: string) => { + await unpinConversation(isInstalledApp, appId, conversationId); + notify({ type: "success", message: t("common.api.success") }); + handleUpdateConversationList(); + }, + [isInstalledApp, appId, notify, t, handleUpdateConversationList] + ); - const [conversationDeleting, setConversationDeleting] = useState(false) - const handleDeleteConversation = useCallback(async ( - conversationId: string, - { - onSuccess, - }: Callback, - ) => { - if (conversationDeleting) - return + const [conversationDeleting, setConversationDeleting] = useState(false); + const handleDeleteConversation = useCallback( + async (conversationId: string, { onSuccess }: Callback) => { + if (conversationDeleting) return; - try { - setConversationDeleting(true) - await delConversation(conversationId) - notify({ type: 'success', message: t('common.api.success') }) - onSuccess() - } - finally { - setConversationDeleting(false) - } + try { + setConversationDeleting(true); + await delConversation(conversationId); + notify({ type: "success", message: t("common.api.success") }); + onSuccess(); + } finally { + setConversationDeleting(false); + } - if (conversationId === currentConversationId) - handleNewConversation() + if (conversationId === currentConversationId) handleNewConversation(); - handleUpdateConversationList() - }, [isInstalledApp, appId, notify, t, handleUpdateConversationList, handleNewConversation, currentConversationId, conversationDeleting]) + handleUpdateConversationList(); + }, + [ + isInstalledApp, + appId, + notify, + t, + handleUpdateConversationList, + handleNewConversation, + currentConversationId, + conversationDeleting, + ] + ); - const [conversationRenaming, setConversationRenaming] = useState(false) - const handleRenameConversation = useCallback(async ( - conversationId: string, - newName: string, - { - onSuccess, - }: Callback, - ) => { - if (conversationRenaming) - return + const [conversationRenaming, setConversationRenaming] = useState(false); + const handleRenameConversation = useCallback( + async ( + conversationId: string, + newName: string, + { onSuccess }: Callback + ) => { + if (conversationRenaming) return; - if (!newName.trim()) { - notify({ - type: 'error', - message: t('common.chat.conversationNameCanNotEmpty'), - }) - return - } + if (!newName.trim()) { + notify({ + type: "error", + message: t("common.chat.conversationNameCanNotEmpty"), + }); + return; + } - setConversationRenaming(true) - try { - await renameConversation(conversationId, newName) + setConversationRenaming(true); + try { + await renameConversation(conversationId, newName); - notify({ - type: 'success', - message: t('common.actionMsg.modifiedSuccessfully'), - }) - setOriginConversationList(produce((draft) => { - const index = originConversationList.findIndex(item => item.id === conversationId) - const item = draft[index] + notify({ + type: "success", + message: t("common.actionMsg.modifiedSuccessfully"), + }); + setOriginConversationList( + produce((draft) => { + const index = originConversationList.findIndex( + (item) => item.id === conversationId + ); + const item = draft[index]; - draft[index] = { - ...item, - name: newName, - } - })) - onSuccess() - } - finally { - setConversationRenaming(false) - } - }, [isInstalledApp, appId, notify, t, conversationRenaming, originConversationList]) + draft[index] = { + ...item, + name: newName, + }; + }) + ); + onSuccess(); + } finally { + setConversationRenaming(false); + } + }, + [ + isInstalledApp, + appId, + notify, + t, + conversationRenaming, + originConversationList, + ] + ); - const handleNewConversationCompleted = useCallback((newConversationId: string) => { - setNewConversationId(newConversationId) - handleConversationIdInfoChange(newConversationId) - setShowNewConversationItemInList(false) - mutateAppConversationData() - }, [mutateAppConversationData, handleConversationIdInfoChange]) + const handleNewConversationCompleted = useCallback( + (newConversationId: string) => { + setNewConversationId(newConversationId); + handleConversationIdInfoChange(newConversationId); + setShowNewConversationItemInList(false); + mutateAppConversationData(); + }, + [mutateAppConversationData, handleConversationIdInfoChange] + ); - const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => { - await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, appId) - notify({ type: 'success', message: t('common.api.success') }) - }, [isInstalledApp, appId, t, notify]) + const [isFeedbackModalVisible, setIsFeedbackModalVisible] = useState(false); + const [currentMessageId, setCurrentMessageId] = useState(null); + const [currentFeedback, setCurrentFeedback] = useState(null); + const [ + currentConversationIdForFeedback, + setCurrentConversationIdForFeedback, + ] = useState(null); + + const handleFeedback = useCallback( + (messageId: string, feedback: Feedback, conversationId: string) => { + setCurrentMessageId(messageId); + setCurrentFeedback(feedback); + setCurrentConversationIdForFeedback(conversationId); + setIsFeedbackModalVisible(true); + }, + [] + ); + + const handleFeedbackSubmit = useCallback( + async (selectedOption: number | null, feedbackText: string) => { + console.log(currentMessageId); + console.log(currentFeedback); + + try { + // 构造请求参数 + const requestBody = { + operationType: selectedOption, // 0 或 1 + feedbackText, // 用户反馈建议 + conversationId: '89fba8a7-e4e2-4167-8b7c-b3c103797053', // 会话 ID + messageId: currentMessageId, // 消息 ID + username: "user_admin", // 用户名 + }; + + // 调用 Java 接口 + const javaResponse = await fetch( + `${process.env.NEXT_PUBLIC_BASE_API_URL}/api/conversation/feedback`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + } + ); + + // 调用原有的 updateFeedback 函数 + await updateFeedback( + { + url: `/messages/${currentMessageId}/feedbacks`, + body: { rating: currentFeedback.rating }, + }, + isInstalledApp, + appId + ); + + // 显示成功通知 + notify({ type: "success", message: t("common.api.success") }); + + // 关闭对话框 + setIsFeedbackModalVisible(false); + } catch (error) { + console.error("Error:", error); + notify({ type: "error", message: t("common.api.failed") }); + } + }, + [ + currentMessageId, + currentFeedback, + currentConversationIdForFeedback, + isInstalledApp, + appId, + notify, + t, + ] + ); return { appInfoError, @@ -427,7 +614,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { currentConversationItem, handleConversationIdInfoChange, appData, - appParams: appParams || {} as ChatConfig, + appParams: appParams || ({} as ChatConfig), appMeta, appPinnedConversationData, appConversationData, @@ -458,5 +645,12 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { chatShouldReloadKey, handleFeedback, currentChatInstanceRef, - } -} + feedbackModal: ( + setIsFeedbackModalVisible(false)} + /> + ), + }; +};