Commit bd13d886 by HoMeTown

feat: 渲染历史消息记录

parent accd38e8
...@@ -49,9 +49,13 @@ ...@@ -49,9 +49,13 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hot-toast": "^2.4.1", "react-hot-toast": "^2.4.1",
"react-markdown": "^9.0.1",
"react-redux": "^9.1.2", "react-redux": "^9.1.2",
"react-router-dom": "^6.26.0", "react-router-dom": "^6.26.0",
"react-virtuoso": "^4.9.0", "react-virtuoso": "^4.9.0",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.0",
"tailwind-merge": "^2.4.0" "tailwind-merge": "^2.4.0"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -15,3 +15,11 @@ export function fetchQueryUserConversationPage<T>(data: T) { ...@@ -15,3 +15,11 @@ export function fetchQueryUserConversationPage<T>(data: T) {
export function fetchCreateConversation<T>(data: T) { export function fetchCreateConversation<T>(data: T) {
return http.post('/conversation/api/conversation/mobile/v1/create_conversation', data) return http.post('/conversation/api/conversation/mobile/v1/create_conversation', data)
} }
/**
* 查询用户问答历史
* @params { * }
*/
export function fetchUserQaRecordPage(conversationId: string) {
return http.post('/conversation/api/conversation/mobile/v1/query_user_qa_record_list', { conversationId })
}
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
<g id="晓得---PC端页面-草稿" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="晓得---PC端页面-草稿" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="晓得-PC端---默认页" transform="translate(-944.000000, -208.000000)"> <g id="晓得-PC端---默认页" transform="translate(-944.000000, -208.000000)">
<g id="编组-4" transform="translate(751.000000, 160.000000)"> <g id="编组-4" transform="translate(751.000000, 160.000000)">
<circle id="椭圆形备份-2" fill="url(#linearGradient-1)" style="mix-blend-mode: multiply;" filter="url(#filter-2)" cx="181" cy="36" r="36"></circle>
<g id="编组" transform="translate(193.000000, 48.000000)" fill="#29B6FD" fill-rule="nonzero"> <g id="编组" transform="translate(193.000000, 48.000000)" fill="#29B6FD" fill-rule="nonzero">
<path d="M108.428571,37.7142857 C109.296447,37.7142857 110,38.4178383 110,39.2857143 L110,42.4285714 C110,43.2964475 109.296447,44 108.428571,44 L105.285714,44 C104.417838,44 103.714286,43.2964475 103.714286,42.4285714 L103.714286,39.2857143 C103.714286,38.4178383 104.417838,37.7142857 105.285714,37.7142857 L108.428571,37.7142857 Z M69.3172289,12.8333331 C68.2893065,16.417293 66.6680662,19.6803452 64.4535095,22.6224888 L64.116876,23.0614034 L64.116876,44 L61.6362271,44 C59.5436815,44 57.8363325,42.3833077 57.7430944,40.3545968 L57.7390837,40.1797268 L57.7390837,28.9956144 C56.2563841,30.0248539 54.6573935,30.9778534 52.9421125,31.8546134 L52.2934315,32.1798246 L52.2934315,24.6052635 C57.0986526,21.8826458 60.4950769,18.0943254 62.4827034,13.2403022 L62.6450773,12.8333331 L69.3172289,12.8333331 Z M94.1063223,20.4078949 C95.1376484,20.4078949 95.9818398,21.1933103 96.0503984,22.1872517 L96.0548936,22.3180315 L96.0548936,25.425439 L93.3075371,25.425439 L93.3075371,27.5482458 L95.4800001,27.5482458 C96.5113262,27.5482458 97.3555176,28.3336612 97.4240763,29.3276025 L97.4285714,29.4583824 L97.4285714,32.5657898 L93.3075371,32.5657898 L93.3075371,39.9384988 C93.3075371,41.9897691 91.6583146,43.6634414 89.5887831,43.7548404 L89.4103945,43.758772 L82.5143512,43.758772 L82.5143512,38.4517542 L87.0278656,38.4517542 L87.0278656,32.5657898 L68.5813296,32.5657898 L68.5813296,27.5482458 L87.0278656,27.5482458 L87.0278656,25.425439 L69.5625281,25.425439 L69.5625281,20.4078949 L94.1063223,20.4078949 Z M43.7614624,23.7850881 L43.7614624,29.574561 L39.542308,29.574561 L39.542308,37.7763161 L44.2520618,37.7763161 L44.2520618,43.758772 L37.1597791,43.758772 C35.0672336,43.758772 33.3598846,42.1420797 33.2666464,40.1133688 L33.2626362,39.9384988 L33.2626362,29.574561 L28.5528824,29.574561 C27.9859676,34.40627 26.4636964,38.9695506 23.9860687,43.2644036 L23.6959488,43.758772 L15.4538797,43.758772 C18.9988557,39.4322301 21.2114025,34.8647103 22.0915205,30.0562141 L22.1750908,29.574561 L17.3672171,29.574561 L17.3672171,23.7850881 L43.7614624,23.7850881 Z M77.0686983,33.8684212 L80.5028941,42.6973686 L74.3213418,42.6973686 L70.8871468,33.8684212 L77.0686983,33.8684212 Z M12.0473361,1.78508812 C14.1398817,1.78508812 15.8472306,3.40178044 15.9404688,5.43049055 L15.944479,5.60536133 L15.944479,40.5263161 L3.89714283,40.5263161 C1.80459733,40.5263161 0.0972483192,38.909623 0.00401019899,36.8809128 L0,36.7060421 L0,5.60536133 C0,3.55409026 1.64922266,1.88041793 3.71875382,1.78901889 L3.89714283,1.78508812 L12.0473361,1.78508812 Z M10.2535265,23.7368424 L5.69095252,23.7368424 L5.69095252,34.5921051 L10.2535265,34.5921051 L10.2535265,23.7368424 Z M31.4474186,0.337719483 L31.8398981,2.99122797 L38.6378211,2.99122797 C40.7303667,2.99122797 42.4377157,4.6079203 42.5309537,6.63663122 L42.534964,6.81150118 L42.534964,8.7807017 L33.0663964,8.7807017 C33.2745295,9.59941506 33.6042988,10.5776187 34.0557043,11.7153109 L34.194775,12.0614034 L42.534964,9.98684236 L42.534964,15.5833331 L36.6968317,16.9824559 C37.5662828,18.456628 38.6830777,19.9983653 40.0472165,21.6076694 L40.4253868,22.0482458 L32.2323776,22.0482458 C31.6156241,21.1660401 30.9868559,20.2011282 30.3460731,19.1535085 L30.0246805,18.6228068 L17.5634568,21.6622805 L17.5634568,15.7280703 L27.5226238,13.3157898 C27.0592799,12.056043 26.6527184,10.7962962 26.3029393,9.53654945 L26.0998856,8.7807017 L17.465337,8.7807017 L17.465337,2.99122797 L24.8733872,2.99122797 L24.4809077,0.337719483 L31.4474186,0.337719483 Z M68.0907303,0 C65.8085971,8.40804605 60.6834967,14.703726 52.7154283,18.887039 L52.2934315,19.1052635 L52.2934315,11.5789475 C56.7415318,8.7669172 59.8470327,5.0443248 61.6099327,0.41117107 L61.7619988,0 L68.0907303,0 Z M91.0784324,0.771929668 C93.170978,0.771929668 94.878327,2.38862199 94.9715651,4.41733292 L94.975575,4.59220369 L94.975575,14.8507793 C94.975575,16.9020504 93.3263525,18.5757227 91.256821,18.6671218 L91.0784324,18.6710525 L70.6418467,18.6710525 L70.6418467,0.771929668 L91.0784324,0.771929668 Z M10.2535265,7.7192983 L5.69095252,7.7192983 L5.69095252,17.9473686 L10.2535265,17.9473686 L10.2535265,7.7192983 Z M88.5977835,11.7719297 L77.0196382,11.7719297 L77.0196382,13.991228 L88.5977835,13.991228 L88.5977835,11.7719297 Z M88.5977835,5.5 L77.0196382,5.5 L77.0196382,7.67105255 L88.5977835,7.67105255 L88.5977835,5.5 Z" id="形状结合"></path> <path d="M108.428571,37.7142857 C109.296447,37.7142857 110,38.4178383 110,39.2857143 L110,42.4285714 C110,43.2964475 109.296447,44 108.428571,44 L105.285714,44 C104.417838,44 103.714286,43.2964475 103.714286,42.4285714 L103.714286,39.2857143 C103.714286,38.4178383 104.417838,37.7142857 105.285714,37.7142857 L108.428571,37.7142857 Z M69.3172289,12.8333331 C68.2893065,16.417293 66.6680662,19.6803452 64.4535095,22.6224888 L64.116876,23.0614034 L64.116876,44 L61.6362271,44 C59.5436815,44 57.8363325,42.3833077 57.7430944,40.3545968 L57.7390837,40.1797268 L57.7390837,28.9956144 C56.2563841,30.0248539 54.6573935,30.9778534 52.9421125,31.8546134 L52.2934315,32.1798246 L52.2934315,24.6052635 C57.0986526,21.8826458 60.4950769,18.0943254 62.4827034,13.2403022 L62.6450773,12.8333331 L69.3172289,12.8333331 Z M94.1063223,20.4078949 C95.1376484,20.4078949 95.9818398,21.1933103 96.0503984,22.1872517 L96.0548936,22.3180315 L96.0548936,25.425439 L93.3075371,25.425439 L93.3075371,27.5482458 L95.4800001,27.5482458 C96.5113262,27.5482458 97.3555176,28.3336612 97.4240763,29.3276025 L97.4285714,29.4583824 L97.4285714,32.5657898 L93.3075371,32.5657898 L93.3075371,39.9384988 C93.3075371,41.9897691 91.6583146,43.6634414 89.5887831,43.7548404 L89.4103945,43.758772 L82.5143512,43.758772 L82.5143512,38.4517542 L87.0278656,38.4517542 L87.0278656,32.5657898 L68.5813296,32.5657898 L68.5813296,27.5482458 L87.0278656,27.5482458 L87.0278656,25.425439 L69.5625281,25.425439 L69.5625281,20.4078949 L94.1063223,20.4078949 Z M43.7614624,23.7850881 L43.7614624,29.574561 L39.542308,29.574561 L39.542308,37.7763161 L44.2520618,37.7763161 L44.2520618,43.758772 L37.1597791,43.758772 C35.0672336,43.758772 33.3598846,42.1420797 33.2666464,40.1133688 L33.2626362,39.9384988 L33.2626362,29.574561 L28.5528824,29.574561 C27.9859676,34.40627 26.4636964,38.9695506 23.9860687,43.2644036 L23.6959488,43.758772 L15.4538797,43.758772 C18.9988557,39.4322301 21.2114025,34.8647103 22.0915205,30.0562141 L22.1750908,29.574561 L17.3672171,29.574561 L17.3672171,23.7850881 L43.7614624,23.7850881 Z M77.0686983,33.8684212 L80.5028941,42.6973686 L74.3213418,42.6973686 L70.8871468,33.8684212 L77.0686983,33.8684212 Z M12.0473361,1.78508812 C14.1398817,1.78508812 15.8472306,3.40178044 15.9404688,5.43049055 L15.944479,5.60536133 L15.944479,40.5263161 L3.89714283,40.5263161 C1.80459733,40.5263161 0.0972483192,38.909623 0.00401019899,36.8809128 L0,36.7060421 L0,5.60536133 C0,3.55409026 1.64922266,1.88041793 3.71875382,1.78901889 L3.89714283,1.78508812 L12.0473361,1.78508812 Z M10.2535265,23.7368424 L5.69095252,23.7368424 L5.69095252,34.5921051 L10.2535265,34.5921051 L10.2535265,23.7368424 Z M31.4474186,0.337719483 L31.8398981,2.99122797 L38.6378211,2.99122797 C40.7303667,2.99122797 42.4377157,4.6079203 42.5309537,6.63663122 L42.534964,6.81150118 L42.534964,8.7807017 L33.0663964,8.7807017 C33.2745295,9.59941506 33.6042988,10.5776187 34.0557043,11.7153109 L34.194775,12.0614034 L42.534964,9.98684236 L42.534964,15.5833331 L36.6968317,16.9824559 C37.5662828,18.456628 38.6830777,19.9983653 40.0472165,21.6076694 L40.4253868,22.0482458 L32.2323776,22.0482458 C31.6156241,21.1660401 30.9868559,20.2011282 30.3460731,19.1535085 L30.0246805,18.6228068 L17.5634568,21.6622805 L17.5634568,15.7280703 L27.5226238,13.3157898 C27.0592799,12.056043 26.6527184,10.7962962 26.3029393,9.53654945 L26.0998856,8.7807017 L17.465337,8.7807017 L17.465337,2.99122797 L24.8733872,2.99122797 L24.4809077,0.337719483 L31.4474186,0.337719483 Z M68.0907303,0 C65.8085971,8.40804605 60.6834967,14.703726 52.7154283,18.887039 L52.2934315,19.1052635 L52.2934315,11.5789475 C56.7415318,8.7669172 59.8470327,5.0443248 61.6099327,0.41117107 L61.7619988,0 L68.0907303,0 Z M91.0784324,0.771929668 C93.170978,0.771929668 94.878327,2.38862199 94.9715651,4.41733292 L94.975575,4.59220369 L94.975575,14.8507793 C94.975575,16.9020504 93.3263525,18.5757227 91.256821,18.6671218 L91.0784324,18.6710525 L70.6418467,18.6710525 L70.6418467,0.771929668 L91.0784324,0.771929668 Z M10.2535265,7.7192983 L5.69095252,7.7192983 L5.69095252,17.9473686 L10.2535265,17.9473686 L10.2535265,7.7192983 Z M88.5977835,11.7719297 L77.0196382,11.7719297 L77.0196382,13.991228 L88.5977835,13.991228 L88.5977835,11.7719297 Z M88.5977835,5.5 L77.0196382,5.5 L77.0196382,7.67105255 L88.5977835,7.67105255 L88.5977835,5.5 Z" id="形状结合"></path>
</g> </g>
......
.chatPage {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.content {
flex: 1 1;
}
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
chatPage: string
content: string
}
declare const cssExports: CssExports
export default cssExports
import React from 'react' import React from 'react'
import { useParams } from 'react-router-dom' import styles from './Chat.module.less'
import { ChatSlogan } from './components/ChatSlogan'
import { ChatContent } from './components/ChatContent'
import { RECOMMEND_QUESTIONS_OTHER } from '@/config/recommendQuestion'
import { ChatEditor } from '@/components/ChatEditor'
export const Chat: React.FC = () => { export const Chat: React.FC = () => {
const { id } = useParams<{ id: string }>()
// const dispatch = useAppDispatch()
// const { currentConversationId } = useAppSelector(state => state.conversation)
// useEffect(() => {
// if (id && id !== currentConversationId) {
// dispatch(setCurrentConversation(id))
// // dispatch(fetchConversationDetails(id))
// }
// }, [id, currentConversationId, dispatch])
return ( return (
<h1> <div className={styles.chatPage}>
聊天页面 - Chat ID: <ChatSlogan />
{id} <div className={styles.content}>
</h1> <ChatContent />
</div>
<div className="box-border px-[0] mx-auto iptContainer w-full max-w-[1000px] flex-shrink-0 sm:px-0 pb-[18px]">
<ChatEditor placeholders={RECOMMEND_QUESTIONS_OTHER} />
<div className="w-full text-center mt-[20px] text-[#3333334d] text-[12px]">
内容由AI模型生成,其准确性和完整性无法保证,仅供参考
</div>
</div>
</div>
) )
} }
import { useEffect } from 'react'
import { useParams } from 'react-router-dom'
import { Spinner } from '@nextui-org/react'
import { ChatWelcome } from '../ChatWelcome'
import { ChatItem } from '../ChatItem'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import { fetchChatRecords } from '@/store/chatSlice'
export const ChatContent: React.FC = () => {
const { id } = useParams<{ id: string }>()
const dispatch = useAppDispatch()
const { records, isLoading, error } = useAppSelector(state => state.chat)
useEffect(() => {
if (id) {
dispatch(fetchChatRecords(id))
}
}, [id, dispatch])
if (isLoading)
return <div className="w-full flex justify-center"><Spinner /></div>
if (error) {
return (
<div>
Error:
{error}
</div>
)
}
return (
<div className="max-w-[1000px] mx-auto box-border py-[32px] flex flex-col gap-[32px]">
<ChatWelcome />
{
records.map((record) => {
return (
<ChatItem record={record} key={record.groupId} />
)
})
}
</div>
)
}
import { ChatItemBot } from './ChatItemBot'
import { ChatItemUser } from './ChatItemUser'
import type { ChatRecord } from '@/store/chatSlice'
interface ChatItemProps {
record: ChatRecord
}
export const ChatItem: React.FC<ChatItemProps> = ({ record }) => {
return (
<>
<ChatItemUser record={record} />
<ChatItemBot record={record} />
</>
)
}
import { Avatar } from '@nextui-org/react'
import { motion } from 'framer-motion'
import ReactMarkdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize from 'rehype-sanitize'
import { formatMarkdown } from './markdownFormatter'
import AvatarBot from '@/assets/avatarBot.png'
import type { ChatRecord } from '@/store/chatSlice'
interface ChatItemBotProps {
record: ChatRecord
}
export const ChatItemBot: React.FC<ChatItemBotProps> = ({ record }) => {
return (
<div className="chatItemBotContainer w-full">
<div className="flex">
<Avatar className="flex-shrink-0" src={AvatarBot} />
<motion.div
className="ml-[20px] bg-white rounded-[20px] box-border px-[24px] py-[20px]"
>
<div className="content">
<ReactMarkdown
rehypePlugins={[rehypeRaw, rehypeSanitize]}
className="markdown-content"
>
{formatMarkdown(record.answerList[0].answer)}
</ReactMarkdown>
</div>
</motion.div>
</div>
</div>
)
}
import { Avatar } from '@nextui-org/react'
import type { ChatRecord } from '@/store/chatSlice'
import AvatarUser from '@/assets/avatarUser.png'
interface ChatItemUserProps {
record: ChatRecord
}
export const ChatItemUser: React.FC<ChatItemUserProps> = ({ record }) => {
return (
<div className="chatItemUser flex justify-end">
<div className="mr-[20px] bg-[#BFE9FE] rounded-[20px] box-border px-[24px] py-[20px] text-[#27353C]">{record.question}</div>
<Avatar className="flex-shrink-0" src={AvatarUser} />
</div>
)
}
export { ChatItem } from './ChatItem'
export function formatMarkdown(text: string): string {
// 首先移除 ♪ 符号之后的所有文本
let formattedText = text.split('♪')[0].trim()
// 处理换行
formattedText = formattedText.replace(/(?<!\n)\n(?!\n)/g, ' \n')
// 处理代码块
formattedText = formattedText.replace(/```(\w+)?\n([\s\S]*?)\n```/g, (match, language, code) => {
return `\n\`\`\`${language || ''}\n${code.trim()}\n\`\`\`\n`
})
// 处理行内代码
formattedText = formattedText.replace(/`([^`\n]+)`/g, '`$1`')
// 处理列表
formattedText = formattedText.replace(/^( *)[-*+] /gm, '$1- ')
// 处理标题
formattedText = formattedText.replace(/^(#{1,6}) /gm, '$1 ')
// 处理粗体和斜体
formattedText = formattedText.replace(/(\*\*|__)(.*?)\1/g, '**$2**')
formattedText = formattedText.replace(/(\*|_)(.*?)\1/g, '*$2*')
// 处理链接
formattedText = formattedText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '[$1]($2)')
return formattedText
}
import TextLogo from '@/assets/svg/textLogo.svg?react'
import GradualSpacing from '@/components/GradualSpacing'
export const ChatSlogan: React.FC = () => {
return (
<div className="w-full">
<div className="max-w-[1000px] mx-auto h-[112px] flex flex-col justify-center">
<div className="flex items-center">
<TextLogo className="w-[70px]" />
<GradualSpacing text="晓得解惑,让沟通更智能" className="ml-[8px] text-[16px] text-[#333] font-medium" />
</div>
<h3 className="text-[12px] text-[#333] font-light">知晓市场脉搏,引领行业潮流,晓得AI助手全方位为您保驾护航</h3>
</div>
</div>
)
}
export { ChatSlogan } from './ChatSlogan'
import { Avatar } from '@nextui-org/react'
import { motion } from 'framer-motion'
import AvatarBot from '@/assets/avatarBot.png'
export const ChatWelcome: React.FC = () => {
return (
<div className="chatWelcomeContainer w-full">
<div className="flex">
<Avatar className="flex-shrink-0" src={AvatarBot} />
<motion.div
className="ml-[20px] bg-white rounded-[20px] box-border px-[24px] py-[20px]"
>
<div className="content">
<p className="text-[18px] font-medium text-[#333]">您好,我是晓得</p>
<p className="mt-[8px] text-13px text-[#27353C] font-300">做为您的智能保险伙伴,您有各类专业相关的问题都可以抛给我哟~让我们互相帮助共同成长吧~</p>
</div>
</motion.div>
</div>
</div>
)
}
...@@ -8,6 +8,8 @@ import BotPopImg from '@/assets/botPop.png' ...@@ -8,6 +8,8 @@ import BotPopImg from '@/assets/botPop.png'
import BotBgImg from '@/assets/botBg.png' import BotBgImg from '@/assets/botBg.png'
import SayHi from '@/assets/svg/sayHi.svg?react' import SayHi from '@/assets/svg/sayHi.svg?react'
import { type WithAuthProps, withAuth } from '@/auth/withAuth' import { type WithAuthProps, withAuth } from '@/auth/withAuth'
import { useAppDispatch } from '@/store/hook'
import { createConversation } from '@/store/conversationSlice'
const BotEye: React.FC = () => { const BotEye: React.FC = () => {
const controls = useAnimation() const controls = useAnimation()
...@@ -54,8 +56,15 @@ const BotAnimateBox: React.FC = () => { ...@@ -54,8 +56,15 @@ const BotAnimateBox: React.FC = () => {
} }
const WelcomeWordBase: React.FC<WithAuthProps> = ({ checkAuth }) => { const WelcomeWordBase: React.FC<WithAuthProps> = ({ checkAuth }) => {
const dispatch = useAppDispatch()
const handleCreateConversation = () => {
dispatch(createConversation({}))
}
const handleGo = () => { const handleGo = () => {
checkAuth() if (checkAuth()) {
handleCreateConversation()
}
} }
return ( return (
<div className="w-full h-auto flex-shrink-0 relative sm:w-[360px] sm:h-[276px]"> <div className="w-full h-auto flex-shrink-0 relative sm:w-[360px] sm:h-[276px]">
......
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { fetchUserQaRecordPage } from '@/api/conversation'
interface Attachment {
type: string
name: string
description: string
}
interface Answer {
answer: string
collectionFlag: boolean
feedbackStatus: string
groupId: string
question: string
recordId: string
terminateFlag: boolean
toolName: string
attachmentList: Attachment[]
}
export interface ChatRecord {
groupId: string
question: string
answerList: Answer[]
productCode: string
qaTime: string
}
interface ChatState {
records: ChatRecord[]
isLoading: boolean
error: string | null
}
const initialState: ChatState = {
records: [],
isLoading: false,
error: null,
}
export const fetchChatRecords = createAsyncThunk(
'chat/fetchRecords',
async (conversationId: string) => {
const response = await fetchUserQaRecordPage(conversationId)
return response.data
},
)
const chatSlice = createSlice({
name: 'chat',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchChatRecords.pending, (state) => {
state.isLoading = true
state.error = null
})
.addCase(fetchChatRecords.fulfilled, (state, action) => {
state.isLoading = false
state.records = action.payload
})
.addCase(fetchChatRecords.rejected, (state, action) => {
state.isLoading = false
state.error = action.error.message || 'Failed to fetch chat records'
})
},
})
export default chatSlice.reducer
import { configureStore } from '@reduxjs/toolkit' import { configureStore } from '@reduxjs/toolkit'
import conversationReducer from './conversationSlice' import conversationReducer from './conversationSlice'
import chatReducer from './chatSlice'
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
conversation: conversationReducer, conversation: conversationReducer,
chat: chatReducer,
}, },
}) })
// 为了在TypeScript中使用,我们导出这些类型 // 为了在TypeScript中使用,我们导出这些类型
......
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