Commit f9032e3b by HoMeTown

feat: 处理box附件

parent 8adc1d19
...@@ -31,7 +31,7 @@ export const Chat: React.FC = () => { ...@@ -31,7 +31,7 @@ export const Chat: React.FC = () => {
const scrollableRef = useRef<HTMLDivElement | any>(null) const scrollableRef = useRef<HTMLDivElement | any>(null)
const position = useScroll(scrollableRef) const position = useScroll(scrollableRef)
const handleSubmitQuestion = async (question: string) => { const handleSubmitQuestion = async (question: string, productCode?: string) => {
const isNew = allItems.length <= 1 const isNew = allItems.length <= 1
dispatch(setIsAsking(true)) dispatch(setIsAsking(true))
// 添加用户提问的问题 // 添加用户提问的问题
...@@ -64,6 +64,7 @@ export const Chat: React.FC = () => { ...@@ -64,6 +64,7 @@ export const Chat: React.FC = () => {
question, question,
conversationId: id, conversationId: id,
stream: true, stream: true,
productCode,
}, },
(msg) => { (msg) => {
if (msg.type === 'DATA') { if (msg.type === 'DATA') {
......
import { Link } from '@nextui-org/react' import { Button, Link } from '@nextui-org/react'
import { motion } from 'framer-motion'
import type { Answer } from '@/types/chat' import type { Answer } from '@/types/chat'
import AnswerProDetailIcon from '@/assets/svg/answerProDetail.svg?react' import AnswerProDetailIcon from '@/assets/svg/answerProDetail.svg?react'
interface ChatAnswerAttachmentProps { interface ChatAnswerAttachmentProps {
answer: Answer answer: Answer
isLastAnswer?: boolean
onSubmitQuestion?: (question: string, productCode?: string) => void
} }
export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ answer }) => { export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ answer, isLastAnswer, onSubmitQuestion }) => {
const handleClickBoxItem = (produceName: string, productCode: string) => {
if (onSubmitQuestion) {
onSubmitQuestion(produceName, productCode)
}
}
return ( return (
<div className="attachmentList flex flex-col gap-[20px] mt-[20px]"> <div className="attachmentList flex flex-col gap-[20px]">
{answer.attachmentList && answer.attachmentList.map((attachment, index) => ( {answer.attachmentList && answer.attachmentList.map((attachment, index) => (
<div key={`${attachment.type}_${index}`}> <div key={`${attachment.type}_${index}`}>
{/* 附件:product-detail */} {/* 附件:product-detail */}
{attachment.type === 'product-detail' && ( {attachment.type === 'product-detail' && (
<div className="bg-[#29B6FD0A] text-[14px] text-primary py-[4px] px-[16px] w-fit flex items-center"> <div className="bg-[#29B6FD0A] text-[14px] text-primary py-[4px] px-[16px] w-fit flex items-center">
...@@ -43,6 +50,31 @@ export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ answ ...@@ -43,6 +50,31 @@ export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ answ
</div> </div>
)} )}
{/* 附件:选择 box */}
{
attachment.type === 'box' && attachment.content.productList.length !== 0 && (
<div>
<div className="mb-[12px]">{attachment.description}</div>
<ul
className="flex flex-col gap-[8px]"
>
{attachment.content.productList.map(product => (
// <div key={product.productCode}>{product.productName}</div>
<motion.li
key={product.productCode}
>
<Button onClick={() => handleClickBoxItem(product.productName, product.productCode)} isDisabled={!isLastAnswer} color="primary" variant="light" className="text-left bg-[#F7FCFF] w-full text-[#333] rounded-[23px] data-[hover=true]:bg-[#E5F6FF] data-[hover=true]:text-primary">
<div className="w-full text-nowrap text-ellipsis overflow-hidden">
<span className="ml-[8px]">{product.productName}</span>
</div>
</Button>
</motion.li>
))}
</ul>
</div>
)
}
</div> </div>
))} ))}
</div> </div>
......
...@@ -14,7 +14,7 @@ interface ChatAnswerBoxProps { ...@@ -14,7 +14,7 @@ interface ChatAnswerBoxProps {
showIndex: number showIndex: number
isLastAnswer: boolean isLastAnswer: boolean
index: number index: number
onSubmitQuestion: (question: string) => void onSubmitQuestion: (question: string, productCode?: string) => void
} }
export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, isLastAnswer, onSubmitQuestion }) => { export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, isLastAnswer, onSubmitQuestion }) => {
...@@ -55,15 +55,14 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, ...@@ -55,15 +55,14 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
<motion.div <motion.div
className="ml-[20px] bg-white rounded-[20px] box-border px-[24px] py-[20px]" className="ml-[20px] bg-white rounded-[20px] box-border px-[24px] py-[20px]"
> >
{item.answer && ( {(item.answer?.length || item.attachmentList?.length)
? (
<div className="content"> <div className="content">
{item.isShow && <ChatAnswerShower answer={item} />} {item.isShow && <ChatAnswerShower onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} answer={item} />}
{!item.isShow && <ChatAnswerParser isStopTyping={item.isStopTyping} onTyping={handleTyping} onComplate={() => handleComplate(item)} answer={item} />} {!item.isShow && <ChatAnswerParser onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} isStopTyping={item.isStopTyping} onTyping={handleTyping} onComplate={() => handleComplate(item)} answer={item} />}
</div> </div>
)} )
{!item.answer && ( : <Spinner size="sm" />}
<Spinner size="sm" />
)}
</motion.div> </motion.div>
<div className="w-[130px]"></div> <div className="w-[130px]"></div>
</div> </div>
......
...@@ -9,15 +9,18 @@ import { fetchTerminateQuestion } from '@/api/chat' ...@@ -9,15 +9,18 @@ import { fetchTerminateQuestion } from '@/api/chat'
interface ChatAnswerParserProps { interface ChatAnswerParserProps {
answer: Answer answer: Answer
isStopTyping: boolean | undefined isStopTyping: boolean | undefined
isLastAnswer: boolean
onTyping: () => void onTyping: () => void
onComplate: () => void onComplate: () => void
onSubmitQuestion: (question: string, productCode?: string) => void
} }
export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onTyping, onComplate, answer, isStopTyping }) => { export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ isLastAnswer, onTyping, onComplate, answer, isStopTyping, onSubmitQuestion }) => {
const formatAnswer = formatMarkdown(answer.answer || '') const formatAnswer = formatMarkdown(answer.answer || '')
const [displayedText, setDisplayedText] = useState('') const [displayedText, setDisplayedText] = useState('')
const [currentIndex, setCurrentIndex] = useState(0) const [currentIndex, setCurrentIndex] = useState(0)
const [isTyping, setIsTyping] = useState(false) const [isTyping, setIsTyping] = useState(false)
const [hideOperate, setHideOperate] = useState(false)
useEffect(() => { useEffect(() => {
if (isStopTyping) { if (isStopTyping) {
...@@ -57,17 +60,25 @@ export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onTyping, on ...@@ -57,17 +60,25 @@ export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onTyping, on
} }
}, [isStopTyping]) }, [isStopTyping])
useEffect(() => {
setHideOperate(answer.attachmentList.some(attachment => attachment.type === 'box'))
}, [answer.attachmentList])
return ( return (
<div className="answerParser"> <div className="answerParser">
{!!displayedText.length && (
<div className={answer.attachmentList?.length ? 'mb-[20px]' : ''}>
<MarkdownDetail> <MarkdownDetail>
{displayedText} {displayedText}
</MarkdownDetail> </MarkdownDetail>
</div>
)}
{!isTyping {!isTyping
&& answer.attachmentList && answer.attachmentList
&& answer.attachmentList?.length !== 0 && answer.attachmentList?.length !== 0
&& <ChatAnswerAttachment answer={answer} />} && <ChatAnswerAttachment isLastAnswer={isLastAnswer} onSubmitQuestion={onSubmitQuestion} answer={answer} />}
{!isTyping && <ChatAnswerOperate answer={answer} />} {!isTyping && !hideOperate && <ChatAnswerOperate answer={answer} />}
</div> </div>
) )
} }
...@@ -6,24 +6,23 @@ import { MarkdownDetail } from '@/components/MarkdownDetail' ...@@ -6,24 +6,23 @@ import { MarkdownDetail } from '@/components/MarkdownDetail'
interface ChatAnswerShowerProps { interface ChatAnswerShowerProps {
answer: Answer answer: Answer
isLastAnswer: boolean
onSubmitQuestion: (question: string) => void
} }
export const ChatAnswerShower: React.FC<ChatAnswerShowerProps> = ({ answer }) => { export const ChatAnswerShower: React.FC<ChatAnswerShowerProps> = ({ answer, isLastAnswer, onSubmitQuestion }) => {
const hideOperate = answer.attachmentList.some(attachment => attachment.type === 'box')
return ( return (
<div className="answerShower"> <div className="answerShower">
{answer.answer && (
<div className={answer.attachmentList?.length ? 'mb-[20px]' : ''}>
<MarkdownDetail> <MarkdownDetail>
{formatMarkdown(answer.answer || '')} {formatMarkdown(answer.answer || '')}
</MarkdownDetail> </MarkdownDetail>
{/* <ReactMarkdown </div>
rehypePlugins={[rehypeRaw, rehypeSanitize]} )}
remarkPlugins={[remarkGfm]} {answer.attachmentList && answer.attachmentList?.length !== 0 && <ChatAnswerAttachment onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} answer={answer} />}
className="markdown-content" {!hideOperate && <ChatAnswerOperate answer={answer} />}
>
{formatMarkdown(answer.answer || '')}
</ReactMarkdown> */}
{answer.attachmentList && answer.attachmentList?.length !== 0 && <ChatAnswerAttachment answer={answer} />}
<ChatAnswerOperate answer={answer} />
</div> </div>
) )
} }
...@@ -113,7 +113,7 @@ export const Collect: React.FC = () => { ...@@ -113,7 +113,7 @@ export const Collect: React.FC = () => {
<div className="mr-[20px]"> <div className="mr-[20px]">
<AIcon /> <AIcon />
</div> </div>
<div> <div className="flex-1">
<MarkdownDetail> <MarkdownDetail>
{formatMarkdown(item.answer || '')} {formatMarkdown(item.answer || '')}
</MarkdownDetail> </MarkdownDetail>
......
...@@ -3,8 +3,15 @@ interface AttachmentContentDoc { ...@@ -3,8 +3,15 @@ interface AttachmentContentDoc {
docName: string docName: string
} }
interface AttachmentContentProduct {
productName: string
productCode: string
}
interface AttachmentContent { interface AttachmentContent {
docList: AttachmentContentDoc[] docList: AttachmentContentDoc[]
description: string
productList: AttachmentContentProduct[]
} }
export interface Attachment { export interface Attachment {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment