This commit is contained in:
Archer
2023-12-11 15:12:14 +08:00
committed by GitHub
parent 84cf6b5658
commit d2d7eac9e0
105 changed files with 1091 additions and 801 deletions

View File

@@ -3,13 +3,15 @@ import { ModalBody, Textarea, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '../MyModal';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { userUpdateChatFeedback } from '@/web/core/chat/api';
import { updateChatUserFeedback } from '@/web/core/chat/api';
const FeedbackModal = ({
chatId,
chatItemId,
onSuccess,
onClose
}: {
chatId: string;
chatItemId: string;
onSuccess: (e: string) => void;
onClose: () => void;
@@ -19,14 +21,15 @@ const FeedbackModal = ({
const { mutate, isLoading } = useRequest({
mutationFn: async () => {
const val = ref.current?.value || 'N/A';
return userUpdateChatFeedback({
const val = ref.current?.value || t('core.chat.feedback.No Content');
return updateChatUserFeedback({
chatId,
chatItemId,
userFeedback: val
userBadFeedback: val
});
},
onSuccess() {
onSuccess(ref.current?.value || 'N/A');
onSuccess(ref.current?.value || t('core.chat.feedback.No Content'));
},
successToast: t('chat.Feedback Success'),
errorToast: t('chat.Feedback Failed')
@@ -40,11 +43,7 @@ const FeedbackModal = ({
title={t('chat.Feedback Modal')}
>
<ModalBody>
<Textarea
ref={ref}
rows={10}
placeholder={t('chat.Feedback Modal Tip') || 'chat.Feedback Modal Tip'}
/>
<Textarea ref={ref} rows={10} placeholder={t('chat.Feedback Modal Tip')} />
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={2} onClick={onClose}>

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useMemo, useState } from 'react';
import { ModalBody, Box, useTheme, Flex, Progress, Link, Image } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react';
import { ModalBody, Box, useTheme, Flex, Progress, Link } from '@chakra-ui/react';
import { getDatasetDataItemById } from '@/web/core/dataset/api';
import { useLoading } from '@/web/common/hooks/useLoading';
import { useToast } from '@/web/common/hooks/useToast';
@@ -11,7 +11,6 @@ import InputDataModal, {
} from '@/pages/dataset/detail/components/InputDataModal';
import MyModal from '../MyModal';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import MyTooltip from '../MyTooltip';
import NextLink from 'next/link';
@@ -19,21 +18,20 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
const QuoteModal = ({
rawSearch = [],
onClose
onClose,
isShare
}: {
rawSearch: SearchDataResponseItemType[];
onClose: () => void;
isShare: boolean;
}) => {
const { t } = useTranslation();
const { isPc } = useSystemStore();
const theme = useTheme();
const router = useRouter();
const { toast } = useToast();
const { setIsLoading, Loading } = useLoading();
const [editInputData, setEditInputData] = useState<InputDataType & { collectionId: string }>();
const isShare = useMemo(() => router.pathname === '/chat/share', [router.pathname]);
/**
* click edit, get new DataItem
*/

View File

@@ -1,40 +1,19 @@
import React from 'react';
import { ModalBody, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '../MyModal';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { userUpdateChatFeedback } from '@/web/core/chat/api';
const ReadFeedbackModal = ({
chatItemId,
content,
isMarked,
onMark,
onSuccess,
onCloseFeedback,
onClose
}: {
chatItemId: string;
content: string;
isMarked: boolean;
onMark: () => void;
onSuccess: () => void;
onCloseFeedback: () => void;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { mutate, isLoading } = useRequest({
mutationFn: async () => {
return userUpdateChatFeedback({
chatItemId,
userFeedback: undefined
});
},
onSuccess() {
onSuccess();
},
errorToast: t('chat.Feedback Update Failed')
});
return (
<MyModal
isOpen={true}
@@ -44,14 +23,9 @@ const ReadFeedbackModal = ({
>
<ModalBody>{content}</ModalBody>
<ModalFooter>
<Button mr={2} isLoading={isLoading} variant={'base'} onClick={mutate}>
{t('chat.Feedback Close')}
<Button mr={2} onClick={onCloseFeedback}>
{t('core.chat.feedback.Feedback Close')}
</Button>
{!isMarked && (
<Button mr={2} onClick={onMark}>
{t('chat.Feedback Mark')}
</Button>
)}
</ModalFooter>
</MyModal>
);

View File

@@ -13,12 +13,19 @@ import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import ChatBoxDivider from '@/components/core/chat/Divider';
import MyIcon from '../Icon';
import { getFileAndOpen } from '@/web/core/dataset/utils';
import { strIsLink } from '@fastgpt/global/common/string/tools';
const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false });
const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false });
const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr: false });
const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemResType[] }) => {
const ResponseTags = ({
responseData = [],
isShare
}: {
responseData?: ChatHistoryItemResType[];
isShare: boolean;
}) => {
const theme = useTheme();
const { isPc } = useSystemStore();
const { t } = useTranslation();
@@ -62,12 +69,13 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
.map((item) => ({
sourceName: item.sourceName,
sourceId: item.sourceId,
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName })
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName }),
canReadQuote: !isShare || strIsLink(item.sourceId)
})),
historyPreview: chatData?.historyPreview,
runningTime: +responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
};
}, [responseData]);
}, [isShare, responseData]);
const TagStyles: BoxProps = {
mr: 2,
@@ -97,7 +105,6 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
}}
overflow={'hidden'}
position={'relative'}
onClick={() => setQuoteModalData(quoteList)}
>
<Image src={item.icon} alt={''} mr={1} w={'12px'} />
<Box className="textEllipsis" flex={'1 0 0'}>
@@ -106,7 +113,7 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
<Box
className="controller"
display={['flex', 'none']}
display={'none'}
pr={2}
position={'absolute'}
right={0}
@@ -127,9 +134,13 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
_hover={{
color: 'green.600'
}}
onClick={(e) => {
e.stopPropagation();
setQuoteModalData(quoteList);
}}
/>
</MyTooltip>
{item.sourceId && (
{item.sourceId && item.canReadQuote && (
<MyTooltip label={t('core.chat.quote.Read Source')}>
<MyIcon
ml={4}
@@ -201,7 +212,11 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
</MyTooltip>
{!!quoteModalData && (
<QuoteModal rawSearch={quoteModalData} onClose={() => setQuoteModalData(undefined)} />
<QuoteModal
rawSearch={quoteModalData}
isShare={isShare}
onClose={() => setQuoteModalData(undefined)}
/>
)}
{!!contextModalData && (
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />

View File

@@ -43,7 +43,7 @@ import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
import { customAlphabet } from 'nanoid';
import { adminUpdateChatFeedback, userUpdateChatFeedback } from '@/web/core/chat/api';
import { updateChatAdminFeedback, updateChatUserFeedback } from '@/web/core/chat/api';
import type { AdminMarkType } from './SelectMarkCollection';
import MyIcon from '@/components/Icon';
@@ -51,7 +51,6 @@ import Avatar from '@/components/Avatar';
import Markdown, { CodeClassName } from '@/components/Markdown';
import MySelect from '@/components/Select';
import MyTooltip from '../MyTooltip';
import ChatBoxDivider from '@/components/core/chat/Divider';
import dynamic from 'next/dynamic';
const ResponseTags = dynamic(() => import('./ResponseTags'));
const FeedbackModal = dynamic(() => import('./FeedbackModal'));
@@ -64,7 +63,6 @@ import { splitGuideModule } from '@fastgpt/global/core/module/utils';
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
import MessageInput from './MessageInput';
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
@@ -104,6 +102,9 @@ type Props = {
userGuideModule?: ModuleItemType;
showFileSelector?: boolean;
active?: boolean; // can use
chatId?: string;
shareId?: string;
outLinkUid?: string;
onUpdateVariable?: (e: Record<string, any>) => void;
onStartChat?: (e: StartChatFnProps) => Promise<{
responseText: string;
@@ -124,6 +125,9 @@ const ChatBox = (
userGuideModule,
showFileSelector,
active = true,
chatId,
shareId,
outLinkUid,
onUpdateVariable,
onStartChat,
onDelMessage
@@ -133,7 +137,6 @@ const ChatBox = (
const ChatBoxRef = useRef<HTMLDivElement>(null);
const theme = useTheme();
const router = useRouter();
const { shareId } = router.query as { shareId?: string };
const { t } = useTranslation();
const { toast } = useToast();
const { isPc, setLoading } = useSystemStore();
@@ -147,10 +150,8 @@ const ChatBox = (
const [chatHistory, setChatHistory] = useState<ChatSiteItemType[]>([]);
const [feedbackId, setFeedbackId] = useState<string>();
const [readFeedbackData, setReadFeedbackData] = useState<{
// read feedback modal data
chatItemId: string;
content: string;
isMarked: boolean;
}>();
const [adminMarkData, setAdminMarkData] = useState<AdminMarkType & { chatItemId: string }>();
const [questionGuides, setQuestionGuide] = useState<string[]>([]);
@@ -653,7 +654,6 @@ const ChatBox = (
<Box mt={['6px', 2]} textAlign={'right'}>
<Card
className="markdown"
whiteSpace={'pre-wrap'}
{...MessageCardStyle}
bg={'myBlue.300'}
borderRadius={'8px 0 8px 8px'}
@@ -706,33 +706,90 @@ const ChatBox = (
}
: undefined
}
onReadFeedback={
onAddUserLike={(() => {
if (feedbackType !== FeedbackTypeEnum.user || item.userBadFeedback) {
return;
}
return () => {
if (!item.dataId || !chatId) return;
const isGoodFeedback = !!item.userGoodFeedback;
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
? {
...chatItem,
userGoodFeedback: isGoodFeedback ? undefined : 'yes'
}
: chatItem
)
);
try {
updateChatUserFeedback({
chatId,
chatItemId: item.dataId,
shareId,
outLinkUid,
userGoodFeedback: isGoodFeedback ? undefined : 'yes'
});
} catch (error) {}
};
})()}
onCloseUserLike={
feedbackType === FeedbackTypeEnum.admin
? () =>
setReadFeedbackData({
chatItemId: item.dataId || '',
content: item.userFeedback || '',
isMarked: !!item.adminFeedback
})
? () => {
if (!item.dataId || !chatId) return;
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
? { ...chatItem, userGoodFeedback: undefined }
: chatItem
)
);
updateChatUserFeedback({
chatId,
chatItemId: item.dataId,
userGoodFeedback: undefined
});
}
: undefined
}
onFeedback={
feedbackType === FeedbackTypeEnum.user
? item.userFeedback
? () => {
if (!item.dataId) return;
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
? { ...chatItem, userFeedback: undefined }
: chatItem
)
);
try {
userUpdateChatFeedback({ chatItemId: item.dataId });
} catch (error) {}
}
: () => setFeedbackId(item.dataId)
onAddUserDislike={(() => {
if (feedbackType !== FeedbackTypeEnum.user || item.userGoodFeedback) {
return;
}
if (item.userBadFeedback) {
return () => {
if (!item.dataId || !chatId) return;
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
? { ...chatItem, userBadFeedback: undefined }
: chatItem
)
);
try {
updateChatUserFeedback({
chatId,
chatItemId: item.dataId,
shareId,
outLinkUid
});
} catch (error) {}
};
} else {
return () => setFeedbackId(item.dataId);
}
})()}
onReadUserDislike={
feedbackType === FeedbackTypeEnum.admin
? () => {
if (!item.dataId) return;
setReadFeedbackData({
chatItemId: item.dataId || '',
content: item.userBadFeedback || ''
});
}
: undefined
}
/>
@@ -786,7 +843,7 @@ const ChatBox = (
isChatting={index === chatHistory.length - 1 && isChatting}
/>
<ResponseTags responseData={item.responseData} />
<ResponseTags responseData={item.responseData} isShare={!!shareId} />
{/* admin mark content */}
{showMarkIcon && item.adminFeedback && (
@@ -828,16 +885,16 @@ const ChatBox = (
showFileSelector={showFileSelector}
/>
) : null}
{/* user feedback modal */}
{!!feedbackId && (
{!!feedbackId && chatId && (
<FeedbackModal
chatId={chatId}
chatItemId={feedbackId}
onClose={() => setFeedbackId(undefined)}
onSuccess={(content: string) => {
setChatHistory((state) =>
state.map((item) =>
item.dataId === feedbackId ? { ...item, userFeedback: content } : item
item.dataId === feedbackId ? { ...item, userBadFeedback: content } : item
)
);
setFeedbackId(undefined);
@@ -847,27 +904,23 @@ const ChatBox = (
{/* admin read feedback modal */}
{!!readFeedbackData && (
<ReadFeedbackModal
{...readFeedbackData}
content={readFeedbackData.content}
onClose={() => setReadFeedbackData(undefined)}
onMark={() => {
const index = chatHistory.findIndex(
(item) => item.dataId === readFeedbackData.chatItemId
);
if (index === -1) return setReadFeedbackData(undefined);
setAdminMarkData({
chatItemId: readFeedbackData.chatItemId,
q: chatHistory[index - 1]?.value || '',
a: chatHistory[index]?.value || ''
});
}}
onSuccess={() => {
onCloseFeedback={() => {
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === readFeedbackData.chatItemId
? { ...chatItem, userFeedback: undefined }
? { ...chatItem, userBadFeedback: undefined }
: chatItem
)
);
try {
if (!chatId) return;
updateChatUserFeedback({
chatId,
chatItemId: readFeedbackData.chatItemId
});
} catch (error) {}
setReadFeedbackData(undefined);
}}
/>
@@ -879,7 +932,7 @@ const ChatBox = (
setAdminMarkData={(e) => setAdminMarkData({ ...e, chatItemId: adminMarkData.chatItemId })}
onClose={() => setAdminMarkData(undefined)}
onSuccess={(adminFeedback) => {
adminUpdateChatFeedback({
updateChatAdminFeedback({
chatItemId: adminMarkData.chatItemId,
...adminFeedback
});
@@ -895,15 +948,16 @@ const ChatBox = (
)
);
if (readFeedbackData) {
userUpdateChatFeedback({
if (readFeedbackData && chatId) {
updateChatUserFeedback({
chatId,
chatItemId: readFeedbackData.chatItemId,
userFeedback: undefined
userBadFeedback: undefined
});
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === readFeedbackData.chatItemId
? { ...chatItem, userFeedback: undefined }
? { ...chatItem, userBadFeedback: undefined }
: chatItem
)
);
@@ -1054,11 +1108,13 @@ function ChatController({
display,
showVoiceIcon,
ttsConfig,
onReadFeedback,
onReadUserDislike,
onCloseUserLike,
onMark,
onRetry,
onDelete,
onFeedback,
onAddUserDislike,
onAddUserLike,
ml,
mr
}: {
@@ -1069,8 +1125,10 @@ function ChatController({
onRetry?: () => void;
onDelete?: () => void;
onMark?: () => void;
onReadFeedback?: () => void;
onFeedback?: () => void;
onReadUserDislike?: () => void;
onCloseUserLike?: () => void;
onAddUserLike?: () => void;
onAddUserDislike?: () => void;
} & FlexProps) {
const theme = useTheme();
const { t } = useTranslation();
@@ -1184,39 +1242,62 @@ function ChatController({
/>
</MyTooltip>
)}
{!!onReadFeedback && (
<MyTooltip label={t('chat.Read User Feedback')}>
{!!onCloseUserLike && chat.userGoodFeedback && (
<MyTooltip label={t('core.chat.feedback.Close User Like')}>
<MyIcon
{...controlIconStyle}
color={'white'}
bg={'green.500'}
fontWeight={'bold'}
name={'core/chat/feedback/goodLight'}
onClick={onCloseUserLike}
/>
</MyTooltip>
)}
{!!onReadUserDislike && chat.userBadFeedback && (
<MyTooltip label={t('core.chat.feedback.Read User dislike')}>
<MyIcon
display={chat.userFeedback ? 'block' : 'none'}
{...controlIconStyle}
color={'white'}
bg={'#FC9663'}
fontWeight={'bold'}
name={'core/chat/feedback/badLight'}
onClick={onReadFeedback}
onClick={onReadUserDislike}
/>
</MyTooltip>
)}
{!!onFeedback && (
<MyTooltip
label={chat.userFeedback ? `取消反馈。\n您当前反馈内容为:\n${chat.userFeedback}` : '反馈'}
>
<MyIcon
{...controlIconStyle}
{...(!!chat.userFeedback
? {
color: 'white',
bg: '#FC9663',
fontWeight: 'bold',
onClick: onFeedback
}
: {
_hover: { color: '#FB7C3C' },
onClick: onFeedback
})}
name={'core/chat/feedback/badLight'}
/>
</MyTooltip>
{!!onAddUserLike && (
<MyIcon
{...controlIconStyle}
{...(!!chat.userGoodFeedback
? {
color: 'white',
bg: 'green.500',
fontWeight: 'bold'
}
: {
_hover: { color: 'green.600' }
})}
name={'core/chat/feedback/goodLight'}
onClick={onAddUserLike}
/>
)}
{!!onAddUserDislike && (
<MyIcon
{...controlIconStyle}
{...(!!chat.userBadFeedback
? {
color: 'white',
bg: '#FC9663',
fontWeight: 'bold',
onClick: onAddUserDislike
}
: {
_hover: { color: '#FB7C3C' },
onClick: onAddUserDislike
})}
name={'core/chat/feedback/badLight'}
/>
)}
</Flex>
);