增加用户反馈功能

This commit is contained in:
张元坤 2025-07-10 09:37:12 +08:00
parent 8094d5e128
commit 374f5171cf

View File

@ -1,25 +1,15 @@
import { import { useCallback, useEffect, useMemo, useRef, useState } from "react";
useCallback, import { useTranslation } from "react-i18next";
useEffect, import useSWR from "swr";
useMemo, import { useLocalStorageState } from "ahooks";
useRef, import { produce } from "immer";
useState, import { message } from "antd";
} from 'react' import { client, getInfo } from "@/app/api/utils/common";
import { useTranslation } from 'react-i18next' import type { Callback, ChatConfig, ChatItem, Feedback } from "../types";
import useSWR from 'swr' import { CONVERSATION_ID_INFO } from "../constants";
import { useLocalStorageState } from 'ahooks' import { buildChatItemTree } from "../utils";
import { produce } from 'immer' import { addFileInfos, sortAgentSorts } from "@/app/components/tools/utils";
import type { import { getProcessedFilesFromResponse } from "@/app/components/base/file-uploader/utils";
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 { import {
delConversation, delConversation,
fetchAppInfo, fetchAppInfo,
@ -32,50 +22,66 @@ import {
renameConversation, renameConversation,
unpinConversation, unpinConversation,
updateFeedback, updateFeedback,
} from '@/service/index' } from "@/service/index";
import type { InstalledApp } from '@/models/explore' import type { InstalledApp } from "@/models/explore";
import type { import type { AppData, ConversationItem } from "@/models/share";
AppData, import { useToastContext } from "@/app/components/base/toast";
ConversationItem, import { changeLanguage } from "@/i18n/i18next-config";
} from '@/models/share' import { useAppFavicon } from "@/hooks/use-app-favicon";
import { useToastContext } from '@/app/components/base/toast' import { InputVarType } from "@/app/components/workflow/types";
import { changeLanguage } from '@/i18n/i18next-config' import { TransferMethod } from "@/types/app";
import { useAppFavicon } from '@/hooks/use-app-favicon' import FeedbackModal from "@/app/components/chat-with-history/FeedbackModal"; // 引入 FeedbackModal 组件
import { InputVarType } from '@/app/components/workflow/types'
import { TransferMethod } from '@/types/app'
function getFormattedChatList(messages: any[]) { function getFormattedChatList(messages: any[]) {
const newChatList: ChatItem[] = [] const newChatList: ChatItem[] = [];
messages.forEach((item) => { 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({ newChatList.push({
id: `question-${item.id}`, id: `question-${item.id}`,
content: item.query, content: item.query,
isAnswer: false, 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, 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) if (answerFiles.length > 0 && questionFiles.length > 0)
// 获取本地文件名 // 获取本地文件名
answerFiles[0].filename = questionFiles[0]?.filename answerFiles[0].filename = questionFiles[0]?.filename;
newChatList.push({ newChatList.push({
id: item.id, id: item.id,
content: item.answer, 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, feedback: item.feedback,
isAnswer: true, isAnswer: true,
citation: item.retriever_resources, 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}`, parentMessageId: `question-${item.id}`,
}) });
}) });
return newChatList return newChatList;
} }
export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]) const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]);
const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR(installedAppInfo ? null : 'appInfo', fetchAppInfo) const {
data: appInfo,
isLoading: appInfoLoading,
error: appInfoError,
} = useSWR(installedAppInfo ? null : "appInfo", fetchAppInfo);
useAppFavicon({ useAppFavicon({
enable: !installedAppInfo, enable: !installedAppInfo,
@ -83,11 +89,11 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
icon: appInfo?.site?.icon, icon: appInfo?.site?.icon,
icon_background: appInfo?.site?.icon_background, icon_background: appInfo?.site?.icon_background,
icon_url: appInfo?.site?.icon_url, icon_url: appInfo?.site?.icon_url,
}) });
const appData = useMemo(() => { const appData = useMemo(() => {
if (isInstalledApp) { if (isInstalledApp) {
const { id, app } = installedAppInfo! const { id, app } = installedAppInfo!;
return { return {
app_id: id, app_id: id,
site: { site: {
@ -97,326 +103,507 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
icon_background: app.icon_background, icon_background: app.icon_background,
icon_url: app.icon_url, icon_url: app.icon_url,
prompt_public: false, prompt_public: false,
copyright: '', copyright: "",
show_workflow_steps: true, show_workflow_steps: true,
use_icon_as_answer_icon: app.use_icon_as_answer_icon, use_icon_as_answer_icon: app.use_icon_as_answer_icon,
}, },
plan: 'basic', plan: "basic",
} as AppData } as AppData;
} }
return appInfo return appInfo;
}, [isInstalledApp, installedAppInfo, appInfo]) }, [isInstalledApp, installedAppInfo, appInfo]);
const appId = useMemo(() => appData?.app_id, [appData]) const appId = useMemo(() => appData?.app_id, [appData]);
useEffect(() => { useEffect(() => {
if (appData?.site?.default_language) if (appData?.site?.default_language)
changeLanguage(appData.site?.default_language) changeLanguage(appData.site?.default_language);
}, [appData]) }, [appData]);
const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, string>>(CONVERSATION_ID_INFO, { const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<
Record<string, string>
>(CONVERSATION_ID_INFO, {
defaultValue: {}, defaultValue: {},
}) });
const currentConversationId = useMemo(() => conversationIdInfo?.[appId || ''] || '', [appId, conversationIdInfo]) const currentConversationId = useMemo(
const handleConversationIdInfoChange = useCallback((changeConversationId: string) => { () => conversationIdInfo?.[appId || ""] || "",
if (appId) { [appId, conversationIdInfo]
setConversationIdInfo({ );
...conversationIdInfo, const handleConversationIdInfoChange = useCallback(
[appId || '']: changeConversationId, (changeConversationId: string) => {
}) if (appId) {
} setConversationIdInfo({
}, [appId, conversationIdInfo, setConversationIdInfo]) ...conversationIdInfo,
const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] = useState(true) [appId || ""]: changeConversationId,
const [newConversationId, setNewConversationId] = useState('') });
}
},
[appId, conversationIdInfo, setConversationIdInfo]
);
const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] =
useState(true);
const [newConversationId, setNewConversationId] = useState("");
const chatShouldReloadKey = useMemo(() => { const chatShouldReloadKey = useMemo(() => {
if (currentConversationId === newConversationId) if (currentConversationId === newConversationId) return "";
return ''
return currentConversationId return currentConversationId;
}, [currentConversationId, newConversationId]) }, [currentConversationId, newConversationId]);
const { data: appParams } = useSWR(['appParams', isInstalledApp, appId], () => fetchAppParams()) const { data: appParams } = useSWR(["appParams", isInstalledApp, appId], () =>
const { data: appMeta } = useSWR(['appMeta', isInstalledApp, appId], () => fetchAppMeta()) fetchAppParams()
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: appMeta } = useSWR(["appMeta", isInstalledApp, appId], () =>
const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR(chatShouldReloadKey ? ['appChatList', chatShouldReloadKey, isInstalledApp, appId] : null, () => fetchChatList(chatShouldReloadKey)) 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( const appPrevChatTree = useMemo(
() => (currentConversationId && appChatListData?.data.length) () =>
? buildChatItemTree(getFormattedChatList(appChatListData.data)) currentConversationId && appChatListData?.data.length
: [], ? buildChatItemTree(getFormattedChatList(appChatListData.data))
[appChatListData, currentConversationId], : [],
) [appChatListData, currentConversationId]
);
const [showNewConversationItemInList, setShowNewConversationItemInList] = useState(false) const [showNewConversationItemInList, setShowNewConversationItemInList] =
useState(false);
const pinnedConversationList = useMemo(() => { const pinnedConversationList = useMemo(() => {
return appPinnedConversationData?.data || [] return appPinnedConversationData?.data || [];
}, [appPinnedConversationData]) }, [appPinnedConversationData]);
const { t } = useTranslation() const { t } = useTranslation();
const newConversationInputsRef = useRef<Record<string, any>>({}) const newConversationInputsRef = useRef<Record<string, any>>({});
const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any>>({}) const [newConversationInputs, setNewConversationInputs] = useState<
const handleNewConversationInputsChange = useCallback((newInputs: Record<string, any>) => { Record<string, any>
newConversationInputsRef.current = newInputs >({});
setNewConversationInputs(newInputs) const handleNewConversationInputsChange = useCallback(
}, []) (newInputs: Record<string, any>) => {
newConversationInputsRef.current = newInputs;
setNewConversationInputs(newInputs);
},
[]
);
const inputsForms = useMemo(() => { const inputsForms = useMemo(() => {
return (appParams?.user_input_form || []).filter((item: any) => !item.external_data_tool).map((item: any) => { return (appParams?.user_input_form || [])
if (item.paragraph) { .filter((item: any) => !item.external_data_tool)
return { .map((item: any) => {
...item.paragraph, if (item.paragraph) {
type: 'paragraph', return {
...item.paragraph,
type: "paragraph",
};
} }
} if (item.number) {
if (item.number) { return {
return { ...item.number,
...item.number, type: "number",
type: 'number', };
} }
} if (item.select) {
if (item.select) { return {
return { ...item.select,
...item.select, type: "select",
type: 'select', };
} }
}
if (item['file-list']) { if (item["file-list"]) {
return { return {
...item['file-list'], ...item["file-list"],
type: 'file-list', type: "file-list",
};
} }
}
if (item.file) { if (item.file) {
return { return {
...item.file, ...item.file,
type: 'file', type: "file",
};
} }
}
return { return {
...item['text-input'], ...item["text-input"],
type: 'text-input', type: "text-input",
} };
}) });
}, [appParams]) }, [appParams]);
useEffect(() => { useEffect(() => {
const conversationInputs: Record<string, any> = {} const conversationInputs: Record<string, any> = {};
inputsForms.forEach((item: any) => { inputsForms.forEach((item: any) => {
conversationInputs[item.variable] = item.default || null conversationInputs[item.variable] = item.default || null;
}) });
handleNewConversationInputsChange(conversationInputs) handleNewConversationInputsChange(conversationInputs);
}, [handleNewConversationInputsChange, inputsForms]) }, [handleNewConversationInputsChange, inputsForms]);
const { data: newConversation } = useSWR(newConversationId ? [isInstalledApp, appId, newConversationId] : null, () => generationConversationName(newConversationId)) const { data: newConversation } = useSWR(
const [originConversationList, setOriginConversationList] = useState<ConversationItem[]>([]) newConversationId ? [isInstalledApp, appId, newConversationId] : null,
() => generationConversationName(newConversationId)
);
const [originConversationList, setOriginConversationList] = useState<
ConversationItem[]
>([]);
useEffect(() => { useEffect(() => {
if (appConversationData?.data && !appConversationDataLoading) if (appConversationData?.data && !appConversationDataLoading)
setOriginConversationList(appConversationData?.data) setOriginConversationList(appConversationData?.data);
}, [appConversationData, appConversationDataLoading]) }, [appConversationData, appConversationDataLoading]);
const conversationList = useMemo(() => { const conversationList = useMemo(() => {
const data = originConversationList.slice() const data = originConversationList.slice();
if (showNewConversationItemInList && data[0]?.id !== '') { if (showNewConversationItemInList && data[0]?.id !== "") {
data.unshift({ data.unshift({
id: '', id: "",
name: t('share.chat.newChatDefaultName'), name: t("share.chat.newChatDefaultName"),
inputs: {}, inputs: {},
introduction: '', introduction: "",
}) });
} }
return data return data;
}, [originConversationList, showNewConversationItemInList, t]) }, [originConversationList, showNewConversationItemInList, t]);
useEffect(() => { useEffect(() => {
if (newConversation) { if (newConversation) {
setOriginConversationList(produce((draft) => { setOriginConversationList(
const index = draft.findIndex(item => item.id === newConversation.id) produce((draft) => {
const index = draft.findIndex(
(item) => item.id === newConversation.id
);
if (index > -1) if (index > -1) draft[index] = newConversation;
draft[index] = newConversation else draft.unshift(newConversation);
else })
draft.unshift(newConversation) );
}))
} }
}, [newConversation]) }, [newConversation]);
const currentConversationItem = useMemo(() => { const currentConversationItem = useMemo(() => {
let conversationItem = conversationList.find(item => item.id === currentConversationId) let conversationItem = conversationList.find(
(item) => item.id === currentConversationId
);
if (!conversationItem && pinnedConversationList.length) if (!conversationItem && pinnedConversationList.length)
conversationItem = pinnedConversationList.find(item => item.id === currentConversationId) conversationItem = pinnedConversationList.find(
(item) => item.id === currentConversationId
);
return conversationItem return conversationItem;
}, [conversationList, currentConversationId, pinnedConversationList]) }, [conversationList, currentConversationId, pinnedConversationList]);
const { notify } = useToastContext() const { notify } = useToastContext();
const checkInputsRequired = useCallback((silent?: boolean) => { const checkInputsRequired = useCallback(
let hasEmptyInput = '' (silent?: boolean) => {
let fileIsUploading = false let hasEmptyInput = "";
const requiredVars = inputsForms.filter(({ required }) => required) let fileIsUploading = false;
if (requiredVars.length) { const requiredVars = inputsForms.filter(({ required }) => required);
requiredVars.forEach(({ variable, label, type }) => { if (requiredVars.length) {
if (hasEmptyInput) requiredVars.forEach(({ variable, label, type }) => {
return if (hasEmptyInput) return;
if (fileIsUploading) if (fileIsUploading) return;
return
if (!newConversationInputsRef.current[variable] && !silent) if (!newConversationInputsRef.current[variable] && !silent)
hasEmptyInput = label as string hasEmptyInput = label as string;
if ((type === InputVarType.singleFile || type === InputVarType.multiFiles) && newConversationInputsRef.current[variable] && !silent) { if (
const files = newConversationInputsRef.current[variable] (type === InputVarType.singleFile ||
if (Array.isArray(files)) type === InputVarType.multiFiles) &&
fileIsUploading = files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId) newConversationInputsRef.current[variable] &&
else !silent
fileIsUploading = files.transferMethod === TransferMethod.local_file && !files.uploadedId ) {
} 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) { if (hasEmptyInput) {
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }) }) notify({
return false type: "error",
} message: t("appDebug.errorMessage.valueOfVarRequired", {
key: hasEmptyInput,
}),
});
return false;
}
if (fileIsUploading) { if (fileIsUploading) {
notify({ type: 'info', message: t('appDebug.errorMessage.waitForFileUpload') }) notify({
return type: "info",
} message: t("appDebug.errorMessage.waitForFileUpload"),
});
return;
}
return true return true;
}, [inputsForms, notify, t]) },
[inputsForms, notify, t]
);
const handleStartChat = useCallback(() => { const handleStartChat = useCallback(() => {
if (checkInputsRequired()) { if (checkInputsRequired()) {
setShowConfigPanelBeforeChat(false) setShowConfigPanelBeforeChat(false);
setShowNewConversationItemInList(true) setShowNewConversationItemInList(true);
} }
}, [setShowConfigPanelBeforeChat, setShowNewConversationItemInList, checkInputsRequired]) }, [
const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ handleStop: () => { } }) setShowConfigPanelBeforeChat,
const handleChangeConversation = useCallback((conversationId: string) => { setShowNewConversationItemInList,
currentChatInstanceRef.current.handleStop() checkInputsRequired,
setNewConversationId('') ]);
handleConversationIdInfoChange(conversationId) const currentChatInstanceRef = useRef<{ handleStop: () => void }>({
handleStop: () => {},
});
const handleChangeConversation = useCallback(
(conversationId: string) => {
currentChatInstanceRef.current.handleStop();
setNewConversationId("");
handleConversationIdInfoChange(conversationId);
if (conversationId === '' && !checkInputsRequired(true)) if (conversationId === "" && !checkInputsRequired(true))
setShowConfigPanelBeforeChat(true) setShowConfigPanelBeforeChat(true);
else else setShowConfigPanelBeforeChat(false);
setShowConfigPanelBeforeChat(false) },
}, [handleConversationIdInfoChange, setShowConfigPanelBeforeChat, checkInputsRequired]) [
handleConversationIdInfoChange,
setShowConfigPanelBeforeChat,
checkInputsRequired,
]
);
const handleNewConversation = useCallback(() => { const handleNewConversation = useCallback(() => {
currentChatInstanceRef.current.handleStop() currentChatInstanceRef.current.handleStop();
setNewConversationId('') setNewConversationId("");
if (showNewConversationItemInList) { if (showNewConversationItemInList) {
handleChangeConversation('') handleChangeConversation("");
} else if (currentConversationId) {
handleConversationIdInfoChange("");
setShowConfigPanelBeforeChat(true);
setShowNewConversationItemInList(true);
handleNewConversationInputsChange({});
} }
else if (currentConversationId) { }, [
handleConversationIdInfoChange('') handleChangeConversation,
setShowConfigPanelBeforeChat(true) currentConversationId,
setShowNewConversationItemInList(true) handleConversationIdInfoChange,
handleNewConversationInputsChange({}) setShowConfigPanelBeforeChat,
} setShowNewConversationItemInList,
}, [handleChangeConversation, currentConversationId, handleConversationIdInfoChange, setShowConfigPanelBeforeChat, setShowNewConversationItemInList, showNewConversationItemInList, handleNewConversationInputsChange]) showNewConversationItemInList,
handleNewConversationInputsChange,
]);
const handleUpdateConversationList = useCallback(() => { const handleUpdateConversationList = useCallback(() => {
mutateAppConversationData() mutateAppConversationData();
mutateAppPinnedConversationData() mutateAppPinnedConversationData();
}, [mutateAppConversationData, mutateAppPinnedConversationData]) }, [mutateAppConversationData, mutateAppPinnedConversationData]);
const handlePinConversation = useCallback(async (conversationId: string) => { const handlePinConversation = useCallback(
await pinConversation(isInstalledApp, appId, conversationId) async (conversationId: string) => {
notify({ type: 'success', message: t('common.api.success') }) await pinConversation(isInstalledApp, appId, conversationId);
handleUpdateConversationList() notify({ type: "success", message: t("common.api.success") });
}, [isInstalledApp, appId, notify, t, handleUpdateConversationList]) handleUpdateConversationList();
},
[isInstalledApp, appId, notify, t, handleUpdateConversationList]
);
const handleUnpinConversation = useCallback(async (conversationId: string) => { const handleUnpinConversation = useCallback(
await unpinConversation(isInstalledApp, appId, conversationId) async (conversationId: string) => {
notify({ type: 'success', message: t('common.api.success') }) await unpinConversation(isInstalledApp, appId, conversationId);
handleUpdateConversationList() notify({ type: "success", message: t("common.api.success") });
}, [isInstalledApp, appId, notify, t, handleUpdateConversationList]) handleUpdateConversationList();
},
[isInstalledApp, appId, notify, t, handleUpdateConversationList]
);
const [conversationDeleting, setConversationDeleting] = useState(false) const [conversationDeleting, setConversationDeleting] = useState(false);
const handleDeleteConversation = useCallback(async ( const handleDeleteConversation = useCallback(
conversationId: string, async (conversationId: string, { onSuccess }: Callback) => {
{ if (conversationDeleting) return;
onSuccess,
}: Callback,
) => {
if (conversationDeleting)
return
try { try {
setConversationDeleting(true) setConversationDeleting(true);
await delConversation(conversationId) await delConversation(conversationId);
notify({ type: 'success', message: t('common.api.success') }) notify({ type: "success", message: t("common.api.success") });
onSuccess() onSuccess();
} } finally {
finally { setConversationDeleting(false);
setConversationDeleting(false) }
}
if (conversationId === currentConversationId) if (conversationId === currentConversationId) handleNewConversation();
handleNewConversation()
handleUpdateConversationList() handleUpdateConversationList();
}, [isInstalledApp, appId, notify, t, handleUpdateConversationList, handleNewConversation, currentConversationId, conversationDeleting]) },
[
isInstalledApp,
appId,
notify,
t,
handleUpdateConversationList,
handleNewConversation,
currentConversationId,
conversationDeleting,
]
);
const [conversationRenaming, setConversationRenaming] = useState(false) const [conversationRenaming, setConversationRenaming] = useState(false);
const handleRenameConversation = useCallback(async ( const handleRenameConversation = useCallback(
conversationId: string, async (
newName: string, conversationId: string,
{ newName: string,
onSuccess, { onSuccess }: Callback
}: Callback, ) => {
) => { if (conversationRenaming) return;
if (conversationRenaming)
return
if (!newName.trim()) { if (!newName.trim()) {
notify({ notify({
type: 'error', type: "error",
message: t('common.chat.conversationNameCanNotEmpty'), message: t("common.chat.conversationNameCanNotEmpty"),
}) });
return return;
} }
setConversationRenaming(true) setConversationRenaming(true);
try { try {
await renameConversation(conversationId, newName) await renameConversation(conversationId, newName);
notify({ notify({
type: 'success', type: "success",
message: t('common.actionMsg.modifiedSuccessfully'), message: t("common.actionMsg.modifiedSuccessfully"),
}) });
setOriginConversationList(produce((draft) => { setOriginConversationList(
const index = originConversationList.findIndex(item => item.id === conversationId) produce((draft) => {
const item = draft[index] const index = originConversationList.findIndex(
(item) => item.id === conversationId
);
const item = draft[index];
draft[index] = { draft[index] = {
...item, ...item,
name: newName, name: newName,
} };
})) })
onSuccess() );
} onSuccess();
finally { } finally {
setConversationRenaming(false) setConversationRenaming(false);
} }
}, [isInstalledApp, appId, notify, t, conversationRenaming, originConversationList]) },
[
isInstalledApp,
appId,
notify,
t,
conversationRenaming,
originConversationList,
]
);
const handleNewConversationCompleted = useCallback((newConversationId: string) => { const handleNewConversationCompleted = useCallback(
setNewConversationId(newConversationId) (newConversationId: string) => {
handleConversationIdInfoChange(newConversationId) setNewConversationId(newConversationId);
setShowNewConversationItemInList(false) handleConversationIdInfoChange(newConversationId);
mutateAppConversationData() setShowNewConversationItemInList(false);
}, [mutateAppConversationData, handleConversationIdInfoChange]) mutateAppConversationData();
},
[mutateAppConversationData, handleConversationIdInfoChange]
);
const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => { const [isFeedbackModalVisible, setIsFeedbackModalVisible] = useState(false);
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, appId) const [currentMessageId, setCurrentMessageId] = useState<string | null>(null);
notify({ type: 'success', message: t('common.api.success') }) const [currentFeedback, setCurrentFeedback] = useState<Feedback | null>(null);
}, [isInstalledApp, appId, t, notify]) const [
currentConversationIdForFeedback,
setCurrentConversationIdForFeedback,
] = useState<string | null>(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 { return {
appInfoError, appInfoError,
@ -427,7 +614,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
currentConversationItem, currentConversationItem,
handleConversationIdInfoChange, handleConversationIdInfoChange,
appData, appData,
appParams: appParams || {} as ChatConfig, appParams: appParams || ({} as ChatConfig),
appMeta, appMeta,
appPinnedConversationData, appPinnedConversationData,
appConversationData, appConversationData,
@ -458,5 +645,12 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
chatShouldReloadKey, chatShouldReloadKey,
handleFeedback, handleFeedback,
currentChatInstanceRef, currentChatInstanceRef,
} feedbackModal: (
} <FeedbackModal
open={isFeedbackModalVisible}
onOk={handleFeedbackSubmit}
onCancel={() => setIsFeedbackModalVisible(false)}
/>
),
};
};