Commit f9032e3b by HoMeTown

feat: 处理box附件

parent 8adc1d19
......@@ -31,7 +31,7 @@ export const Chat: React.FC = () => {
const scrollableRef = useRef<HTMLDivElement | any>(null)
const position = useScroll(scrollableRef)
const handleSubmitQuestion = async (question: string) => {
const handleSubmitQuestion = async (question: string, productCode?: string) => {
const isNew = allItems.length <= 1
dispatch(setIsAsking(true))
// 添加用户提问的问题
......@@ -64,6 +64,7 @@ export const Chat: React.FC = () => {
question,
conversationId: id,
stream: true,
productCode,
},
(msg) => {
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 AnswerProDetailIcon from '@/assets/svg/answerProDetail.svg?react'
interface ChatAnswerAttachmentProps {
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 (
<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) => (
<div key={`${attachment.type}_${index}`}>
{/* 附件:product-detail */}
{attachment.type === 'product-detail' && (
<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
</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>
......
......@@ -14,7 +14,7 @@ interface ChatAnswerBoxProps {
showIndex: number
isLastAnswer: boolean
index: number
onSubmitQuestion: (question: string) => void
onSubmitQuestion: (question: string, productCode?: string) => void
}
export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, isLastAnswer, onSubmitQuestion }) => {
......@@ -55,15 +55,14 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
<motion.div
className="ml-[20px] bg-white rounded-[20px] box-border px-[24px] py-[20px]"
>
{item.answer && (
<div className="content">
{item.isShow && <ChatAnswerShower answer={item} />}
{!item.isShow && <ChatAnswerParser isStopTyping={item.isStopTyping} onTyping={handleTyping} onComplate={() => handleComplate(item)} answer={item} />}
</div>
)}
{!item.answer && (
<Spinner size="sm" />
)}
{(item.answer?.length || item.attachmentList?.length)
? (
<div className="content">
{item.isShow && <ChatAnswerShower onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} answer={item} />}
{!item.isShow && <ChatAnswerParser onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} isStopTyping={item.isStopTyping} onTyping={handleTyping} onComplate={() => handleComplate(item)} answer={item} />}
</div>
)
: <Spinner size="sm" />}
</motion.div>
<div className="w-[130px]"></div>
</div>
......
......@@ -9,15 +9,18 @@ import { fetchTerminateQuestion } from '@/api/chat'
interface ChatAnswerParserProps {
answer: Answer
isStopTyping: boolean | undefined
isLastAnswer: boolean
onTyping: () => 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 [displayedText, setDisplayedText] = useState('')
const [currentIndex, setCurrentIndex] = useState(0)
const [isTyping, setIsTyping] = useState(false)
const [hideOperate, setHideOperate] = useState(false)
useEffect(() => {
if (isStopTyping) {
......@@ -57,17 +60,25 @@ export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onTyping, on
}
}, [isStopTyping])
useEffect(() => {
setHideOperate(answer.attachmentList.some(attachment => attachment.type === 'box'))
}, [answer.attachmentList])
return (
<div className="answerParser">
<MarkdownDetail>
{displayedText}
</MarkdownDetail>
{!!displayedText.length && (
<div className={answer.attachmentList?.length ? 'mb-[20px]' : ''}>
<MarkdownDetail>
{displayedText}
</MarkdownDetail>
</div>
)}
{!isTyping
&& answer.attachmentList
&& answer.attachmentList?.length !== 0
&& <ChatAnswerAttachment answer={answer} />}
&& <ChatAnswerAttachment isLastAnswer={isLastAnswer} onSubmitQuestion={onSubmitQuestion} answer={answer} />}
{!isTyping && <ChatAnswerOperate answer={answer} />}
{!isTyping && !hideOperate && <ChatAnswerOperate answer={answer} />}
</div>
)
}
......@@ -6,24 +6,23 @@ import { MarkdownDetail } from '@/components/MarkdownDetail'
interface ChatAnswerShowerProps {
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 (
<div className="answerShower">
<MarkdownDetail>
{formatMarkdown(answer.answer || '')}
</MarkdownDetail>
{/* <ReactMarkdown
rehypePlugins={[rehypeRaw, rehypeSanitize]}
remarkPlugins={[remarkGfm]}
className="markdown-content"
>
{formatMarkdown(answer.answer || '')}
</ReactMarkdown> */}
{answer.attachmentList && answer.attachmentList?.length !== 0 && <ChatAnswerAttachment answer={answer} />}
<ChatAnswerOperate answer={answer} />
{answer.answer && (
<div className={answer.attachmentList?.length ? 'mb-[20px]' : ''}>
<MarkdownDetail>
{formatMarkdown(answer.answer || '')}
</MarkdownDetail>
</div>
)}
{answer.attachmentList && answer.attachmentList?.length !== 0 && <ChatAnswerAttachment onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} answer={answer} />}
{!hideOperate && <ChatAnswerOperate answer={answer} />}
</div>
)
}
......@@ -113,7 +113,7 @@ export const Collect: React.FC = () => {
<div className="mr-[20px]">
<AIcon />
</div>
<div>
<div className="flex-1">
<MarkdownDetail>
{formatMarkdown(item.answer || '')}
</MarkdownDetail>
......
......@@ -3,8 +3,15 @@ interface AttachmentContentDoc {
docName: string
}
interface AttachmentContentProduct {
productName: string
productCode: string
}
interface AttachmentContent {
docList: AttachmentContentDoc[]
description: string
productList: AttachmentContentProduct[]
}
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