mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-14 01:15:51 +08:00
### What problem does this PR solve? feat: Add FileIcon #1880 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
bd19656c8f
commit
5169299826
3
web/src/components/file-icon/index.less
Normal file
3
web/src/components/file-icon/index.less
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.thumbnailImg {
|
||||||
|
max-width: 20px;
|
||||||
|
}
|
25
web/src/components/file-icon/index.tsx
Normal file
25
web/src/components/file-icon/index.tsx
Normal file
@ -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 ? (
|
||||||
|
<img src={fileThumbnail} className={styles.thumbnailImg}></img>
|
||||||
|
) : (
|
||||||
|
<SvgIcon name={`file-icon/${fileExtension}`} width={24}></SvgIcon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileIcon;
|
15
web/src/components/message-input/index.less
Normal file
15
web/src/components/message-input/index.less
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,13 +2,36 @@ import { Authorization } from '@/constants/authorization';
|
|||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useRemoveNextDocument } from '@/hooks/document-hooks';
|
import { useRemoveNextDocument } from '@/hooks/document-hooks';
|
||||||
import { getAuthorization } from '@/utils/authorization-util';
|
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 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 get from 'lodash/get';
|
||||||
import { ChangeEventHandler, useCallback, useState } from 'react';
|
import { ChangeEventHandler, useCallback, useState } from 'react';
|
||||||
|
import FileIcon from '../file-icon';
|
||||||
|
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
|
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
const getFileId = (file: UploadFile) => get(file, 'response.data.0');
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
@ -49,7 +72,6 @@ const MessageInput = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
|
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
|
||||||
console.log('🚀 ~ newFileList:', newFileList);
|
|
||||||
setFileList(newFileList);
|
setFileList(newFileList);
|
||||||
};
|
};
|
||||||
const isUploadingFile = fileList.some((x) => x.status === 'uploading');
|
const isUploadingFile = fileList.some((x) => x.status === 'uploading');
|
||||||
@ -65,10 +87,13 @@ const MessageInput = ({
|
|||||||
}, [fileList, onPressEnter, isUploadingFile]);
|
}, [fileList, onPressEnter, isUploadingFile]);
|
||||||
|
|
||||||
const handleRemove = useCallback(
|
const handleRemove = useCallback(
|
||||||
(file: UploadFile) => {
|
async (file: UploadFile) => {
|
||||||
const ids = get(file, 'response.data', []);
|
const ids = get(file, 'response.data', []);
|
||||||
if (ids.length) {
|
if (ids.length) {
|
||||||
removeDocument(ids[0]);
|
await removeDocument(ids[0]);
|
||||||
|
setFileList((preList) => {
|
||||||
|
return preList.filter((x) => getFileId(x) !== ids[0]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[removeDocument],
|
[removeDocument],
|
||||||
@ -82,26 +107,43 @@ const MessageInput = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={10} vertical>
|
<Flex gap={20} vertical className={styles.messageInputWrapper}>
|
||||||
<Input
|
<Input
|
||||||
size="large"
|
size="large"
|
||||||
placeholder={t('sendPlaceholder')}
|
placeholder={t('sendPlaceholder')}
|
||||||
value={value}
|
value={value}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
suffix={
|
suffix={
|
||||||
<Button
|
<Space>
|
||||||
type="primary"
|
<Upload
|
||||||
onClick={handlePressEnter}
|
action="/v1/document/upload_and_parse"
|
||||||
loading={sendLoading}
|
// listType="picture-card"
|
||||||
disabled={sendDisabled || isUploadingFile}
|
fileList={fileList}
|
||||||
>
|
onPreview={handlePreview}
|
||||||
{t('send')}
|
onChange={handleChange}
|
||||||
</Button>
|
multiple
|
||||||
|
headers={{ [Authorization]: getAuthorization() }}
|
||||||
|
data={{ conversation_id: conversationId }}
|
||||||
|
method="post"
|
||||||
|
onRemove={handleRemove}
|
||||||
|
showUploadList={false}
|
||||||
|
>
|
||||||
|
<Button icon={<UploadOutlined />}></Button>
|
||||||
|
</Upload>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={handlePressEnter}
|
||||||
|
loading={sendLoading}
|
||||||
|
disabled={sendDisabled || isUploadingFile}
|
||||||
|
>
|
||||||
|
{t('send')}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
}
|
}
|
||||||
onPressEnter={handlePressEnter}
|
onPressEnter={handlePressEnter}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
/>
|
/>
|
||||||
<Upload
|
{/* <Upload
|
||||||
action="/v1/document/upload_and_parse"
|
action="/v1/document/upload_and_parse"
|
||||||
listType="picture-card"
|
listType="picture-card"
|
||||||
fileList={fileList}
|
fileList={fileList}
|
||||||
@ -114,7 +156,71 @@ const MessageInput = ({
|
|||||||
onRemove={handleRemove}
|
onRemove={handleRemove}
|
||||||
>
|
>
|
||||||
{fileList.length >= 8 ? null : uploadButton}
|
{fileList.length >= 8 ? null : uploadButton}
|
||||||
</Upload>
|
</Upload> */}
|
||||||
|
{fileList.length > 0 && (
|
||||||
|
<List
|
||||||
|
grid={{
|
||||||
|
gutter: 16,
|
||||||
|
xs: 1,
|
||||||
|
sm: 2,
|
||||||
|
md: 2,
|
||||||
|
lg: 1,
|
||||||
|
xl: 2,
|
||||||
|
xxl: 4,
|
||||||
|
}}
|
||||||
|
dataSource={fileList}
|
||||||
|
renderItem={(item) => {
|
||||||
|
const fileExtension = getExtension(item.name);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item>
|
||||||
|
<Card className={styles.documentCard}>
|
||||||
|
<>
|
||||||
|
<Flex gap={10} align="center">
|
||||||
|
{item.status === 'uploading' || !item.response ? (
|
||||||
|
<Spin
|
||||||
|
indicator={
|
||||||
|
<LoadingOutlined style={{ fontSize: 24 }} spin />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FileIcon
|
||||||
|
id={getFileId(item)}
|
||||||
|
name={item.name}
|
||||||
|
></FileIcon>
|
||||||
|
)}
|
||||||
|
<Flex vertical style={{ width: '90%' }}>
|
||||||
|
<Text
|
||||||
|
ellipsis={{ tooltip: item.name }}
|
||||||
|
className={styles.nameText}
|
||||||
|
>
|
||||||
|
<b> {item.name}</b>
|
||||||
|
</Text>
|
||||||
|
{item.percent !== 100 ? (
|
||||||
|
'上传中'
|
||||||
|
) : !item.response ? (
|
||||||
|
'解析中'
|
||||||
|
) : (
|
||||||
|
<Space>
|
||||||
|
<span>{fileExtension?.toUpperCase()},</span>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
|
||||||
|
{item.status !== 'uploading' && (
|
||||||
|
<CloseCircleOutlined
|
||||||
|
className={styles.deleteIcon}
|
||||||
|
onClick={() => handleRemove(item)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</List.Item>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -14,9 +14,9 @@ import {
|
|||||||
import MarkdownContent from '@/pages/chat/markdown-content';
|
import MarkdownContent from '@/pages/chat/markdown-content';
|
||||||
import { getExtension, isImage } from '@/utils/document-util';
|
import { getExtension, isImage } from '@/utils/document-util';
|
||||||
import { Avatar, Button, Flex, List, Typography } from 'antd';
|
import { Avatar, Button, Flex, List, Typography } from 'antd';
|
||||||
|
import FileIcon from '../file-icon';
|
||||||
import IndentedTreeModal from '../indented-tree/modal';
|
import IndentedTreeModal from '../indented-tree/modal';
|
||||||
import NewDocumentLink from '../new-document-link';
|
import NewDocumentLink from '../new-document-link';
|
||||||
import SvgIcon from '../svg-icon';
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
@ -126,23 +126,13 @@ const MessageItem = ({
|
|||||||
bordered
|
bordered
|
||||||
dataSource={referenceDocumentList}
|
dataSource={referenceDocumentList}
|
||||||
renderItem={(item) => {
|
renderItem={(item) => {
|
||||||
const fileThumbnail = fileThumbnails[item.doc_id];
|
|
||||||
|
|
||||||
const fileExtension = getExtension(item.doc_name);
|
|
||||||
return (
|
return (
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Flex gap={'small'} align="center">
|
<Flex gap={'small'} align="center">
|
||||||
{fileThumbnail ? (
|
<FileIcon
|
||||||
<img
|
id={item.doc_id}
|
||||||
src={fileThumbnail}
|
name={item.doc_name}
|
||||||
className={styles.thumbnailImg}
|
></FileIcon>
|
||||||
></img>
|
|
||||||
) : (
|
|
||||||
<SvgIcon
|
|
||||||
name={`file-icon/${fileExtension}`}
|
|
||||||
width={24}
|
|
||||||
></SvgIcon>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<NewDocumentLink
|
<NewDocumentLink
|
||||||
documentId={item.doc_id}
|
documentId={item.doc_id}
|
||||||
@ -162,23 +152,14 @@ const MessageItem = ({
|
|||||||
bordered
|
bordered
|
||||||
dataSource={documentList}
|
dataSource={documentList}
|
||||||
renderItem={(item) => {
|
renderItem={(item) => {
|
||||||
|
// TODO:
|
||||||
const fileThumbnail =
|
const fileThumbnail =
|
||||||
documentThumbnails[item.id] || fileThumbnails[item.id];
|
documentThumbnails[item.id] || fileThumbnails[item.id];
|
||||||
const fileExtension = getExtension(item.name);
|
const fileExtension = getExtension(item.name);
|
||||||
return (
|
return (
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Flex gap={'small'} align="center">
|
<Flex gap={'small'} align="center">
|
||||||
{fileThumbnail ? (
|
<FileIcon id={item.id} name={item.name}></FileIcon>
|
||||||
<img
|
|
||||||
src={fileThumbnail}
|
|
||||||
className={styles.thumbnailImg}
|
|
||||||
></img>
|
|
||||||
) : (
|
|
||||||
<SvgIcon
|
|
||||||
name={`file-icon/${fileExtension}`}
|
|
||||||
width={24}
|
|
||||||
></SvgIcon>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isImage(fileExtension) ? (
|
{isImage(fileExtension) ? (
|
||||||
<NewDocumentLink
|
<NewDocumentLink
|
||||||
|
Loading…
x
Reference in New Issue
Block a user