Commit 8c98a5a1 by Liu

fix:点击各按钮时传参高亮展示存入toolID

parent 57f63bd8
业务洞察最新工作流逻辑
第一步问问题 点击业务洞察按钮【开启主工作流】
会返回workflowSessionId
第二步问问题 拿主工作流返回的workflowSessionId,recordType:A12和输入的问题继续调用提问接口【提问主工作流】
会返回开启子工作流需要的answer
answer不为json:正常提问
answer为json:返回子工作流recordType,question
第三步问问题 拿第二步返回的recordType【开启子工作流】
返回workflowSessionId,cardList
判断cardList是否为空,是否展示输入框
第四步问问题 拿第三步返回的cardList【提问子工作流】
cardList为空,则正常传question和recordType和子工作流workflowSessionId,不传paramMap
cardList不为空,则传recordType,展示输入框填入参数后添加到paramMap中和子工作流workflowSessionId传给提问接口
子工作流返回:子工作流answer内end=true时跳出子工作流,recordType:A12再次开启主工作流,此时清空paramMap
若是正常回答完成后开启主工作流不需要传question
若子工作流回答不了的情况下需要带着answer.question提问主工作流
......@@ -39,14 +39,30 @@ export function fetchDeleteUserConversation(conversationIdList: string[]) {
* 获取历史会话ID
* @params
*/
export function fetchSessionConversationId<T>(data: T) {
export async function fetchSessionConversationId<T>(data: T) {
const payload = (data || {}) as Record<string, any>
const sessionToolId = safeSessionStorageGetItem('currentToolId') || ''
// 首次进入时 `currentToolId` 可能还没被 `ChatEditor` 写入 sessionStorage。
// 这里做一个极短轮询,避免第一次请求 `toolId/busiId` 读到空字符串。
let sessionToolId = safeSessionStorageGetItem('currentToolId') || ''
if (!sessionToolId) {
const maxWaitMs = 600
const intervalMs = 100
const start = Date.now()
while (Date.now() - start < maxWaitMs) {
await new Promise(resolve => setTimeout(resolve, intervalMs))
sessionToolId = safeSessionStorageGetItem('currentToolId') || ''
if (sessionToolId) {
break
}
}
}
const requestData = {
...payload,
busiType: '01',
busiId: sessionToolId || payload?.busiId || '',
toolId: sessionToolId || payload?.toolId || '',
busiId: sessionToolId,
toolId: sessionToolId,
}
return http.post('/conversation/api/conversation/mobile/v1/get_user_conversation_session', requestData)
}
......@@ -36,7 +36,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
const isAsking = useAppSelector((state: RootState) => state.chat.isAsking)
const [toolList, setToolList] = useState<any[]>([])
const [selectedToolId, setSelectedToolId] = useState<string | null>(null)
const [isToolBtnActive, setIsToolBtnActive] = useState<boolean>(true)
const [_isToolBtnActive, setIsToolBtnActive] = useState<boolean>(true)
const currentToolId = useAppSelector((state: RootState) => state.conversation.currentToolId)
const [showToolQuestion, setShowToolQuestion] = useState<boolean>(false)
const [sessionToolId, setSessionToolId] = useState<string | null>(null)
......@@ -146,12 +146,17 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
})
if (res?.data && Array.isArray(res.data) && res.data.length > 0) {
setToolList(res.data)
// 仅在“没有任何持久来源”时,才写入默认工具,避免覆盖已有的会话 toolId
const storedToolId = safeSessionStorageGetItem('currentToolId')
const hasAnyToolSource = Boolean(storedToolId || currentToolId || toolIdFromUrl)
if (!hasAnyToolSource) {
// 存入第0个工具信息到缓存
const defaultTool = res.data[0]
safeSessionStorageSetItem('currentToolId', defaultTool.toolId)
safeSessionStorageSetItem('currentToolName', defaultTool.toolName)
}
}
}
catch (error) {
console.error('获取工具列表失败:', error)
}
......@@ -160,25 +165,20 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 根据 currentToolId 以及 sessionStorage 中的记录决定高亮逻辑
useEffect(() => {
console.log('[chat-toolid-log] ChatEditor currentToolId:', currentToolId, '| sessionToolId:', sessionToolId, '| fromCollect:', fromCollect)
// 关键修复:首次进入/切换路由时,Redux 里的 toolId 可能先到,但 sessionToolId 还没同步。
// 此时不应该“清空 Redux / 写空缓存”,而是用 Redux 回填 sessionStorage,避免出现“高亮有值但缓存为空”。
if (currentToolId && !sessionToolId && !fromCollect) {
// 清除过期的 Redux 值
dispatch(clearCurrentToolId())
console.log('[chat-toolid-log] ChatEditor 清除 Redux - 逻辑三 - line:155')
// 如果 URL 中还有 toolId,也清除它
if (toolIdFromUrl) {
const newSearchParams = new URLSearchParams(searchParams)
newSearchParams.delete('toolId')
setSearchParams(newSearchParams, { replace: true })
}
setSelectedToolId(null)
setIsToolBtnActive(true)
// 存入默认高亮按钮信息到缓存
setSelectedToolId(currentToolId)
setIsToolBtnActive(false)
// 用 Redux 回填缓存,确保后续接口/其他组件读取到一致的 toolId
safeSessionStorageSetItem('currentToolId', String(currentToolId))
if (toolList.length > 0) {
const defaultTool = toolList[0]
safeSessionStorageSetItem('currentToolId', defaultTool.toolId)
safeSessionStorageSetItem('currentToolName', defaultTool.toolName)
console.log('[chat-toolid-log] ChatEditor 逻辑三 setItem:', defaultTool.toolId, '| line:169')
const currentTool = toolList.find(tool => String(tool.toolId) === String(currentToolId))
if (currentTool?.toolName) {
safeSessionStorageSetItem('currentToolName', currentTool.toolName)
}
}
setSessionToolId(String(currentToolId))
return
}
......@@ -188,11 +188,14 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
setSearchParams(newSearchParams, { replace: true })
setSelectedToolId(null)
setIsToolBtnActive(true)
// 存入默认高亮按钮信息到缓存
// 默认高亮第0个工具:直接写入第0个工具的 toolId(与你的期望保持一致)
if (toolList.length > 0) {
const defaultTool = toolList[0]
safeSessionStorageSetItem('currentToolId', defaultTool.toolId)
safeSessionStorageSetItem('currentToolId', String(defaultTool.toolId))
safeSessionStorageSetItem('currentToolName', defaultTool.toolName)
setSessionToolId(String(defaultTool.toolId))
setSelectedToolId(String(defaultTool.toolId))
setIsToolBtnActive(false)
}
return
}
......@@ -236,11 +239,14 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
else {
setSelectedToolId(null)
setIsToolBtnActive(true)
// 存入默认高亮按钮信息到缓存
// 默认高亮第0个工具:直接写入第0个工具的 toolId(与你的期望保持一致)
if (toolList.length > 0) {
const defaultTool = toolList[0]
safeSessionStorageSetItem('currentToolId', defaultTool.toolId)
safeSessionStorageSetItem('currentToolId', String(defaultTool.toolId))
safeSessionStorageSetItem('currentToolName', defaultTool.toolName)
setSessionToolId(String(defaultTool.toolId))
setSelectedToolId(String(defaultTool.toolId))
setIsToolBtnActive(false)
}
}
}, [currentToolId, sessionToolId, toolIdFromUrl, searchParams, setSearchParams, fromCollect, toolList])
......@@ -313,17 +319,9 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
return
if (checkAuth()) {
if (content.trim()) {
// 只在提质增效模式下传递 toolId,制度活化不传
let toolId: string | undefined
if (selectedToolId) {
// 提质增效模式:使用选中的 toolId
toolId = selectedToolId
}
else {
// 制度活化:使用第0个工具的 toolId
toolId = toolList[0]?.toolId
}
// 提交时优先使用当前选中 toolId,其次使用缓存中的 currentToolId
// 不在点击时/提交时使用第0个默认值兜底,避免覆盖用户真实选择
const toolId = selectedToolId || safeSessionStorageGetItem('currentToolId') || undefined
onSubmit?.(content.trim(), toolId)
setContent('')
if (editorRef.current) {
......@@ -358,26 +356,27 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 获取默认工具信息
const defaultTool = toolList.length > 0 ? toolList[0] : { toolId: '', toolName: '默认工具' }
// 通知上层开始 loading
onToolClick?.(true, undefined, defaultTool.toolName, false, undefined, true)
onToolClick?.(true, String(defaultTool.toolId || ''), defaultTool.toolName, false, undefined, true)
try {
// 重置提交按钮状态
dispatch(setIsAsking(false))
// 先更新 Redux,确保状态同步
dispatch(clearCurrentToolId())
// 你的期望:默认高亮第0个且写入缓存,因此这里不清空 toolId,而是把第0个当作“已选中”
const defaultToolId = String(defaultTool.toolId || '')
dispatch(setCurrentToolId(defaultToolId))
// 立即更新本地状态,让 UI 立即响应
setIsToolBtnActive(true)
setSelectedToolId(null)
setIsToolBtnActive(false)
setSelectedToolId(defaultToolId)
safeSessionStorageRemoveItem('showToolQuestion')
// 存入默认高亮按钮信息到缓存
if (toolList.length > 0) {
safeSessionStorageRemoveItem('currentToolId')
safeSessionStorageSetItem('currentToolId', defaultToolId)
safeSessionStorageSetItem('currentToolName', defaultTool.toolName)
setSessionToolId(null)
console.log('[chat-toolid-log] ChatEditor defaultTool clearSessionToolId', '| line:364')
setSessionToolId(defaultToolId)
console.log('[chat-toolid-log] ChatEditor defaultTool setSessionToolId:', defaultToolId, '| line:364')
}
setShowToolQuestion(false)
// 先通知上层更新欢迎语(即便后续接口异常也能生效)
onToolClick?.(true, undefined, defaultTool.toolName, false)
onToolClick?.(true, defaultToolId, defaultTool.toolName, false)
// 清空路由中的 toolId 参数
if (toolIdFromUrl) {
const newSearchParams = new URLSearchParams(searchParams)
......@@ -388,21 +387,18 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 先调用 fetchSessionConversationId 获取会话ID
// 等待 token 就绪后再调用接口
await waitForToken()
const requestData = {
busiId: defaultTool.toolId,
}
const res = await fetchSessionConversationId(requestData)
const res = await fetchSessionConversationId({})
if (res?.data?.conversationId) {
const conversationId = res.data.conversationId
// 获取到会话ID后,通知上层更新欢迎语并传递 conversationId
onToolClick?.(true, undefined, defaultTool.toolName, false, conversationId)
onToolClick?.(true, defaultToolId, defaultTool.toolName, false, conversationId)
// 在 navigate 之前设置标记,避免 Chat 组件的 useEffect 重复调用接口
sessionStorage.setItem('toolHistoryLoading', conversationId)
// 更新路由到新的会话ID
navigate(`/chat/${conversationId}`, {
replace: true,
state: {
toolId: defaultTool.toolId,
toolId: defaultToolId,
skipHistoryLoad: true, // 标记跳过历史记录加载,因为 ChatEditor 会处理
},
})
......@@ -414,7 +410,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
window.dispatchEvent(new CustomEvent('toolHistoryLoaded', {
detail: {
conversationId,
toolId: defaultTool.toolId,
toolId: defaultToolId,
toolName: defaultTool.toolName,
qaRecords: qaRes?.data || [],
},
......@@ -430,7 +426,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
}
finally {
// 通知上层结束 loading
onToolClick?.(true, undefined, defaultTool.toolName, false, undefined, false)
onToolClick?.(true, String(defaultTool.toolId || ''), defaultTool.toolName, false, undefined, false)
}
}
......@@ -461,10 +457,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 先调用 fetchSessionConversationId 获取会话ID
// 等待 token 就绪后再调用接口
await waitForToken()
const requestData = {
busiId: tool.toolId,
}
const res = await fetchSessionConversationId(requestData)
const res = await fetchSessionConversationId({})
if (res?.data?.conversationId) {
const conversationId = res.data.conversationId
// 获取到会话ID后,通知上层更新欢迎语并传递 conversationId
......@@ -495,7 +488,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
}))
// 提质增效模式下,延迟触发自动提问接口
// 使用双重 requestAnimationFrame 确保 Chat 组件的 currentIdRef 已经更新
if (tool.toolId === '6712395743241') {
if (String(tool.toolId) === '6712395743241') {
console.log('[ChatEditor] 提质增效模式 - 准备延迟触发 autoSubmitQuestion', { conversationId, toolId: tool.toolId })
requestAnimationFrame(() => {
requestAnimationFrame(() => {
......@@ -512,6 +505,21 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
})
})
}
// 业务洞察模式:点击按钮后先开启主工作流(不展示用户问题)
if (String(tool.toolId) === '6712395743242') {
console.log('[ChatEditor] 业务洞察模式 - 准备触发 startBusinessInsight', { conversationId, toolId: tool.toolId })
requestAnimationFrame(() => {
requestAnimationFrame(() => {
console.log('[ChatEditor] 业务洞察模式 - 触发 startBusinessInsight 事件', { conversationId, toolId: tool.toolId })
window.dispatchEvent(new CustomEvent('startBusinessInsight', {
detail: {
conversationId,
toolId: tool.toolId,
},
}))
})
})
}
// 清除标记,避免影响后续路由切换
sessionStorage.removeItem('toolHistoryLoading')
}
......@@ -660,9 +668,8 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
const toolIdStr = String(tool.toolId)
const isSelectedByState = selectedToolId && toolIdStr === String(selectedToolId)
const isSelectedBySession = !selectedToolId && sessionToolId && toolIdStr === String(sessionToolId)
// 默认高亮第0个工具:路由内没有 toolId 或 toolId 为空时默认高亮,点击后也要高亮
const isDefaultHighlight = index === 0 && isToolBtnActive && !selectedToolId && !sessionToolId && !toolIdFromUrl
const isSelected = isSelectedByState || isSelectedBySession || isDefaultHighlight
// 高亮仅根据“真实选择状态”决定:点击后的 selectedToolId 或缓存 sessionToolId
const isSelected = Boolean(isSelectedByState || isSelectedBySession)
const baseBtnClass
= 'w-auto h-[32px] px-3 rounded-full shadow-none text-[12px] flex items-center gap-2 transition-all duration-200 border'
......@@ -673,15 +680,11 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
const selectedColor = isSelected ? 'primary' : 'default'
const handleButtonPress = async () => {
// 点击即同步缓存,制度活化清空 toolId,工具模式写入当前 toolId
if (index === 0) {
safeSessionStorageRemoveItem('currentToolId')
setSessionToolId(null)
}
else {
// 你的期望:默认高亮第0个也要写入缓存,因此无论点击哪个按钮都写入该按钮的 toolId
safeSessionStorageSetItem('currentToolId', toolIdStr)
setSessionToolId(toolIdStr)
}
setSelectedToolId(toolIdStr)
setIsToolBtnActive(false)
safeSessionStorageSetItem('currentToolName', tool.toolName || '')
// 高亮状态直接返回,避免重复触发后续逻辑
if (isSelected)
......
import { useCallback, useEffect, useRef } from 'react'
import type { ChatRecord } from '@/types/chat'
type SubmitQuestion = (
question: string,
productCode?: string,
toolId?: string,
extraParams?: {
paramMap?: Record<string, string>
recordId?: string
recordType?: string
workFlowSessionId?: string
isReask?: boolean
busiType?: string
},
workFlowSessionId?: string,
recordId?: string,
) => void
function tryParseWorkflowDirective(answerText: string): { recordType: string, question?: string } | null {
const trimmed = (answerText || '').trim()
if (!trimmed)
return null
if (!(trimmed.startsWith('{') && trimmed.endsWith('}')))
return null
try {
const obj = JSON.parse(trimmed)
if (obj && typeof obj === 'object' && typeof obj.recordType === 'string') {
return {
recordType: obj.recordType,
question: typeof obj.question === 'string' ? obj.question : undefined,
}
}
}
catch {
// ignore
}
return null
}
export function useBusinessInsightWorkflow(params: {
enabled: boolean
toolId: string
conversationIdRef: React.MutableRefObject<string | undefined>
items: ChatRecord[]
onSubmitQuestion: SubmitQuestion
}) {
const { enabled, toolId, conversationIdRef, items, onSubmitQuestion } = params
const modeRef = useRef<'main' | 'sub'>('main')
const startMain = useCallback(() => {
if (!enabled)
return
modeRef.current = 'main'
// 开启主工作流:不传 question,避免展示用户气泡
onSubmitQuestion('', undefined, toolId, { recordType: 'A12', busiType: '01' })
}, [enabled, onSubmitQuestion, toolId])
// 监听外部事件:点击业务洞察按钮后开启主工作流
useEffect(() => {
if (!enabled)
return
const handler = (event: CustomEvent) => {
const { conversationId, toolId: eventToolId } = (event.detail || {}) as any
if (String(eventToolId ?? '') !== String(toolId))
return
if (conversationId && conversationId !== conversationIdRef.current)
return
startMain()
}
window.addEventListener('startBusinessInsight', handler as EventListener)
return () => window.removeEventListener('startBusinessInsight', handler as EventListener)
}, [enabled, startMain, toolId, conversationIdRef])
// 最小状态机:主流程 answer=JSON → 开启子流程;子流程 end=true → 回主流程
useEffect(() => {
if (!enabled)
return
const last = [...items].reverse().find(r => r.role === 'ai' && r.answerList?.[0])
const lastAnswer = last?.answerList?.[0]
if (!lastAnswer)
return
// 只在本轮流式结束后处理,避免边打字边误触发
if (lastAnswer.endAnswerFlag !== true)
return
// 子流程结束回主流程(后端约定:answer.end === true)
const end = (lastAnswer as any)?.end === true
if (modeRef.current === 'sub' && end) {
modeRef.current = 'main'
const backQuestion = (lastAnswer as any)?.question
const q = typeof backQuestion === 'string' ? backQuestion.trim() : ''
if (q) {
onSubmitQuestion(q, undefined, toolId, { recordType: 'A12', busiType: '01' })
}
else {
onSubmitQuestion('', undefined, toolId, { recordType: 'A12', busiType: '01' })
}
return
}
// 主流程收到 JSON 指令:开启子流程
if (modeRef.current === 'main') {
const directive = tryParseWorkflowDirective(lastAnswer.answer)
if (directive?.recordType && directive.recordType !== 'A12') {
modeRef.current = 'sub'
// 第三步:开启子工作流(不传 question,避免展示用户气泡)
onSubmitQuestion('', undefined, toolId, { recordType: directive.recordType, busiType: '01' })
}
}
}, [enabled, items, onSubmitQuestion, toolId])
return {
startMain,
}
}
......@@ -21,6 +21,7 @@ import { useAppDispatch, useAppSelector } from '@/store/hook'
import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react'
import { setIsAsking } from '@/store/chatSlice'
import SdreamLoading from '@/components/SdreamLoading'
import { useBusinessInsightWorkflow } from '@/hooks/useBusinessInsightWorkflow'
function makeLocalGroupId(prefix: string) {
try {
......@@ -308,7 +309,7 @@ export const Chat: React.FC = () => {
// 判断是否为提质增效模式的自动调用(空问题)
const isEmptyQuestion = !question.trim()
const isQualityImprovement = resolvedToolId === '6712395743241'
const isBusinessInsight = resolvedToolId === '6712395743242'
const isBusinessInsight = String(resolvedToolId ?? '') === '6712395743242'
console.log('工具id3', {
question,
questionLength: question.length,
......@@ -455,6 +456,12 @@ export const Chat: React.FC = () => {
requestBody.recordType = '01'
}
// 业务洞察:toolId 为 6712395743242(基础逻辑:默认 recordType=A12,允许 extraParams 覆盖)
if (String(resolvedToolId ?? '') === '6712395743242' && !extraParams?.recordType) {
requestBody.busiType = '01'
requestBody.recordType = 'A12'
}
fetchStreamResponse(
fetchUrl,
requestBody,
......@@ -586,16 +593,37 @@ export const Chat: React.FC = () => {
})
}
// 业务洞察最小工作流状态机(主↔子切换)
useBusinessInsightWorkflow({
enabled: String(safeSessionStorageGetItem('currentToolId') || currentToolId || '') === '6712395743242',
toolId: '6712395743242',
conversationIdRef: currentIdRef,
items: allItems,
onSubmitQuestion: handleSubmitQuestion,
})
/** 处理 workflow 表单提交 */
const handleWorkflowSubmit = useCallback(async (paramMap: Record<string, string>, recordId?: string, workFlowSessionId?: string) => {
// 优先取最近一次带 cardList 的 AI 回答里的 recordType(业务洞察子工作流会用到)
let recordType: string | undefined
for (let i = allItems.length - 1; i >= 0; i--) {
const item = allItems[i]
const a = item.role === 'ai' ? item.answerList?.[0] : undefined
const hasCards = Array.isArray(a?.cardList) && a!.cardList.length > 0
if (a && hasCards && a.recordType) {
recordType = a.recordType
break
}
}
// workflow 提交时,question 可以为空字符串,但需要传递 paramMap、recordId 和 workFlowSessionId
await handleSubmitQuestion('', undefined, undefined, {
paramMap,
recordId,
workFlowSessionId,
recordType: 'A01',
recordType: recordType || 'A01',
busiType: '01',
})
}, [])
}, [allItems, handleSubmitQuestion])
/** 获取qa记录 */
const getUserQaRecordPage = useCallback(async (conversationId: string, toolId?: string) => {
......@@ -1110,14 +1138,19 @@ export const Chat: React.FC = () => {
// 如果历史记录中 firstQaFlag 为 true,隐藏 UI 但保留组件渲染(确保自动提交逻辑能执行)
const shouldHideHistoryItem = record.role === 'ai' && record.answerList?.[0]?.firstQaFlag === true
// 如果是自动调用提问接口(空问题且是流式返回的新记录),不显示问题和回答
// 判断条件:question === undefined(空问题)且 isShow === false(流式返回的新记录,不是历史记录)
const shouldHideEmptyQuestionAnswer = record.role === 'ai' && record.question === undefined && record.answerList?.[0]?.isShow === false
// 白名单:当同一条 AI 记录里同时有 question + answer(且 firstQaFlag 不为 true)时,强制展示,避免误隐藏
const hasQuestion = typeof record.question === 'string' && record.question.trim().length > 0
const firstAnswer = record.answerList?.[0]
const hasAnswer = typeof firstAnswer?.answer === 'string' && firstAnswer.answer.trim().length > 0
const hasCardList = Array.isArray(firstAnswer?.cardList) && firstAnswer!.cardList.length > 0
// 自动调用提问接口(空 question)时:
// - 第一步/第三步“开启工作流”不应展示在页面内(通常没有 answer,也没有 cardList)
// - 但第三步可能返回 cardList(需要展示表单),因此仅在“既无 answer 也无 cardList”时才隐藏
const shouldHideEmptyQuestionAnswer = record.role === 'ai'
&& record.question === undefined
&& firstAnswer?.isShow === false
&& !hasAnswer
&& !hasCardList
// 白名单:当同一条 AI 记录里同时有 question + answer(且 firstQaFlag 不为 true)时,强制展示,避免误隐藏
const hasQuestion = typeof record.question === 'string' && record.question.trim().length > 0
const shouldForceShow = record.role === 'ai'
&& hasQuestion
&& (hasAnswer || hasCardList)
......
......@@ -79,6 +79,7 @@ export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer
recordType,
)
if (res.ok) {
console.log('推荐问题列表', res.data.questionList)
setQuestionList(res.data.questionList)
}
}
......
......@@ -7,14 +7,14 @@ import styles from './Home.module.less'
import { QuestionList } from './components/QuestionList'
import HomeIcon2 from '@/assets/homeIcon2.png'
import SmartIce from '@/assets/smart-ice.png'
import { clearCurrentToolId, fetchConversations, setCurrentConversation, setCurrentToolId, setNavigationFlag } from '@/store/conversationSlice'
import { fetchConversations, setCurrentConversation, setCurrentToolId, setNavigationFlag } from '@/store/conversationSlice'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import { fetchEfficiencyQuestionList } from '@/api/home'
import SdreamLoading from '@/components/SdreamLoading'
import { fetchLoginByToken, fetchLoginByUid } from '@/api/common'
import { fetchSessionConversationId } from '@/api/conversation'
import { fetchCheckTokenApi } from '@/api/chat'
import { getUserRolesFromRoute, safeLocalStorageGetItem, safeLocalStorageSetItem, safeSessionStorageGetItem, safeSessionStorageRemoveItem, safeSessionStorageSetItem, waitForToken } from '@/lib/utils'
import { getUserRolesFromRoute, safeLocalStorageGetItem, safeLocalStorageSetItem, safeSessionStorageGetItem, safeSessionStorageSetItem, waitForToken } from '@/lib/utils'
// 从 localStorage 获取 token 的辅助函数
function getTokenFromStorage(): string | null {
......@@ -70,14 +70,13 @@ export const Home: React.FC = () => {
const currentConversationId = useAppSelector(state => state.conversation.currentConversationId)
const hasFetched = useRef(false)
const hasCalledBusinessApis = useRef(false) // 防止业务接口重复调用
const initialToolIdRef = useRef<string>('') // 首次进入时缓存的 toolId,避免登录清理后首次请求丢失
const prevPathRef = useRef<string>(location.pathname) // 记录上一次路径,用于检测从收藏页面返回
// 使用 useState
const [otherQuestions, setOtherQuestions] = useState<any>({ content: [] })
const [isToolBtnActive, setIsToolBtnActive] = useState<boolean>(false)
const [shouldChangeStyle, setShouldChangeStyle] = useState<boolean>(true)
// 保存原始的configType为07的数据
const [originalOtherQuestions, _setOriginalOtherQuestions] = useState<any>({ content: [] })
const [_originalOtherQuestions, _setOriginalOtherQuestions] = useState<any>({ content: [] })
const [token, setToken] = useLocalStorageState<string | undefined>('__TOKEN__', {
defaultValue: '',
......@@ -149,7 +148,7 @@ export const Home: React.FC = () => {
}
// 从 sessionStorage 获取 toolId 并确定 recordType
const toolId = safeSessionStorageGetItem('currentToolId') || initialToolIdRef.current || ''
const toolId = safeSessionStorageGetItem('currentToolId') || ''
const recordType = getRecordTypeByToolId(toolId)
const res = await fetchEfficiencyQuestionList({
......@@ -158,6 +157,7 @@ export const Home: React.FC = () => {
recordType,
})
if (res && res.data && res.data.questionList) {
console.log('推荐问题列表homeNew', res.data.questionList)
setOtherQuestions((prev: any) => ({
...prev,
content: res.data.questionList || [],
......@@ -182,25 +182,6 @@ export const Home: React.FC = () => {
}))
setIsDataLoaded(false) // 重置加载状态
try {
const storedToolId = safeSessionStorageGetItem('currentToolId') || ''
// 首页初始化加载常见问题时,允许忽略路由中的 toolId,避免带入上一次的工具 ID
const shouldForceClearToolId = !storedToolId
let finalToolId = toolId || ''
// 场景:首页首次挂载(ignoreUrlToolId === true)且为制度活化(!isToolBtn && !toolId)
// 此时无论 sessionStorage 中是否残留上一次的 toolId,都强制使用空字符串,避免带入历史工具 ID
if (ignoreUrlToolId && !isToolBtn && !toolId) {
finalToolId = ''
}
else if (shouldForceClearToolId && !isToolBtn) {
finalToolId = ''
}
else if (!finalToolId && !isToolBtn) {
// 仅在工具模式下才使用回退逻辑,避免制度活化误用上一次的 toolId
finalToolId = storedToolId
}
// 优先使用传入的 conversationId,其次从 Redux 获取,最后从路由中获取
const conversationId = conversationIdParam || currentConversationId || (location.pathname.startsWith('/chat/')
? location.pathname.split('/')[2]
......@@ -211,8 +192,8 @@ export const Home: React.FC = () => {
return
}
// 从 sessionStorage 获取 toolId 并确定 recordType
const currentToolId = safeSessionStorageGetItem('currentToolId') || finalToolId || initialToolIdRef.current || ''
// 常见问题只根据缓存 currentToolId 决定 recordType
const currentToolId = safeSessionStorageGetItem('currentToolId') || ''
const recordType = getRecordTypeByToolId(currentToolId)
// 调用真实 API 获取常见问题列表
......@@ -234,7 +215,7 @@ export const Home: React.FC = () => {
finally {
setIsDataLoaded(true) // 无论成功失败都标记为已加载
}
}, [originalOtherQuestions, location.search, location.pathname, currentConversationId])
}, [location.search, location.pathname, currentConversationId])
// 监听工具按钮点击事件
useEffect(() => {
......@@ -244,18 +225,16 @@ export const Home: React.FC = () => {
// 更新样式控制状态
setShouldChangeStyle(true)
// 保存当前选择的 toolId 到 Redux
if (!isToolBtn && toolId) {
// 提质增效模式,保存 toolId
// 统一规则:只要事件里带了 toolId(包括第0个默认按钮),就写入并同步 Redux
if (toolId) {
dispatch(setCurrentToolId(toolId))
safeSessionStorageSetItem('currentToolId', toolId)
console.log('[chat-toolid-log] HomeNew setItem:', toolId, '| line:253')
}
else {
// 制度活化,清除 toolId
dispatch(clearCurrentToolId())
sessionStorage.removeItem('currentToolId')
console.log('[chat-toolid-log] HomeNew removeItem', '| line:260')
// 点击事件没有 toolId 时不覆盖缓存,避免“先清空再被默认第0个回填”
console.log('[chat-toolid-log] HomeNew ignore empty toolId event', '| line:260')
return
}
// 如果事件传递了 conversationId,延迟调用以确保路由更新完成
......@@ -303,23 +282,13 @@ export const Home: React.FC = () => {
const searchParams = new URLSearchParams(url.search)
const from = searchParams.get('from')
const _loginCode = searchParams.get('loginCode')
// 记录首次进入时的 toolId,避免在登录流程清理后丢失首次会话参数
if (!initialToolIdRef.current) {
initialToolIdRef.current = safeSessionStorageGetItem('currentToolId') || ''
}
// 如果有 loginCode,执行 SSO 登录(包括 from=tactics 的情况)
if (_loginCode && viteOutputObj === 'inner') {
// 立即设置标志,防止重复执行(仅组件级别,每次组件挂载时会重置)
hasFetched.current = true
// 每次进入页面调用 sso_login 时,先清空 sessionStorage 中的 currentToolId
// 避免关闭标签页后再次打开时使用上次的历史 toolId
safeSessionStorageRemoveItem('currentToolId')
try {
const res = await fetchLoginByToken(_loginCode)
if (res.data) {
// 登录成功后先清理旧状态,避免沿用上一次的工具模式
dispatch(clearCurrentToolId())
safeSessionStorageRemoveItem('currentToolId')
const currentUrl = new URL(window.location.href)
if (currentUrl.searchParams.has('toolId')) {
currentUrl.searchParams.delete('toolId')
......@@ -383,8 +352,6 @@ export const Home: React.FC = () => {
const actualToken = getTokenFromStorage() || res.data.token
if (actualToken && !hasCalledBusinessApis.current) {
hasCalledBusinessApis.current = true
// 触发自定义事件,通知 ChatEditor 强制重置为制度活化
window.dispatchEvent(new CustomEvent('forceResetToGeneralMode'))
const conversationId = await getSessionConversationId()
dispatch(fetchConversations())
// 2. 拉取常见问题等业务数据
......@@ -433,9 +400,6 @@ export const Home: React.FC = () => {
try {
const res = await fetchLoginByUid('123123')
if (res.data) {
// 登录成功后先清理旧状态,避免沿用上一次的工具模式
dispatch(clearCurrentToolId())
safeSessionStorageRemoveItem('currentToolId')
const currentUrl = new URL(window.location.href)
if (currentUrl.searchParams.has('toolId')) {
currentUrl.searchParams.delete('toolId')
......@@ -496,8 +460,6 @@ export const Home: React.FC = () => {
const actualToken = getTokenFromStorage() || res.data.token
if (actualToken && !hasCalledBusinessApis.current) {
hasCalledBusinessApis.current = true
// 触发自定义事件,通知 ChatEditor 强制重置为制度活化
window.dispatchEvent(new CustomEvent('forceResetToGeneralMode'))
const conversationId = await getSessionConversationId()
dispatch(fetchConversations())
// 2. 拉取常见问题等业务数据
......@@ -523,13 +485,6 @@ export const Home: React.FC = () => {
login()
}, []) // 依赖数组为空,只在组件挂载时执行一次
useEffect(() => {
// 只有登录成功(拿到 token)后才拉业务接口
if (!token)
return
dispatch(clearCurrentToolId())
}, [token, dispatch])
// 监听路径变化,检测从收藏页面返回时刷新问题列表
useEffect(() => {
const currentPath = location.pathname
......
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