Commit 7437b1d0 by Liu

feat: update chat answers

parent 8ca45a16
......@@ -48,6 +48,7 @@
"@types/node": "^22.0.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"baseline-browser-mapping": "^2.8.31",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.9",
......@@ -7811,6 +7812,16 @@
"node": "^4.5.0 || >= 5.9"
}
},
"node_modules/baseline-browser-mapping": {
"version": "2.8.31",
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz",
"integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
}
},
"node_modules/big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz",
......
......@@ -80,6 +80,7 @@
"@types/node": "^22.0.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"baseline-browser-mapping": "^2.8.31",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.9",
......
......@@ -15,7 +15,7 @@ import { FilePreviewModal } from '@/components/FilePreviewModal'
interface ChatAnswerAttachmentProps {
answer: Answer
isLastAnswer?: boolean
onSubmitQuestion?: (question: string, productCode?: string) => void
onSubmitQuestion?: (question: string, productCode?: string, toolId?: 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) => void
onSubmitQuestion: (question: string, productCode?: string, toolId?: string) => void
}
export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex, isLastAnswer, onSubmitQuestion }) => {
......@@ -51,9 +51,10 @@ 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={`${item.recordId}-${index}`}>
<div className="chatItemBotContainer w-full" key={itemKey}>
<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) => void
onSubmitQuestion: (question: string, productCode?: string, toolId?: string) => void
}
function CheckIcon({ ...props }) {
......
......@@ -3,15 +3,22 @@ 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) => void
onSubmitQuestion: (question: string, productCode?: string, toolId?: 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 || '')
......@@ -32,8 +39,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, 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]">
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]">
<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) => void
onSubmitQuestion: (question: string, productCode?: string, toolId?: string) => void
}
export const ChatAnswerShower: React.FC<ChatAnswerShowerProps> = ({ answer, isLastAnswer, onSubmitQuestion }) => {
......
......@@ -5,7 +5,6 @@ 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'
......@@ -48,10 +47,7 @@ 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: [] })
......@@ -59,6 +55,32 @@ 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)
......@@ -70,14 +92,11 @@ 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)
}
}
}
......@@ -90,7 +109,7 @@ finally {
setIsLoading(false)
setIsDataLoaded(true)
}
}, [])
}, [fetchGeneralQuestions])
const initConversation = () => {
const fromCollect = location.state?.fromCollect
......@@ -138,11 +157,12 @@ finally {
}
}
else if (isToolBtn) {
// 通用模式:恢复刷新时的状态,包括左侧内容也要恢复到初始状态
setOtherQuestions(originalOtherQuestions)
setIsDataLoaded(true) // 恢复原始数据时标记为已加载
// 通用模式:调用接口获取通用问题,toolId 传空字符串
setIsDataLoaded(false)
await fetchGeneralQuestions()
setIsDataLoaded(true)
}
}, [originalOtherQuestions])
}, [originalOtherQuestions, fetchGeneralQuestions])
// 进入页面时,如果路由中有 toolId 且不为空,执行工具点击逻辑
useEffect(() => {
......@@ -152,8 +172,6 @@ finally {
if (toolIdFromUrl && toolIdFromUrl !== '') {
// 有 toolId,执行工具点击逻辑
dispatch(setCurrentToolId(toolIdFromUrl))
setShouldChangeStyle(true)
setIsToolBtnActive(false)
_handleToolClick(false, toolIdFromUrl)
}
}
......@@ -162,33 +180,16 @@ finally {
// 监听工具按钮点击事件
useEffect(() => {
const handleToolClickEvent = (event: CustomEvent) => {
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)
}
const { isToolBtn, toolId } = event.detail
// 保存当前选择的 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)
......@@ -197,7 +198,7 @@ finally {
return () => {
window.removeEventListener('toolButtonClick', handleToolClickEvent as EventListener)
}
}, [_handleToolClick, isToolBtnActive, shouldChangeStyle, dispatch])
}, [_handleToolClick, dispatch])
const login = useCallback(async () => {
// 防止重复调用
......@@ -275,41 +276,30 @@ 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: shouldChangeStyle ? 'calc(-64px + 100vh)' : '500px', background: shouldChangeStyle ? 'linear-gradient(180deg, #F0F8FF 0%, #FFFFFF 50%, #FFFFFF 100%)' : '', borderRadius: '24px' }}
style={{
height: 'calc(-64px + 100vh)',
background: '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={shouldChangeStyle ? '288px' : 'auto'}
height="288px"
title={otherQuestions.configName}
iconImg={HomeIcon2}
isToolBtn={shouldChangeStyle}
isToolBtn
isLoaded={isDataLoaded}
/>
</motion.div>
{shouldChangeStyle && (
<div>
<img src={SmartIce} alt="Smart Ice" className="w-[260px] h-[218px] mt-[-12px] object-contain" />
</div>
)}
<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">
......
......@@ -139,7 +139,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 +155,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,14 +193,17 @@ 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>
<span
className="w-[6px] h-[6px] rounded-full inline-block mr-[6px]"
style={{ backgroundColor: dotColor }}
/>
<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