Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
sdream-ai-fe
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
侯明涛
sdream-ai-fe
Commits
e71e99cf
Commit
e71e99cf
authored
Jan 23, 2025
by
HoMeTown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 最大对话轮数处理
parent
b065bf04
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
101 additions
and
19 deletions
+101
-19
rsbuild.config.ts
+1
-0
src/api/chat.ts
+6
-2
src/pages/Chat/Chat.tsx
+36
-2
src/pages/Chat/components/ChatItem/ChatAnswerAttchment.tsx
+2
-2
src/pages/Chat/components/ChatItem/ChatAnswerBox.tsx
+1
-1
src/pages/Chat/components/ChatItem/ChatAnswerParser.tsx
+39
-5
src/pages/Chat/components/ChatItem/ChatAnswerRecommend.tsx
+5
-2
src/pages/Chat/components/ChatItem/ChatAnswerShower.tsx
+3
-3
src/pages/Collect/Collect.tsx
+1
-1
src/types/chat.ts
+7
-1
No files found.
rsbuild.config.ts
View file @
e71e99cf
...
...
@@ -92,6 +92,7 @@ export default defineConfig({
pathRewrite
:
{
[
`^
${
apiUrl
}
`
]:
''
},
},
},
compress
:
false
,
},
security
:
{
// 与 Web 安全有关的选项
...
...
src/api/chat.ts
View file @
e71e99cf
...
...
@@ -63,8 +63,12 @@ export function fetchStreamResponse(url: string, body: Record<string, any>, onMe
method
:
'POST'
,
body
:
JSON
.
stringify
(
body
),
})
.
then
(
response
=>
response
.
body
?.
getReader
())
.
then
(
reader
=>
processMessage
(
reader
))
.
then
((
response
)
=>
{
return
response
.
body
?.
getReader
()
})
.
then
((
reader
)
=>
{
return
processMessage
(
reader
)
})
.
catch
(
error
=>
onMessage
({
type
:
'ERROR'
,
content
:
error
,
...
...
src/pages/Chat/Chat.tsx
View file @
e71e99cf
...
...
@@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom'
import
{
Button
}
from
'@nextui-org/react'
import
{
motion
}
from
'framer-motion'
import
{
useScroll
}
from
'ahooks'
import
{
toast
}
from
'react-hot-toast'
import
styles
from
'./Chat.module.less'
import
{
ChatSlogan
}
from
'./components/ChatSlogan'
import
{
ChatMaskBar
}
from
'./components/ChatMaskBar'
...
...
@@ -15,12 +16,13 @@ import { ChatEditor } from '@/components/ChatEditor'
import
type
{
ChatRecord
}
from
'@/types/chat'
import
{
fetchUserQaRecordPage
}
from
'@/api/conversation'
import
{
fetchCheckTokenApi
,
fetchStreamResponse
}
from
'@/api/chat'
import
{
clearShouldSendQuestion
,
fetchConversations
}
from
'@/store/conversationSlice'
import
{
clearShouldSendQuestion
,
createConversation
,
fetchConversations
}
from
'@/store/conversationSlice'
import
type
{
RootState
}
from
'@/store'
import
{
useAppDispatch
,
useAppSelector
}
from
'@/store/hook'
import
ScrollBtoIcon
from
'@/assets/svg/scrollBto.svg?react'
import
{
setIsAsking
}
from
'@/store/chatSlice'
import
SdreamLoading
from
'@/components/SdreamLoading'
import
AddNewChat
from
'@/assets/svg/addNewChat.svg?react'
export
const
Chat
:
React
.
FC
=
()
=>
{
let
ignore
=
false
...
...
@@ -68,7 +70,7 @@ export const Chat: React.FC = () => {
productCode
,
},
(
msg
)
=>
{
if
(
msg
.
type
===
'DATA
'
)
{
if
(
msg
?.
type
===
'DATA'
&&
msg
?.
content
?.
code
===
'00000000
'
)
{
setAllItems
((
prevItems
)
=>
{
const
newItems
=
[...
prevItems
]
// 创建数组的浅拷贝
const
lastIndex
=
newItems
.
length
-
1
...
...
@@ -89,6 +91,38 @@ export const Chat: React.FC = () => {
return
newItems
})
}
if
(
msg
?.
type
===
'DATA'
&&
msg
?.
content
?.
code
===
'01010005'
)
{
// showToast('已超过会话支持的轮数上线!', 'error')
toast
(
t
=>
(
<
div
className=
"flex items-center"
>
<
p
className=
"text-[14px]"
>
⚠️ 超过最大轮数上线!请新建对话 👉🏻
</
p
>
<
Button
color=
"primary"
size=
"sm"
variant=
"light"
isIconOnly
onClick=
{
()
=>
{
dispatch
(
createConversation
({
conversationData
:
{},
shouldNavigate
:
true
,
shouldSendQuestion
:
''
,
}))
toast
.
dismiss
(
t
.
id
)
}
}
>
<
AddNewChat
/>
</
Button
>
</
div
>
),
{
position
:
'bottom-center'
,
duration
:
0
,
style
:
{
// transform: 'translateY(-400px)',
marginBottom
:
'120px'
,
},
})
return
}
if
(
msg
.
type
===
'END'
)
{
if
(
isNew
)
{
setTimeout
(()
=>
{
...
...
src/pages/Chat/components/ChatItem/ChatAnswerAttchment.tsx
View file @
e71e99cf
...
...
@@ -38,8 +38,8 @@ export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ from
}
}
return
(
<
div
className=
"
attachment
List flex flex-col gap-[20px]"
>
{
answer
.
attachmentList
&&
answer
.
attachment
List
.
map
((
attachment
,
index
)
=>
{
<
div
className=
"
card
List flex flex-col gap-[20px]"
>
{
answer
.
cardList
&&
answer
.
card
List
.
map
((
attachment
,
index
)
=>
{
if
(
attachment
?.
type
)
{
return
(
<
div
key=
{
`${attachment.type}_${index}`
}
>
...
...
src/pages/Chat/components/ChatItem/ChatAnswerBox.tsx
View file @
e71e99cf
...
...
@@ -56,7 +56,7 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
<
div
className=
"ml-[20px] bg-white rounded-[20px] box-border px-[16px] py-[12px] sm:px-[24px] sm:py-[20px]"
>
{
(
item
.
answer
?.
length
||
item
.
attachment
List
?.
length
)
{
(
item
.
answer
?.
length
||
item
.
card
List
?.
length
)
?
(
<
div
className=
"content"
>
{
item
.
isShow
&&
<
ChatAnswerShower
onSubmitQuestion=
{
onSubmitQuestion
}
isLastAnswer=
{
isLastAnswer
}
answer=
{
item
}
/>
}
...
...
src/pages/Chat/components/ChatItem/ChatAnswerParser.tsx
View file @
e71e99cf
import
React
,
{
useEffect
,
useState
}
from
'react'
import
{
Chip
}
from
'@nextui-org/react'
import
{
ChatAnswerAttachment
}
from
'./ChatAnswerAttchment'
import
{
ChatAnswerOperate
}
from
'./ChatAnswerOperate'
import
{
formatMarkdown
}
from
'./markdownFormatter'
...
...
@@ -16,6 +17,24 @@ interface ChatAnswerParserProps {
onSubmitQuestion
:
(
question
:
string
,
productCode
?:
string
)
=>
void
}
function
CheckIcon
({
size
,
height
,
width
,
...
props
})
{
return
(
<
svg
fill=
"none"
height=
{
size
||
height
||
24
}
viewBox=
"0 0 24 24"
width=
{
size
||
width
||
24
}
xmlns=
"http://www.w3.org/2000/svg"
{
...
props
}
>
<
path
d=
"M12 2C6.49 2 2 6.49 2 12C2 17.51 6.49 22 12 22C17.51 22 22 17.51 22 12C22 6.49 17.51 2 12 2ZM16.78 9.7L11.11 15.37C10.97 15.51 10.78 15.59 10.58 15.59C10.38 15.59 10.19 15.51 10.05 15.37L7.22 12.54C6.93 12.25 6.93 11.77 7.22 11.48C7.51 11.19 7.99 11.19 8.28 11.48L10.58 13.78L15.72 8.64C16.01 8.35 16.49 8.35 16.78 8.64C17.07 8.93 17.07 9.4 16.78 9.7Z"
fill=
"currentColor"
/>
</
svg
>
)
}
export
const
ChatAnswerParser
:
React
.
FC
<
ChatAnswerParserProps
>
=
({
isLastAnswer
,
onTyping
,
onComplate
,
answer
,
isStopTyping
,
onSubmitQuestion
})
=>
{
const
formatAnswer
=
formatMarkdown
(
answer
.
answer
||
''
)
const
[
displayedText
,
setDisplayedText
]
=
useState
(
''
)
...
...
@@ -124,21 +143,36 @@ export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ isLastAnswer
},
[
isStopTyping
])
useEffect
(()
=>
{
setHideOperate
((
answer
.
attachment
List
||
[]).
some
(
attachment
=>
attachment
?.
type
===
'box'
||
attachment
?.
type
?.
includes
(
'card-'
)))
},
[
answer
.
attachment
List
])
setHideOperate
((
answer
.
card
List
||
[]).
some
(
attachment
=>
attachment
?.
type
===
'box'
||
attachment
?.
type
?.
includes
(
'card-'
)))
},
[
answer
.
card
List
])
return
(
<
div
className=
"answerParser"
>
<
div
className=
"mb-[8px]"
>
{
/* <Chip color="primary" className="mb-[12px]">{answer.step?.message}</Chip> */
}
{
answer
.
step
?.
step
===
'answering'
&&
(
<
Chip
color=
"warning"
variant=
"flat"
>
{
answer
.
step
?.
message
}
</
Chip
>
)
}
{
answer
.
step
?.
step
===
'finished'
&&
(
<
Chip
color=
"primary"
variant=
"flat"
startContent=
{
<
CheckIcon
size=
{
18
}
/>
}
>
{
answer
.
step
?.
message
}
</
Chip
>
)
}
</
div
>
{
!!
displayedText
.
length
&&
(
<
div
className=
{
answer
.
attachment
List
?.
length
?
'mb-[20px]'
:
''
}
>
<
div
className=
{
answer
.
card
List
?.
length
?
'mb-[20px]'
:
''
}
>
<
MarkdownDetail
>
{
displayedText
}
</
MarkdownDetail
>
</
div
>
)
}
{
!
isTyping
&&
answer
.
attachment
List
&&
answer
.
attachment
List
?.
length
!==
0
&&
answer
.
card
List
&&
answer
.
card
List
?.
length
!==
0
&&
<
ChatAnswerAttachment
fromParser
isLastAnswer=
{
isLastAnswer
}
onSubmitQuestion=
{
onSubmitQuestion
}
answer=
{
answer
}
/>
}
{
!
isTyping
&&
!
hideOperate
&&
<
ChatAnswerOperate
answer=
{
answer
}
/>
}
...
...
src/pages/Chat/components/ChatItem/ChatAnswerRecommend.tsx
View file @
e71e99cf
...
...
@@ -11,11 +11,14 @@ interface ChatAnswerRecommendProps {
export
const
ChatAnswerRecommend
:
React
.
FC
<
ChatAnswerRecommendProps
>
=
({
answer
,
onSubmitQuestion
})
=>
{
let
isGet
=
false
const
[
questionList
,
setQuestionList
]
=
useState
<
string
[]
>
([])
const
[
loading
,
setLoading
]
=
useState
<
boolean
>
(
false
)
const
getAnswerRecommend
=
async
()
=>
{
setLoading
(
true
)
const
res
=
await
fetchQueryRecommendQuestion
(
answer
.
conversationId
||
''
,
answer
.
recordId
||
''
)
if
(
res
.
ok
)
{
setQuestionList
(
res
.
data
.
questionList
)
}
setLoading
(
false
)
}
useEffect
(()
=>
{
...
...
@@ -26,7 +29,7 @@ export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer
},
[])
return
(
<
div
className=
"pl-[62px] mt-[12px] flex flex-col"
>
{
questionList
.
length
!==
0
&&
(
{
!
loading
&&
questionList
.
length
!==
0
&&
(
<
div
className=
"flex flex-col gap-[8px]"
>
{
questionList
.
map
((
item
,
index
)
=>
(
...
...
@@ -41,7 +44,7 @@ export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer
</
div
>
)
}
{
questionList
.
length
===
0
&&
(
loading
&&
questionList
.
length
===
0
&&
(
<
div
className=
"flex flex-col gap-[8px]"
>
<
Skeleton
className=
"w-full sm:w-[300px] rounded-lg"
>
<
div
className=
"h-[40px] w-full rounded-lg bg-[#fff]"
></
div
>
...
...
src/pages/Chat/components/ChatItem/ChatAnswerShower.tsx
View file @
e71e99cf
...
...
@@ -11,17 +11,17 @@ interface ChatAnswerShowerProps {
}
export
const
ChatAnswerShower
:
React
.
FC
<
ChatAnswerShowerProps
>
=
({
answer
,
isLastAnswer
,
onSubmitQuestion
})
=>
{
const
hideOperate
=
(
answer
.
attachment
List
||
[]).
some
(
attachment
=>
attachment
.
type
===
'box'
||
attachment
?.
type
?.
includes
(
'card-'
))
const
hideOperate
=
(
answer
.
card
List
||
[]).
some
(
attachment
=>
attachment
.
type
===
'box'
||
attachment
?.
type
?.
includes
(
'card-'
))
return
(
<
div
className=
"answerShower"
>
{
answer
.
answer
&&
(
<
div
className=
{
answer
.
attachment
List
?.
length
?
'sm:mb-[20px]'
:
''
}
>
<
div
className=
{
answer
.
card
List
?.
length
?
'sm:mb-[20px]'
:
''
}
>
<
MarkdownDetail
>
{
formatMarkdown
(
answer
.
answer
||
''
)
}
</
MarkdownDetail
>
</
div
>
)
}
{
answer
.
attachmentList
&&
answer
.
attachment
List
?.
length
!==
0
&&
<
ChatAnswerAttachment
onSubmitQuestion=
{
onSubmitQuestion
}
isLastAnswer=
{
isLastAnswer
}
answer=
{
answer
}
/>
}
{
answer
.
cardList
&&
answer
.
card
List
?.
length
!==
0
&&
<
ChatAnswerAttachment
onSubmitQuestion=
{
onSubmitQuestion
}
isLastAnswer=
{
isLastAnswer
}
answer=
{
answer
}
/>
}
{
/* {} */
}
{
!
hideOperate
&&
<
ChatAnswerOperate
answer=
{
answer
}
/>
}
</
div
>
...
...
src/pages/Collect/Collect.tsx
View file @
e71e99cf
...
...
@@ -131,7 +131,7 @@ export const Collect: React.FC = () => {
<
MarkdownDetail
>
{
formatMarkdown
(
item
.
answer
||
''
)
}
</
MarkdownDetail
>
{
item
.
attachmentList
&&
item
.
attachment
List
?.
length
!==
0
&&
<
ChatAnswerAttachment
answer=
{
item
}
/>
}
{
item
.
cardList
&&
item
.
card
List
?.
length
!==
0
&&
<
ChatAnswerAttachment
answer=
{
item
}
/>
}
<
div
className=
"mt-[12px] flex gap-[4px] justify-between items-center"
>
<
div
className=
"text-12px text-[#B2B8C1]"
>
{
item
.
collectionTime
}
...
...
src/types/chat.ts
View file @
e71e99cf
...
...
@@ -22,6 +22,11 @@ export interface Attachment {
content
:
AttachmentContent
}
interface
AnswerStep
{
step
:
string
message
:
string
}
export
interface
Answer
{
endAnswerFlag
?:
boolean
isStopTyping
?:
boolean
...
...
@@ -35,7 +40,8 @@ export interface Answer {
conversationId
?:
string
terminateFlag
?:
boolean
toolName
?:
string
attachmentList
:
Attachment
[]
cardList
:
Attachment
[]
step
?:
AnswerStep
}
export
interface
OriginalRecord
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment