Commit 7594fa09 by Liu

fix:对话样式

parent e3e83b1c
// 问答功能独立聊天页 // 问答功能独立聊天页
import React, { useCallback, useEffect, useRef, useState } from 'react' import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useLocation, useParams } from 'react-router-dom' import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { Button } from '@heroui/react' import { Button } from '@heroui/react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useScroll } from 'ahooks' import { useScroll } from 'ahooks'
import styles from '../Chat/Chat.module.less' import styles from '../Chat/Chat.module.less'
import { processApiResponse } from '../Chat/helper' import { processApiResponse } from '../Chat/helper'
import { ChatWelcome } from '../Chat/components/ChatWelcome'
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 { TacticsWelcome } from './components/TacticsWelcome'
import { ChatEditor } from '@/components/ChatEditor' import { ChatEditor } from '@/components/ChatEditor'
import type { ChatRecord } from '@/types/chat' import type { ChatRecord } from '@/types/chat'
import { fetchTacticsQaRecordPage } from '@/api/tactics' import { fetchTacticsQaRecordPage } from '@/api/tactics'
import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat' import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat'
import { clearTacticsShouldSendQuestion } from '@/store/tacticsSlice' import { clearTacticsNavigationFlag, clearTacticsShouldSendQuestion, createTacticsConversation } from '@/store/tacticsSlice'
import type { RootState } from '@/store' import type { RootState } from '@/store'
import { useAppDispatch, useAppSelector } from '@/store/hook' import { useAppDispatch, useAppSelector } from '@/store/hook'
import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react' import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react'
...@@ -23,10 +23,15 @@ import SdreamLoading from '@/components/SdreamLoading' ...@@ -23,10 +23,15 @@ import SdreamLoading from '@/components/SdreamLoading'
export const TacticsChat: React.FC = () => { export const TacticsChat: React.FC = () => {
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [allItems, setAllItems] = useState<ChatRecord[]>([]) const [allItems, setAllItems] = useState<ChatRecord[]>([])
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { shouldSendQuestion: shouldSendQuestionFromState } = useAppSelector((state: RootState) => state.tactics) const {
shouldSendQuestion: shouldSendQuestionFromState,
shouldNavigateToNewConversation,
currentConversationId,
} = useAppSelector((state: RootState) => state.tactics)
// 优先从 location.state 获取,其次从 Redux state 获取 // 优先从 location.state 获取,其次从 Redux state 获取
const shouldSendQuestion = (location.state as { shouldSendQuestion?: string } | null)?.shouldSendQuestion || shouldSendQuestionFromState const shouldSendQuestion = (location.state as { shouldSendQuestion?: string } | null)?.shouldSendQuestion || shouldSendQuestionFromState
const scrollableRef = useRef<HTMLDivElement | any>(null) const scrollableRef = useRef<HTMLDivElement | any>(null)
...@@ -34,6 +39,7 @@ export const TacticsChat: React.FC = () => { ...@@ -34,6 +39,7 @@ export const TacticsChat: React.FC = () => {
const currentIdRef = useRef<string | undefined>(id) const currentIdRef = useRef<string | undefined>(id)
const lastSentQuestionRef = useRef<string>('') const lastSentQuestionRef = useRef<string>('')
const abortControllerRef = useRef<AbortController | null>(null) const abortControllerRef = useRef<AbortController | null>(null)
const hasCreatedRef = useRef(false)
/** 处理正常stream的数据 */ /** 处理正常stream的数据 */
const handleStreamMesageData = (msg: any, question: string) => { const handleStreamMesageData = (msg: any, question: string) => {
...@@ -223,12 +229,30 @@ export const TacticsChat: React.FC = () => { ...@@ -223,12 +229,30 @@ export const TacticsChat: React.FC = () => {
getUserQaRecordPage(id) getUserQaRecordPage(id)
} }
else { else {
// 如果没有 id,显示欢迎语 // 如果没有 id,进入页面时创建新会话
if (!hasCreatedRef.current) {
hasCreatedRef.current = true
dispatch(
createTacticsConversation({
conversationData: {},
shouldNavigate: true,
shouldSendQuestion: '',
}),
)
}
setAllItems([{ role: 'system' } as ChatRecord]) setAllItems([{ role: 'system' } as ChatRecord])
setIsLoading(false) setIsLoading(false)
} }
}, [id, getUserQaRecordPage, dispatch]) }, [id, getUserQaRecordPage, dispatch])
// 创建新会话成功后跳转到新会话页面
useEffect(() => {
if (shouldNavigateToNewConversation && currentConversationId) {
navigate(`/tactics/chat/${currentConversationId}`)
dispatch(clearTacticsNavigationFlag())
}
}, [shouldNavigateToNewConversation, currentConversationId, navigate, dispatch])
// 处理shouldSendQuestion的变化 - 自动发送问题 // 处理shouldSendQuestion的变化 - 自动发送问题
useEffect(() => { useEffect(() => {
if ( if (
...@@ -291,7 +315,7 @@ export const TacticsChat: React.FC = () => { ...@@ -291,7 +315,7 @@ export const TacticsChat: React.FC = () => {
className="w-full chatItem mx-auto" className="w-full chatItem mx-auto"
key={uniqueKey} key={uniqueKey}
> >
{record.role === 'system' && <ChatWelcome toolName="问答功能" />} {record.role === 'system' && <TacticsWelcome />}
{record.role === 'user' && <ChatItemUser record={record} />} {record.role === 'user' && <ChatItemUser record={record} />}
{record.role === 'ai' && ( {record.role === 'ai' && (
<ChatAnswerBox <ChatAnswerBox
......
...@@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef } from 'react' ...@@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef } from 'react'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { useLocalStorageState } from 'ahooks' import { useLocalStorageState } from 'ahooks'
import styles from '../Home/Home.module.less' import styles from '../Home/Home.module.less'
import { ChatWelcome } from '../Chat/components/ChatWelcome' import { TacticsWelcome } from './components/TacticsWelcome'
import { clearTacticsNavigationFlag, createTacticsConversation, fetchTacticsConversations } from '@/store/tacticsSlice' import { clearTacticsNavigationFlag, createTacticsConversation, fetchTacticsConversations } from '@/store/tacticsSlice'
import { useAppDispatch, useAppSelector } from '@/store/hook' import { useAppDispatch, useAppSelector } from '@/store/hook'
import type { RootState } from '@/store' import type { RootState } from '@/store'
...@@ -149,7 +149,7 @@ export const TacticsHome: React.FC = () => { ...@@ -149,7 +149,7 @@ export const TacticsHome: React.FC = () => {
<div className="flex-1 overflow-hidden flex flex-col"> <div className="flex-1 overflow-hidden flex flex-col">
{/* 欢迎语区域 */} {/* 欢迎语区域 */}
<div className="flex-1 overflow-y-auto scrollbar-hide px-[16px] pt-[24px]"> <div className="flex-1 overflow-y-auto scrollbar-hide px-[16px] pt-[24px]">
<ChatWelcome /> <TacticsWelcome />
</div> </div>
{/* 底部输入框 */} {/* 底部输入框 */}
<div className="box-border px-[16px] pb-[18px] pt-[12px] bg-white border-t border-gray-100"> <div className="box-border px-[16px] pb-[18px] pt-[12px] bg-white border-t border-gray-100">
......
import type React from 'react'
import { Avatar } from '@heroui/react'
import { motion } from 'framer-motion'
import AvatarBot from '@/assets/avatarBot.png'
import AIIcon from '@/assets/ai-icon.png'
export const TacticsWelcome: React.FC = () => {
const viteOutputObj = import.meta.env.VITE_OUTPUT_OBJ || 'open'
const welcomeText = '正在为您分析策略,请耐心等待一会儿哦~'
return (
<div className="chatWelcomeContainer w-full">
<div className="h-[20px] sm:h-[32px] w-full"></div>
<div className="flex items-start">
<Avatar className="mr-[12px] flex-shrink-0" src={viteOutputObj === 'inner' ? AIIcon : AvatarBot} />
<motion.div
className="rounded-[20px] box-border px-[16px] py-[16px] sm:px-[24px] sm:py-[20px]"
style={{ background: '#F7FAFD' }}
>
<div className="content">
<p
className="font-medium text-[#333]"
style={{ fontSize: '16px' }}
>
{welcomeText}
</p>
</div>
</motion.div>
</div>
<div className="h-[20px] sm:h-[32px] w-full"></div>
</div>
)
}
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