balibabu commited on
Commit
fb3b3ab
·
1 Parent(s): 3bce5a6

feat: Add FileIcon #1880 (#1960)

Browse files

### What problem does this PR solve?

feat: Add FileIcon #1880

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/components/file-icon/index.less ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .thumbnailImg {
2
+ max-width: 20px;
3
+ }
web/src/components/file-icon/index.tsx ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { getExtension } from '@/utils/document-util';
2
+ import SvgIcon from '../svg-icon';
3
+
4
+ import { useSelectFileThumbnails } from '@/hooks/knowledge-hooks';
5
+ import styles from './index.less';
6
+
7
+ interface IProps {
8
+ name: string;
9
+ id: string;
10
+ }
11
+
12
+ const FileIcon = ({ name, id }: IProps) => {
13
+ const fileExtension = getExtension(name);
14
+ // TODO: replace this line with react query
15
+ const fileThumbnails = useSelectFileThumbnails();
16
+ const fileThumbnail = fileThumbnails[id];
17
+
18
+ return fileThumbnail ? (
19
+ <img src={fileThumbnail} className={styles.thumbnailImg}></img>
20
+ ) : (
21
+ <SvgIcon name={`file-icon/${fileExtension}`} width={24}></SvgIcon>
22
+ );
23
+ };
24
+
25
+ export default FileIcon;
web/src/components/message-input/index.less ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .messageInputWrapper {
2
+ margin-right: 20px;
3
+ .documentCard {
4
+ :global(.ant-card-body) {
5
+ padding: 10px;
6
+ position: relative;
7
+ }
8
+ }
9
+ .deleteIcon {
10
+ position: absolute;
11
+ right: -4px;
12
+ top: -4px;
13
+ color: #d92d20;
14
+ }
15
+ }
web/src/components/message-input/index.tsx CHANGED
@@ -2,13 +2,36 @@ import { Authorization } from '@/constants/authorization';
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import { useRemoveNextDocument } from '@/hooks/document-hooks';
4
  import { getAuthorization } from '@/utils/authorization-util';
5
- import { PlusOutlined } from '@ant-design/icons';
 
 
 
 
 
 
6
  import type { GetProp, UploadFile } from 'antd';
7
- import { Button, Flex, Input, Upload, UploadProps } from 'antd';
 
 
 
 
 
 
 
 
 
 
 
8
  import get from 'lodash/get';
9
  import { ChangeEventHandler, useCallback, useState } from 'react';
 
 
 
10
 
11
  type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
 
 
 
12
 
