Commit 1f2aae8c by Liu

fix:调用常见问题逻辑

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