This commit is contained in:
archer
2023-07-11 15:57:01 +08:00
parent cd77d81135
commit eb768d9c04
47 changed files with 1949 additions and 1280 deletions

View File

@@ -0,0 +1,160 @@
import React from 'react';
import { AddIcon } from '@chakra-ui/icons';
import {
Box,
Button,
Flex,
useTheme,
Menu,
MenuButton,
MenuList,
MenuItem
} from '@chakra-ui/react';
import { useRouter } from 'next/router';
import MyIcon from '@/components/Icon';
import type { ShareChatHistoryItemType, ExportChatType } from '@/types/chat';
import { useChatStore } from '@/store/chat';
import { useGlobalStore } from '@/store/global';
import Avatar from '@/components/Avatar';
const ChatHistorySlider = ({
appName,
appAvatar,
history,
activeHistoryId,
onChangeChat,
onDelHistory,
onCloseSlider
}: {
appName: string;
appAvatar: string;
history: {
id: string;
title: string;
}[];
activeHistoryId: string;
onChangeChat: (historyId?: string) => void;
onDelHistory: (historyId: string) => void;
onCloseSlider: () => void;
}) => {
const router = useRouter();
const theme = useTheme();
const { isPc } = useGlobalStore();
return (
<Flex
position={'relative'}
flexDirection={'column'}
w={'100%'}
h={'100%'}
bg={'white'}
px={[2, 5]}
borderRight={['', theme.borders.base]}
>
{isPc && (
<Flex pt={5} pb={2} alignItems={'center'} whiteSpace={'nowrap'}>
<Avatar src={appAvatar} />
<Box ml={2} fontWeight={'bold'} className={'textEllipsis'}>
{appName}
</Box>
</Flex>
)}
{/* 新对话 */}
<Box w={'100%'} h={'36px'} my={5}>
<Button
variant={'base'}
w={'100%'}
h={'100%'}
color={'myBlue.700'}
borderRadius={'xl'}
leftIcon={<MyIcon name={'edit'} w={'16px'} />}
overflow={'hidden'}
onClick={() => onChangeChat()}
>
</Button>
</Box>
{/* chat history */}
<Box flex={'1 0 0'} h={0} overflow={'overlay'}>
{history.map((item) => (
<Flex
position={'relative'}
key={item.id}
alignItems={'center'}
py={3}
px={4}
cursor={'pointer'}
userSelect={'none'}
borderRadius={'lg'}
mb={2}
_hover={{
bg: 'myGray.100',
'& .more': {
display: 'block'
}
}}
{...(item.id === activeHistoryId
? {
backgroundColor: 'myBlue.100 !important',
color: 'myBlue.700'
}
: {
onClick: () => {
onChangeChat(item.id);
}
})}
>
<MyIcon name={item.id === activeHistoryId ? 'chatFill' : 'chatLight'} w={'16px'} />
<Box flex={'1 0 0'} ml={3} className="textEllipsis">
{item.title}
</Box>
<Box className="more" display={['block', 'none']}>
<Menu autoSelect={false} isLazy offset={[0, 5]}>
<MenuButton
_hover={{ bg: 'white' }}
cursor={'pointer'}
borderRadius={'md'}
onClick={(e) => {
e.stopPropagation();
}}
>
<MyIcon name={'more'} w={'14px'} p={1} />
</MenuButton>
<MenuList color={'myGray.700'} minW={`90px !important`}>
<MenuItem>
<MyIcon mr={2} name={'setTop'} w={'16px'}></MyIcon>
</MenuItem>
<MenuItem
_hover={{ color: 'red.500' }}
onClick={(e) => {
e.stopPropagation();
onDelHistory(item.id);
if (item.id === activeHistoryId) {
onChangeChat();
}
}}
>
<MyIcon mr={2} name={'delete'} w={'16px'}></MyIcon>
</MenuItem>
</MenuList>
</Menu>
</Box>
</Flex>
))}
{history.length === 0 && (
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'30vh'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
</Box>
</Flex>
)}
</Box>
</Flex>
);
};
export default ChatHistorySlider;

View File