13
  interface IProps {
14
  disabled: boolean;
@@ -49,7 +72,6 @@ const MessageInput = ({
49
  };
50
 
51
  const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
52
- console.log('🚀 ~ newFileList:', newFileList);
53
  setFileList(newFileList);
54
  };
55
  const isUploadingFile = fileList.some((x) => x.status === 'uploading');
@@ -65,10 +87,13 @@ const MessageInput = ({
65
  }, [fileList, onPressEnter, isUploadingFile]);
66
 
67
  const handleRemove = useCallback(
68
- (file: UploadFile) => {
69
  const ids = get(file, 'response.data', []);
70
  if (ids.length) {
71
- removeDocument(ids[0]);
 
 
 
72
  }
73
  },
74
  [removeDocument],
@@ -82,26 +107,43 @@ const MessageInput = ({
82
  );
83
 
84
  return (
85
- <Flex gap={10} vertical>
86
  <Input
87
  size="large"
88
  placeholder={t('sendPlaceholder')}
89
  value={value}
90
  disabled={disabled}
91
  suffix={
92
- <Button
93
- type="primary"
94
- onClick={handlePressEnter}
95
- loading={sendLoading}
96
- disabled={sendDisabled || isUploadingFile}
97
- >
98
- {t('send')}
99
- </Button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
101
  onPressEnter={handlePressEnter}
102
  onChange={onInputChange}
103
  />
104
- <Upload
105
  action="/v1/document/upload_and_parse"
106
  listType="picture-card"
107
  fileList={fileList}
@@ -114,7 +156,71 @@ const MessageInput = ({
114
  onRemove={handleRemove}
115
  >
116
  {fileList.length >= 8 ? null : uploadButton}
117
- </Upload>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  </Flex>
119
  );
120
  };
 
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import { useRemoveNextDocument } from '@/hooks/document-hooks';
4
  import { getAuthorization } from '@/utils/authorization-util';
5
+ import { getExtension } from '@/utils/document-util';
6
+ import {
7
+ CloseCircleOutlined,
8
+ LoadingOutlined,
9
+ PlusOutlined,
10
+ UploadOutlined,
11
+ } from '@ant-design/icons';
12
  import type { GetProp, UploadFile } from 'antd';
13
+ import {
14
+ Button,
15
+ Card,
16
+ Flex,
17
+ Input,
18
+ List,
19
+ Space,
20
+ Spin,
21
+ Typography,
22
+ Upload,
23
+ UploadProps,
24
+ } from 'antd';
25
  import get from 'lodash/get';
26
  import { ChangeEventHandler, useCallback, useState } from 'react';
27
+ import FileIcon from '../file-icon';
28
+
29
+ import styles from './index.less';
30
 
31
  type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
32
+ const { Text } = Typography;
33
+
34
+ const getFileId = (file: UploadFile) => get(file, 'response.data.0');
35
 
36
  interface IProps {
37
  disabled: boolean;
 
72
  };
73
 
74
  const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
 
75
  setFileList(newFileList);
76
  };
77
  const isUploadingFile = fileList.some((x) => x.status === 'uploading');
 
87
  }, [fileList, onPressEnter, isUploadingFile]);
88
 
89
  const handleRemove = useCallback(
90
+ async (file: UploadFile) => {
91
  const ids = get(file, 'response.data', []);
92
  if (ids.length) {
93
+ await removeDocument(ids[0]);
94
+ setFileList((preList) => {
95
+ return preList.filter((x) => getFileId(x) !== ids[0]);
96
+ });
97
  }
98
  },
99
  [removeDocument],
 
107
  );
108
 
109
  return (
110
+ <Flex gap={20} vertical className={styles.messageInputWrapper}>
111
  <Input
112
  size="large"
113
  placeholder={t('sendPlaceholder')}
114
  value={value}
115
  disabled={disabled}
116
  suffix={
117
+ <Space>
118
+ <Upload
119
+ action="/v1/document/upload_and_parse"
120
+ // listType="picture-card"
121
+ fileList={fileList}
122
+ onPreview={handlePreview}
123
+ onChange={handleChange}
124
+ multiple
125
+ headers={{ [Authorization]: getAuthorization() }}
126
+ data={{ conversation_id: conversationId }}
127
+ method="post"
128
+ onRemove={handleRemove}
129
+ showUploadList={false}
130
+ >
131
+ <Button icon={<UploadOutlined />}></Button>
132
+ </Upload>
133
+ <Button
134
+ type="primary"
135
+ onClick={handlePressEnter}
136
+ loading={sendLoading}
137
+ disabled={sendDisabled || isUploadingFile}
138
+ >
139
+ {t('send')}
140
+ </Button>
141
+ </Space>
142
  }
143
  onPressEnter={handlePressEnter}
144
  onChange={onInputChange}
145
  />
146
+ {/* <Upload
147
  action="/v1/document/upload_and_parse"
148
  listType="picture-card"
149
  fileList={fileList}
 
156
  onRemove={handleRemove}
157
  >
158
  {fileList.length >= 8 ? null : uploadButton}
159
+ </Upload> */}
160
+ {fileList.length > 0 && (
161
+ <List
162
+ grid={{
163
+ gutter: 16,
164
+ xs: 1,
165
+ sm: 2,
166
+ md: 2,
167
+ lg: 1,
168
+ xl: 2,
169
+ xxl: 4,
170
+ }}
171
+ dataSource={fileList}
172
+ renderItem={(item) => {
173
+ const fileExtension = getExtension(item.name);
174
+
175
+ return (
176
+ <List.Item>
177
+ <Card className={styles.documentCard}>
178
+ <>
179
+ <Flex gap={10} align="center">
180
+ {item.status === 'uploading' || !item.response ? (
181
+ <Spin
182
+ indicator={
183
+ <LoadingOutlined style={{ fontSize: 24 }} spin />
184
+ }
185
+ />
186
+ ) : (
187
+ <FileIcon
188
+ id={getFileId(item)}
189
+ name={item.name}
190
+ ></FileIcon>
191
+ )}
192
+ <Flex vertical style={{ width: '90%' }}>
193
+ <Text
194
+ ellipsis={{ tooltip: item.name }}
195
+ className={styles.nameText}
196
+ >
197
+ <b> {item.name}</b>
198
+ </Text>
199
+ {item.percent !== 100 ? (
200
+ '上传中'
201
+ ) : !item.response ? (
202
+ '解析中'
203
+ ) : (
204
+ <Space>
205
+ <span>{fileExtension?.toUpperCase()},</span>
206
+ </Space>
207
+ )}
208
+ </Flex>
209
+ </Flex>
210
+ </>
211
+
212
+ {item.status !== 'uploading' && (
213
+ <CloseCircleOutlined
214
+ className={styles.deleteIcon}
215
+ onClick={() => handleRemove(item)}
216
+ />
217
+ )}
218
+ </Card>
219
+ </List.Item>
220
+ );
221
+ }}
222
+ />
223
+ )}
224
  </Flex>
225
  );
226
  };
web/src/components/message-item/index.tsx CHANGED
@@ -14,9 +14,9 @@ import {
14
  import MarkdownContent from '@/pages/chat/markdown-content';
15
  import { getExtension, isImage } from '@/utils/document-util';
16
  import { Avatar, Button, Flex, List, Typography } from 'antd';
 
17
  import IndentedTreeModal from '../indented-tree/modal';
18
  import NewDocumentLink from '../new-document-link';
19
- import SvgIcon from '../svg-icon';
20
  import styles from './index.less';
21
 
22
  const { Text } = Typography;
@@ -126,23 +126,13 @@ const MessageItem = ({
126
  bordered
127
  dataSource={referenceDocumentList}
128
  renderItem={(item) => {
129
- const fileThumbnail = fileThumbnails[item.doc_id];
130
-
131
- const fileExtension = getExtension(item.doc_name);
132
  return (
133
  <List.Item>
134
  <Flex gap={'small'} align="center">
135
- {fileThumbnail ? (
136
- <img
137
- src={fileThumbnail}
138
- className={styles.thumbnailImg}
139
- ></img>
140
- ) : (
141
- <SvgIcon
142
- name={`file-icon/${fileExtension}`}
143
- width={24}
144
- ></SvgIcon>
145
- )}
146
 
147
  <NewDocumentLink
148
  documentId={item.doc_id}
@@ -162,23 +152,14 @@ const MessageItem = ({
162
  bordered
163
  dataSource={documentList}
164
  renderItem={(item) => {
 
165
  const fileThumbnail =
166
  documentThumbnails[item.id] || fileThumbnails[item.id];
167
  const fileExtension = getExtension(item.name);
168
  return (
169
  <List.Item>
170
  <Flex gap={'small'} align="center">
171
- {fileThumbnail ? (
172
- <img
173
- src={fileThumbnail}
174
- className={styles.thumbnailImg}
175
- ></img>
176
- ) : (
177
- <SvgIcon
178
- name={`file-icon/${fileExtension}`}
179
- width={24}
180
- ></SvgIcon>
181
- )}
182
 
183
  {isImage(fileExtension) ? (
184
  <NewDocumentLink
 
14
  import MarkdownContent from '@/pages/chat/markdown-content';
15
  import { getExtension, isImage } from '@/utils/document-util';
16
  import { Avatar, Button, Flex, List, Typography } from 'antd';
17
+ import FileIcon from '../file-icon';
18
  import IndentedTreeModal from '../indented-tree/modal';
19
  import NewDocumentLink from '../new-document-link';
 
20
  import styles from './index.less';
21
 
22
  const { Text } = Typography;
 
126
  bordered
127
  dataSource={referenceDocumentList}
128
  renderItem={(item) => {
 
 
 
129
  return (
130
  <List.Item>
131
  <Flex gap={'small'} align="center">
132
+ <FileIcon
133
+ id={item.doc_id}
134
+ name={item.doc_name}
135
+ ></FileIcon>
 
 
 
 
 
 
 
136
 
137
  <NewDocumentLink
138
  documentId={item.doc_id}
 
152
  bordered
153
  dataSource={documentList}
154
  renderItem={(item) => {
155
+ // TODO:
156
  const fileThumbnail =
157
  documentThumbnails[item.id] || fileThumbnails[item.id];
158
  const fileExtension = getExtension(item.name);
159
  return (
160
  <List.Item>
161
  <Flex gap={'small'} align="center">
162
+ <FileIcon id={item.id} name={item.name}></FileIcon>
 
 
 
 
 
 
 
 
 
 
163
 
164
  {isImage(fileExtension) ? (
165
  <NewDocumentLink