import React, { useCallback, useRef, useState, useMemo } from 'react'; import type { MouseEvent } from 'react'; import { AddIcon } from '@chakra-ui/icons'; import { Box, Button, Flex, useTheme, Menu, MenuList, MenuItem, useOutsideClick } from '@chakra-ui/react'; import { ChatIcon } from '@chakra-ui/icons'; import { useQuery } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import { useLoading } from '@/hooks/useLoading'; import { useUserStore } from '@/store/user'; import MyIcon from '@/components/Icon'; import type { HistoryItemType, ExportChatType } from '@/types/chat'; import { useChatStore } from '@/store/chat'; import ModelList from './ModelList'; import { useGlobalStore } from '@/store/global'; import styles from '../index.module.scss'; import { useEditTitle } from './useEditTitle'; import { putChatHistory } from '@/api/chat'; import { useToast } from '@/hooks/useToast'; import { formatTimeToChatTime, getErrText } from '@/utils/tools'; const PcSliderBar = ({ onclickDelHistory, onclickExportChat }: { onclickDelHistory: (historyId: string) => Promise; onclickExportChat: (type: ExportChatType) => void; }) => { const router = useRouter(); const { toast } = useToast(); const { modelId = '', chatId = '' } = router.query as { modelId: string; chatId: string }; const ContextMenuRef = useRef(null); const theme = useTheme(); const { isPc } = useGlobalStore(); const { Loading, setIsLoading } = useLoading(); const [contextMenuData, setContextMenuData] = useState<{ left: number; top: number; history: HistoryItemType; }>(); const { history, loadHistory } = useChatStore(); const { myModels, myCollectionModels, loadMyModels } = useUserStore(); const models = useMemo( () => [...myModels, ...myCollectionModels], [myCollectionModels, myModels] ); // custom title edit const { onOpenModal, EditModal: EditTitleModal } = useEditTitle({ title: '自定义历史记录标题', placeholder: '如果设置为空,会自动跟随聊天记录。' }); // close contextMenu useOutsideClick({ ref: ContextMenuRef, handler: () => setTimeout(() => { setContextMenuData(undefined); }, 10) }); const onclickContextMenu = useCallback( (e: MouseEvent, history: HistoryItemType) => { e.preventDefault(); // 阻止默认右键菜单 if (!isPc) return; setContextMenuData({ left: e.clientX + 15, top: e.clientY + 10, history }); }, [isPc] ); useQuery(['loadModels'], () => loadMyModels(false)); const { isLoading: isLoadingHistory } = useQuery(['loadingHistory'], () => loadHistory({ pageNum: 1 }) ); return ( {/* 新对话 */} {isPc && ( {models.length > 1 && ( )} )} {/* chat history */} {history.map((item) => ( { if (item._id === chatId) return; if (isPc) { router.replace(`/chat?modelId=${item.modelId}&chatId=${item._id}`); } else { router.push(`/chat?modelId=${item.modelId}&chatId=${item._id}`); } }} onContextMenu={(e) => onclickContextMenu(e, item)} > {item.title} {formatTimeToChatTime(item.updateTime)} {item.latestChat || '……'} {/* phone quick delete */} {!isPc && ( { e.stopPropagation(); setIsLoading(true); try { await onclickDelHistory(item._id); } catch (error) { console.log(error); } setIsLoading(false); }} /> )} ))} {!isLoadingHistory && history.length === 0 && ( 还没有聊天记录 )} {/* context menu */} {contextMenuData && ( { try { await putChatHistory({ chatId: contextMenuData.history._id, top: !contextMenuData.history.top }); loadHistory({ pageNum: 1, init: true }); } catch (error) {} }} > {contextMenuData.history.top ? '取消置顶' : '置顶'} { setIsLoading(true); try { await onclickDelHistory(contextMenuData.history._id); if (contextMenuData.history._id === chatId) { router.replace(`/chat?modelId=${modelId}`); } } catch (error) { console.log(error); } setIsLoading(false); }} > 删除记录 onOpenModal({ defaultVal: contextMenuData.history.title, onSuccess: async (val: string) => { await putChatHistory({ chatId: contextMenuData.history._id, customTitle: val, top: contextMenuData.history.top }); toast({ title: '自定义标题成功', status: 'success' }); loadHistory({ pageNum: 1, init: true }); }, onError(err) { toast({ title: getErrText(err), status: 'error' }); } }) } > 自定义标题 onclickExportChat('html')}>导出HTML格式 onclickExportChat('pdf')}>导出PDF格式 onclickExportChat('md')}>导出Markdown格式 )} ); }; export default PcSliderBar;