Commit 934534e1 by Liu

fix:1127上线版本

parent 9d816518
......@@ -8,7 +8,7 @@ import SendIcon from '@/assets/svg/send.svg?react'
import { type WithAuthProps, withAuth } from '@/auth/withAuth'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import { fetchToolList } from '@/api/home'
import { clearCurrentToolId, createConversation, setCurrentToolId } from '@/store/conversationSlice'
import { createConversation } from '@/store/conversationSlice'
interface ChatEditorProps {
onChange?: (value: string) => void
......@@ -138,10 +138,6 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
if (!checkAuth())
return
try {
// 先清除 toolId,确保创建会话时能获取到正确的状态
dispatch(clearCurrentToolId())
// 清除 sessionStorage 中的 toolId
sessionStorage.removeItem('currentToolId')
await dispatch(createConversation({
conversationData: {},
shouldNavigate: true,
......@@ -161,8 +157,6 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
if (!checkAuth())
return
try {
// 先设置 toolId,确保创建会话时能获取到正确的状态
dispatch(setCurrentToolId(tool.toolId))
await dispatch(createConversation({
conversationData: {},
shouldNavigate: true,
......@@ -282,13 +276,13 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
{toolList && toolList.length > 0 && (
<div className="absolute left-4 bottom-2 flex items-center gap-3 pointer-events-auto pl-[16px]">
{toolList.map((tool: any, index: number) => {
// (index === 1 || tool.toolName === '通用模式')在默认状态下高亮
const isSelected = (selectedToolId === tool.toolId && !isToolBtnActive) || ((index === 1 || tool.toolName === '通用模式') && isToolBtnActive)
// index === 1 的按钮(通用模式)在默认状态下高亮
const isSelected = (selectedToolId === tool.toolId && !isToolBtnActive) || (index === 1 && isToolBtnActive)
const handleButtonPress = async () => {
// 高亮状态直接返回,避免重复触发
if (isSelected)
return
if ((index === 1 || tool.toolName === '通用模式'))
if (index === 1)
await handleGeneralClick()
else
await handleToolClick(tool)
......
......@@ -24,14 +24,9 @@ export const HistoryBarList: React.FC<HistoryBarListProps> = ({ searchValue, onS
if (isMobile()) {
onSetHistoryVisible(false)
}
// 直接导航到历史记录,不设置shouldSendQuestion
// 将 toolId 拼接到路由上,默认为空字符串
if (!conversation.toolId) {
sessionStorage.removeItem('currentToolId')
}
const toolId = conversation.toolId || ''
const toolIdParam = `?toolId=${toolId}`
navigate(`/chat/${conversation.conversationId}${toolIdParam}`)
navigate(`/chat/${conversation.conversationId}`)
}
const handleFilter = useDebounceFn(() => {
......
......@@ -22,7 +22,7 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
const dispatch = useAppDispatch()
const navigate = useNavigate()
const { currentConversationId, shouldNavigateToNewConversation, currentToolId } = useAppSelector(state => state.conversation)
const { currentConversationId, shouldNavigateToNewConversation } = useAppSelector(state => state.conversation)
const handleCreateConversation = () => {
dispatch(createConversation({
......@@ -77,12 +77,6 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
if (conversationId) {
sessionStorage.setItem('currentConversationId', conversationId)
}
// 从URL参数中提取toolId并保存到sessionStorage
const urlParams = new URLSearchParams(location.search)
const toolId = urlParams.get('toolId')
if (toolId && toolId !== '') {
sessionStorage.setItem('currentToolId', toolId)
}
navigate('/collect')
}
......@@ -109,12 +103,10 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
useEffect(() => {
if (shouldNavigateToNewConversation && currentConversationId) {
// 将 toolId 拼接到路由上,默认为空字符串
const toolIdParam = currentToolId ? `?toolId=${currentToolId}` : '?toolId='
navigate(`/chat/${currentConversationId}${toolIdParam}`)
navigate(`/chat/${currentConversationId}`)
dispatch(clearNavigationFlag())
}
}, [shouldNavigateToNewConversation, currentConversationId, currentToolId, navigate, dispatch])
}, [shouldNavigateToNewConversation, currentConversationId, navigate, dispatch])
// keep latest conversation id in sessionStorage for cross-page returns (e.g., from collect)
useEffect(() => {
......
......@@ -15,7 +15,7 @@ import { FilePreviewModal } from '@/components/FilePreviewModal'
interface ChatAnswerAttachmentProps {
answer: Answer
isLastAnswer?: boolean
onSubmitQuestion?: (question: string, productCode?: string, toolId?: string) => void
onSubmitQuestion?: (question: string, productCode?: string) => void
}
export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({
......
......@@ -16,7 +16,7 @@ interface ChatAnswerBoxProps {
showIndex: number
isLastAnswer: boolean
index: number
onSubmitQuestion: (question: string, productCode?: string, toolId?: string) => void
onSubmitQuestion: (question: string, productCode?: string) => void
}
export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, isLastAnswer, onSubmitQuestion }) => {
......@@ -51,10 +51,9 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
return (
<div>
{innerRecord.answerList.map((item, index) => {
const itemKey = item.recordId || item.messageId || item.answerId || `${innerRecord.conversationId}-${showIndex}`
return (
index === showIndex && (
<div className="chatItemBotContainer w-full" key={itemKey}>
<div className="chatItemBotContainer w-full" key={`${item.recordId}-${index}`}>
<div className="flex">
<Avatar
className="sm:mr-[20px] hidden sm:block flex-shrink-0"
......
......@@ -14,7 +14,7 @@ interface ChatAnswerParserProps {
isLastAnswer: boolean
onTyping: () => void
onComplate: () => void
onSubmitQuestion: (question: string, productCode?: string, toolId?: string) => void
onSubmitQuestion: (question: string, productCode?: string) => void
}
function CheckIcon({ ...props }) {
......
......@@ -3,22 +3,15 @@ import { Button, Skeleton } from '@heroui/react'
import type { Answer } from '@/types/chat'
import { fetchQueryRecommendQuestion } from '@/api/chat'
import SendIcon from '@/assets/svg/sendBlack.svg?react'
import { useAppSelector } from '@/store/hook'
interface ChatAnswerRecommendProps {
answer: Answer
onSubmitQuestion: (question: string, productCode?: string, toolId?: string) => void
onSubmitQuestion: (question: string) => void
}
export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer, onSubmitQuestion }) => {
let isGet = false
const [questionList, setQuestionList] = useState<string[]>([])
const [loading, setLoading] = useState<boolean>(false)
const currentToolId = useAppSelector(state => state.conversation.currentToolId)
const handleRecommendClick = (question: string) => {
const toolIdParam = currentToolId || ''
onSubmitQuestion(question, undefined, toolIdParam)
}
const getAnswerRecommend = async () => {
setLoading(true)
const res = await fetchQueryRecommendQuestion(answer.conversationId || '', answer.recordId || '')
......@@ -39,8 +32,8 @@ export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer
{!loading && questionList.length !== 0 && questionList.length > 0 && (
<div className="flex flex-col gap-[8px]">
{
questionList.map(item => (
<Button onPress={() => handleRecommendClick(item)} key={`${answer.recordId}-${item}`} color="primary" variant="light" className="text-left bg-[#fff] w-fit max-w-full text-[#333] rounded-[8px] data-[hover=true]:bg-[#F6F6F8] data-[hover=true]:text-[#333]">
questionList.map((item, index) => (
<Button onPress={() => onSubmitQuestion(item)} key={index} color="primary" variant="light" className="text-left bg-[#fff] w-fit max-w-full text-[#333] rounded-[8px] data-[hover=true]:bg-[#F6F6F8] data-[hover=true]:text-[#333]">
<div className="w-full sm:w-full text-nowrap text-ellipsis overflow-hidden">
{item}
</div>
......
......@@ -7,7 +7,7 @@ import { MarkdownDetail } from '@/components/MarkdownDetail'
interface ChatAnswerShowerProps {
answer: Answer
isLastAnswer: boolean
onSubmitQuestion: (question: string, productCode?: string, toolId?: string) => void
onSubmitQuestion: (question: string) => void
}
export const ChatAnswerShower: React.FC<ChatAnswerShowerProps> = ({ answer, isLastAnswer, onSubmitQuestion }) => {
......
......@@ -14,12 +14,10 @@ import QIcon from '@/assets/svg/qicon.svg?react'
import EmptyIcon from '@/assets/svg/empty.svg?react'
import ShineBorder from '@/components/ShineBorder'
import SdreamLoading from '@/components/SdreamLoading'
import { useAppSelector } from '@/store/hook'
export const Collect: React.FC = () => {
const navigate = useNavigate()
const hasLoaded = useRef(false)
const { conversations } = useAppSelector(state => state.conversation)
const [isLoading, setIsLoading] = useState(false)
const [isOpen, setIsOpen] = useState(false)
......@@ -88,15 +86,7 @@ export const Collect: React.FC = () => {
// 添加返回上一页的函数
const handleGoBack = () => {
const conversationId = sessionStorage.getItem('currentConversationId') || ''
// 优先从 sessionStorage 中获取 toolId
let toolId = sessionStorage.getItem('currentToolId') || ''
// 如果 sessionStorage 中没有,则从会话列表中查找
if (!toolId) {
const targetConversation = conversations.find(conv => conv.conversationId === conversationId)
toolId = targetConversation?.toolId || ''
}
const toolIdParam = `?toolId=${toolId}`
navigate(`/chat/${conversationId}${toolIdParam}`, { state: { fromCollect: true } })
navigate(`/chat/${conversationId}`, { state: { fromCollect: true } })
}
useEffect(() => {
......@@ -141,11 +131,11 @@ export const Collect: React.FC = () => {
}}
className="flex flex-col gap-[24px]"
>
{collectList.map((item: any) => (
{collectList.map((item: any, index: number) => (
<ShineBorder
borderRadius={20}
className="text-[#27353C] w-full mx-auto bg-white rounded-[20px] box-border px-[24px] py-[20px]"
key={item.collectionId ?? `${item.question}-${item.collectionTime}`}
key={`${item.collectionId}_${index}`}
>
<div className="flex mb-[20px]">
<div className="mr-[20px]">
......
......@@ -5,6 +5,7 @@ import { Outlet, useLocation } from 'react-router-dom'
import { useLocalStorageState } from 'ahooks'
import styles from './Home.module.less'
import { QuestionList } from './components/QuestionList'
import HomeIcon1 from '@/assets/homeIcon1.png'
import HomeIcon2 from '@/assets/homeIcon2.png'
import SmartIce from '@/assets/smart-ice.png'
import { clearCurrentToolId, createConversation, fetchConversations, setCurrentToolId } from '@/store/conversationSlice'
......@@ -47,7 +48,10 @@ export const Home: React.FC = () => {
const location = useLocation()
const hasFetched = useRef(false)
// 使用 useState
const [productQuestions, setProductQuestions] = useState<any>({ content: [] })
const [otherQuestions, setOtherQuestions] = useState<any>({ content: [] })
const [isToolBtnActive, setIsToolBtnActive] = useState<boolean>(false)
const [shouldChangeStyle, setShouldChangeStyle] = useState<boolean>(false)
// 保存原始的configType为07的数据
const [originalOtherQuestions, setOriginalOtherQuestions] = useState<any>({ content: [] })
......@@ -55,32 +59,6 @@ export const Home: React.FC = () => {
defaultValue: '',
})
const fetchGeneralQuestions = useCallback(
async (fallbackData?: any) => {
try {
const res = await fetchEfficiencyQuestionList({ toolId: '' })
if (res && res.data && res.data.questions) {
setOtherQuestions((prev: any) => ({
...prev,
content: res.data.questions || [],
}))
return
}
}
catch (error) {
console.error('获取通用问题失败:', error)
}
// 如果接口无数据或失败,显示空态但保留基础信息
const baseData = fallbackData || originalOtherQuestions
setOtherQuestions((prev: any) => ({
...prev,
...baseData,
content: [],
}))
},
[originalOtherQuestions],
)
/** 获取qa记录 */
const getQuestionList = useCallback(async () => {
setIsLoading(true)
......@@ -92,11 +70,14 @@ export const Home: React.FC = () => {
if (res && res.data) {
for (let index = 0; index < res.data.length; index++) {
const element = res.data[index]
if (element.configType === '06') {
element.content = JSON.parse(element.content)
setProductQuestions(element)
}
if (element.configType === '07') {
element.content = JSON.parse(element.content)
setOtherQuestions(element)
setOriginalOtherQuestions(element) // 保存原始数据
await fetchGeneralQuestions(element)
}
}
}
......@@ -109,7 +90,7 @@ finally {
setIsLoading(false)
setIsDataLoaded(true)
}
}, [fetchGeneralQuestions])
}, [])
const initConversation = () => {
const fromCollect = location.state?.fromCollect
......@@ -157,39 +138,42 @@ finally {
}
}
else if (isToolBtn) {
// 通用模式:调用接口获取通用问题,toolId 传空字符串
setIsDataLoaded(false)
await fetchGeneralQuestions()
setIsDataLoaded(true)
// 通用模式:恢复刷新时的状态,包括左侧内容也要恢复到初始状态
setOtherQuestions(originalOtherQuestions)
setIsDataLoaded(true) // 恢复原始数据时标记为已加载
}
}, [originalOtherQuestions, fetchGeneralQuestions])
// 进入页面时,如果路由中有 toolId 且不为空,执行工具点击逻辑
useEffect(() => {
if (location.pathname.startsWith('/chat/')) {
const urlParams = new URLSearchParams(window.location.search)
const toolIdFromUrl = urlParams.get('toolId')
if (toolIdFromUrl && toolIdFromUrl !== '') {
// 有 toolId,执行工具点击逻辑
dispatch(setCurrentToolId(toolIdFromUrl))
_handleToolClick(false, toolIdFromUrl)
}
}
}, [location.pathname, location.search, dispatch, _handleToolClick])
}, [originalOtherQuestions])
// 监听工具按钮点击事件
useEffect(() => {
const handleToolClickEvent = (event: CustomEvent) => {
const { isToolBtn, toolId } = event.detail
const { isToolBtn, toolId, shouldChangeStyle: shouldChangeStyleParam } = event.detail
// eslint-disable-next-line no-console
console.log('🔧 [Home] 工具按钮点击事件触发:', {
isToolBtn,
toolId,
shouldChangeStyle: shouldChangeStyleParam,
当前isToolBtnActive: isToolBtnActive,
当前shouldChangeStyle: shouldChangeStyle,
})
setIsToolBtnActive(isToolBtn)
// 更新样式控制状态
if (shouldChangeStyleParam !== undefined) {
setShouldChangeStyle(shouldChangeStyleParam)
}
// 保存当前选择的 toolId 到 Redux
if (!isToolBtn && toolId) {
// 提质增效模式,保存 toolId
dispatch(setCurrentToolId(toolId))
// eslint-disable-next-line no-console
console.log('✅ [Home] 已保存 toolId 到 Redux:', toolId)
}
else {
// 通用模式,清除 toolId
dispatch(clearCurrentToolId())
// eslint-disable-next-line no-console
console.log('🔄 [Home] 已清除 Redux 中的 toolId')
}
_handleToolClick(isToolBtn, toolId)
......@@ -198,7 +182,7 @@ finally {
return () => {
window.removeEventListener('toolButtonClick', handleToolClickEvent as EventListener)
}
}, [_handleToolClick, dispatch])
}, [_handleToolClick, isToolBtnActive, shouldChangeStyle, dispatch])
const login = useCallback(async () => {
// 防止重复调用
......@@ -276,30 +260,41 @@ finally {
<div className="flex-1 items-center pt-[24px] sm:pt-[32px] scrollbar-hide">
<div className="w-full">
<div className="flex justify-center gap-[20px]">
{/* 左侧区域 - 常见问题 */}
{/* 左侧区域 - 产品问答和您可以试着问我 */}
<div
className="flex flex-col gap-[20px] items-center overflow-y-auto scrollbar-hide"
style={{
height: 'calc(-64px + 100vh)',
background: 'linear-gradient(180deg, #F0F8FF 0%, #FFFFFF 50%, #FFFFFF 100%)',
borderRadius: '24px',
}}
style={{ height: shouldChangeStyle ? 'calc(-64px + 100vh)' : '500px', background: shouldChangeStyle ? 'linear-gradient(180deg, #F0F8FF 0%, #FFFFFF 50%, #FFFFFF 100%)' : '', borderRadius: '24px' }}
>
{!shouldChangeStyle && (
<motion.div className="w-full sm:w-auto" {...getAnimationProps(2)}>
<QuestionList
questions={productQuestions.content}
dotColor="#D4CCFF"
background="linear-gradient(180deg, #EBE6FF 0%, #FFFFFF 50%, #FFFFFF 100%)"
title={productQuestions.configName}
iconImg={HomeIcon1}
isToolBtn={shouldChangeStyle}
isLoaded={isDataLoaded}
/>
</motion.div>
)}
<motion.div className="w-full sm:w-auto" {...getAnimationProps(3)}>
<QuestionList
questions={otherQuestions.content}
dotColor="#CBECFF"
background="linear-gradient(180deg, #F0F8FF 0%, #FFFFFF 50%, #FFFFFF 100%)"
height="288px"
height={shouldChangeStyle ? '288px' : 'auto'}
title={otherQuestions.configName}
iconImg={HomeIcon2}
isToolBtn
isToolBtn={shouldChangeStyle}
isLoaded={isDataLoaded}
/>
</motion.div>
<div>
<img src={SmartIce} alt="Smart Ice" className="w-[260px] h-[218px] mt-[-12px] object-contain" />
</div>
{shouldChangeStyle && (
<div>
<img src={SmartIce} alt="Smart Ice" className="w-[260px] h-[218px] mt-[-12px] object-contain" />
</div>
)}
</div>
{/* 右侧区域 */}
<div className="hidden sm:flex flex-1 h-full">
......
......@@ -98,11 +98,7 @@ const QuestionListBase: React.FC<QuestionListProps & WithAuthProps> = ({
// 使用现有会话
dispatch(setCurrentConversation(targetConversationId))
dispatch(setShouldSendQuestion(item))
// 从会话列表中查找对应的 toolId,默认为空字符串
const targetConversation = conversations.find(conv => conv.conversationId === targetConversationId)
const toolId = targetConversation?.toolId || ''
const toolIdParam = `?toolId=${toolId}`
navigate(`/chat/${targetConversationId}${toolIdParam}`)
navigate(`/chat/${targetConversationId}`)
}
else {
// 如果没有现有会话,仍然创建新会话(向后兼容)
......@@ -139,7 +135,7 @@ const QuestionListBase: React.FC<QuestionListProps & WithAuthProps> = ({
</div>
</div>
{showRefresh
? (
? (
<div onClick={handleRefresh} className="flex-shrink-0 ml-[8px] flex items-center gap-[4px]">
<div className="cursor-pointer">
<motion.div
......@@ -155,18 +151,18 @@ const QuestionListBase: React.FC<QuestionListProps & WithAuthProps> = ({
</div>
<div className="text-[12px] text-[#29B6FD] cursor-pointer">换一换</div>
</div>
)
: null}
)
: null}
</h3>
{isLoaded && questions && questions.length === 0
? (
<div className="mt-[34px] flex flex-col items-center justify-center h-[200px]">
<div className="flex flex-col items-center">
<Image src={emptyIcon} alt="空数据" className="w-[72px] h-[72px]" />
<div className="mt-[16px] text-[14px] text-[#27353C]">问题正在路上...</div>
</div>
</div>
)
<div className="mt-[34px] flex flex-col items-center justify-center h-[200px]">
<div className="flex flex-col items-center">
<Image src={emptyIcon} alt="空数据" className="w-[72px] h-[72px]" />
<div className="mt-[16px] text-[14px] text-[#27353C]">问题正在路上...</div>
</div>
</div>
)
: (
<motion.ul
key={displayedItems.join(',')}
......@@ -193,17 +189,14 @@ const QuestionListBase: React.FC<QuestionListProps & WithAuthProps> = ({
className="text-left bg-[#F8FBFF] w-full text-[#333] rounded-[23px] data-[hover=true]:bg-[#F0F8FF] data-[hover=true]:text-primary h-8"
>
<div className="w-full text-nowrap text-ellipsis overflow-hidden">
<span
className="w-[6px] h-[6px] rounded-full inline-block mr-[6px]"
style={{ backgroundColor: dotColor }}
/>
<span className="w-[6px] h-[6px] rounded-full inline-block mr-[6px]" style={{ backgroundColor: dotColor }}></span>
<span className="ml-[8px]">{item}</span>
</div>
</Button>
</motion.li>
))}
</motion.ul>
)}
)}
</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