From 167d5b52ee42eadc65423ca24a4806dcc307af4f Mon Sep 17 00:00:00 2001 From: bill Date: Thu, 15 Aug 2024 14:00:09 +0800 Subject: [PATCH] feat: Add FileIcon #1880 --- web/src/components/file-icon/index.less | 3 + web/src/components/file-icon/index.tsx | 25 ++++ web/src/components/message-input/index.less | 15 +++ web/src/components/message-input/index.tsx | 138 +++++++++++++++++--- web/src/components/message-item/index.tsx | 33 +---- 5 files changed, 172 insertions(+), 42 deletions(-) create mode 100644 web/src/components/file-icon/index.less create mode 100644 web/src/components/file-icon/index.tsx create mode 100644 web/src/components/message-input/index.less diff --git a/web/src/components/file-icon/index.less b/web/src/components/file-icon/index.less new file mode 100644 index 0000000000..a549a06151 --- /dev/null +++ b/web/src/components/file-icon/index.less @@ -0,0 +1,3 @@ +.thumbnailImg { + max-width: 20px; +} diff --git a/web/src/components/file-icon/index.tsx b/web/src/components/file-icon/index.tsx new file mode 100644 index 0000000000..6a84fc78e5 --- /dev/null +++ b/web/src/components/file-icon/index.tsx @@ -0,0 +1,25 @@ +import { getExtension } from '@/utils/document-util'; +import SvgIcon from '../svg-icon'; + +import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks'; +import styles from './index.less'; + +interface IProps { + name: string; + id: string; +} + +const FileIcon = ({ name, id }: IProps) => { + const fileExtension = getExtension(name); + // TODO: replace this line with react query + const fileThumbnails = useSelectFileThumbnails(); + const fileThumbnail = fileThumbnails[id]; + + return fileThumbnail ? ( + + ) : ( + + ); +}; + +export default FileIcon; diff --git a/web/src/components/message-input/index.less b/web/src/components/message-input/index.less new file mode 100644 index 0000000000..5b203d69ed --- /dev/null +++ b/web/src/components/message-input/index.less @@ -0,0 +1,15 @@ +.messageInputWrapper { + margin-right: 20px; + .documentCard { + :global(.ant-card-body) { + padding: 10px; + position: relative; + } + } + .deleteIcon { + position: absolute; + right: -4px; + top: -4px; + color: #d92d20; + } +} diff --git a/web/src/components/message-input/index.tsx b/web/src/components/message-input/index.tsx index 7885f49ae3..2b86c50fa3 100644 --- a/web/src/components/message-input/index.tsx +++ b/web/src/components/message-input/index.tsx @@ -2,13 +2,36 @@ import { Authorization } from '@/constants/authorization'; import { useTranslate } from '@/hooks/common-hooks'; import { useRemoveNextDocument } from '@/hooks/document-hooks'; import { getAuthorization } from '@/utils/authorization-util'; -import { PlusOutlined } from '@ant-design/icons'; +import { getExtension } from '@/utils/document-util'; +import { + CloseCircleOutlined, + LoadingOutlined, + PlusOutlined, + UploadOutlined, +} from '@ant-design/icons'; import type { GetProp, UploadFile } from 'antd'; -import { Button, Flex, Input, Upload, UploadProps } from 'antd'; +import { + Button, + Card, + Flex, + Input, + List, + Space, + Spin, + Typography, + Upload, + UploadProps, +} from 'antd'; import get from 'lodash/get'; import { ChangeEventHandler, useCallback, useState } from 'react'; +import FileIcon from '../file-icon'; + +import styles from './index.less'; type FileType = Parameters>[0]; +const { Text } = Typography; + +const getFileId = (file: UploadFile) => get(file, 'response.data.0'); interface IProps { disabled: boolean; @@ -49,7 +72,6 @@ const MessageInput = ({ }; const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => { - console.log('🚀 ~ newFileList:', newFileList); setFileList(newFileList); }; const isUploadingFile = fileList.some((x) => x.status === 'uploading'); @@ -65,10 +87,13 @@ const MessageInput = ({ }, [fileList, onPressEnter, isUploadingFile]); const handleRemove = useCallback( - (file: UploadFile) => { + async (file: UploadFile) => { const ids = get(file, 'response.data', []); if (ids.length) { - removeDocument(ids[0]); + await removeDocument(ids[0]); + setFileList((preList) => { + return preList.filter((x) => getFileId(x) !== ids[0]); + }); } }, [removeDocument], @@ -82,26 +107,43 @@ const MessageInput = ({ ); return ( - + - {t('send')} - + + + + + + } onPressEnter={handlePressEnter} onChange={onInputChange} /> - {fileList.length >= 8 ? null : uploadButton} - + */} + {fileList.length > 0 && ( + { + const fileExtension = getExtension(item.name); + + return ( + + + <> + + {item.status === 'uploading' || !item.response ? ( + + } + /> + ) : ( + + )} + + + {item.name} + + {item.percent !== 100 ? ( + '上传中' + ) : !item.response ? ( + '解析中' + ) : ( + + {fileExtension?.toUpperCase()}, + + )} + + + + + {item.status !== 'uploading' && ( + handleRemove(item)} + /> + )} + + + ); + }} + /> + )} ); }; diff --git a/web/src/components/message-item/index.tsx b/web/src/components/message-item/index.tsx index c0bc4a9b1c..89e0a7edee 100644 --- a/web/src/components/message-item/index.tsx +++ b/web/src/components/message-item/index.tsx @@ -14,9 +14,9 @@ import { import MarkdownContent from '@/pages/chat/markdown-content'; import { getExtension, isImage } from '@/utils/document-util'; import { Avatar, Button, Flex, List, Typography } from 'antd'; +import FileIcon from '../file-icon'; import IndentedTreeModal from '../indented-tree/modal'; import NewDocumentLink from '../new-document-link'; -import SvgIcon from '../svg-icon'; import styles from './index.less'; const { Text } = Typography; @@ -126,23 +126,13 @@ const MessageItem = ({ bordered dataSource={referenceDocumentList} renderItem={(item) => { - const fileThumbnail = fileThumbnails[item.doc_id]; - - const fileExtension = getExtension(item.doc_name); return ( - {fileThumbnail ? ( - - ) : ( - - )} + { + // TODO: const fileThumbnail = documentThumbnails[item.id] || fileThumbnails[item.id]; const fileExtension = getExtension(item.name); return ( - {fileThumbnail ? ( - - ) : ( - - )} + {isImage(fileExtension) ? (