fix: fix the chat/share page pane (#5485)

This commit is contained in:
伍闲犬
2025-08-18 17:33:37 +08:00
committed by GitHub
parent a4991ecd06
commit f59d70baa0
9 changed files with 206 additions and 128 deletions

View File

@@ -30,13 +30,19 @@ import {
DEFAULT_LOGO_BANNER_COLLAPSED_URL
} from '@/pageComponents/chat/constants';
import { useChatStore } from '@/web/core/chat/context/useChatStore';
import { usePathname } from 'next/navigation';
import type { ChatSettingSchema } from '@fastgpt/global/core/chat/setting/type';
const ChatHeader = ({
history,
showHistory,
apps,
totalRecordsCount
totalRecordsCount,
pane,
chatSettings
}: {
pane: ChatSidebarPaneEnum;
chatSettings: ChatSettingSchema | undefined;
history: ChatItemType[];
showHistory?: boolean;
apps?: AppListItemType[];
@@ -44,16 +50,14 @@ const ChatHeader = ({
}) => {
const { t } = useTranslation();
const { isPc } = useSystem();
const pathname = usePathname();
const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
const isPlugin = chatData.app.type === AppTypeEnum.plugin;
const router = useRouter();
const isChat = router.pathname === '/chat';
const isChat = pathname === '/chat';
const isShare = pathname === '/chat/share';
return isPc && isPlugin ? null : (
<Flex
@@ -79,12 +83,12 @@ const ChatHeader = ({
apps={apps}
appId={chatData.appId}
name={
pane === ChatSidebarPaneEnum.HOME
pane === ChatSidebarPaneEnum.HOME && !isShare
? chatSettings?.homeTabTitle || 'FastGPT'
: chatData.app.name
}
avatar={
pane === ChatSidebarPaneEnum.HOME
pane === ChatSidebarPaneEnum.HOME && !isShare
? chatSettings?.squareLogoUrl || DEFAULT_LOGO_BANNER_COLLAPSED_URL
: chatData.app.avatar
}

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { Grid, Image, Box, Button, Flex, useTheme, IconButton, GridItem } from '@chakra-ui/react';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
@@ -19,6 +19,8 @@ import MyDivider from '@fastgpt/web/components/common/MyDivider';
import { useMemoizedFn } from 'ahooks';
import { useUserStore } from '@/web/support/user/useUserStore';
import UserAvatarPopover from '@/pageComponents/chat/UserAvatarPopover';
import { usePathname } from 'next/navigation';
import type { ChatSettingSchema } from '@fastgpt/global/core/chat/setting/type';
type HistoryItemType = {
id: string;
@@ -30,12 +32,19 @@ type HistoryItemType = {
const ChatHistorySlider = ({
confirmClearText,
customSliderTitle
customSliderTitle,
pane,
chatSettings,
onPaneChange
}: {
confirmClearText: string;
customSliderTitle?: string;
pane: ChatSidebarPaneEnum;
chatSettings: ChatSettingSchema | undefined;
onPaneChange?: (pane: ChatSidebarPaneEnum) => void;
}) => {
const theme = useTheme();
const pathname = usePathname();
const { t } = useTranslation();
const { isPc } = useSystem();
@@ -54,11 +63,9 @@ const ChatHistorySlider = ({
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
const isActivePane = useCallback((active: ChatSidebarPaneEnum) => active === pane, [pane]);
const isActivePane = useMemoizedFn((active: ChatSidebarPaneEnum) => active === pane);
const isShare = pathname === '/chat/share';
const concatHistory = useMemo(() => {
const formatHistories: HistoryItemType[] = histories.map((item) => {
@@ -132,64 +139,69 @@ const ChatHistorySlider = ({
<MyDivider h="0.5px" bg="myGray.100" my={2} mx={2} w="calc(100% - 16px)" />
<Grid templateRows="repeat(1, 1fr)" rowGap={2} py={2}>
<GridItem
onClick={() => {
handlePaneChange(ChatSidebarPaneEnum.HOME);
onCloseSlider();
setChatId();
}}
>
<Flex
p={2}
mx={2}
gap={2}
cursor={'pointer'}
borderRadius={'8px'}
alignItems={'center'}
bg={isActivePane(ChatSidebarPaneEnum.HOME) ? 'primary.100' : 'transparent'}
color={isActivePane(ChatSidebarPaneEnum.HOME) ? 'primary.600' : 'myGray.500'}
_hover={{
bg: 'primary.100',
color: 'primary.600'
}}
>
<MyIcon name="core/chat/sidebar/home" w="20px" h="20px" />
<Box fontSize="sm" fontWeight={500} flexShrink={0} whiteSpace="nowrap">
{t('chat:sidebar.home')}
</Box>
</Flex>
</GridItem>
{!isShare && (
<>
<Grid templateRows="repeat(1, 1fr)" rowGap={2} py={2}>
<GridItem
onClick={() => {
onPaneChange?.(ChatSidebarPaneEnum.HOME);
onCloseSlider();
setChatId();
}}
>
<Flex
p={2}
mx={2}
gap={2}
cursor={'pointer'}
borderRadius={'8px'}
alignItems={'center'}
bg={isActivePane(ChatSidebarPaneEnum.HOME) ? 'primary.100' : 'transparent'}
color={isActivePane(ChatSidebarPaneEnum.HOME) ? 'primary.600' : 'myGray.500'}
_hover={{
bg: 'primary.100',
color: 'primary.600'
}}
>
<MyIcon name="core/chat/sidebar/home" w="20px" h="20px" />
<Box fontSize="sm" fontWeight={500} flexShrink={0} whiteSpace="nowrap">
{t('chat:sidebar.home')}
</Box>
</Flex>
</GridItem>
<GridItem
onClick={() => {
handlePaneChange(ChatSidebarPaneEnum.TEAM_APPS);
onCloseSlider();
}}
>
<Flex
p={2}
mx={2}
gap={2}
cursor={'pointer'}
borderRadius={'8px'}
alignItems={'center'}
bg={isActivePane(ChatSidebarPaneEnum.TEAM_APPS) ? 'primary.100' : 'transparent'}
color={isActivePane(ChatSidebarPaneEnum.TEAM_APPS) ? 'primary.600' : 'myGray.500'}
_hover={{
bg: 'primary.100',
color: 'primary.600'
}}
>
<MyIcon name="common/app" w="20px" h="20px" />
<Box fontSize="sm" fontWeight={500} flexShrink={0} whiteSpace="nowrap">
{t('chat:sidebar.team_apps')}
</Box>
</Flex>
</GridItem>
</Grid>
<MyDivider h="0.5px" bg="myGray.100" my={2} mx={2} w="calc(100% - 16px)" />
<GridItem
onClick={() => {
onPaneChange?.(ChatSidebarPaneEnum.TEAM_APPS);
onCloseSlider();
}}
>
<Flex
p={2}
mx={2}
gap={2}
cursor={'pointer'}
borderRadius={'8px'}
alignItems={'center'}
bg={isActivePane(ChatSidebarPaneEnum.TEAM_APPS) ? 'primary.100' : 'transparent'}
color={
isActivePane(ChatSidebarPaneEnum.TEAM_APPS) ? 'primary.600' : 'myGray.500'
}
_hover={{
bg: 'primary.100',
color: 'primary.600'
}}
>
<MyIcon name="common/app" w="20px" h="20px" />
<Box fontSize="sm" fontWeight={500} flexShrink={0} whiteSpace="nowrap">
{t('chat:sidebar.team_apps')}
</Box>
</Flex>
</GridItem>
</Grid>
<MyDivider h="0.5px" bg="myGray.100" my={2} mx={2} w="calc(100% - 16px)" />
</>
)}
</>
)}
@@ -380,27 +392,29 @@ const ChatHistorySlider = ({
</Flex>
</UserAvatarPopover>
<Flex
_hover={{ bg: 'myGray.200' }}
bg={isActivePane(ChatSidebarPaneEnum.SETTING) ? 'myGray.200' : 'transparent'}
borderRadius={'8px'}
p={2}
cursor={'pointer'}
w="40px"
h="40px"
alignItems="center"
justifyContent="center"
onClick={() => {
handlePaneChange(ChatSidebarPaneEnum.SETTING);
onCloseSlider();
}}
>
<MyIcon
w="20px"
name="common/setting"
fill={isActivePane(ChatSidebarPaneEnum.SETTING) ? 'primary.500' : 'myGray.400'}
/>
</Flex>
{!isShare && (
<Flex
_hover={{ bg: 'myGray.200' }}
bg={isActivePane(ChatSidebarPaneEnum.SETTING) ? 'myGray.200' : 'transparent'}
borderRadius={'8px'}
p={2}
cursor={'pointer'}
w="40px"
h="40px"
alignItems="center"
justifyContent="center"
onClick={() => {
onPaneChange?.(ChatSidebarPaneEnum.SETTING);
onCloseSlider();
}}
>
<MyIcon
w="20px"
name="common/setting"
fill={isActivePane(ChatSidebarPaneEnum.SETTING) ? 'primary.500' : 'myGray.400'}
/>
</Flex>
)}
</Flex>
)}

View File

@@ -27,7 +27,10 @@ const ChatSetting = () => {
const isOpenSlider = useContextSelector(ChatContext, (v) => v.isOpenSlider);
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider);
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
const SettingHeader = useCallback(
({ children }: { children?: React.ReactNode }) => (
@@ -65,6 +68,9 @@ const ChatSetting = () => {
<DrawerContent maxWidth="75vw">
<ChatHistorySlider
confirmClearText={t('common:core.chat.Confirm to clear history')}
pane={pane}
chatSettings={chatSettings}
onPaneChange={handlePaneChange}
/>
</DrawerContent>
</Drawer>

View File

@@ -28,7 +28,9 @@ const MyApps = () => {
(v) => v
);
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
const isOpenSlider = useContextSelector(ChatContext, (v) => v.isOpenSlider);
@@ -78,6 +80,9 @@ const MyApps = () => {
<DrawerContent maxWidth="75vw">
<ChatHistorySlider
confirmClearText={t('common:core.chat.Confirm to clear history')}
pane={pane}
chatSettings={chatSettings}
onPaneChange={handlePaneChange}
/>
</DrawerContent>
</Drawer>

View File

@@ -37,7 +37,6 @@ const AppChatWindow = ({ myApps }: Props) => {
const { t } = useTranslation();
const { isPc } = useSystem();
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
const isOpenSlider = useContextSelector(ChatContext, (v) => v.isOpenSlider);
const forbidLoadChat = useContextSelector(ChatContext, (v) => v.forbidLoadChat);
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
@@ -51,6 +50,10 @@ const AppChatWindow = ({ myApps }: Props) => {
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
const { loading } = useRequest2(
async () => {
if (!appId || forbidLoadChat.current) return;
@@ -122,7 +125,12 @@ const AppChatWindow = ({ myApps }: Props) => {
{/* show history slider */}
{isPc || !appId ? (
<SideBar externalTrigger={Boolean(datasetCiteData)}>
<ChatHistorySlider confirmClearText={t('common:core.chat.Confirm to clear history')} />
<ChatHistorySlider
confirmClearText={t('common:core.chat.Confirm to clear history')}
pane={pane}
chatSettings={chatSettings}
onPaneChange={handlePaneChange}
/>
</SideBar>
) : (
<Drawer
@@ -134,7 +142,12 @@ const AppChatWindow = ({ myApps }: Props) => {
>
<DrawerOverlay backgroundColor="rgba(255,255,255,0.5)" />
<DrawerContent maxWidth="75vw">
<ChatHistorySlider confirmClearText={t('common:core.chat.Confirm to clear history')} />
<ChatHistorySlider
confirmClearText={t('common:core.chat.Confirm to clear history')}
pane={pane}
chatSettings={chatSettings}
onPaneChange={handlePaneChange}
/>
</DrawerContent>
</Drawer>
)}
@@ -148,6 +161,8 @@ const AppChatWindow = ({ myApps }: Props) => {
flexDirection={'column'}
>
<ChatHeader
pane={pane}
chatSettings={chatSettings}
showHistory
apps={myApps}
history={chatRecords}

View File

@@ -77,8 +77,6 @@ const HomeChatWindow = ({ myApps }: Props) => {
const { llmModelList, defaultModels, feConfigs } = useSystemStore();
const { chatId, appId, outLinkAuthData } = useChatStore();
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
const isOpenSlider = useContextSelector(ChatContext, (v) => v.isOpenSlider);
const forbidLoadChat = useContextSelector(ChatContext, (v) => v.forbidLoadChat);
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
@@ -89,7 +87,9 @@ const HomeChatWindow = ({ myApps }: Props) => {
const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData);
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
@@ -101,6 +101,14 @@ const HomeChatWindow = ({ myApps }: Props) => {
const [selectedModel, setSelectedModel] = useLocalStorageState('chat_home_model', {
defaultValue: defaultModels.llm?.model
});
const selectedModelAvatar = useMemo(() => {
const modelData = getModelFromList(llmModelList, selectedModel || '');
return modelData?.avatar || HUGGING_FACE_ICON;
}, [selectedModel, llmModelList]);
const selectedModelButtonLabel = useMemo(() => {
const modelData = availableModels.find((model) => model.value === selectedModel);
return modelData?.label || selectedModel;
}, [selectedModel, availableModels]);
const availableTools = useMemo(
() => chatSettings?.selectedTools || [],
@@ -246,33 +254,38 @@ const HomeChatWindow = ({ myApps }: Props) => {
<>
{/* 模型选择 */}
{availableModels.length > 0 && (
<Box flex={['1', '0']} w={[0, 'auto']}>
<AIModelSelector
h={['30px', '36px']}
boxShadow={'none'}
size="sm"
bg={'myGray.50'}
rounded="full"
list={availableModels}
value={selectedModel}
onChange={async (model) => {
setChatBoxData((state) => ({
...state,
app: {
...state.app,
chatConfig: {
...state.app.chatConfig,
fileSelectConfig: {
...defaultFileSelectConfig,
canSelectImg: !!getWebLLMModel(model).vision
}
<AIModelSelector
h={['30px', '36px']}
boxShadow={'none'}
size="sm"
bg={'myGray.50'}
rounded="full"
list={availableModels}
value={selectedModel}
maxW={['114px', 'fit-content']}
valueLabel={
<Flex maxW={['74px', '100%']} alignItems={'center'} gap={1}>
{isPc && <Avatar src={selectedModelAvatar} w={4} h={4} />}
<Box className="textEllipsis">{selectedModelButtonLabel}</Box>
</Flex>
}
onChange={async (model) => {
setChatBoxData((state) => ({
...state,
app: {
...state.app,
chatConfig: {
...state.app.chatConfig,
fileSelectConfig: {
...defaultFileSelectConfig,
canSelectImg: !!getWebLLMModel(model).vision
}
}
}));
setSelectedModel(model);
}}
/>
</Box>
}
}));
setSelectedModel(model);
}}
/>
)}
{/* 工具选择下拉框 */}
@@ -348,7 +361,9 @@ const HomeChatWindow = ({ myApps }: Props) => {
selectedToolIds,
setSelectedToolIds,
setChatBoxData,
isPc
isPc,
selectedModelAvatar,
selectedModelButtonLabel
]
);
@@ -363,6 +378,9 @@ const HomeChatWindow = ({ myApps }: Props) => {
<ChatHistorySlider
customSliderTitle={t('chat:history_slider.home.title')}
confirmClearText={t('common:core.chat.Confirm to clear history')}
pane={pane}
chatSettings={chatSettings}
onPaneChange={handlePaneChange}
/>
</SideBar>
) : (
@@ -378,6 +396,9 @@ const HomeChatWindow = ({ myApps }: Props) => {
<ChatHistorySlider
customSliderTitle={t('chat:history_slider.home.title')}
confirmClearText={t('common:core.chat.Confirm to clear history')}
pane={pane}
chatSettings={chatSettings}
onPaneChange={handlePaneChange}
/>
</DrawerContent>
</Drawer>
@@ -407,6 +428,8 @@ const HomeChatWindow = ({ myApps }: Props) => {
)
) : (
<ChatHeader
pane={pane}
chatSettings={chatSettings}
showHistory
apps={myApps}
history={chatRecords}

View File

@@ -25,6 +25,7 @@ import {
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useContextSelector } from 'use-context-selector';
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
import { usePathname } from 'next/navigation';
type Props = {
activeAppId: string;
@@ -326,6 +327,7 @@ const NavigationSection = () => {
};
const BottomSection = () => {
const pathname = usePathname();
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const isProVersion = !!feConfigs.isPlus;
@@ -335,6 +337,7 @@ const BottomSection = () => {
const avatar = userInfo?.avatar;
const username = userInfo?.username;
const isAdmin = !!userInfo?.team.permission.hasManagePer;
const isShare = pathname === '/chat/share';
const isCollapsed = useContextSelector(ChatSettingContext, (v) => v.collapse === 1);
const isSettingActive = useContextSelector(
@@ -354,7 +357,7 @@ const BottomSection = () => {
h={isCollapsed ? 'auto' : '40px'}
minH="40px"
>
{isAdmin && isProVersion && (
{isAdmin && isProVersion && !isShare && (
<MotionBox
order={isCollapsed ? 1 : 2}
layout={false}

View File

@@ -40,6 +40,7 @@ import { type AppSchema } from '@fastgpt/global/core/app/type';
import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { ChatTypeEnum } from '@/components/core/chat/ChatContainer/ChatBox/constants';
import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
const CustomPluginRunBox = dynamic(() => import('@/pageComponents/chat/CustomPluginRunBox'));
@@ -220,6 +221,8 @@ const OutLink = (props: Props) => {
const RenderHistoryList = useMemo(() => {
const Children = (
<ChatHistorySlider
chatSettings={undefined}
pane={ChatSidebarPaneEnum.RECENTLY_USED_APPS}
confirmClearText={t('common:core.chat.Confirm to clear share chat history')}
/>
);
@@ -272,6 +275,8 @@ const OutLink = (props: Props) => {
{/* header */}
{showHead === '1' ? (
<ChatHeader
chatSettings={undefined}
pane={ChatSidebarPaneEnum.RECENTLY_USED_APPS}
history={chatRecords}
totalRecordsCount={totalRecordsCount}
showHistory={showHistory === '1'}

View File

@@ -11,6 +11,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useRouter } from 'next/router';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { createContext } from 'use-context-selector';
import { usePathname } from 'next/navigation';
type ChatSettingReturnType = ChatSettingSchema | undefined;
@@ -41,12 +42,13 @@ export const ChatSettingContext = createContext<ChatSettingContextValue>({
export const ChatSettingContextProvider = ({ children }: { children: React.ReactNode }) => {
const router = useRouter();
const pathname = usePathname();
const { feConfigs } = useSystemStore();
const { appId, setLastPane, setLastChatAppId, lastPane } = useChatStore();
const { pane = lastPane || ChatSidebarPaneEnum.HOME } = router.query as {
pane: ChatSidebarPaneEnum;
};
const { pane = lastPane || ChatSidebarPaneEnum.HOME } = (
pathname === '/chat/share' ? { pane: ChatSidebarPaneEnum.RECENTLY_USED_APPS } : router.query
) as { pane: ChatSidebarPaneEnum };
const [collapse, setCollapse] = useState<CollapseStatusType>(defaultCollapseStatus);
@@ -86,6 +88,7 @@ export const ChatSettingContextProvider = ({ children }: { children: React.React
await router.replace({
query: {
...router.query,
appId: _id,
pane: newPane
}
@@ -101,7 +104,7 @@ export const ChatSettingContextProvider = ({ children }: { children: React.React
if (!Object.values(ChatSidebarPaneEnum).includes(pane)) {
handlePaneChange(ChatSidebarPaneEnum.HOME);
}
}, [pane]);
}, [pane, handlePaneChange]);
const logos: Pick<ChatSettingSchema, 'wideLogoUrl' | 'squareLogoUrl'> = useMemo(
() => ({