balibabu commited on
Commit
d237d49
·
1 Parent(s): d3b461d

feat: Delete the files uploaded in the external dialog box #1880 (#1967)

Browse files

### What problem does this PR solve?

feat: Delete the files uploaded in the external dialog box #1880

### Type of change


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

web/src/components/message-input/index.less CHANGED
@@ -14,6 +14,8 @@
14
  }
15
  .listWrapper {
16
  padding: 0 10px;
 
 
17
  }
18
  .inputWrapper {
19
  border-radius: 8px;
 
14
  }
15
  .listWrapper {
16
  padding: 0 10px;
17
+ overflow: auto;
18
+ max-height: 170px;
19
  }
20
  .inputWrapper {
21
  border-radius: 8px;
web/src/components/message-input/index.tsx CHANGED
@@ -1,13 +1,18 @@
1
  import { Authorization } from '@/constants/authorization';
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import {
 
4
  useFetchDocumentInfosByIds,
5
  useRemoveNextDocument,
6
  } from '@/hooks/document-hooks';
7
  import { getAuthorization } from '@/utils/authorization-util';
8
  import { getExtension } from '@/utils/document-util';
9
  import { formatBytes } from '@/utils/file-util';
10
- import { CloseCircleOutlined, LoadingOutlined } from '@ant-design/icons';
 
 
 
 
11
  import type { GetProp, UploadFile } from 'antd';
12
  import {
13
  Button,
@@ -41,6 +46,16 @@ const getFileIds = (fileList: UploadFile[]) => {
41
  return ids;
42
  };
43
 
 
 
 
 
 
 
 
 
 
 
44
  interface IProps {
45
  disabled: boolean;
46
  value: string;
@@ -50,6 +65,7 @@ interface IProps {
50
  onInputChange: ChangeEventHandler<HTMLInputElement>;
51
  conversationId: string;
52
  uploadUrl?: string;
 
53
  }
54
 
55
  const getBase64 = (file: FileType): Promise<string> =>
@@ -61,6 +77,7 @@ const getBase64 = (file: FileType): Promise<string> =>
61
  });
62
 
63
  const MessageInput = ({
 
64
  disabled,
65
  value,
66
  onPressEnter,
@@ -72,6 +89,7 @@ const MessageInput = ({
72
  }: IProps) => {
73
  const { t } = useTranslate('chat');
74
  const { removeDocument } = useRemoveNextDocument();
 
75
  const { data: documentInfos, setDocumentIds } = useFetchDocumentInfosByIds();
76
 
77
  const [fileList, setFileList] = useState<UploadFile[]>([]);
@@ -89,7 +107,7 @@ const MessageInput = ({
89
 
90
  const handlePressEnter = useCallback(async () => {
91
  if (isUploadingFile) return;
92
- const ids = getFileIds(fileList);
93
 
94
  onPressEnter(ids);
95
  setFileList([]);
@@ -98,14 +116,24 @@ const MessageInput = ({
98
  const handleRemove = useCallback(
99
  async (file: UploadFile) => {
100
  const ids = get(file, 'response.data', []);
101
- if (ids.length) {
102
- await removeDocument(ids[0]);
 
 
 
 
 
103
  setFileList((preList) => {
104
  return preList.filter((x) => getFileId(x) !== ids[0]);
105
  });
 
 
 
 
 
106
  }
107
  },
108
- [removeDocument],
109
  );
110
 
111
  const getDocumentInfoById = useCallback(
@@ -192,6 +220,11 @@ const MessageInput = ({
192
  <LoadingOutlined style={{ fontSize: 24 }} spin />
193
  }
194
  />
 
 
 
 
 
195
  ) : (
196
  <FileIcon id={id} name={item.name}></FileIcon>
197
  )}
@@ -202,26 +235,33 @@ const MessageInput = ({
202
  >
203
  <b> {item.name}</b>
204
  </Text>
205
- {item.percent !== 100 ? (
206
- t('uploading')
207
- ) : !item.response ? (
208
- t('parsing')
209
  ) : (
210
- <Space>
211
- <span>{fileExtension?.toUpperCase()},</span>
212
- <span>
213
- {formatBytes(getDocumentInfoById(id)?.size ?? 0)}
214
- </span>
215
- </Space>
 
 
 
 
 
 
 
 
 
 
216
  )}
217
  </Flex>
218
  </Flex>
219
 
220
  {item.status !== 'uploading' && (
221
- <CloseCircleOutlined
222
- className={styles.deleteIcon}
223
- onClick={() => handleRemove(item)}
224
- />
225
  )}
226
  </Card>
227
  </List.Item>
 
1
  import { Authorization } from '@/constants/authorization';
2
  import { useTranslate } from '@/hooks/common-hooks';
3
  import {
4
+ useDeleteDocument,
5
  useFetchDocumentInfosByIds,
6
  useRemoveNextDocument,
7
  } from '@/hooks/document-hooks';
8
  import { getAuthorization } from '@/utils/authorization-util';
9
  import { getExtension } from '@/utils/document-util';
10
  import { formatBytes } from '@/utils/file-util';
11
+ import {
12
+ CloseCircleOutlined,
13
+ InfoCircleOutlined,
14
+ LoadingOutlined,
15
+ } from '@ant-design/icons';
16
  import type { GetProp, UploadFile } from 'antd';
17
  import {
18
  Button,
 
46
  return ids;
47
  };
48
 
49
+ const isUploadError = (file: UploadFile) => {
50
+ const retcode = get(file, 'response.retcode');
51
+ return typeof retcode === 'number' && retcode !== 0;
52
+ };
53
+
54
+ const isUploadSuccess = (file: UploadFile) => {
55
+ const retcode = get(file, 'response.retcode');
56
+ return typeof retcode === 'number' && retcode === 0;
57
+ };
58
+
59
  interface IProps {
60
  disabled: boolean;
61
  value: string;
 
65
  onInputChange: ChangeEventHandler<HTMLInputElement>;
66
  conversationId: string;
67
  uploadUrl?: string;
68
+ isShared?: boolean;
69
  }
70
 
71
  const getBase64 = (file: FileType): Promise<string> =>
 
77
  });
78
 
79
  const MessageInput = ({
80
+ isShared = false,
81
  disabled,
82
  value,
83
  onPressEnter,
 
89
  }: IProps) => {
90
  const { t } = useTranslate('chat');
91
  const { removeDocument } = useRemoveNextDocument();
92
+ const { deleteDocument } = useDeleteDocument();
93
  const { data: documentInfos, setDocumentIds } = useFetchDocumentInfosByIds();
94
 
95
  const [fileList, setFileList] = useState<UploadFile[]>([]);
 
107
 
108
  const handlePressEnter = useCallback(async () => {
109
  if (isUploadingFile) return;
110
+ const ids = getFileIds(fileList.filter((x) => isUploadSuccess(x)));
111
 
112
  onPressEnter(ids);
113
  setFileList([]);
 
116
  const handleRemove = useCallback(
117
  async (file: UploadFile) => {
118
  const ids = get(file, 'response.data', []);
119
+ // Upload Successfully
120
+ if (Array.isArray(ids) && ids.length) {
121
+ if (isShared) {
122
+ await deleteDocument(ids);
123
+ } else {
124
+ await removeDocument(ids[0]);
125
+ }
126
  setFileList((preList) => {
127
  return preList.filter((x) => getFileId(x) !== ids[0]);
128
  });
129
+ } else {
130
+ // Upload failed
131
+ setFileList((preList) => {
132
+ return preList.filter((x) => x.uid !== file.uid);
133
+ });
134
  }
135
  },
136
+ [removeDocument, deleteDocument, isShared],
137
  );
138
 
139
  const getDocumentInfoById = useCallback(
 
220
  <LoadingOutlined style={{ fontSize: 24 }} spin />
221
  }
222
  />
223
+ ) : !getFileId(item) ? (
224
+ <InfoCircleOutlined
225
+ size={30}
226
+ // width={30}
227
+ ></InfoCircleOutlined>
228
  ) : (
229
  <FileIcon id={id} name={item.name}></FileIcon>
230
  )}
 
235
  >
236
  <b> {item.name}</b>
237
  </Text>
238
+ {isUploadError(item) ? (
239
+ t('uploadFailed')
 
 
240
  ) : (
241
+ <>
242
+ {item.percent !== 100 ? (
243
+ t('uploading')
244
+ ) : !item.response ? (
245
+ t('parsing')
246
+ ) : (
247
+ <Space>
248
+ <span>{fileExtension?.toUpperCase()},</span>
249
+ <span>
250
+ {formatBytes(
251
+ getDocumentInfoById(id)?.size ?? 0,
252
+ )}
253
+ </span>
254
+ </Space>
255
+ )}
256
+ </>
257
  )}
258
  </Flex>
259
  </Flex>
260
 
261
  {item.status !== 'uploading' && (
262
+ <span className={styles.deleteIcon}>
263
+ <CloseCircleOutlined onClick={() => handleRemove(item)} />
264
+ </span>
 
265
  )}
266
  </Card>
267
  </List.Item>
web/src/hooks/document-hooks.ts CHANGED
@@ -313,3 +313,23 @@ export const useRemoveNextDocument = () => {
313
 
314
  return { data, loading, removeDocument: mutateAsync };
315
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
 
314
  return { data, loading, removeDocument: mutateAsync };
315
  };
316
+
317
+ export const useDeleteDocument = () => {
318
+ // const queryClient = useQueryClient();
319
+ const {
320
+ data,
321
+ isPending: loading,
322
+ mutateAsync,
323
+ } = useMutation({
324
+ mutationKey: ['deleteDocument'],
325
+ mutationFn: async (documentIds: string[]) => {
326
+ const data = await kbService.document_delete({ doc_ids: documentIds });
327
+ // if (data.retcode === 0) {
328
+ // queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
329
+ // }
330
+ return data;
331
+ },
332
+ });
333
+
334
+ return { data, loading, deleteDocument: mutateAsync };
335
+ };
web/src/locales/en.ts CHANGED
@@ -424,6 +424,7 @@ The above is the content you need to summarize.`,
424
  searching: 'searching...',
425
  parsing: 'Parsing',
426
  uploading: 'Uploading',
 
427
  },
428
  setting: {
429
  profile: 'Profile',
 
424
  searching: 'searching...',
425
  parsing: 'Parsing',
426
  uploading: 'Uploading',
427
+ uploadFailed: 'Upload failed',
428
  },
429
  setting: {
430
  profile: 'Profile',
web/src/locales/zh-traditional.ts CHANGED
@@ -394,6 +394,7 @@ export default {
394
  searching: '搜索中',
395
  parsing: '解析中',
396
  uploading: '上傳中',
 
397
  },
398
  setting: {
399
  profile: '概述',
 
394
  searching: '搜索中',
395
  parsing: '解析中',
396
  uploading: '上傳中',
397
+ uploadFailed: '上傳失敗',
398
  },
399
  setting: {
400
  profile: '概述',
web/src/locales/zh.ts CHANGED
@@ -411,6 +411,7 @@ export default {
411
  searching: '搜索中',
412
  parsing: '解析中',
413
  uploading: '上传中',
 
414
  },
415
  setting: {
416
  profile: '概要',
 
411
  searching: '搜索中',
412
  parsing: '解析中',
413
  uploading: '上传中',
414
+ uploadFailed: '上传失败',
415
  },
416
  setting: {
417
  profile: '概要',
web/src/pages/chat/share/large.tsx CHANGED
@@ -65,6 +65,7 @@ const ChatContainer = () => {
65
  </Flex>
66
 
67
  <MessageInput
 
68
  value={value}
69
  disabled={false}
70
  sendDisabled={sendDisabled}
 
65
  </Flex>
66
 
67
  <MessageInput
68
+ isShared
69
  value={value}
70
  disabled={false}
71
  sendDisabled={sendDisabled}
web/src/pages/chat/shared-hooks.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
  } from '@/hooks/chat-hooks';
6
  import { useSendMessageWithSse } from '@/hooks/logic-hooks';
7
  import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
8
- import { IAnswer } from '@/interfaces/database/chat';
9
  import api from '@/utils/api';
10
  import omit from 'lodash/omit';
11
  import trim from 'lodash/trim';
@@ -57,7 +57,7 @@ export const useSelectCurrentSharedConversation = (conversationId: string) => {
57
 
58
  const ref = useScrollToBottom(currentConversation);
59
 
60
- const addNewestConversation = useCallback((message: string) => {
61
  setCurrentConversation((pre) => {
62
  return {
63
  ...pre,
@@ -65,14 +65,15 @@ export const useSelectCurrentSharedConversation = (conversationId: string) => {
65
  ...(pre.message ?? []),
66
  {
67
  role: MessageType.User,
68
- content: message,
 
69
  id: uuid(),
70
  } as IMessage,
71
  {
72
  role: MessageType.Assistant,
73
  content: '',
74
  id: uuid(),
75
- reference: [],
76
  } as IMessage,
77
  ],
78
  };
@@ -140,7 +141,7 @@ export const useSendButtonDisabled = (value: string) => {
140
 
141
  export const useSendSharedMessage = (
142
  conversation: IClientConversation,
143
- addNewestConversation: (message: string) => void,
144
  removeLatestMessage: () => void,
145
  setCurrentConversation: Dispatch<SetStateAction<IClientConversation>>,
146
  addNewestAnswer: (answer: IAnswer) => void,
@@ -205,14 +206,17 @@ export const useSendSharedMessage = (
205
  }
206
  }, [answer, addNewestAnswer]);
207
 
208
- const handlePressEnter = useCallback(() => {
209
- if (trim(value) === '') return;
210
- if (done) {
211
- setValue('');
212
- addNewestConversation(value);
213
- handleSendMessage(value.trim());
214
- }
215
- }, [addNewestConversation, done, handleSendMessage, setValue, value]);
 
 
 
216
 
217
  return {
218
  handlePressEnter,
 
5
  } from '@/hooks/chat-hooks';
6
  import { useSendMessageWithSse } from '@/hooks/logic-hooks';
7
  import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
8
+ import { IAnswer, Message } from '@/interfaces/database/chat';
9
  import api from '@/utils/api';
10
  import omit from 'lodash/omit';
11
  import trim from 'lodash/trim';
 
57
 
58
  const ref = useScrollToBottom(currentConversation);
59
 
60
+ const addNewestConversation = useCallback((message: Partial<Message>) => {
61
  setCurrentConversation((pre) => {
62
  return {
63
  ...pre,
 
65
  ...(pre.message ?? []),
66
  {
67
  role: MessageType.User,
68
+ content: message.content,
69
+ doc_ids: message.doc_ids,
70
  id: uuid(),
71
  } as IMessage,
72
  {
73
  role: MessageType.Assistant,
74
  content: '',
75
  id: uuid(),
76
+ reference: {},
77
  } as IMessage,
78
  ],
79
  };
 
141
 
142
  export const useSendSharedMessage = (
143
  conversation: IClientConversation,
144
+ addNewestConversation: (message: Partial<Message>, answer?: string) => void,
145
  removeLatestMessage: () => void,
146
  setCurrentConversation: Dispatch<SetStateAction<IClientConversation>>,
147
  addNewestAnswer: (answer: IAnswer) => void,
 
206
  }
207
  }, [answer, addNewestAnswer]);
208
 
209
+ const handlePressEnter = useCallback(
210
+ (documentIds: string[]) => {
211
+ if (trim(value) === '') return;
212
+ if (done) {
213
+ setValue('');
214
+ addNewestConversation({ content: value, doc_ids: documentIds });
215
+ handleSendMessage(value.trim());
216
+ }
217
+ },
218
+ [addNewestConversation, done, handleSendMessage, setValue, value],
219
+ );
220
 
221
  return {
222
  handlePressEnter,
web/src/services/knowledge-service.ts CHANGED
@@ -12,6 +12,7 @@ const {
12
  get_document_list,
13
  document_change_status,
14
  document_rm,
 
15
  document_create,
16
  document_change_parser,
17
  document_thumbnails,
@@ -131,6 +132,10 @@ const methods = {
131
  url: knowledge_graph,
132
  method: 'get',
133
  },
 
 
 
 
134
  };
135
 
136
  const kbService = registerServer<keyof typeof methods>(methods, request);
 
12
  get_document_list,
13
  document_change_status,
14
  document_rm,
15
+ document_delete,
16
  document_create,
17
  document_change_parser,
18
  document_thumbnails,
 
132
  url: knowledge_graph,
133
  method: 'get',
134
  },
135
+ document_delete: {
136
+ url: document_delete,
137
+ method: 'delete',
138
+ },
139
  };
140
 
141
  const kbService = registerServer<keyof typeof methods>(methods, request);
web/src/utils/api.ts CHANGED
@@ -41,6 +41,7 @@ export default {
41
  get_document_list: `${api_host}/document/list`,
42
  document_change_status: `${api_host}/document/change_status`,
43
  document_rm: `${api_host}/document/rm`,
 
44
  document_rename: `${api_host}/document/rename`,
45
  document_create: `${api_host}/document/create`,
46
  document_run: `${api_host}/document/run`,
 
41
  get_document_list: `${api_host}/document/list`,
42
  document_change_status: `${api_host}/document/change_status`,
43
  document_rm: `${api_host}/document/rm`,
44
+ document_delete: `${api_host}/api/document`,
45
  document_rename: `${api_host}/document/rename`,
46
  document_create: `${api_host}/document/create`,
47
  document_run: `${api_host}/document/run`,
web/src/utils/register-server.ts CHANGED
@@ -6,6 +6,8 @@ type Service<T extends string> = Record<
6
  (params?: any, urlAppendix?: string) => any
7
  >;
8
 
 
 
9
  const registerServer = <T extends string>(
10
  opt: Record<T, { url: string; method: string }>,
11
  request: RequestMethod,
@@ -18,7 +20,7 @@ const registerServer = <T extends string>(
18
  if (urlAppendix) {
19
  url = url + '/' + urlAppendix;
20
  }
21
- if (opt[key].method === 'post' || opt[key].method === 'POST') {
22
  return request(url, {
23
  method: opt[key].method,
24
  data: params,
 
6
  (params?: any, urlAppendix?: string) => any
7
  >;
8
 
9
+ const Methods = ['post', 'delete', 'put'];
10
+
11
  const registerServer = <T extends string>(
12
  opt: Record<T, { url: string; method: string }>,
13
  request: RequestMethod,
 
20
  if (urlAppendix) {
21
  url = url + '/' + urlAppendix;
22
  }
23
+ if (Methods.some((x) => x === opt[key].method.toLowerCase())) {
24
  return request(url, {
25
  method: opt[key].method,
26
  data: params,