diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 997e489f9..3e6b6975e 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -97,6 +97,8 @@ export const iconPaths = { 'core/app/variable/textarea': () => import('./icons/core/app/variable/textarea.svg'), 'core/chat/QGFill': () => import('./icons/core/chat/QGFill.svg'), 'core/chat/cancelSpeak': () => import('./icons/core/chat/cancelSpeak.svg'), + 'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'), + 'core/chat/sideLine': () => import('./icons/core/chat/sideLine.svg'), 'core/chat/chatFill': () => import('./icons/core/chat/chatFill.svg'), 'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'), 'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'), diff --git a/packages/web/components/common/Icon/icons/core/chat/chevronSelector.svg b/packages/web/components/common/Icon/icons/core/chat/chevronSelector.svg new file mode 100644 index 000000000..7262690df --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/chat/chevronSelector.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/core/chat/sideLine.svg b/packages/web/components/common/Icon/icons/core/chat/sideLine.svg new file mode 100644 index 000000000..1d063172d --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/chat/sideLine.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index cd3289aeb..52387c19b 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -1,6 +1,7 @@ { "add_new": "Add new", "App": "App", + "all_apps": "All Apps", "click_to_resume": "Resume", "code_editor": "Code edit", "Export": "Export", diff --git a/packages/web/i18n/zh/common.json b/packages/web/i18n/zh/common.json index 70e8359d7..66ef3d30a 100644 --- a/packages/web/i18n/zh/common.json +++ b/packages/web/i18n/zh/common.json @@ -1,6 +1,7 @@ { "add_new": "新增", "App": "应用", + "all_apps": "全部应用", "click_to_resume": "点击恢复", "code_editor": "代码编辑", "Export": "导出", diff --git a/projects/app/src/pages/chat/components/ChatHeader.tsx b/projects/app/src/pages/chat/components/ChatHeader.tsx index 8804d1e3c..703ca4030 100644 --- a/projects/app/src/pages/chat/components/ChatHeader.tsx +++ b/projects/app/src/pages/chat/components/ChatHeader.tsx @@ -1,11 +1,11 @@ -import React from 'react'; -import { Flex, useTheme, Box } from '@chakra-ui/react'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; +import React, { useState, useCallback } from 'react'; +import { Flex, useTheme, Box, useDisclosure } from '@chakra-ui/react'; import MyIcon from '@fastgpt/web/components/common/Icon'; import Avatar from '@fastgpt/web/components/common/Avatar'; import ToolMenu from './ToolMenu'; import type { ChatItemType } from '@fastgpt/global/core/chat/type'; import { useTranslation } from 'next-i18next'; + import MyTag from '@fastgpt/web/components/common/Tag/index'; import { useContextSelector } from 'use-context-selector'; import { ChatContext } from '@/web/core/chat/context/chatContext'; @@ -13,85 +13,287 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { InitChatResponse } from '@/global/core/chat/api'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; +import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; +import { useRouter } from 'next/router'; +import { AppListItemType } from '@fastgpt/global/core/app/type'; +import { + GetResourceFolderListProps, + GetResourceListItemResponse +} from '@fastgpt/global/common/parentFolder/type'; +import { getMyApps } from '@/web/core/app/api'; +import SelectOneResource from '@/components/common/folder/SelectOneResource'; const ChatHeader = ({ chatData, history, showHistory, - onRoute2AppDetail + onRoute2AppDetail, + apps }: { - chatData: InitChatResponse; history: ChatItemType[]; showHistory?: boolean; onRoute2AppDetail?: () => void; + apps?: AppListItemType[]; + chatData: InitChatResponse; }) => { - const theme = useTheme(); - const { t } = useTranslation(); - const { isPc } = useSystem(); - - const chatModels = chatData.app.chatModels; const isPlugin = chatData.app.type === AppTypeEnum.plugin; - - const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider); - - return isPc && isPlugin ? null : ( - - {isPc ? ( - <> - - {chatData.title} - - - - - {history.length === 0 - ? t('common:core.chat.New Chat') - : t('core.chat.History Amount', { amount: history.length })} - - - {!!chatModels && chatModels.length > 0 && ( - - - - - {chatModels.join(',')} - - - - )} - - - ) : ( - <> - {showHistory && ( - + {isPc && isPlugin ? null : ( + + {isPc ? ( + + ) : ( + )} - - - - {chatData.app.name} - - - + {/* control */} + {!isPlugin && } + )} + + ); +}; - {/* control */} - {!isPlugin && } - +const MobileDrawer = ({ + onCloseDrawer, + appId, + apps +}: { + onCloseDrawer: () => void; + appId: string; + apps?: AppListItemType[]; +}) => { + enum TabEnum { + recently = 'recently', + app = 'app' + } + const { t } = useTranslation(); + const { isPc } = useSystem(); + const router = useRouter(); + const isTeamChat = router.pathname === '/chat/team'; + const [currentTab, setCurrentTab] = useState(TabEnum.recently); + const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => { + return getMyApps({ parentId }).then((res) => + res.map((item) => ({ + id: item._id, + name: item.name, + avatar: item.avatar, + isFolder: item.type === AppTypeEnum.folder + })) + ); + }, []); + const { onChangeAppId } = useContextSelector(ChatContext, (v) => v); + return ( + <> + { + onCloseDrawer(); + }} + > + {/* menu */} + e.stopPropagation()} + background={'white'} + position={'relative'} + > + {!isPc && appId && ( + + flex={'1 0 0'} + width={isTeamChat ? '30%' : '60%'} + mr={10} + inlineStyles={{ + px: 1 + }} + list={[ + ...(isTeamChat + ? [{ label: t('common:all_apps'), value: TabEnum.recently }] + : [ + { label: t('common:core.chat.Recent use'), value: TabEnum.recently }, + { label: t('common:all_apps'), value: TabEnum.app } + ]) + ]} + value={currentTab} + onChange={setCurrentTab} + /> + )} + + e.stopPropagation()} + borderRadius={'0 0 10px 10px'} + position={'relative'} + padding={3} + pt={0} + pb={4} + > + {/* history */} + {currentTab === TabEnum.recently && ( + <> + {Array.isArray(apps) && + apps.map((item) => ( + + onChangeAppId(item._id) + })} + > + + + {item.name} + + + + ))} + + )} + {currentTab === TabEnum.app && !isPc && ( + <> + { + if (!id) return; + onChangeAppId(id); + }} + server={getAppList} + /> + + )} + + + + ); +}; + +const MobileHeader = ({ + showHistory, + go2AppDetail, + name, + avatar, + appId, + apps +}: { + showHistory?: boolean; + go2AppDetail?: () => void; + avatar: string; + name: string; + apps?: AppListItemType[]; + appId: string; +}) => { + const { isPc } = useSystem(); + const router = useRouter(); + const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider); + const { isOpen: isOpenDrawer, onToggle: toggleDrawer, onClose: onCloseDrawer } = useDisclosure(); + const isShareChat = router.pathname === '/chat/share'; + + return ( + <> + {showHistory && ( + + )} + + + + {name} + + {isShareChat ? null : ( + + )} + + {!isPc && isOpenDrawer && !isShareChat && ( + + )} + + ); +}; + +const PcHeader = ({ + title, + chatModels, + history +}: { + title: string; + chatModels?: string[]; + history: ChatItemType[]; +}) => { + const { t } = useTranslation(); + return ( + <> + + {title} + + + + + {history.length === 0 + ? t('common:core.chat.New Chat') + : t('common:core.chat.History Amount', { amount: history.length })} + + + {!!chatModels && chatModels.length > 0 && ( + + + + + {chatModels.join(',')} + + + + )} + + ); }; diff --git a/projects/app/src/pages/chat/components/ChatHistorySlider.tsx b/projects/app/src/pages/chat/components/ChatHistorySlider.tsx index 6fd0f6cc9..bfc225823 100644 --- a/projects/app/src/pages/chat/components/ChatHistorySlider.tsx +++ b/projects/app/src/pages/chat/components/ChatHistorySlider.tsx @@ -1,6 +1,6 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { Box, Button, Flex, useTheme, IconButton } from '@chakra-ui/react'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useEditTitle } from '@/web/common/hooks/useEditTitle'; import { useRouter } from 'next/router'; import Avatar from '@fastgpt/web/components/common/Avatar'; @@ -8,22 +8,13 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; -import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; import { useUserStore } from '@/web/support/user/useUserStore'; import { AppListItemType } from '@fastgpt/global/core/app/type'; import { useI18n } from '@/web/context/I18n'; import MyMenu from '@fastgpt/web/components/common/MyMenu'; -import SelectOneResource from '@/components/common/folder/SelectOneResource'; -import { - GetResourceFolderListProps, - GetResourceListItemResponse -} from '@fastgpt/global/common/parentFolder/type'; -import { getMyApps } from '@/web/core/app/api'; -import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { useContextSelector } from 'use-context-selector'; import { ChatContext } from '@/web/core/chat/context/chatContext'; import MyBox from '@fastgpt/web/components/common/MyBox'; -import { useSystem } from '@fastgpt/web/hooks/useSystem'; type HistoryItemType = { id: string; @@ -32,12 +23,6 @@ type HistoryItemType = { top?: boolean; }; -enum TabEnum { - recently = 'recently', - 'app' = 'app', - 'history' = 'history' -} - const ChatHistorySlider = ({ appId, appName, @@ -69,12 +54,9 @@ const ChatHistorySlider = ({ const { isPc } = useSystem(); const { userInfo } = useUserStore(); - const [currentTab, setCurrentTab] = useState(TabEnum.history); - const { histories, onChangeChatId, - onChangeAppId, chatId: activeChatId, isLoading } = useContextSelector(ChatContext, (v) => v); @@ -86,7 +68,10 @@ const ChatHistorySlider = ({ customTitle: item.customTitle, top: item.top })); - const newChat: HistoryItemType = { id: activeChatId, title: t('common:core.chat.New Chat') }; + const newChat: HistoryItemType = { + id: activeChatId, + title: t('common:core.chat.New Chat') + }; const activeChat = histories.find((item) => item.chatId === activeChatId); return !activeChat ? [newChat].concat(formatHistories) : formatHistories; @@ -106,17 +91,6 @@ const ChatHistorySlider = ({ [appId, userInfo?.team.permission.hasWritePer] ); - const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => { - return getMyApps({ parentId }).then((res) => - res.map((item) => ({ - id: item._id, - name: item.name, - avatar: item.avatar, - isFolder: item.type === AppTypeEnum.folder - })) - ); - }, []); - return ( + {!isPc && appId && ( - - flex={'1 0 0'} - mr={1} - inlineStyles={{ - px: 1 - }} - list={[ - ...(isTeamChat - ? [{ label: t('common:App'), value: TabEnum.recently }] - : [ - { label: t('common:core.chat.Recent use'), value: TabEnum.recently }, - { label: t('common:App'), value: TabEnum.app } - ]), - { label: t('common:core.chat.History'), value: TabEnum.history } - ]} - value={currentTab} - onChange={setCurrentTab} - /> + + + + {t('common:core.chat.History')} + + )}