balibabu commited on
Commit
f305776
·
1 Parent(s): 51482f3

feat: save the selected parser to the backend on the upload file page and upload document (#54)

Browse files

* feat: add pagination to document table

* feat: fetch document list by page

* feat: poll the document list

* feat: upload document

* feat: save the selected parser to the backend on the upload file page

web/src/assets/svg/select-files-end.svg ADDED
web/src/assets/svg/select-files-start.svg ADDED
web/src/components/deleting-confirm/index.tsx CHANGED
@@ -8,20 +8,31 @@ interface IProps {
8
  onCancel?: (...args: any[]) => any;
9
  }
10
 
11
- export const showDeleteConfirm = ({ onOk, onCancel }: IProps) => {
12
- confirm({
13
- title: 'Are you sure delete this item?',
14
- icon: <ExclamationCircleFilled />,
15
- content: 'Some descriptions',
16
- okText: 'Yes',
17
- okType: 'danger',
18
- cancelText: 'No',
19
- onOk() {
20
- onOk?.();
21
- },
22
- onCancel() {
23
- onCancel?.();
24
- },
 
 
 
 
 
 
 
 
 
 
 
25
  });
26
  };
27
 
 
8
  onCancel?: (...args: any[]) => any;
9
  }
10
 
11
+ export const showDeleteConfirm = ({
12
+ onOk,
13
+ onCancel,
14
+ }: IProps): Promise<number> => {
15
+ return new Promise((resolve, reject) => {
16
+ confirm({
17
+ title: 'Are you sure delete this item?',
18
+ icon: <ExclamationCircleFilled />,
19
+ content: 'Some descriptions',
20
+ okText: 'Yes',
21
+ okType: 'danger',
22
+ cancelText: 'No',
23
+ async onOk() {
24
+ try {
25
+ const ret = await onOk?.();
26
+ resolve(ret);
27
+ console.info(ret);
28
+ } catch (error) {
29
+ reject(error);
30
+ }
31
+ },
32
+ onCancel() {
33
+ onCancel?.();
34
+ },
35
+ });
36
  });
37
  };
38
 
web/src/hooks/knowledgeHook.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { useSearchParams } from 'umi';
 
 
2
 
