Commit 1f2aae8c by Liu

fix:调用常见问题逻辑

parent 0cfabedf
import { subDays, subMinutes } from 'date-fns' // NOTE: 该文件中的 mock 数据已废弃,保留代码仅作参考,全部使用注释禁用。
import type { Conversation } from '@/types/conversation' // import { subDays, subMinutes } from 'date-fns'
// import type { Conversation } from '@/types/conversation'
interface ConversationPageResponse { //
code: string // interface ConversationPageResponse {
message: string // code: string
data: { // message: string
records: Conversation[] // data: {
total: number // records: Conversation[]
size: number // total: number
current: number // size: number
pages: number // current: number
} // pages: number
ok: boolean // }
} // ok: boolean
// }
const now = new Date() //
// const now = new Date()
function formatDate(date: Date) { //
return date.toISOString().slice(0, 19).replace('T', ' ') // function formatDate(date: Date) {
} // return date.toISOString().slice(0, 19).replace('T', ' ')
// }
function createMockConversation({ //
id, // function createMockConversation({
title, // id,
daysAgo = 0, // title,
minutesAgo = 0, // daysAgo = 0,
qaNum, // minutesAgo = 0,
toolId, // qaNum,
isCurrent = false, // toolId,
}: { // isCurrent = false,
id: string // }: {
title: string // id: string
daysAgo?: number // title: string
minutesAgo?: number // daysAgo?: number
qaNum: number // minutesAgo?: number
toolId?: string // qaNum: number
isCurrent?: boolean // toolId?: string
}): Conversation { // isCurrent?: boolean
const endTime = subMinutes(subDays(now, daysAgo), minutesAgo) // }): Conversation {
const startTime = subMinutes(endTime, 5) // const endTime = subMinutes(subDays(now, daysAgo), minutesAgo)
return { // const startTime = subMinutes(endTime, 5)
conversationId: id, // return {
conversationTitle: title, // conversationId: id,
currentConversationFlag: isCurrent, // conversationTitle: title,
startTime: formatDate(startTime), // currentConversationFlag: isCurrent,
endTime: formatDate(endTime), // startTime: formatDate(startTime),
qaNum, // endTime: formatDate(endTime),
...(toolId ? { toolId } : {}), // qaNum,
} // ...(toolId ? { toolId } : {}),
} // }
// }
const conversationRecords: Conversation[] = [ //
createMockConversation({ // const conversationRecords: Conversation[] = [
id: '1995419273074701146', // createMockConversation({
title: '未知会话内容', // id: '1995419273074701146',
qaNum: 3, // title: '未知会话内容',
toolId: '6712395743240', // qaNum: 3,
isCurrent: true, // toolId: '6712395743240',
}), // isCurrent: true,
createMockConversation({ // }),
id: '1995417608679268353', // createMockConversation({
title: '资产审评的目标及关键要素1', // id: '1995417608679268353',
qaNum: 1, // title: '资产审评的目标及关键要素1',
toolId: '6712395743241', // qaNum: 1,
minutesAgo: 20, // toolId: '6712395743241',
}), // minutesAgo: 20,
createMockConversation({ // }),
id: '19954143358382255489', // createMockConversation({
title: '对公授信材料清单2', // id: '19954143358382255489',
qaNum: 4, // title: '对公授信材料清单2',
daysAgo: 2, // qaNum: 4,
minutesAgo: 10, // daysAgo: 2,
toolId: '6712395743241', // minutesAgo: 10,
}), // toolId: '6712395743241',
createMockConversation({ // }),
id: '19954114588762233881', // createMockConversation({
title: '项目贷款调研要点3', // id: '19954114588762233881',
toolId: '6712395743240', // title: '项目贷款调研要点3',
qaNum: 2, // toolId: '6712395743240',
daysAgo: 9, // qaNum: 2,
}), // daysAgo: 9,
createMockConversation({ // }),
id: '19954099873351298711', // createMockConversation({
title: '投融资合规的最新政策4', // id: '19954099873351298711',
qaNum: 6, // title: '投融资合规的最新政策4',
daysAgo: 21, // qaNum: 6,
}), // daysAgo: 21,
] // }),
// ]
const _conversationPageMockResponse: ConversationPageResponse = { //
code: '00000000', // const conversationPageMockResponse: ConversationPageResponse = {
message: '成功', // code: '00000000',
data: { // message: '成功',
records: conversationRecords, // data: {
total: conversationRecords.length, // records: conversationRecords,
size: 100, // total: conversationRecords.length,
current: 1, // size: 100,
pages: 1, // current: 1,
}, // pages: 1,
ok: true, // },
} // ok: true,
// }
//
// export function mockFetchConversationPage() { // export function mockFetchConversationPage() {
// return Promise.resolve(conversationPageMockResponse) // return Promise.resolve(conversationPageMockResponse)
// } // }
interface EfficiencyQuestionResponse { // NOTE: 该文件中的 mock 数据已废弃,保留代码仅作参考,全部使用注释禁用。
code: string // interface EfficiencyQuestionResponse {
message: string // code: string
data: { // message: string
questions: string[] // data: {
} // questions: string[]
ok: boolean // }
} // ok: boolean
// }
const _efficiencyQuestionMockResponse: EfficiencyQuestionResponse = { //
code: '00000000', // const efficiencyQuestionMockResponse: EfficiencyQuestionResponse = {
message: '成功', // code: '00000000',
data: { // message: '成功',
questions: [ // data: {
'灾备部署有哪些要求?', // questions: [
'触达中心如何设计渠道?', // '灾备部署有哪些要求?',
'用户旅程设计如何整合信息?', // '触达中心如何设计渠道?',
'文本管理应该具备哪些能力?', // '用户旅程设计如何整合信息?',
'差旅规则有哪些?', // '文本管理应该具备哪些能力?',
'反洗钱小组的职责是什么?', // '差旅规则有哪些?',
'有效投诉的处理流程是什么?', // '反洗钱小组的职责是什么?',
'公司项目审核会主要从哪几个方面审核项目?', // '有效投诉的处理流程是什么?',
], // '公司项目审核会主要从哪几个方面审核项目?',
}, // ],
ok: true, // },
} // ok: true,
// }
//
// export function mockFetchEfficiencyQuestionList(): Promise<EfficiencyQuestionResponse> { // export function mockFetchEfficiencyQuestionList(): Promise<EfficiencyQuestionResponse> {
// return Promise.resolve(efficiencyQuestionMockResponse) // return Promise.resolve(efficiencyQuestionMockResponse)
// } // }
//
interface ToolListResponse { // interface ToolListResponse {
code: string // code: string
message: string // message: string
data: Array<{ // data: Array<{
toolId: string // toolId: string
toolName: string // toolName: string
toolContent: string // toolContent: string
toolIcon: string // toolIcon: string
toolType: string // toolType: string
userRole: string // userRole: string
showOrder: number // showOrder: number
}> // }>
ok: boolean // ok: boolean
} // }
//
const _toolListMockResponse: ToolListResponse = { // const toolListMockResponse: ToolListResponse = {
code: '00000000', // code: '00000000',
message: '成功', // message: '成功',
data: [ // data: [
{ // {
toolId: '6712395743241', // toolId: '6712395743241',
toolName: '提质增效', // toolName: '提质增效',
toolContent: 'https://sit-wechat.guominpension.com/underwrite', // toolContent: 'https://sit-wechat.guominpension.com/underwrite',
toolIcon: 'http://p-cf-co-1255000025.cos.bj.csyun001.ccbcos.co/tool-increase.svg', // toolIcon: 'http://p-cf-co-1255000025.cos.bj.csyun001.ccbcos.co/tool-increase.svg',
toolType: '03', // toolType: '03',
userRole: '02', // userRole: '02',
showOrder: 8, // showOrder: 8,
}, // },
{ // {
toolId: '6712395743240', // toolId: '6712395743240',
toolName: '数据助手', // toolName: '数据助手',
toolContent: 'https://sit-wechat.guominpension.com/underwrite', // toolContent: 'https://sit-wechat.guominpension.com/underwrite',
toolIcon: 'http://p-cf-co-1255000025.cos.bj.csyun001.ccbcos.co/tool-data.svg', // toolIcon: 'http://p-cf-co-1255000025.cos.bj.csyun001.ccbcos.co/tool-data.svg',
toolType: '03', // toolType: '03',
userRole: '01', // userRole: '01',
showOrder: 8, // showOrder: 8,
}, // },
{ // {
toolId: 'general-mode', // toolId: 'general-mode',
toolName: '通用模式', // toolName: '通用模式',
toolContent: 'https://sit-wechat.guominpension.com/underwrite', // toolContent: 'https://sit-wechat.guominpension.com/underwrite',
toolIcon: 'http://p-cf-co-1255000025.cos.bj.csyun001.ccbcos.co/tool-normal.svg', // toolIcon: 'http://p-cf-co-1255000025.cos.bj.csyun001.ccbcos.co/tool-normal.svg',
toolType: '01', // toolType: '01',
userRole: '00', // userRole: '00',
showOrder: 8, // showOrder: 8,
}, // },
], // ],
ok: true, // ok: true,
} // }
//
// export function mockFetchToolList(): Promise<ToolListResponse> { // export function mockFetchToolList(): Promise<ToolListResponse> {
// return Promise.resolve(toolListMockResponse) // return Promise.resolve(toolListMockResponse)
// } // }
...@@ -9,9 +9,9 @@ import SendIcon from '@/assets/svg/send.svg?react' ...@@ -9,9 +9,9 @@ import SendIcon from '@/assets/svg/send.svg?react'
import { type WithAuthProps, withAuth } from '@/auth/withAuth' import { type WithAuthProps, withAuth } from '@/auth/withAuth'
import { useAppDispatch, useAppSelector } from '@/store/hook' import { useAppDispatch, useAppSelector } from '@/store/hook'
import { fetchToolList } from '@/api/home' import { fetchToolList } from '@/api/home'
import { clearCurrentToolId, createConversation, setCurrentToolId } from '@/store/conversationSlice'
import { getUserRolesForApi } from '@/lib/utils' import { getUserRolesForApi } from '@/lib/utils'
import { clearCurrentToolId, createConversation, setCurrentToolId } from '@/store/conversationSlice'
// 使用本地 mock 工具列表常量,便于离线/联调阶段使用(已废弃,仅保留注释)
// const MOCK_TOOL_LIST = [ // const MOCK_TOOL_LIST = [
// { // {
// toolId: '6712395743241', // toolId: '6712395743241',
...@@ -73,9 +73,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth, ...@@ -73,9 +73,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 获取工具列表 // 获取工具列表
const getToolList = async () => { const getToolList = async () => {
try { try {
// 使用 mock 数据(已注释) // 调用真实接口获取工具列表
// setToolList([...MOCK_TOOL_LIST])
// 真实API调用
const userRoles = getUserRolesForApi() const userRoles = getUserRolesForApi()
const res = await fetchToolList({ userRoles }) const res = await fetchToolList({ userRoles })
if (res?.data) { if (res?.data) {
...@@ -86,24 +84,23 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth, ...@@ -86,24 +84,23 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
setToolList(uniqueList) setToolList(uniqueList)
} }
else { else {
// 当接口无数据时,不使用本地 mock,直接置空
setToolList([]) setToolList([])
} }
} }
catch (error) { catch (error) {
console.error('获取工具列表失败:', error) console.error('获取工具列表失败:', error)
// 接口异常时不再回退到本地 mock 常量
setToolList([]) setToolList([])
} }
} }
// 根据 currentToolId 以及 sessionStorage 中的记录决定高亮逻辑 // 根据 currentToolId 以及 sessionStorage 中的记录决定高亮逻辑
useEffect(() => { useEffect(() => {
// eslint-disable-next-line no-console // 只有当 sessionStorage 或 URL 中还能找到 toolId 时,才认为 Redux 中的 currentToolId 仍然有效
console.log('[ChatEditor] currentToolId 或 sessionToolId 变化:', { const hasPersistentToolSource = Boolean(sessionToolId || toolIdFromUrl)
currentToolId,
sessionToolId, if (currentToolId && hasPersistentToolSource) {
toolIdFromUrl,
})
if (currentToolId) {
setSelectedToolId(currentToolId) setSelectedToolId(currentToolId)
setIsToolBtnActive(false) setIsToolBtnActive(false)
return return
...@@ -442,12 +439,10 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth, ...@@ -442,12 +439,10 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
const getToolIconSrc = () => { const getToolIconSrc = () => {
if (!tool.toolIcon) if (!tool.toolIcon)
return '' return ''
// 如果已经是完整的 data URL,直接返回
// 已经是完整的 dataURL,直接返回 if (tool.toolIcon.startsWith('data:'))
if (typeof tool.toolIcon === 'string' && tool.toolIcon.startsWith('data:image'))
return tool.toolIcon return tool.toolIcon
// 否则拼接为 base64 图片格式
// 否则默认按 png 的纯 base64 拼接,如有需要可根据后端类型字段动态调整
return `data:image/png;base64,${tool.toolIcon}` return `data:image/png;base64,${tool.toolIcon}`
} }
......
...@@ -51,6 +51,17 @@ export const HistoryBarList: React.FC<HistoryBarListProps> = ({ searchValue, onS ...@@ -51,6 +51,17 @@ export const HistoryBarList: React.FC<HistoryBarListProps> = ({ searchValue, onS
toolId: conversation.toolId || null, toolId: conversation.toolId || null,
}, },
}) })
// 通知首页根据对应 toolId 重新拉取常见问题(与底部工具按钮保持一致逻辑)
window.dispatchEvent(new CustomEvent('toolButtonClick', {
detail: {
// 有 toolId 表示工具模式,没 toolId 表示通用模式
isToolBtn: !conversation.toolId,
toolId: conversation.toolId || '',
toolName: '',
shouldChangeStyle: true,
},
}))
} }
const handleFilter = useDebounceFn(() => { const handleFilter = useDebounceFn(() => {
......
...@@ -13,9 +13,31 @@ const USER_ROLES_STORAGE_KEY = '__USER_ROLES__' ...@@ -13,9 +13,31 @@ const USER_ROLES_STORAGE_KEY = '__USER_ROLES__'
* 从路由获取 userRoles 并存储到 localStorage * 从路由获取 userRoles 并存储到 localStorage
* @returns 返回获取到的 userRoles 数组 * @returns 返回获取到的 userRoles 数组
*/ */
function getQueryBeforeSecondQuestion(): string {
try {
const href = window.location.href || ''
const firstQuestionIndex = href.indexOf('?')
if (firstQuestionIndex === -1)
return ''
const queryCandidate = href.slice(firstQuestionIndex + 1)
const secondQuestionIndex = queryCandidate.indexOf('?')
const sliced = secondQuestionIndex === -1
? queryCandidate
: queryCandidate.slice(0, secondQuestionIndex)
// 兼容后端返回 `&&` 作为分隔符的情况,统一替换成单个 `&`
return sliced.replace(/&{2,}/g, '&')
}
catch {
return ''
}
}
export function getUserRolesFromRouteAndStore(): string[] { export function getUserRolesFromRouteAndStore(): string[] {
try { try {
const searchParams = new URLSearchParams(window.location.search) const sanitizedSearch = getQueryBeforeSecondQuestion()
const searchParams = new URLSearchParams(sanitizedSearch || window.location.search)
const rolesFromRepeatedKeys = searchParams.getAll('userRoles').filter(Boolean) const rolesFromRepeatedKeys = searchParams.getAll('userRoles').filter(Boolean)
let userRoles: string[] = [] let userRoles: string[] = []
......
...@@ -78,42 +78,45 @@ export const Home: React.FC = () => { ...@@ -78,42 +78,45 @@ export const Home: React.FC = () => {
// 处理工具按钮点击 // 处理工具按钮点击
const _handleToolClick = useCallback(async (isToolBtn: boolean, toolId?: string) => { const _handleToolClick = useCallback(async (isToolBtn: boolean, toolId?: string) => {
// if (!isToolBtn && toolId) { // 提质增效模式 / 数据助手 / 通用模式:都先清空数据,重新拉常见问题
// 提质增效模式:只修改左侧页面内容,加载工具相关问题 setOtherQuestions((prev: any) => ({
// 先清空数据,确保显示空数据样式 ...prev,
setOtherQuestions((prev: any) => ({ content: [],
...prev, }))
content: [], setIsDataLoaded(false) // 重置加载状态
})) try {
setIsDataLoaded(false) // 重置加载状态 /**
try { * 获取最终用于请求的 toolId:
// 获取 toolId:优先使用传入参数,其次从 sessionStorage,再次从路由参数,都没有则使用空字符串 * - 如果传入了 toolId:
let finalToolId = toolId || '' * - 通用模式(isToolBtn === true):保持传入值(可能是空字符串),不再从 sessionStorage / URL 回退
if (!finalToolId) { * - 工具模式(isToolBtn === false):如果传入为空,再从 sessionStorage / URL 回退
finalToolId = sessionStorage.getItem('currentToolId') || '' * - 如果没有传入 toolId:保留原有回退逻辑
} */
let finalToolId = toolId || ''
if (!finalToolId && !isToolBtn) {
// 仅在工具模式下才使用回退逻辑,避免通用模式误用上一次的 toolId
finalToolId = sessionStorage.getItem('currentToolId') || ''
if (!finalToolId) { if (!finalToolId) {
const searchParams = new URLSearchParams(location.search) const searchParams = new URLSearchParams(location.search)
finalToolId = searchParams.get('toolId') || '' finalToolId = searchParams.get('toolId') || ''
} }
const res = await fetchEfficiencyQuestionList({ toolId: finalToolId })
if (res && res.data && res.data.questions) {
setOtherQuestions((prev: any) => ({
...prev,
content: res.data.questions || [],
}))
}
}
catch (error) {
console.error('获取工具相关问题失败:', error)
} }
finally {
setIsDataLoaded(true) // 无论成功失败都标记为已加载 const res = await fetchEfficiencyQuestionList({ toolId: finalToolId })
if (res && res.data && res.data.questions) {
setOtherQuestions((prev: any) => ({
...prev,
content: res.data.questions || [],
}))
} }
// } }
// else if (isToolBtn) { catch (error) {
// setIsDataLoaded(true) // 恢复原始数据时标记为已加载 console.error('获取工具相关问题失败:', error)
// } }
finally {
setIsDataLoaded(true) // 无论成功失败都标记为已加载
}
}, [originalOtherQuestions, location.search]) }, [originalOtherQuestions, location.search])
// 监听工具按钮点击事件 // 监听工具按钮点击事件
......
...@@ -3,7 +3,6 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' ...@@ -3,7 +3,6 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { processConversationData } from './conversationSlice.helper' import { processConversationData } from './conversationSlice.helper'
import type { Conversation, ConversationState } from '@/types/conversation' import type { Conversation, ConversationState } from '@/types/conversation'
import { fetchCreateConversation, fetchDeleteUserConversation, fetchQueryUserConversationPage } from '@/api/conversation' import { fetchCreateConversation, fetchDeleteUserConversation, fetchQueryUserConversationPage } from '@/api/conversation'
// import { mockFetchConversationPage } from '@/api/mock/conversation'
const initialState: ConversationState = { const initialState: ConversationState = {
conversations: [], conversations: [],
...@@ -19,13 +18,10 @@ export const fetchConversations = createAsyncThunk( ...@@ -19,13 +18,10 @@ export const fetchConversations = createAsyncThunk(
'conversation/fetchConversations', 'conversation/fetchConversations',
async (_, { rejectWithValue }) => { async (_, { rejectWithValue }) => {
try { try {
// 使用mock数据(已注释) // 调用真实接口获取会话列表
// const response = await mockFetchConversationPage()
// 真实API调用
const response = await fetchQueryUserConversationPage({ const response = await fetchQueryUserConversationPage({
keyword: '', current: 1,
pageNum: 0, size: 100,
pageSize: 100,
}) })
const records = response.data?.records || [] const records = response.data?.records || []
return processConversationData(records) return processConversationData(records)
......
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