Commit 68a333d9 by Liu

fix: 权限分配逻辑&&history展示

parent 10a746a8
...@@ -9,30 +9,7 @@ import { type WithAuthProps, withAuth } from '@/auth/withAuth' ...@@ -9,30 +9,7 @@ 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 { clearCurrentToolId, createConversation, setCurrentToolId } from '@/store/conversationSlice'
import { getUserRolesForApi } from '@/lib/utils'
function getUserRolesFromRoute(): string[] {
try {
const searchParams = new URLSearchParams(window.location.search)
const rolesFromRepeatedKeys = searchParams.getAll('userRoles').filter(Boolean)
if (rolesFromRepeatedKeys.length)
return Array.from(new Set(rolesFromRepeatedKeys))
const commaSeparated = searchParams.get('userRoles')
if (commaSeparated) {
const roles = commaSeparated
.split(',')
.map(role => role.trim())
.filter(Boolean)
if (roles.length)
return Array.from(new Set(roles))
}
return []
}
catch {
return []
}
}
interface ChatEditorProps { interface ChatEditorProps {
onChange?: (value: string) => void onChange?: (value: string) => void
...@@ -62,7 +39,8 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth, ...@@ -62,7 +39,8 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
// 获取工具列表 // 获取工具列表
const getToolList = async () => { const getToolList = async () => {
try { try {
const userRoles = getUserRolesFromRoute() // 使用统一的方法获取 userRoles(先同步路由到 localStorage,然后读取)
const userRoles = getUserRolesForApi()
const res = await fetchToolList({ userRoles }) const res = await fetchToolList({ userRoles })
if (res?.data) { if (res?.data) {
// 根据 toolId 去重,防止重复渲染 // 根据 toolId 去重,防止重复渲染
......
...@@ -6,3 +6,80 @@ import { twMerge } from 'tailwind-merge' ...@@ -6,3 +6,80 @@ import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))
} }
const USER_ROLES_STORAGE_KEY = '__USER_ROLES__'
/**
* 从路由获取 userRoles 并存储到 localStorage
* @returns 返回获取到的 userRoles 数组
*/
export function getUserRolesFromRouteAndStore(): string[] {
try {
const searchParams = new URLSearchParams(window.location.search)
const rolesFromRepeatedKeys = searchParams.getAll('userRoles').filter(Boolean)
let userRoles: string[] = []
if (rolesFromRepeatedKeys.length) {
userRoles = Array.from(new Set(rolesFromRepeatedKeys))
}
else {
const commaSeparated = searchParams.get('userRoles')
if (commaSeparated) {
const roles = commaSeparated
.split(',')
.map(role => role.trim())
.filter(Boolean)
if (roles.length) {
userRoles = Array.from(new Set(roles))
}
}
}
// 如果获取到了 userRoles,存储到 localStorage
if (userRoles.length > 0) {
try {
localStorage.setItem(USER_ROLES_STORAGE_KEY, JSON.stringify(userRoles))
}
catch (error) {
console.error('存储 userRoles 到 localStorage 失败:', error)
}
}
return userRoles
}
catch {
return []
}
}
/**
* 从 localStorage 读取 userRoles
* @returns 返回 userRoles 数组,如果没有则返回空数组
*/
export function getUserRolesFromStorage(): string[] {
try {
const stored = localStorage.getItem(USER_ROLES_STORAGE_KEY)
if (stored) {
const userRoles = JSON.parse(stored)
if (Array.isArray(userRoles) && userRoles.length > 0) {
return userRoles
}
}
}
catch (error) {
console.error('从 localStorage 读取 userRoles 失败:', error)
}
return []
}
/**
* 获取 userRoles(先同步路由到 localStorage,然后读取)
* 这是推荐的统一方法,确保调用 fetchToolList 时能获取到正确的 userRoles
* @returns 返回 userRoles 数组,如果没有则返回空数组
*/
export function getUserRolesForApi(): string[] {
// 先同步路由中的 userRoles 到 localStorage(如果路由中有的话)
getUserRolesFromRouteAndStore()
// 然后从 localStorage 读取(localStorage.setItem 是同步的,所以可以立即读取)
return getUserRolesFromStorage()
}
...@@ -14,6 +14,7 @@ import { fetchUserQaRecordPage } from '@/api/conversation' ...@@ -14,6 +14,7 @@ import { fetchUserQaRecordPage } from '@/api/conversation'
import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat' import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat'
import { fetchToolList } from '@/api/home' import { fetchToolList } from '@/api/home'
import { clearCurrentToolId, clearShouldSendQuestion, fetchConversations, setCurrentToolId } from '@/store/conversationSlice' import { clearCurrentToolId, clearShouldSendQuestion, fetchConversations, setCurrentToolId } from '@/store/conversationSlice'
import { getUserRolesForApi } from '@/lib/utils'
import type { RootState } from '@/store' import type { RootState } from '@/store'
import { useAppDispatch, useAppSelector } from '@/store/hook' import { useAppDispatch, useAppSelector } from '@/store/hook'
import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react' import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react'
...@@ -25,7 +26,7 @@ export const Chat: React.FC = () => { ...@@ -25,7 +26,7 @@ export const Chat: React.FC = () => {
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [allItems, setAllItems] = useState<ChatRecord[]>([]) const [allItems, setAllItems] = useState<ChatRecord[]>([])
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { shouldSendQuestion, currentToolId } = useAppSelector((state: RootState) => state.conversation) const { shouldSendQuestion, currentToolId, conversations } = useAppSelector((state: RootState) => state.conversation)
const scrollableRef = useRef<HTMLDivElement | any>(null) const scrollableRef = useRef<HTMLDivElement | any>(null)
const position = useScroll(scrollableRef) const position = useScroll(scrollableRef)
const currentIdRef = useRef<string | undefined>(id) const currentIdRef = useRef<string | undefined>(id)
...@@ -228,43 +229,59 @@ export const Chat: React.FC = () => { ...@@ -228,43 +229,59 @@ export const Chat: React.FC = () => {
return item return item
}) })
setAllItems(processedMessages) setAllItems(processedMessages)
// 优先从 qaRecords 中查找 toolId(这是实际使用的)
const latestToolId = [...qaRecords].reverse().find(item => Boolean(item.toolId))?.toolId?.trim?.() const latestToolId = [...qaRecords].reverse().find(item => Boolean(item.toolId))?.toolId?.trim?.()
const hasQaRecords = qaRecords.length > 0 const hasQaRecords = qaRecords.length > 0
// 如果 qaRecords 中没有 toolId,尝试从 conversations 中查找当前会话的 toolId(会话级别)
const conversationToolId = latestToolId || (conversations.find(conv => conv.conversationId === conversationId)?.toolId?.trim?.())
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('[Chat] 从历史记录获取 toolId:', { console.log('[Chat] 从历史记录获取 toolId:', {
conversationId, conversationId,
latestToolId, latestToolIdFromQaRecords: latestToolId,
conversationToolId,
hasQaRecords, hasQaRecords,
currentToolIdInRedux: currentToolId, currentToolIdInRedux: currentToolId,
}) })
// 优化:如果 Redux 中已有 toolId 且与历史记录中的一致,则不重复设置 // 确定最终使用的 toolId:优先使用 qaRecords 中的,其次使用 conversation 中的
// 这样可以避免覆盖 HistoryBarList 中设置的值 const finalToolId = latestToolId || conversationToolId || undefined
if (hasQaRecords) {
if (latestToolId) { if (hasQaRecords || conversationToolId) {
// 只有当 Redux 中的 toolId 与历史记录中的不一致时,才更新 if (finalToolId) {
if (currentToolId !== latestToolId) { // 只有当 Redux 中的 toolId 与最终确定的 toolId 不一致时,才更新
if (currentToolId !== finalToolId) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('[Chat] 更新 toolId (不一致):', { console.log('[Chat] 更新 toolId (不一致):', {
from: currentToolId, from: currentToolId,
to: latestToolId, to: finalToolId,
source: latestToolId ? 'qaRecords' : 'conversation',
}) })
dispatch(setCurrentToolId(latestToolId)) dispatch(setCurrentToolId(finalToolId))
} }
else { else {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('[Chat] toolId 已一致,无需更新:', latestToolId) console.log('[Chat] toolId 已一致,无需更新:', finalToolId)
} }
} }
else { else {
// 如果历史记录中没有 toolId,但 Redux 中有,需要清除 // 如果 qaRecords 和 conversation 中都没有 toolId,清除 Redux 中的 toolId
if (currentToolId) { if (currentToolId) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('[Chat] 清除 toolId (历史记录中没有)') console.log('[Chat] 清除 toolId (qaRecords 和 conversation 中都没有)')
dispatch(clearCurrentToolId()) dispatch(clearCurrentToolId())
} }
} }
} }
else {
// 如果没有 qaRecords 且没有 conversation,清除 toolId
if (currentToolId) {
// eslint-disable-next-line no-console
console.log('[Chat] 清除 toolId (没有历史记录)')
dispatch(clearCurrentToolId())
}
}
} }
catch { catch {
// 错误处理 // 错误处理
...@@ -272,7 +289,7 @@ export const Chat: React.FC = () => { ...@@ -272,7 +289,7 @@ export const Chat: React.FC = () => {
finally { finally {
setIsLoading(false) setIsLoading(false)
} }
}, [dispatch, currentToolId]) }, [dispatch, currentToolId, conversations])
/** 点击滚动到底部 */ /** 点击滚动到底部 */
const scrollToBottom = () => { const scrollToBottom = () => {
...@@ -316,29 +333,8 @@ export const Chat: React.FC = () => { ...@@ -316,29 +333,8 @@ export const Chat: React.FC = () => {
const getToolNameFromToolId = async () => { const getToolNameFromToolId = async () => {
if (currentToolId) { if (currentToolId) {
try { try {
const userRoles: string[] = [] // 使用统一的方法获取 userRoles(先同步路由到 localStorage,然后读取)
try { const userRoles = getUserRolesForApi()
const searchParams = new URLSearchParams(window.location.search)
const rolesFromRepeatedKeys = searchParams.getAll('userRoles').filter(Boolean)
if (rolesFromRepeatedKeys.length) {
userRoles.push(...Array.from(new Set(rolesFromRepeatedKeys)))
}
else {
const commaSeparated = searchParams.get('userRoles')
if (commaSeparated) {
const roles = commaSeparated
.split(',')
.map(role => role.trim())
.filter(Boolean)
if (roles.length) {
userRoles.push(...Array.from(new Set(roles)))
}
}
}
}
catch {
// 忽略错误
}
const res = await fetchToolList({ userRoles }) const res = await fetchToolList({ userRoles })
if (res?.data) { if (res?.data) {
const tool = res.data.find((t: any) => t.toolId === currentToolId) const tool = res.data.find((t: any) => t.toolId === currentToolId)
......
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