3
  export const useKnowledgeBaseId = (): string => {
4
  const [searchParams] = useSearchParams();
@@ -6,3 +8,41 @@ export const useKnowledgeBaseId = (): string => {
6
 
7
  return knowledgeBaseId || '';
8
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import showDeleteConfirm from '@/components/deleting-confirm';
2
+ import { IKnowledge } from '@/interfaces/database/knowledge';
3
+ import { useDispatch, useSearchParams, useSelector } from 'umi';
4
 
5
  export const useKnowledgeBaseId = (): string => {
6
  const [searchParams] = useSearchParams();
 
8
 
9
  return knowledgeBaseId || '';
10
  };
11
+
12
+ export const useDeleteDocumentById = (): {
13
+ removeDocument: (documentId: string) => Promise<number>;
14
+ } => {
15
+ const dispatch = useDispatch();
16
+ const knowledgeBaseId = useKnowledgeBaseId();
17
+
18
+ const removeDocument = (documentId: string) => () => {
19
+ return dispatch({
20
+ type: 'kFModel/document_rm',
21
+ payload: {
22
+ doc_id: documentId,
23
+ kb_id: knowledgeBaseId,
24
+ },
25
+ });
26
+ };
27
+
28
+ const onRmDocument = (documentId: string): Promise<number> => {
29
+ return showDeleteConfirm({ onOk: removeDocument(documentId) });
30
+ };
31
+
32
+ return {
33
+ removeDocument: onRmDocument,
34
+ };
35
+ };
36
+
37
+ export const useGetDocumentDefaultParser = (knowledgeBaseId: string) => {
38
+ const data: IKnowledge[] = useSelector(
39
+ (state: any) => state.knowledgeModel.data,
40
+ );
41
+
42
+ const item = data.find((x) => x.id === knowledgeBaseId);
43
+
44
+ return {
45
+ defaultParserId: item?.parser_id ?? '',
46
+ parserConfig: item?.parser_config ?? '',
47
+ };
48
+ };
web/src/interfaces/common.ts ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ export interface Pagination {
2
+ current: number;
3
+ pageSize: number;
4
+ }
5
+
6
+ export interface BaseState {
7
+ pagination: Pagination;
8
+ }
web/src/interfaces/database/knowledge.ts CHANGED
@@ -1,5 +1,33 @@
1
  import { RunningStatus } from '@/constants/knowledge';
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  export interface IKnowledgeFile {
4
  chunk_num: number;
5
  create_date: string;
@@ -24,3 +52,16 @@ export interface IKnowledgeFile {
24
  update_date: string;
25
  update_time: number;
26
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { RunningStatus } from '@/constants/knowledge';
2
 
3
+ // knowledge base
4
+ export interface IKnowledge {
5
+ avatar?: any;
6
+ chunk_num: number;
7
+ create_date: string;
8
+ create_time: number;
9
+ created_by: string;
10
+ description: string;
11
+ doc_num: number;
12
+ id: string;
13
+ name: string;
14
+ parser_config: Parserconfig;
15
+ parser_id: string;
16
+ permission: string;
17
+ similarity_threshold: number;
18
+ status: string;
19
+ tenant_id: string;
20
+ token_num: number;
21
+ update_date: string;
22
+ update_time: number;
23
+ vector_similarity_weight: number;
24
+ }
25
+
26
+ export interface Parserconfig {
27
+ from_page: number;
28
+ to_page: number;
29
+ }
30
+
31
  export interface IKnowledgeFile {
32
  chunk_num: number;
33
  create_date: string;
 
52
  update_date: string;
53
  update_time: number;
54
  }
55
+
56
+ export interface ITenantInfo {
57
+ asr_id: string;
58
+ embd_id: string;
59
+ img2txt_id: string;
60
+ llm_id: string;
61
+ name: string;
62
+ parser_ids: string;
63
+ role: string;
64
+ tenant_id: string;
65
+ chat_id: string;
66
+ speech2text_id: string;
67
+ }
web/src/less/variable.less CHANGED
@@ -5,6 +5,7 @@
5
  @gray3: rgba(52, 48, 62, 1);
6
  @gray8: rgba(165, 163, 169, 1);
7
  @gray11: rgba(232, 232, 234, 1);
 
8
 
9
  @fontSize12: 12px;
10
  @fontSize14: 14px;
 
5
  @gray3: rgba(52, 48, 62, 1);
6
  @gray8: rgba(165, 163, 169, 1);
7
  @gray11: rgba(232, 232, 234, 1);
8
+ @purple: rgba(127, 86, 217, 1);
9
 
10
  @fontSize12: 12px;
11
  @fontSize14: 14px;
web/src/pages/add-knowledge/components/knowledge-dataset/knowledge-upload-file/index.less ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .uploadWrapper {
2
+ display: flex;
3
+ flex-direction: column;
4
+ padding: 32px;
5
+ height: 100%;
6
+ .backToList {
7
+ padding-bottom: 60px;
8
+ }
9
+ .footer {
10
+ text-align: right;
11
+ .nextButton {
12
+ background-color: @purple;
13
+ }
14
+ }
15
+ .uploadContent {
16
+ flex: 1;
17
+ padding-top: 60px;
18
+ }
19
+ .uploader {
20
+ display: block;
21
+ height: 200px;
22
+ }
23
+ .hiddenUploader {
24
+ :global(.ant-upload-drag) {
25
+ display: none;
26
+ }
27
+ }
28
+ .fileIcon {
29
+ font-size: 40px;
30
+ align-items: end;
31
+ }
32
+ .deleteIcon {
33
+ font-size: 20px;
34
+ }
35
+ .uploaderItem {
36
+ margin-top: 16px;
37
+ :global(.ant-card-body) {
38
+ padding: 16px;
39
+ }
40
+ }
41
+ .uploaderItemProgress {
42
+ padding-left: 50px;
43
+ }
44
+ .uploaderItemTextWrapper {
45
+ flex: 1;
46
+ text-align: left;
47
+ padding-left: 10px;
48
+ }
49
+ }
50
+
51
+ .progressWrapper {
52
+ text-align: center;
53
+ }
54
+
55
+ .progress {
56
+ width: 50%;
57
+ margin: 0;
58
+ }
59
+
60
+ .selectFilesText {
61
+ transform: translateX(10%);
62
+ }
63
+ .changeSpecificCategoryText {
64
+ transform: translateX(30%);
65
+ }
web/src/pages/add-knowledge/components/knowledge-dataset/knowledge-upload-file/index.tsx CHANGED
@@ -1,5 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  const KnowledgeUploadFile = () => {
2
- return <div>KnowledgeUploadFile</div>;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  };
4
 
5
  export default KnowledgeUploadFile;
 
1
+ import { ReactComponent as SelectFilesEndIcon } from '@/assets/svg/select-files-end.svg';
2
+ import { ReactComponent as SelectFilesStartIcon } from '@/assets/svg/select-files-start.svg';
3
+ import {
4
+ useDeleteDocumentById,
5
+ useGetDocumentDefaultParser,
6
+ useKnowledgeBaseId,
7
+ } from '@/hooks/knowledgeHook';
8
+ import { ITenantInfo } from '@/interfaces/database/knowledge';
9
+ import uploadService from '@/services/uploadService';
10
+ import {
11
+ ArrowLeftOutlined,
12
+ DeleteOutlined,
13
+ EditOutlined,
14
+ FileDoneOutlined,
15
+ InboxOutlined,
16
+ } from '@ant-design/icons';
17
+ import {
18
+ Button,
19
+ Card,
20
+ Flex,
21
+ Popover,
22
+ Progress,
23
+ Radio,
24
+ RadioChangeEvent,
25
+ Space,
26
+ Upload,
27
+ UploadFile,
28
+ UploadProps,
29
+ } from 'antd';
30
+ import classNames from 'classnames';
31
+ import { ReactElement, useEffect, useState } from 'react';
32
+ import { Nullable } from 'typings';
33
+ import { Link, useDispatch, useNavigate, useSelector } from 'umi';
34
+
35
+ import { KnowledgeRouteKey } from '@/constants/knowledge';
36
+ import styles from './index.less';
37
+
38
+ const { Dragger } = Upload;
39
+
40
+ type UploadRequestOption = Parameters<
41
+ NonNullable<UploadProps['customRequest']>
42
+ >[0];
43
+
44
+ const UploaderItem = ({
45
+ file,
46
+ actions,
47
+ isUpload,
48
+ parserArray,
49
+ }: {
50
+ isUpload: boolean;
51
+ originNode: ReactElement;
52
+ file: UploadFile;
53
+ fileList: object[];
54
+ parserArray: string[];
55
+ actions: { download: Function; preview: Function; remove: any };
56
+ }) => {
57
+ const { parserConfig, defaultParserId } = useGetDocumentDefaultParser(
58
+ file?.response?.kb_id,
59
+ );
60
+ const { removeDocument } = useDeleteDocumentById();
61
+ const [value, setValue] = useState(defaultParserId);
62
+ const dispatch = useDispatch();
63
+
64
+ const documentId = file?.response?.id;
65
+
66
+ const onChange = (e: RadioChangeEvent) => {
67
+ const val = e.target.value;
68
+ setValue(val);
69
+ saveParser(val);
70
+ };
71
+
72
+ const content = (
73
+ <Radio.Group onChange={onChange} value={value}>
74
+ <Space direction="vertical">
75
+ {parserArray.map((x) => (
76
+ <Radio value={x} key={x}>
77
+ {x}
78
+ </Radio>
79
+ ))}
80
+ </Space>
81
+ </Radio.Group>
82
+ );
83
+
84
+ const handleRemove = async () => {
85
+ const ret: any = await removeDocument(documentId);
86
+ if (ret === 0) {
87
+ actions?.remove();
88
+ }
89
+ };
90
+
91
+ const saveParser = (parserId: string) => {
92
+ dispatch({
93
+ type: 'kFModel/document_change_parser',
94
+ payload: {
95
+ parser_id: parserId,
96
+ doc_id: documentId,
97
+ parser_config: parserConfig,
98
+ },
99
+ });
100
+ };
101
+
102
+ useEffect(() => {
103
+ setValue(defaultParserId);
104
+ }, [defaultParserId]);
105
+
106
+ return (
107
+ <Card className={styles.uploaderItem}>
108
+ <Flex justify="space-between">
109
+ <FileDoneOutlined className={styles.fileIcon} />
110
+ <section className={styles.uploaderItemTextWrapper}>
111
+ <div>
112
+ <b>{file.name}</b>
113
+ </div>
114
+ <span>{file.size}</span>
115
+ </section>
116
+ {isUpload ? (
117
+ <DeleteOutlined
118
+ className={styles.deleteIcon}
119
+ onClick={handleRemove}
120
+ />
121
+ ) : (
122
+ <Popover content={content} placement="bottom">
123
+ <EditOutlined />
124
+ </Popover>
125
+ )}
126
+ </Flex>
127
+ <Flex>
128
+ <Progress
129
+ showInfo={false}
130
+ percent={100}
131
+ className={styles.uploaderItemProgress}
132
+ strokeColor="
133
+ rgba(127, 86, 217, 1)
134
+ "
135
+ />
136
+ <span>100%</span>
137
+ </Flex>
138
+ </Card>
139
+ );
140
+ };
141
+
142
  const KnowledgeUploadFile = () => {
143
+ const knowledgeBaseId = useKnowledgeBaseId();
144
+ const [isUpload, setIsUpload] = useState(true);
145
+ const dispatch = useDispatch();
146
+ const tenantIfo: Nullable<ITenantInfo> = useSelector(
147
+ (state: any) => state.settingModel.tenantIfo,
148
+ );
149
+ const navigate = useNavigate();
150
+
151
+ const parserArray = tenantIfo?.parser_ids.split(',') ?? [];
152
+
153
+ const createRequest: (props: UploadRequestOption) => void = async function ({
154
+ file,
155
+ onSuccess,
156
+ onError,
157
+ onProgress,
158
+ }) {
159
+ const { data } = await uploadService.uploadFile(file, knowledgeBaseId);
160
+ if (data.retcode === 0) {
161
+ onSuccess && onSuccess(data.data);
162
+ } else {
163
+ onError && onError(data.data);
164
+ }
165
+ };
166
+
167
+ const props: UploadProps = {
168
+ name: 'file',
169
+ multiple: true,
170
+ itemRender(originNode, file, fileList, actions) {
171
+ return (
172
+ <UploaderItem
173
+ isUpload={isUpload}
174
+ file={file}
175
+ fileList={fileList}
176
+ originNode={originNode}
177
+ actions={actions}
178
+ parserArray={parserArray}
179
+ ></UploaderItem>
180
+ );
181
+ },
182
+ customRequest: createRequest,
183
+ onDrop(e) {
184
+ console.log('Dropped files', e.dataTransfer.files);
185
+ },
186
+ };
187
+
188
+ const handleNextClick = () => {
189
+ if (!isUpload) {
190
+ navigate(`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`);
191
+ } else {
192
+ setIsUpload(false);
193
+ }
194
+ };
195
+
196
+ useEffect(() => {
197
+ dispatch({
198
+ type: 'settingModel/getTenantInfo',
199
+ });
200
+ }, []);
201
+
202
+ return (
203
+ <div className={styles.uploadWrapper}>
204
+ <section>
205
+ <Space className={styles.backToList}>
206
+ <ArrowLeftOutlined />
207
+ <Link to={`/knowledge/dataset?id=${knowledgeBaseId}`}>
208
+ Back to select files
209
+ </Link>
210
+ </Space>
211
+ <div className={styles.progressWrapper}>
212
+ <Flex align="center" justify="center">
213
+ <SelectFilesStartIcon></SelectFilesStartIcon>
214
+ <Progress
215
+ percent={100}
216
+ showInfo={false}
217
+ className={styles.progress}
218
+ strokeColor="
219
+ rgba(127, 86, 217, 1)
220
+ "
221
+ />
222
+ <SelectFilesEndIcon></SelectFilesEndIcon>
223
+ </Flex>
224
+ <Flex justify="space-around">
225
+ <p className={styles.selectFilesText}>
226
+ <b>Select files</b>
227
+ </p>
228
+ <p className={styles.changeSpecificCategoryText}>
229
+ <b>Change specific category</b>
230
+ </p>
231
+ </Flex>
232
+ </div>
233
+ </section>
234
+ <section className={styles.uploadContent}>
235
+ <Dragger
236
+ {...props}
237
+ className={classNames(styles.uploader, {
238
+ [styles.hiddenUploader]: !isUpload,
239
+ })}
240
+ >
241
+ <p className="ant-upload-drag-icon">
242
+ <InboxOutlined />
243
+ </p>
244
+ <p className="ant-upload-text">
245
+ Click or drag file to this area to upload
246
+ </p>
247
+ <p className="ant-upload-hint">
248
+ Support for a single or bulk upload. Strictly prohibited from
249
+ uploading company data or other banned files.
250
+ </p>
251
+ </Dragger>
252
+ </section>
253
+ <section className={styles.footer}>
254
+ <Button
255
+ type="primary"
256
+ className={styles.nextButton}
257
+ onClick={handleNextClick}
258
+ >
259
+ Next
260
+ </Button>
261
+ </section>
262
+ </div>
263
+ );
264
  };
265
 
266
  export default KnowledgeUploadFile;
web/src/pages/add-knowledge/components/knowledge-file/index.tsx CHANGED
@@ -1,5 +1,9 @@
1
  import { KnowledgeRouteKey } from '@/constants/knowledge';
2
- import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
 
 
 
 
3
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
4
  import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil';
5
  import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
@@ -15,8 +19,8 @@ import {
15
  Tag,
16
  } from 'antd';
17
  import type { ColumnsType } from 'antd/es/table';
18
- import { debounce } from 'lodash';
19
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
20
  import { useDispatch, useNavigate, useSelector } from 'umi';
21
  import CreateEPModal from './createEFileModal';
22
  import styles from './index.less';
@@ -30,50 +34,90 @@ const KnowledgeFile = () => {
30
  const dispatch = useDispatch();
31
  const kFModel = useSelector((state: any) => state.kFModel);
32
  const effects = useSelector((state: any) => state.loading.effects);
33
- const { data } = kFModel;
34
  const knowledgeBaseId = useKnowledgeBaseId();
 
35
 
36
  const loading = getOneNamespaceEffectsLoading('kFModel', effects, [
37
  'getKfList',
38
  'updateDocumentStatus',
39
  ]);
40
- const [inputValue, setInputValue] = useState('');
41
  const [doc_id, setDocId] = useState('0');
42
  const [parser_id, setParserId] = useState('0');
43
  let navigate = useNavigate();
44
 
45
- const getKfList = (keywords?: string) => {
46
  const payload = {
47
  kb_id: knowledgeBaseId,
48
- keywords,
49
  };
50
- if (!keywords) {
51
- delete payload.keywords;
52
- }
53
  dispatch({
54
  type: 'kFModel/getKfList',
55
  payload,
56
  });
57
  };
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  useEffect(() => {
60
  if (knowledgeBaseId) {
61
  getKfList();
 
 
 
 
62
  }
 
 
 
 
 
63
  }, [knowledgeBaseId]);
64
 
65
- const debounceChange = debounce(getKfList, 300);
66
- const debounceCallback = useCallback(
67
- (value: string) => debounceChange(value),
68
- [],
69
- );
70
  const handleInputChange = (
71
  e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
72
  ) => {
73
  const value = e.target.value;
74
- setInputValue(value);
75
- debounceCallback(e.target.value);
 
76
  };
 
77
  const onChangeStatus = (e: boolean, doc_id: string) => {
78
  dispatch({
79
  type: 'kFModel/updateDocumentStatus',
@@ -85,13 +129,7 @@ const KnowledgeFile = () => {
85
  });
86
  };
87
  const onRmDocument = () => {
88
- dispatch({
89
- type: 'kFModel/document_rm',
90
- payload: {
91
- doc_id,
92
- kb_id: knowledgeBaseId,
93
- },
94
- });
95
  };
96
  const showCEFModal = () => {
97
  dispatch({
@@ -226,7 +264,6 @@ const KnowledgeFile = () => {
226
  key: 'action',
227
  render: (_, record) => (
228
  <ParsingActionCell
229
- documentId={doc_id}
230
  knowledgeBaseId={knowledgeBaseId}
231
  setDocumentAndParserId={setDocumentAndParserId(record)}
232
  record={record}
@@ -248,12 +285,12 @@ const KnowledgeFile = () => {
248
  <div className={styles.filter}>
249
  <Space>
250
  <h3>Total</h3>
251
- <Tag color="purple">100 files</Tag>
252
  </Space>
253
  <Space>
254
  <Input
255
  placeholder="Seach your files"
256
- value={inputValue}
257
  style={{ width: 220 }}
258
  allowClear
259
  onChange={handleInputChange}
@@ -272,7 +309,7 @@ const KnowledgeFile = () => {
272
  columns={finalColumns}
273
  dataSource={data}
274
  loading={loading}
275
- pagination={false}
276
  scroll={{ scrollToFirstRowOnChange: true, x: true, y: 'fill' }}
277
  />
278
  <CreateEPModal getKfList={getKfList} kb_id={knowledgeBaseId} />
 
1
  import { KnowledgeRouteKey } from '@/constants/knowledge';
2
+ import {
3
+ useDeleteDocumentById,
4
+ useKnowledgeBaseId,
5
+ } from '@/hooks/knowledgeHook';
6
+ import { Pagination } from '@/interfaces/common';
7
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
8
  import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil';
9
  import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
 
19
  Tag,
20
  } from 'antd';
21
  import type { ColumnsType } from 'antd/es/table';
22
+ import { PaginationProps } from 'antd/lib';
23
+ import React, { useEffect, useMemo, useState } from 'react';
24
  import { useDispatch, useNavigate, useSelector } from 'umi';
25
  import CreateEPModal from './createEFileModal';
26
  import styles from './index.less';
 
34
  const dispatch = useDispatch();
35
  const kFModel = useSelector((state: any) => state.kFModel);
36
  const effects = useSelector((state: any) => state.loading.effects);
37
+ const { data, total } = kFModel;
38
  const knowledgeBaseId = useKnowledgeBaseId();
39
+ const { removeDocument } = useDeleteDocumentById();
40
 
41
  const loading = getOneNamespaceEffectsLoading('kFModel', effects, [
42
  'getKfList',
43
  'updateDocumentStatus',
44
  ]);
 
45
  const [doc_id, setDocId] = useState('0');
46
  const [parser_id, setParserId] = useState('0');
47
  let navigate = useNavigate();
48
 
49
+ const getKfList = () => {
50
  const payload = {
51
  kb_id: knowledgeBaseId,
 
52
  };
53
+
 
 
54
  dispatch({
55
  type: 'kFModel/getKfList',
56
  payload,
57
  });
58
  };
59
 
60
+ const throttledGetDocumentList = () => {
61
+ dispatch({
62
+ type: 'kFModel/throttledGetDocumentList',
63
+ payload: knowledgeBaseId,
64
+ });
65
+ };
66
+
67
+ const setPagination = (pageNumber = 1, pageSize?: number) => {
68
+ const pagination: Pagination = {
69
+ current: pageNumber,
70
+ } as Pagination;
71
+ if (pageSize) {
72
+ pagination.pageSize = pageSize;
73
+ }
74
+ dispatch({
75
+ type: 'kFModel/setPagination',
76
+ payload: pagination,
77
+ });
78
+ };
79
+
80
+ const onPageChange: PaginationProps['onChange'] = (pageNumber, pageSize) => {
81
+ setPagination(pageNumber, pageSize);
82
+ getKfList();
83
+ };
84
+
85
+ const pagination: PaginationProps = useMemo(() => {
86
+ return {
87
+ showQuickJumper: true,
88
+ total,
89
+ showSizeChanger: true,
90
+ current: kFModel.pagination.currentPage,
91
+ pageSize: kFModel.pagination.pageSize,
92
+ pageSizeOptions: [1, 2, 10, 20, 50, 100],
93
+ onChange: onPageChange,
94
+ };
95
+ }, [total, kFModel.pagination]);
96
+
97
  useEffect(() => {
98
  if (knowledgeBaseId) {
99
  getKfList();
100
+ dispatch({
101
+ type: 'kFModel/pollGetDocumentList-start',
102
+ payload: knowledgeBaseId,
103
+ });
104
  }
105
+ return () => {
106
+ dispatch({
107
+ type: 'kFModel/pollGetDocumentList-stop',
108
+ });
109
+ };
110
  }, [knowledgeBaseId]);
111
 
 
 
 
 
 
112
  const handleInputChange = (
113
  e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
114
  ) => {
115
  const value = e.target.value;
116
+ dispatch({ type: 'kFModel/setSearchString', payload: value });
117
+ setPagination();
118
+ throttledGetDocumentList();
119
  };
120
+
121
  const onChangeStatus = (e: boolean, doc_id: string) => {
122
  dispatch({
123
  type: 'kFModel/updateDocumentStatus',
 
129
  });
130
  };
131
  const onRmDocument = () => {
132
+ removeDocument(doc_id);
 
 
 
 
 
 
133
  };
134
  const showCEFModal = () => {
135
  dispatch({
 
264
  key: 'action',
265
  render: (_, record) => (
266
  <ParsingActionCell
 
267
  knowledgeBaseId={knowledgeBaseId}
268
  setDocumentAndParserId={setDocumentAndParserId(record)}
269
  record={record}
 
285
  <div className={styles.filter}>
286
  <Space>
287
  <h3>Total</h3>
288
+ <Tag color="purple">{total} files</Tag>
289
  </Space>
290
  <Space>
291
  <Input
292
  placeholder="Seach your files"
293
+ value={kFModel.searchString}
294
  style={{ width: 220 }}
295
  allowClear
296
  onChange={handleInputChange}
 
309
  columns={finalColumns}
310
  dataSource={data}
311
  loading={loading}
312
+ pagination={pagination}
313
  scroll={{ scrollToFirstRowOnChange: true, x: true, y: 'fill' }}
314
  />
315
  <CreateEPModal getKfList={getKfList} kb_id={knowledgeBaseId} />
web/src/pages/add-knowledge/components/knowledge-file/model.ts CHANGED
@@ -1,3 +1,4 @@
 
1
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
2
  import kbService from '@/services/kbService';
3
  import { message } from 'antd';
@@ -6,14 +7,16 @@ import pick from 'lodash/pick';
6
  import { Nullable } from 'typings';
7
  import { DvaModel } from 'umi';
8
 
9
- export interface KFModelState {
10
  isShowCEFwModal: boolean;
11
  isShowTntModal: boolean;
12
  isShowSegmentSetModal: boolean;
13
  isShowRenameModal: boolean;
14
  tenantIfo: any;
15
  data: IKnowledgeFile[];
 
16
  currentRecord: Nullable<IKnowledgeFile>;
 
17
  }
18
 
19
  const model: DvaModel<KFModelState> = {
@@ -25,7 +28,13 @@ const model: DvaModel<KFModelState> = {
25
  isShowRenameModal: false,
26
  tenantIfo: {},
27
  data: [],
 
28
  currentRecord: null,
 
 
 
 
 
29
  },
30
  reducers: {
31
  updateState(state, { payload }) {
@@ -40,6 +49,12 @@ const model: DvaModel<KFModelState> = {
40
  setCurrentRecord(state, { payload }) {
41
  return { ...state, currentRecord: payload };
42
  },
 
 
 
 
 
 
43
  },
44
  subscriptions: {
45
  setup({ dispatch, history }) {
@@ -69,22 +84,41 @@ const model: DvaModel<KFModelState> = {
69
  callback && callback(res);
70
  }
71
  },
72
- *getKfList({ payload = {} }, { call, put }) {
73
- const { data, response } = yield call(
74
- kbService.get_document_list,
75
- payload,
76
- );
77
- const { retcode, data: res, retmsg } = data;
 
 
 
 
 
 
78
 
79
  if (retcode === 0) {
80
  yield put({
81
  type: 'updateState',
82
  payload: {
83
- data: res,
 
84
  },
85
  });
86
  }
87
  },
 
 
 
 
 
 
 
 
 
 
 
 
88
  *updateDocumentStatus({ payload = {} }, { call, put }) {
89
  const { data, response } = yield call(
90
  kbService.document_change_status,
@@ -106,11 +140,12 @@ const model: DvaModel<KFModelState> = {
106
  const { retcode, data: res, retmsg } = data;
107
  if (retcode === 0) {
108
  message.success('删除成功!');
109
- put({
110
  type: 'getKfList',
111
  payload: { kb_id: payload.kb_id },
112
  });
113
  }
 
114
  },
115
  *document_rename({ payload = {} }, { call, put }) {
116
  const { data } = yield call(
 
1
+ import { BaseState } from '@/interfaces/common';
2
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
3
  import kbService from '@/services/kbService';
4
  import { message } from 'antd';
 
7
  import { Nullable } from 'typings';
8
  import { DvaModel } from 'umi';
9
 
10
+ export interface KFModelState extends BaseState {
11
  isShowCEFwModal: boolean;
12
  isShowTntModal: boolean;
13
  isShowSegmentSetModal: boolean;
14
  isShowRenameModal: boolean;
15
  tenantIfo: any;
16
  data: IKnowledgeFile[];
17
+ total: number;
18
  currentRecord: Nullable<IKnowledgeFile>;
19
+ searchString: string;
20
  }
21
 
22
  const model: DvaModel<KFModelState> = {
 
28
  isShowRenameModal: false,
29
  tenantIfo: {},
30
  data: [],
31
+ total: 0,
32
  currentRecord: null,
33
+ searchString: '',
34
+ pagination: {
35
+ current: 1,
36
+ pageSize: 10,
37
+ },
38
  },
39
  reducers: {
40
  updateState(state, { payload }) {
 
49
  setCurrentRecord(state, { payload }) {
50
  return { ...state, currentRecord: payload };
51
  },
52
+ setSearchString(state, { payload }) {
53
+ return { ...state, searchString: payload };
54
+ },
55
+ setPagination(state, { payload }) {
56
+ return { ...state, pagination: { ...state.pagination, ...payload } };
57
+ },
58
  },
59
  subscriptions: {
60
  setup({ dispatch, history }) {
 
84
  callback && callback(res);
85
  }
86
  },
87
+ *getKfList({ payload = {} }, { call, put, select }) {
88
+ const state: KFModelState = yield select((state: any) => state.kFModel);
89
+ const requestBody = {
90
+ ...payload,
91
+ page: state.pagination.current,
92
+ page_size: state.pagination.pageSize,
93
+ };
94
+ if (state.searchString) {
95
+ requestBody['keywords'] = state.searchString;
96
+ }
97
+ const { data } = yield call(kbService.get_document_list, requestBody);
98
+ const { retcode, data: res } = data;
99
 
100
  if (retcode === 0) {
101
  yield put({
102
  type: 'updateState',
103
  payload: {
104
+ data: res.docs,
105
+ total: res.total,
106
  },
107
  });
108
  }
109
  },
110
+ throttledGetDocumentList: [
111
+ function* ({ payload }, { call, put }) {
112
+ yield put({ type: 'getKfList', payload: { kb_id: payload } });
113
+ },
114
+ { type: 'throttle', ms: 1000 }, // TODO: Provide type support for this effect
115
+ ],
116
+ pollGetDocumentList: [
117
+ function* ({ payload }, { call, put }) {
118
+ yield put({ type: 'getKfList', payload: { kb_id: payload } });
119
+ },
120
+ { type: 'poll', delay: 5000 }, // TODO: Provide type support for this effect
121
+ ],
122
  *updateDocumentStatus({ payload = {} }, { call, put }) {
123
  const { data, response } = yield call(
124
  kbService.document_change_status,
 
140
  const { retcode, data: res, retmsg } = data;
141
  if (retcode === 0) {
142
  message.success('删除成功!');
143
+ yield put({
144
  type: 'getKfList',
145
  payload: { kb_id: payload.kb_id },
146
  });
147
  }
148
+ return retcode;
149
  },
150
  *document_rename({ payload = {} }, { call, put }) {
151
  const { data } = yield call(
web/src/pages/add-knowledge/components/knowledge-file/parsing-action-cell/index.tsx CHANGED
@@ -5,19 +5,18 @@ import { Button, Dropdown, MenuProps, Space, Tooltip } from 'antd';
5
  import { useDispatch } from 'umi';
6
 
7
  interface IProps {
8
- documentId: string;
9
  knowledgeBaseId: string;
10
  record: IKnowledgeFile;
11
  setDocumentAndParserId: () => void;
12
  }
13
 
14
  const ParsingActionCell = ({
15
- documentId,
16
  knowledgeBaseId,
17
  record,
18
  setDocumentAndParserId,
19
  }: IProps) => {
20
  const dispatch = useDispatch();
 
21
 
22
  const removeDocument = () => {
23
  dispatch({
 
5
  import { useDispatch } from 'umi';
6
 
7
  interface IProps {
 
8
  knowledgeBaseId: string;
9
  record: IKnowledgeFile;
10
  setDocumentAndParserId: () => void;
11
  }
12
 
13
  const ParsingActionCell = ({
 
14
  knowledgeBaseId,
15
  record,
16
  setDocumentAndParserId,
17
  }: IProps) => {
18
  const dispatch = useDispatch();
19
+ const documentId = record.id;
20
 
21
  const removeDocument = () => {
22
  dispatch({
web/src/pages/add-knowledge/components/knowledge-file/segmentSetModal.tsx CHANGED
@@ -18,8 +18,7 @@ const SegmentSetModal: React.FC<kFProps> = ({
18
  const kFModel = useSelector((state: any) => state.kFModel);
19
  const settingModel = useSelector((state: any) => state.settingModel);
20
  const [selectedTag, setSelectedTag] = useState('');
21
- const { tenantIfo = {} } = settingModel;
22
- const { parser_ids = '' } = tenantIfo;
23
  const { isShowSegmentSetModal } = kFModel;
24
  const { t } = useTranslation();
25
 
 
18
  const kFModel = useSelector((state: any) => state.kFModel);
19
  const settingModel = useSelector((state: any) => state.settingModel);
20
  const [selectedTag, setSelectedTag] = useState('');
21
+ const parser_ids = settingModel?.tenantIfo?.parser_ids ?? '';
 
22
  const { isShowSegmentSetModal } = kFModel;
23
  const { t } = useTranslation();
24
 
web/src/pages/add-knowledge/components/knowledge-file/upload.tsx CHANGED
@@ -1,7 +1,8 @@
 
1
  import uploadService from '@/services/uploadService';
2
  import type { UploadProps } from 'antd';
3
- import { Button, Upload } from 'antd';
4
  import React from 'react';
 
5
  interface PropsType {
6
  kb_id: string;
7
  getKfList: () => void;
@@ -12,6 +13,8 @@ type UploadRequestOption = Parameters<
12
  >[0];
13
 
14
  const FileUpload: React.FC<PropsType> = ({ kb_id, getKfList }) => {
 
 
15
  const createRequest: (props: UploadRequestOption) => void = async function ({
16
  file,
17
  onSuccess,
@@ -30,9 +33,9 @@ const FileUpload: React.FC<PropsType> = ({ kb_id, getKfList }) => {
30
  showUploadList: false,
31
  };
32
  return (
33
- <Upload {...uploadProps}>
34
- <Button type="link">导入文件</Button>
35
- </Upload>
36
  );
37
  };
38
 
 
1
+ import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
2
  import uploadService from '@/services/uploadService';
3
  import type { UploadProps } from 'antd';
 
4
  import React from 'react';
5
+ import { Link } from 'umi';
6
  interface PropsType {
7
  kb_id: string;
8
  getKfList: () => void;
 
13
  >[0];
14
 
15
  const FileUpload: React.FC<PropsType> = ({ kb_id, getKfList }) => {
16
+ const knowledgeBaseId = useKnowledgeBaseId();
17
+
18
  const createRequest: (props: UploadRequestOption) => void = async function ({
19
  file,
20
  onSuccess,
 
33
  showUploadList: false,
34
  };
35
  return (
36
+ // <Upload {...uploadProps}>
37
+ <Link to={`/knowledge/dataset/upload?id=${knowledgeBaseId}`}>导入文件</Link>
38
+ // </Upload>
39
  );
40
  };
41
 
web/src/pages/add-knowledge/components/knowledge-sidebar/index.tsx CHANGED
@@ -75,7 +75,6 @@ const KnowledgeSidebar = () => {
75
  useEffect(() => {
76
  const widthSize = () => {
77
  const width = getWidth();
78
- console.log(width);
79
 
80
  setWindowWidth(width);
81
  };
@@ -106,7 +105,7 @@ const KnowledgeSidebar = () => {
106
  [styles.defaultWidth]: windowWidth.width > 957,
107
  [styles.minWidth]: windowWidth.width <= 957,
108
  })}
109
- inlineCollapsed={collapsed}
110
  items={items}
111
  onSelect={handleSelect}
112
  />
 
75
  useEffect(() => {
76
  const widthSize = () => {
77
  const width = getWidth();
 
78
 
79
  setWindowWidth(width);
80
  };
 
105
  [styles.defaultWidth]: windowWidth.width > 957,
106
  [styles.minWidth]: windowWidth.width <= 957,
107
  })}
108
+ // inlineCollapsed={collapsed}
109
  items={items}
110
  onSelect={handleSelect}
111
  />
web/src/pages/add-knowledge/index.less CHANGED
@@ -13,6 +13,6 @@
13
  .content {
14
  background-color: white;
15
  margin-top: 16px;
16
- // flex: 1;
17
  }
18
  }
 
13
  .content {
14
  background-color: white;
15
  margin-top: 16px;
16
+ flex: 1;
17
  }
18
  }
web/src/pages/setting/model.ts CHANGED
@@ -1,6 +1,8 @@
 
1
  import userService from '@/services/userService';
2
  import authorizationUtil from '@/utils/authorizationUtil';
3
  import { message } from 'antd';
 
4
  import { DvaModel } from 'umi';
5
 
6
  export interface SettingModelState {
@@ -9,7 +11,7 @@ export interface SettingModelState {
9
  isShowSAKModal: boolean;
10
  isShowSSModal: boolean;
11
  llm_factory: string;
12
- tenantIfo: any;
13
  llmInfo: any;
14
  myLlm: any[];
15
  factoriesList: any[];
@@ -23,7 +25,7 @@ const model: DvaModel<SettingModelState> = {
23
  isShowSAKModal: false,
24
  isShowSSModal: false,
25
  llm_factory: '',
26
- tenantIfo: {},
27
  llmInfo: {},
28
  myLlm: [],
29
  factoriesList: [],
@@ -73,23 +75,11 @@ const model: DvaModel<SettingModelState> = {
73
  }
74
  },
75
  *getTenantInfo({ payload = {} }, { call, put }) {
76
- yield put({
77
- type: 'updateState',
78
- payload: {
79
- loading: true,
80
- },
81
- });
82
  const { data } = yield call(userService.get_tenant_info, payload);
83
  const { retcode, data: res } = data;
84
  // llm_id 对应chat_id
85
  // asr_id 对应speech2txt
86
 
87
- yield put({
88
- type: 'updateState',
89
- payload: {
90
- loading: false,
91
- },
92
- });
93
  if (retcode === 0) {
94
  res.chat_id = res.llm_id;
95
  res.speech2text_id = res.asr_id;
 
1
+ import { ITenantInfo } from '@/interfaces/database/knowledge';
2
  import userService from '@/services/userService';
3
  import authorizationUtil from '@/utils/authorizationUtil';
4
  import { message } from 'antd';
5
+ import { Nullable } from 'typings';
6
  import { DvaModel } from 'umi';
7
 
8
  export interface SettingModelState {
 
11
  isShowSAKModal: boolean;
12
  isShowSSModal: boolean;
13
  llm_factory: string;
14
+ tenantIfo: Nullable<ITenantInfo>;
15
  llmInfo: any;
16
  myLlm: any[];
17
  factoriesList: any[];
 
25
  isShowSAKModal: false,
26
  isShowSSModal: false,
27
  llm_factory: '',
28
+ tenantIfo: null,
29
  llmInfo: {},
30
  myLlm: [],
31
  factoriesList: [],
 
75
  }
76
  },
77
  *getTenantInfo({ payload = {} }, { call, put }) {
 
 
 
 
 
 
78
  const { data } = yield call(userService.get_tenant_info, payload);
79
  const { retcode, data: res } = data;
80
  // llm_id 对应chat_id
81
  // asr_id 对应speech2txt
82
 
 
 
 
 
 
 
83
  if (retcode === 0) {
84
  res.chat_id = res.llm_id;
85
  res.speech2text_id = res.asr_id;
web/src/services/uploadService.ts CHANGED
@@ -1,21 +1,21 @@
1
- import request from '@/utils/request';
2
  import api from '@/utils/api';
 
3
 
4
  const { upload } = api;
5
 
6
  const uploadService = {
7
- uploadFile: function (file, kb_id) {
8
  const formData = new FormData();
9
  formData.append('file', file);
10
  formData.append('kb_id', kb_id);
11
 
12
  const options = {
13
  method: 'post',
14
- data: formData
15
  };
16
 
17
  return request(upload, options);
18
- }
19
  };
20
 
21
  export default uploadService;
 
 
1
  import api from '@/utils/api';
2
+ import request from '@/utils/request';
3
 
4
  const { upload } = api;
5
 
6
  const uploadService = {
7
+ uploadFile: function (file: any, kb_id: string) {
8
  const formData = new FormData();
9
  formData.append('file', file);
10
  formData.append('kb_id', kb_id);
11
 
12
  const options = {
13
  method: 'post',
14
+ data: formData,
15
  };
16
 
17
  return request(upload, options);
18
+ },
19
  };
20
 
21
  export default uploadService;