Commit 80e91beb by Liu

fix:流式换行处理

parent 4780fa33
...@@ -8,6 +8,7 @@ import { processApiResponse } from './helper' ...@@ -8,6 +8,7 @@ import { processApiResponse } from './helper'
import { ChatWelcome } from './components/ChatWelcome' import { ChatWelcome } from './components/ChatWelcome'
import { ChatItemUser } from './components/ChatItem/ChatItemUser' import { ChatItemUser } from './components/ChatItem/ChatItemUser'
import { ChatAnswerBox } from './components/ChatItem/ChatAnswerBox' import { ChatAnswerBox } from './components/ChatItem/ChatAnswerBox'
import { trimSpacesOnly } from './components/ChatItem/markdownFormatter'
import { ChatEditor } from '@/components/ChatEditor' import { ChatEditor } from '@/components/ChatEditor'
import type { ChatRecord } from '@/types/chat' import type { ChatRecord } from '@/types/chat'
import { fetchUserQaRecordPage } from '@/api/conversation' import { fetchUserQaRecordPage } from '@/api/conversation'
...@@ -162,9 +163,11 @@ export const Chat: React.FC = () => { ...@@ -162,9 +163,11 @@ export const Chat: React.FC = () => {
// 创建最后一项的新对象,合并现有数据和新的 answer // 创建最后一项的新对象,合并现有数据和新的 answer
const originalAnswer = (newItems[lastIndex].answerList?.[0]?.answer || '') + msg.content.data.answer const originalAnswer = (newItems[lastIndex].answerList?.[0]?.answer || '') + msg.content.data.answer
// 移除所有括号及其内容 // 移除所有括号及其内容
let filteredAnswer = originalAnswer.replace(/\([^)]*\)/g, '').trim() let filteredAnswer = originalAnswer.replace(/\([^)]*\)/g, '')
// 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容 // 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容
filteredAnswer = filteredAnswer.replace(/\[参考文档(?:[^]*》\s*)+\]/g, '').trim() filteredAnswer = filteredAnswer.replace(/\[参考文档(?:[^]*》\s*)+\]/g, '')
// 只移除空格和制表符,保留换行符(避免移除末尾的 \n\n)
filteredAnswer = trimSpacesOnly(filteredAnswer)
newItems[lastIndex] = { newItems[lastIndex] = {
...newItems[lastIndex], ...newItems[lastIndex],
......
/**
* 只移除字符串开头和结尾的空格和制表符,保留换行符
* 用于流式处理时保留末尾的 \n\n 换行符
*/
export function trimSpacesOnly(text: string): string {
return text.replace(/^[ \t]+|[ \t]+$/g, '')
}
export function formatMarkdown(text: string): string { export function formatMarkdown(text: string): string {
// 首先移除 ♪ 符号之后的所有文本 // 首先移除 ♪ 符号之后的所有文本
let formattedText = text.split('♪')[0].trim() // 使用 trimSpacesOnly 保留换行符,避免移除末尾的 \n\n
let formattedText = text.split('♪')[0]
formattedText = trimSpacesOnly(formattedText)
// 处理换行 // 处理换行
formattedText = formattedText.replace(/(?<!\n)\n(?!\n)/g, ' \n') // 先处理连续的换行符(\n\n 或更多),确保它们被保留为段落分隔
// 然后将单独的 \n 替换为 Markdown 的硬换行(两个空格 + \n)
// 注意:\n\n 在 Markdown 中表示段落分隔,应该被保留
formattedText = formattedText.replace(/\n{3,}/g, '\n\n') // 将3个或更多连续换行符压缩为2个
formattedText = formattedText.replace(/(?<!\n)\n(?!\n)/g, ' \n') // 单独的 \n 转换为硬换行
// 处理代码块 // 处理代码块
formattedText = formattedText.replace(/```(\w+)?\n([\s\S]*?)\n```/g, (match, language, code) => { formattedText = formattedText.replace(/```(\w+)?\n([\s\S]*?)\n```/g, (match, language, code) => {
......
...@@ -8,6 +8,7 @@ import styles from '../Chat/Chat.module.less' ...@@ -8,6 +8,7 @@ import styles from '../Chat/Chat.module.less'
import { processApiResponse } from '../Chat/helper' import { processApiResponse } from '../Chat/helper'
import { ChatItemUser } from '../Chat/components/ChatItem/ChatItemUser' import { ChatItemUser } from '../Chat/components/ChatItem/ChatItemUser'
import { ChatAnswerBox } from '../Chat/components/ChatItem/ChatAnswerBox' import { ChatAnswerBox } from '../Chat/components/ChatItem/ChatAnswerBox'
import { trimSpacesOnly } from '../Chat/components/ChatItem/markdownFormatter'
import { TacticsWelcome } from './components/TacticsWelcome' import { TacticsWelcome } from './components/TacticsWelcome'
import DeleteIcon from '@/assets/svg/delete.svg?react' import DeleteIcon from '@/assets/svg/delete.svg?react'
import RefreshIcon from '@/assets/svg/refresh.svg?react' import RefreshIcon from '@/assets/svg/refresh.svg?react'
...@@ -282,9 +283,11 @@ export const TacticsChat: React.FC = () => { ...@@ -282,9 +283,11 @@ export const TacticsChat: React.FC = () => {
// 移除 ♪ 符号之后的所有文本(与历史记录保持一致) // 移除 ♪ 符号之后的所有文本(与历史记录保持一致)
let filteredAnswer = originalAnswer.split('♪')[0] let filteredAnswer = originalAnswer.split('♪')[0]
// 移除所有括号及其内容 // 移除所有括号及其内容
filteredAnswer = filteredAnswer.replace(/\([^)]*\)/g, '').trim() filteredAnswer = filteredAnswer.replace(/\([^)]*\)/g, '')
// 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容 // 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容
filteredAnswer = filteredAnswer.replace(/\[参考文档(?:[^]*》\s*)+\]/g, '').trim() filteredAnswer = filteredAnswer.replace(/\[参考文档(?:[^]*》\s*)+\]/g, '')
// 只移除空格和制表符,保留换行符(避免移除末尾的 \n\n)
filteredAnswer = trimSpacesOnly(filteredAnswer)
newItems[lastAiIndex] = { newItems[lastAiIndex] = {
...newItems[lastAiIndex], ...newItems[lastAiIndex],
...@@ -312,9 +315,11 @@ export const TacticsChat: React.FC = () => { ...@@ -312,9 +315,11 @@ export const TacticsChat: React.FC = () => {
// 移除 ♪ 符号之后的所有文本(与历史记录保持一致) // 移除 ♪ 符号之后的所有文本(与历史记录保持一致)
let filteredAnswer = newAnswer.split('♪')[0] let filteredAnswer = newAnswer.split('♪')[0]
// 移除所有括号及其内容 // 移除所有括号及其内容
filteredAnswer = filteredAnswer.replace(/\([^)]*\)/g, '').trim() filteredAnswer = filteredAnswer.replace(/\([^)]*\)/g, '')
// 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容 // 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容
filteredAnswer = filteredAnswer.replace(/\[参考文档(?:[^]*》\s*)+\]/g, '').trim() filteredAnswer = filteredAnswer.replace(/\[参考文档(?:[^]*》\s*)+\]/g, '')
// 只移除空格和制表符,保留换行符(避免移除末尾的 \n\n)
filteredAnswer = trimSpacesOnly(filteredAnswer)
newItems.push({ newItems.push({
role: 'ai', role: 'ai',
question, question,
......
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