@@ -1,212 +0,0 @@
import React, { useCallback, useRef, useState } 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 { useRouter } from 'next/router';
import { formatTimeToChatTime } from '@/utils/tools';
import MyIcon from '@/components/Icon';
import type { ShareChatHistoryItemType, ExportChatType } from '@/types/chat';
import { useChatStore } from '@/store/chat';
import { useGlobalStore } from '@/store/global';
import styles from '../index.module.scss';
const PcSliderBar = ({
onclickDelHistory,
onclickExportChat,
onCloseSlider
}: {
onclickDelHistory: (historyId: string) => void;
onclickExportChat: (type: ExportChatType) => void;
onCloseSlider: () => void;
}) => {
const router = useRouter();
const { shareId = '', historyId = '' } = router.query as {
shareId: string;
historyId: string;
};
const theme = useTheme();
const { isPc } = useGlobalStore();
const ContextMenuRef = useRef(null);
const onclickContext = useRef(false);
const [contextMenuData, setContextMenuData] = useState<{
left: number;
top: number;
history: ShareChatHistoryItemType;
}>();
const { shareChatHistory } = useChatStore();
// close contextMenu
useOutsideClick({
ref: ContextMenuRef,
handler: () => {
setTimeout(() => {
if (contextMenuData && !onclickContext.current) {
setContextMenuData(undefined);
}
}, 10);
setTimeout(() => {
onclickContext.current = false;
}, 10);
}
});
const onclickContextMenu = useCallback(
(e: MouseEvent<HTMLDivElement>, history: ShareChatHistoryItemType) => {
e.preventDefault(); // 阻止默认右键菜单
if (!isPc) return;
onclickContext.current = true;
setContextMenuData({
left: e.clientX,
top: e.clientY,
history
});
},
[isPc]
);
const replaceChatPage = useCallback(
({ hId = '', shareId }: { hId?: string; shareId: string }) => {
if (hId === historyId) return;
router.replace(`/chat/share?shareId=${shareId}&historyId=${hId}`);
!isPc && onCloseSlider();
},
[historyId, isPc, onCloseSlider, router]
);
return (
<Flex
position={'relative'}
flexDirection={'column'}
w={'100%'}
h={'100%'}
bg={'white'}
borderRight={['', theme.borders.base]}
>
{/* 新对话 */}
<Box
className={styles.newChat}
zIndex={1000}
w={'90%'}
h={'40px'}
my={5}
mx={'auto'}
position={'relative'}
>
<Button
variant={'base'}
w={'100%'}
h={'100%'}
leftIcon={<AddIcon />}
onClick={() => replaceChatPage({ shareId })}
>
</Button>
</Box>
{/* chat history */}
<Box flex={'1 0 0'} h={0} overflow={'overlay'}>
{shareChatHistory.map((item) => (
<Flex
position={'relative'}
key={item._id}
alignItems={'center'}
py={3}
pr={[0, 3]}
pl={[6, 3]}
cursor={'pointer'}
transition={'background-color .2s ease-in'}
borderLeft={['none', '5px solid transparent']}
userSelect={'none'}
_hover={{
backgroundColor: ['', '#dee0e3']
}}
{...(item._id === historyId
? {
backgroundColor: '#eff0f1',
borderLeftColor: 'myBlue.600 !important'
}
: {})}
onClick={() => replaceChatPage({ hId: item._id, shareId: item.shareId })}
onContextMenu={(e) => onclickContextMenu(e, item)}
>
<ChatIcon fontSize={'16px'} color={'myGray.500'} />
<Box flex={'1 0 0'} w={0} ml={3}>
<Flex alignItems={'center'}>
<Box flex={'1 0 0'} w={0} className="textEllipsis" color={'myGray.1000'}>
{item.title}
</Box>
<Box color={'myGray.400'} fontSize={'sm'}>
{formatTimeToChatTime(item.updateTime)}
</Box>
</Flex>
<Box className="textEllipsis" mt={1} fontSize={'sm'} color={'myGray.500'}>
{item.latestChat || '……'}
</Box>
</Box>
{/* phone quick delete */}
{!isPc && (
<MyIcon
px={3}
name={'delete'}
w={'16px'}
onClickCapture={(e) => {
e.stopPropagation();
onclickDelHistory(item._id);
item._id === historyId && replaceChatPage({ shareId: item.shareId });
}}
/>
)}
</Flex>
))}
{shareChatHistory.length === 0 && (
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'30vh'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
</Box>
</Flex>
)}
</Box>
{/* context menu */}
{contextMenuData && (
<Box zIndex={10} position={'fixed'} top={contextMenuData.top} left={contextMenuData.left}>
<Box ref={ContextMenuRef}></Box>
<Menu isOpen>
<MenuList>
<MenuItem
onClick={() => {
onclickDelHistory(contextMenuData.history._id);
contextMenuData.history._id === historyId && replaceChatPage({ shareId });
}}
>
</MenuItem>
<MenuItem onClick={() => onclickExportChat('html')}>HTML格式</MenuItem>
<MenuItem onClick={() => onclickExportChat('pdf')}>PDF格式</MenuItem>
<MenuItem onClick={() => onclickExportChat('md')}>Markdown格式</MenuItem>
</MenuList>
</Menu>
</Box>
)}
</Flex>
);
};
export default PcSliderBar;