balibabu commited on
Commit
dd4cbfc
·
1 Parent(s): 4d5f1c9

feat: support DeepSeek (#667)

Browse files

### What problem does this PR solve?

#666
feat: support DeepSeek
feat: preview word and excel

### Type of change


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

web/package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
web/package.json CHANGED
@@ -3,7 +3,7 @@
3
  "author": "zhaofengchao <[email protected]>",
4
  "scripts": {
5
  "build": "umi build",
6
- "dev": "cross-env PORT=9000 umi dev",
7
  "postinstall": "umi setup",
8
  "lint": "umi lint --eslint-only",
9
  "setup": "umi setup",
@@ -13,6 +13,7 @@
13
  "@ant-design/icons": "^5.2.6",
14
  "@ant-design/pro-components": "^2.6.46",
15
  "@ant-design/pro-layout": "^7.17.16",
 
16
  "ahooks": "^3.7.10",
17
  "antd": "^5.12.7",
18
  "axios": "^1.6.3",
@@ -25,6 +26,7 @@
25
  "rc-tween-one": "^3.0.6",
26
  "react-chat-elements": "^12.0.13",
27
  "react-copy-to-clipboard": "^5.1.0",
 
28
  "react-i18next": "^14.0.0",
29
  "react-infinite-scroll-component": "^6.1.0",
30
  "react-markdown": "^9.0.1",
 
3
  "author": "zhaofengchao <[email protected]>",
4
  "scripts": {
5
  "build": "umi build",
6
+ "dev": "cross-env PORT=9200 umi dev",
7
  "postinstall": "umi setup",
8
  "lint": "umi lint --eslint-only",
9
  "setup": "umi setup",
 
13
  "@ant-design/icons": "^5.2.6",
14
  "@ant-design/pro-components": "^2.6.46",
15
  "@ant-design/pro-layout": "^7.17.16",
16
+ "@js-preview/excel": "^1.7.8",
17
  "ahooks": "^3.7.10",
18
  "antd": "^5.12.7",
19
  "axios": "^1.6.3",
 
26
  "rc-tween-one": "^3.0.6",
27
  "react-chat-elements": "^12.0.13",
28
  "react-copy-to-clipboard": "^5.1.0",
29
+ "react-file-viewer": "^1.2.1",
30
  "react-i18next": "^14.0.0",
31
  "react-infinite-scroll-component": "^6.1.0",
32
  "react-markdown": "^9.0.1",
web/src/assets/svg/llm/deepseek.svg ADDED
web/src/constants/common.ts CHANGED
@@ -46,3 +46,25 @@ export const LanguageTranslationMap = {
46
  Chinese: 'zh',
47
  'Traditional Chinese': 'zh-TRADITIONAL',
48
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  Chinese: 'zh',
47
  'Traditional Chinese': 'zh-TRADITIONAL',
48
  };
49
+
50
+ export const FileMimeTypeMap = {
51
+ bmp: 'image/bmp',
52
+ csv: 'text/csv',
53
+ odt: 'application/vnd.oasis.opendocument.text',
54
+ doc: 'application/msword',
55
+ docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
56
+ gif: 'image/gif',
57
+ htm: 'text/htm',
58
+ html: 'text/html',
59
+ jpg: 'image/jpg',
60
+ jpeg: 'image/jpeg',
61
+ pdf: 'application/pdf',
62
+ png: 'image/png',
63
+ ppt: 'application/vnd.ms-powerpoint',
64
+ pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
65
+ tiff: 'image/tiff',
66
+ txt: 'text/plain',
67
+ xls: 'application/vnd.ms-excel',
68
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
69
+ mp4: 'video/mp4',
70
+ };
web/src/pages/document-viewer/excel/index.tsx ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import jsPreviewExcel from '@js-preview/excel';
2
+ import '@js-preview/excel/lib/index.css';
3
+ import { useEffect } from 'react';
4
+
5
+ const Excel = ({ filePath }: { filePath: string }) => {
6
+ const fetchDocument = async () => {
7
+ const myExcelPreviewer = jsPreviewExcel.init(
8
+ document.getElementById('excel'),
9
+ );
10
+ const jsonFile = new XMLHttpRequest();
11
+ jsonFile.open('GET', filePath, true);
12
+ jsonFile.send();
13
+ jsonFile.responseType = 'arraybuffer';
14
+ jsonFile.onreadystatechange = () => {
15
+ if (jsonFile.readyState === 4 && jsonFile.status === 200) {
16
+ myExcelPreviewer
17
+ .preview(jsonFile.response)
18
+ .then((res: any) => {
19
+ console.log('succeed');
20
+ })
21
+ .catch((e) => {
22
+ console.log('failed', e);
23
+ });
24
+ }
25
+ };
26
+ };
27
+
28
+ useEffect(() => {
29
+ fetchDocument();
30
+ }, []);
31
+
32
+ return <div id="excel" style={{ height: '100%' }}></div>;
33
+ };
34
+
35
+ export default Excel;
web/src/pages/document-viewer/index.less ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ .viewerWrapper {
2
+ width: 100%;
3
+ :global {
4
+ .pdf-canvas {
5
+ text-align: center;
6
+ }
7
+ }
8
+ }
web/src/pages/document-viewer/index.tsx ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { api_host } from '@/utils/api';
2
+ import FileViewer from 'react-file-viewer';
3
+ import { useParams, useSearchParams } from 'umi';
4
+ import Excel from './excel';
5
+
6
+ import styles from './index.less';
7
+
8
+ const DocumentViewer = () => {
9
+ const { id: documentId } = useParams();
10
+ const api = `${api_host}/file/get/${documentId}`;
11
+ const [currentQueryParameters] = useSearchParams();
12
+ const ext = currentQueryParameters.get('ext');
13
+
14
+ const onError = (e: any) => {
15
+ console.error(e, 'error in file-viewer');
16
+ };
17
+
18
+ return (
19
+ <section className={styles.viewerWrapper}>
20
+ {ext === 'xlsx' && <Excel filePath={api}></Excel>}
21
+ {ext !== 'xlsx' && (
22
+ <FileViewer fileType={ext} filePath={api} onError={onError} />
23
+ )}
24
+ </section>
25
+ );
26
+ };
27
+
28
+ export default DocumentViewer;
web/src/pages/file-manager/action-cell/index.tsx CHANGED
@@ -9,7 +9,7 @@ import {
9
  LinkOutlined,
10
  } from '@ant-design/icons';
11
  import { Button, Space, Tooltip } from 'antd';
12
- import { useHandleDeleteFile } from '../hooks';
13
 
14
  import styles from './index.less';
15
 
@@ -35,6 +35,7 @@ const ActionCell = ({
35
  [documentId],
36
  setSelectedRowKeys,
37
  );
 
38
 
39
  const onDownloadDocument = () => {
40
  downloadFile({
@@ -58,6 +59,15 @@ const ActionCell = ({
58
 
59
  return (
60
  <Space size={0}>
 
 
 
 
 
 
 
 
 
61
  <Tooltip title={t('addToKnowledge')}>
62
  <Button
63
  type="text"
 
9
  LinkOutlined,
10
  } from '@ant-design/icons';
11
  import { Button, Space, Tooltip } from 'antd';
12
+ import { useHandleDeleteFile, useNavigateToDocument } from '../hooks';
13
 
14
  import styles from './index.less';
15
 
 
35
  [documentId],
36
  setSelectedRowKeys,
37
  );
38
+ const navigateToDocument = useNavigateToDocument(record.id, record.name);
39
 
40
  const onDownloadDocument = () => {
41
  downloadFile({
 
59
 
60
  return (
61
  <Space size={0}>
62
+ {/* <Tooltip title={t('addToKnowledge')}>
63
+ <Button
64
+ type="text"
65
+ className={styles.iconButton}
66
+ onClick={navigateToDocument}
67
+ >
68
+ <EyeOutlined size={20} />
69
+ </Button>
70
+ </Tooltip> */}
71
  <Tooltip title={t('addToKnowledge')}>
72
  <Button
73
  type="text"
web/src/pages/file-manager/hooks.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
  import { useGetPagination, useSetPagination } from '@/hooks/logicHooks';
14
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
15
  import { IFile } from '@/interfaces/database/file-manager';
 
16
  import { PaginationProps } from 'antd';
17
  import { UploadFile } from 'antd/lib';
18
  import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -338,3 +339,12 @@ export const useHandleBreadcrumbClick = () => {
338
 
339
  return { handleBreadcrumbClick };
340
  };
 
 
 
 
 
 
 
 
 
 
13
  import { useGetPagination, useSetPagination } from '@/hooks/logicHooks';
14
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
15
  import { IFile } from '@/interfaces/database/file-manager';
16
+ import { getExtension } from '@/utils/documentUtils';
17
  import { PaginationProps } from 'antd';
18
  import { UploadFile } from 'antd/lib';
19
  import { useCallback, useEffect, useMemo, useState } from 'react';
 
339
 
340
  return { handleBreadcrumbClick };
341
  };
342
+
343
+ export const useNavigateToDocument = (documentId: string, name: string) => {
344
+ const navigate = useNavigate();
345
+ const navigateToDocument = () => {
346
+ navigate(`/document/${documentId}?ext=${getExtension(name)}`);
347
+ };
348
+
349
+ return navigateToDocument;
350
+ };
web/src/pages/file-manager/index.tsx CHANGED
@@ -1,7 +1,7 @@
1
  import { useSelectFileList } from '@/hooks/fileManagerHooks';
2
  import { IFile } from '@/interfaces/database/file-manager';
3
  import { formatDate } from '@/utils/date';
4
- import { Button, Flex, Space, Table, Tag } from 'antd';
5
  import { ColumnsType } from 'antd/es/table';
6
  import ActionCell from './action-cell';
7
  import FileToolbar from './file-toolbar';
@@ -26,6 +26,8 @@ import ConnectToKnowledgeModal from './connect-to-knowledge-modal';
26
  import FolderCreateModal from './folder-create-modal';
27
  import styles from './index.less';
28
 
 
 
29
  const FileManager = () => {
30
  const { t } = useTranslate('fileManager');
31
  const fileList = useSelectFileList();
@@ -69,6 +71,7 @@ const FileManager = () => {
69
  title: t('name'),
70
  dataIndex: 'name',
71
  key: 'name',
 
72
  render(value, record) {
73
  return (
74
  <Flex gap={10} align="center">
@@ -82,10 +85,10 @@ const FileManager = () => {
82
  className={styles.linkButton}
83
  onClick={() => navigateToOtherFolder(record.id)}
84
  >
85
- {value}
86
  </Button>
87
  ) : (
88
- value
89
  )}
90
  </Flex>
91
  );
@@ -160,6 +163,7 @@ const FileManager = () => {
160
  rowSelection={rowSelection}
161
  loading={loading}
162
  pagination={pagination}
 
163
  />
164
  <RenameModal
165
  visible={fileRenameVisible}
 
1
  import { useSelectFileList } from '@/hooks/fileManagerHooks';
2
  import { IFile } from '@/interfaces/database/file-manager';
3
  import { formatDate } from '@/utils/date';
4
+ import { Button, Flex, Space, Table, Tag, Typography } from 'antd';
5
  import { ColumnsType } from 'antd/es/table';
6
  import ActionCell from './action-cell';
7
  import FileToolbar from './file-toolbar';
 
26
  import FolderCreateModal from './folder-create-modal';
27
  import styles from './index.less';
28
 
29
+ const { Text } = Typography;
30
+
31
  const FileManager = () => {
32
  const { t } = useTranslate('fileManager');
33
  const fileList = useSelectFileList();
 
71
  title: t('name'),
72
  dataIndex: 'name',
73
  key: 'name',
74
+ fixed: 'left',
75
  render(value, record) {
76
  return (
77
  <Flex gap={10} align="center">
 
85
  className={styles.linkButton}
86
  onClick={() => navigateToOtherFolder(record.id)}
87
  >
88
+ <Text ellipsis={{ tooltip: value }}>{value}</Text>
89
  </Button>
90
  ) : (
91
+ <Text ellipsis={{ tooltip: value }}>{value}</Text>
92
  )}
93
  </Flex>
94
  );
 
163
  rowSelection={rowSelection}
164
  loading={loading}
165
  pagination={pagination}
166
+ scroll={{ scrollToFirstRowOnChange: true, x: '100%' }}
167
  />
168
  <RenameModal
169
  visible={fileRenameVisible}
web/src/pages/file-manager/model.ts CHANGED
@@ -2,7 +2,9 @@ import { paginationModel } from '@/base';
2
  import { BaseState } from '@/interfaces/common';
3
  import { IFile, IFolder } from '@/interfaces/database/file-manager';
4
  import i18n from '@/locales/config';
5
- import fileManagerService from '@/services/fileManagerService';
 
 
6
  import { message } from 'antd';
7
  import omit from 'lodash/omit';
8
  import { DvaModel } from 'umi';
@@ -139,6 +141,11 @@ const model: DvaModel<FileManagerModelState> = {
139
  }
140
  return data.retcode;
141
  },
 
 
 
 
 
142
  },
143
  };
144
 
 
2
  import { BaseState } from '@/interfaces/common';
3
  import { IFile, IFolder } from '@/interfaces/database/file-manager';
4
  import i18n from '@/locales/config';
5
+ import fileManagerService, {
6
+ getDocumentFile,
7
+ } from '@/services/fileManagerService';
8
  import { message } from 'antd';
9
  import omit from 'lodash/omit';
10
  import { DvaModel } from 'umi';
 
141
  }
142
  return data.retcode;
143
  },
144
+ *getDocumentFile({ payload = {} }, { call }) {
145
+ const ret = yield call(getDocumentFile, payload);
146
+
147
+ return ret;
148
+ },
149
  },
150
  };
151
 
web/src/pages/user-setting/setting-model/index.tsx CHANGED
@@ -45,6 +45,7 @@ const IconMap = {
45
  文心一言: 'wenxin',
46
  Ollama: 'ollama',
47
  Xinference: 'xinference',
 
48
  };
49
 
50
  const LlmIcon = ({ name }: { name: string }) => {
 
45
  文心一言: 'wenxin',
46
  Ollama: 'ollama',
47
  Xinference: 'xinference',
48
+ DeepSeek: 'deepseek',
49
  };
50
 
51
  const LlmIcon = ({ name }: { name: string }) => {
web/src/routes.ts CHANGED
@@ -88,6 +88,10 @@ const routes = [
88
  path: '/flow',
89
  component: '@/pages/flow',
90
  },
 
 
 
 
91
  ],
92
  },
93
  {
 
88
  path: '/flow',
89
  component: '@/pages/flow',
90
  },
91
+ {
92
+ path: 'document/:id',
93
+ component: '@/pages/document-viewer',
94
+ },
95
  ],
96
  },
97
  {
web/src/services/fileManagerService.ts CHANGED
@@ -1,6 +1,7 @@
1
  import api from '@/utils/api';
2
  import registerServer from '@/utils/registerServer';
3
  import request from '@/utils/request';
 
4
 
5
  const {
6
  listFile,
@@ -10,6 +11,8 @@ const {
10
  getAllParentFolder,
11
  createFolder,
12
  connectFileToKnowledge,
 
 
13
  } = api;
14
 
15
  const methods = {
@@ -41,6 +44,11 @@ const methods = {
41
  url: connectFileToKnowledge,
42
  method: 'post',
43
  },
 
 
 
 
 
44
  } as const;
45
 
46
  const fileManagerService = registerServer<keyof typeof methods>(
@@ -49,3 +57,45 @@ const fileManagerService = registerServer<keyof typeof methods>(
49
  );
50
 
51
  export default fileManagerService;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import api from '@/utils/api';
2
  import registerServer from '@/utils/registerServer';
3
  import request from '@/utils/request';
4
+ import pureRequest from 'axios';
5
 
6
  const {
7
  listFile,
 
11
  getAllParentFolder,
12
  createFolder,
13
  connectFileToKnowledge,
14
+ get_document_file,
15
+ getFile,
16
  } = api;
17
 
18
  const methods = {
 
44
  url: connectFileToKnowledge,
45
  method: 'post',
46
  },
47
+ getDocumentFile: {
48
+ url: getFile,
49
+ method: 'get',
50
+ responseType: 'blob',
51
+ },
52
  } as const;
53
 
54
  const fileManagerService = registerServer<keyof typeof methods>(
 
57
  );
58
 
59
  export default fileManagerService;
60
+
61
+ export const getDocumentFile = (documentId: string) => {
62
+ return pureRequest(getFile + '/' + documentId, {
63
+ responseType: 'blob',
64
+ method: 'get',
65
+ // headers: {
66
+ // 'content-type':
67
+ // 'text/plain;charset=UTF-8, application/vnd.openxmlformats-officeddocument.spreadsheetml.sheet;charset=UTF-8',
68
+ // },
69
+
70
+ // parseResponse: false,
71
+ // getResponse: true,
72
+ })
73
+ .then((res) => {
74
+ const x = res?.headers?.get('content-disposition');
75
+ const y = res?.headers?.get('Content-Type');
76
+ console.info(res);
77
+ console.info(x);
78
+ console.info('Content-Type', y);
79
+ return res;
80
+ })
81
+ .then((res) => {
82
+ // const objectURL = URL.createObjectURL(res);
83
+
84
+ // let btn = document.createElement('a');
85
+
86
+ // btn.download = '文件名.pdf';
87
+
88
+ // btn.href = objectURL;
89
+
90
+ // btn.click();
91
+
92
+ // URL.revokeObjectURL(objectURL);
93
+
94
+ // btn = null;
95
+
96
+ return res;
97
+ })
98
+ .catch((err) => {
99
+ console.info(err);
100
+ });
101
+ };
web/src/utils/api.ts CHANGED
@@ -75,4 +75,5 @@ export default {
75
  getAllParentFolder: `${api_host}/file/all_parent_folder`,
76
  createFolder: `${api_host}/file/create`,
77
  connectFileToKnowledge: `${api_host}/file2document/convert`,
 
78
  };
 
75
  getAllParentFolder: `${api_host}/file/all_parent_folder`,
76
  createFolder: `${api_host}/file/create`,
77
  connectFileToKnowledge: `${api_host}/file2document/convert`,
78
+ getFile: `${api_host}/file/get`,
79
  };
web/src/utils/registerServer.ts CHANGED
@@ -1,3 +1,4 @@
 
1
  import { RequestMethod } from 'umi-request';
2
 
3
  type Service<T extends string> = Record<T, (params: any) => any>;
@@ -10,6 +11,7 @@ const registerServer = <T extends string>(
10
  for (let key in opt) {
11
  server[key] = (params: any, urlAppendix?: string) => {
12
  let url = opt[key].url;
 
13
  if (urlAppendix) {
14
  url = url + '/' + urlAppendix;
15
  }
@@ -22,6 +24,7 @@ const registerServer = <T extends string>(
22
 
23
  if (opt[key].method === 'get' || opt[key].method === 'GET') {
24
  return request.get(url, {
 
25
  params,
26
  });
27
  }
 
1
+ import omit from 'lodash/omit';
2
  import { RequestMethod } from 'umi-request';
3
 
4
  type Service<T extends string> = Record<T, (params: any) => any>;
 
11
  for (let key in opt) {
12
  server[key] = (params: any, urlAppendix?: string) => {
13
  let url = opt[key].url;
14
+ const requestOptions = opt[key];
15
  if (urlAppendix) {
16
  url = url + '/' + urlAppendix;
17
  }
 
24
 
25
  if (opt[key].method === 'get' || opt[key].method === 'GET') {
26
  return request.get(url, {
27
+ ...omit(requestOptions, ['method', 'url']),
28
  params,
29
  });
30
  }
web/typings.d.ts CHANGED
@@ -10,6 +10,7 @@ import { LoginModelState } from '@/pages/login/model';
10
  import { SettingModelState } from '@/pages/user-setting/model';
11
 
12
  declare module 'lodash';
 
13
 
14
  function useSelector<TState = RootState, TSelected = unknown>(
15
  selector: (state: TState) => TSelected,
 
10
  import { SettingModelState } from '@/pages/user-setting/model';
11
 
12
  declare module 'lodash';
13
+ declare module 'react-file-viewer';
14
 
15
  function useSelector<TState = RootState, TSelected = unknown>(
16
  selector: (state: TState) => TSelected,