Commit f682c8e8 by HoMeTown

feat: 停止生成

parent 9cce45e0
...@@ -126,3 +126,12 @@ export function fetchQueryRecommendQuestion(conversationId: string, recordId: st ...@@ -126,3 +126,12 @@ export function fetchQueryRecommendQuestion(conversationId: string, recordId: st
recordId, recordId,
}) })
} }
/**
* 停止问答
* @param params
* @returns
*/
export function fetchTerminateQuestion(params: any) {
return http.post('/conversation/api/conversation/mobile/v1/terminate_question', params)
}
<?xml version="1.0" encoding="UTF-8"?>
<svg width="131px" height="59px" viewBox="0 0 131 59" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组</title>
<g id="晓得---PC端页面-草稿" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="晓得-PC端---工具页" transform="translate(-930.000000, -183.000000)" fill="#29B6FD" fill-rule="nonzero">
<g id="编组-4" transform="translate(751.000000, 140.000000)">
<g id="工具" transform="translate(179.000000, 43.000000)">
<path d="M57.8,54.136 L57.8,49.56 C57.8,47.350861 56.009139,45.56 53.8,45.56 L33.416,45.56 L33.416,45.56 L33.416,11.576 L54.536,11.576 L54.536,7 C54.536,4.790861 52.745139,3 50.536,3 L3.464,3 L3.464,3 L3.464,11.576 L24.584,11.576 L24.584,45.56 L0.2,45.56 L0.2,54.136 L57.8,54.136 Z M127.88,50.232 C121.096,49.5066667 113.501333,47.9066667 105.096,45.432 L130.888,45.432 L130.888,42.136 C130.888,39.926861 129.097139,38.136 126.888,38.136 L124.04,38.136 L124.04,38.136 L124.04,4.312 C124.04,2.102861 122.249139,0.312 120.04,0.312 L77.96,0.312 L77.96,0.312 L77.96,38.136 L71.112,38.136 L71.112,45.432 L96.904,45.432 C88.4986667,47.9066667 80.904,49.5066667 74.12,50.232 L74.12,58.04 C82.6106667,56.9733333 90.3546667,55.352 97.352,53.176 L97.352,45.432 L104.648,45.432 L104.648,53.176 C111.645333,55.352 119.389333,56.9733333 127.88,58.04 L127.88,50.232 Z M115.72,10.36 L86.28,10.36 L86.28,7.544 L115.72,7.544 L115.72,10.36 Z M115.72,19.64 L86.28,19.64 L86.28,16.824 L115.72,16.824 L115.72,19.64 Z M115.72,28.856 L86.28,28.856 L86.28,26.104 L115.72,26.104 L115.72,28.856 Z M115.72,38.136 L86.28,38.136 L86.28,35.384 L115.72,35.384 L115.72,38.136 Z"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
import { Avatar, Spinner } from '@nextui-org/react' import { Avatar, Button, Spinner } from '@nextui-org/react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useState } from 'react' import { useEffect, useState } from 'react'
import { ChatAnswerShower } from './ChatAnswerShower' import { ChatAnswerShower } from './ChatAnswerShower'
import { ChatAnswerParser } from './ChatAnswerParser' import { ChatAnswerParser } from './ChatAnswerParser'
import { ChatAnswerRecommend } from './ChatAnswerRecommend' import { ChatAnswerRecommend } from './ChatAnswerRecommend'
...@@ -20,16 +20,33 @@ interface ChatAnswerBoxProps { ...@@ -20,16 +20,33 @@ interface ChatAnswerBoxProps {
export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, isLastAnswer, onSubmitQuestion }) => { export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, isLastAnswer, onSubmitQuestion }) => {
const [isShowRecommend, setIsShowRecommend] = useState(false) const [isShowRecommend, setIsShowRecommend] = useState(false)
const [recommendUseAnswer, setRecommendUseAnswer] = useState<Answer>() const [recommendUseAnswer, setRecommendUseAnswer] = useState<Answer>()
const [innerRecord, setInnerRecord] = useState<ChatRecord>(record)
const [isTyping, setIsTyping] = useState(false)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const handleTyping = () => {
setIsTyping(true)
}
const handleComplate = (answer: Answer) => { const handleComplate = (answer: Answer) => {
setIsShowRecommend(true) setIsShowRecommend(true)
setRecommendUseAnswer(answer) setRecommendUseAnswer(answer)
dispatch(setIsAsking(false)) dispatch(setIsAsking(false))
setIsTyping(false)
}
const handleStopTyping = () => {
const _innerRecord = JSON.parse(JSON.stringify(innerRecord))
_innerRecord.answerList[showIndex].isStopTyping = true
setInnerRecord(_innerRecord)
} }
useEffect(() => {
setInnerRecord(record)
}, [record])
return ( return (
<div> <div>
{record.answerList.map((item, index) => { {innerRecord.answerList.map((item, index) => {
return ( return (
index === showIndex && ( index === showIndex && (
<div className="chatItemBotContainer w-full" key={`${item.recordId}-${index}`}> <div className="chatItemBotContainer w-full" key={`${item.recordId}-${index}`}>
...@@ -41,7 +58,7 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, ...@@ -41,7 +58,7 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
{item.answer && ( {item.answer && (
<div className="content"> <div className="content">
{item.isShow && <ChatAnswerShower answer={item} />} {item.isShow && <ChatAnswerShower answer={item} />}
{!item.isShow && <ChatAnswerParser onComplate={() => handleComplate(item)} answer={item} />} {!item.isShow && <ChatAnswerParser isStopTyping={item.isStopTyping} onTyping={handleTyping} onComplate={() => handleComplate(item)} answer={item} />}
</div> </div>
)} )}
{!item.answer && ( {!item.answer && (
...@@ -50,6 +67,14 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, ...@@ -50,6 +67,14 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
</motion.div> </motion.div>
<div className="w-[130px]"></div> <div className="w-[130px]"></div>
</div> </div>
{isTyping && (
<div className="pl-[62px] mt-[12px]">
<Button onClick={handleStopTyping} color="primary" variant="bordered">
停止生成
</Button>
</div>
)}
{isLastAnswer {isLastAnswer
&& isShowRecommend && isShowRecommend
&& recommendUseAnswer && recommendUseAnswer
......
...@@ -4,25 +4,34 @@ import { ChatAnswerOperate } from './ChatAnswerOperate' ...@@ -4,25 +4,34 @@ import { ChatAnswerOperate } from './ChatAnswerOperate'
import { formatMarkdown } from './markdownFormatter' import { formatMarkdown } from './markdownFormatter'
import type { Answer } from '@/types/chat' import type { Answer } from '@/types/chat'
import { MarkdownDetail } from '@/components/MarkdownDetail' import { MarkdownDetail } from '@/components/MarkdownDetail'
import { fetchTerminateQuestion } from '@/api/chat'
interface ChatAnswerParserProps { interface ChatAnswerParserProps {
answer: Answer answer: Answer
isStopTyping: boolean | undefined
onTyping: () => void
onComplate: () => void onComplate: () => void
} }
export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onComplate, answer }) => { export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onTyping, onComplate, answer, isStopTyping }) => {
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)
useEffect(() => { useEffect(() => {
setIsTyping(true) if (isStopTyping) {
return
}
if (!isTyping) {
onTyping()
setIsTyping(true)
}
if (currentIndex < formatAnswer.length) { if (currentIndex < formatAnswer.length) {
const timer = setTimeout(() => { const timer = setTimeout(() => {
setDisplayedText(formatAnswer.slice(0, currentIndex + 1)) setDisplayedText(formatAnswer.slice(0, currentIndex + 1))
setCurrentIndex(prevIndex => prevIndex + 1) setCurrentIndex(prevIndex => prevIndex + 1)
}, 10) // 调整此值以改变打字速度 }, 100) // 调整此值以改变打字速度
return () => clearTimeout(timer) return () => clearTimeout(timer)
} }
...@@ -32,6 +41,20 @@ export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onComplate, ...@@ -32,6 +41,20 @@ export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ onComplate,
} }
}, [answer, currentIndex]) }, [answer, currentIndex])
const handleStopTyping = async () => {
const res = await fetchTerminateQuestion(answer)
if (res.ok) {
setIsTyping(false)
onComplate()
}
}
useEffect(() => {
if (isStopTyping) {
handleStopTyping()
}
}, [isStopTyping])
return ( return (
<div className="answerParser"> <div className="answerParser">
<MarkdownDetail> <MarkdownDetail>
......
import type React from 'react' import type React from 'react'
import TextLogo from '@/assets/svg/textLogo.svg?react' import TextLogo from '@/assets/svg/toolsLogo.svg?react'
import { GradientsBall } from '@/components/GradientsBall/GradientsBall' import { GradientsBall } from '@/components/GradientsBall/GradientsBall'
export const Slogan: React.FC = () => { export const Slogan: React.FC = () => {
......
...@@ -15,6 +15,7 @@ export interface Attachment { ...@@ -15,6 +15,7 @@ export interface Attachment {
} }
export interface Answer { export interface Answer {
isStopTyping?: boolean
isShow: boolean isShow: boolean
answer: string answer: string
collectionFlag?: boolean collectionFlag?: boolean
......
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