增加用户反馈功能

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

View File

@ -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<Record<string, string>>(CONVERSATION_ID_INFO, {
const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<
Record<string, string>
>(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<Record<string, any>>({})
const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any>>({})
const handleNewConversationInputsChange = useCallback((newInputs: Record<string, any>) => {
newConversationInputsRef.current = newInputs
setNewConversationInputs(newInputs)
}, [])
return appPinnedConversationData?.data || [];
}, [appPinnedConversationData]);
const { t } = useTranslation();
const newConversationInputsRef = useRef<Record<string, any>>({});
const [newConversationInputs, setNewConversationInputs] = useState<
Record<string, any>
>({});
const handleNewConversationInputsChange = useCallback(
(newInputs: Record<string, any>) => {
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<string, any> = {}
const conversationInputs: Record<string, any> = {};
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<ConversationItem[]>([])
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<string | null>(null);
const [currentFeedback, setCurrentFeedback] = useState<Feedback | null>(null);
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 {
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: (
<FeedbackModal
open={isFeedbackModalVisible}
onOk={handleFeedbackSubmit}
onCancel={() => setIsFeedbackModalVisible(false)}
/>
),
};
};