balibabu
commited on
Commit
·
85a7d1b
1
Parent(s):
ac557fe
Feat: Add TagTable #4367 (#4368)
Browse files### What problem does this PR solve?
Feat: Add TagTable #4367
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/components/auto-keywords-item.tsx +2 -10
- web/src/components/chunk-method-modal/hooks.ts +1 -1
- web/src/components/confirm-delete-dialog.tsx +5 -1
- web/src/components/max-token-number.tsx +0 -1
- web/src/components/page-rank.tsx +1 -6
- web/src/components/parse-configuration/index.tsx +2 -0
- web/src/hooks/knowledge-hooks.ts +81 -2
- web/src/interfaces/database/knowledge.ts +2 -0
- web/src/locales/en.ts +5 -0
- web/src/locales/zh-traditional.ts +5 -0
- web/src/locales/zh.ts +5 -0
- web/src/pages/add-knowledge/components/knowledge-setting/category-panel.tsx +2 -0
- web/src/pages/add-knowledge/components/knowledge-setting/hooks.ts +44 -1
- web/src/pages/add-knowledge/components/knowledge-setting/tag-table/index.tsx +309 -0
- web/src/pages/add-knowledge/components/knowledge-setting/tag-table/rename-dialog/index.tsx +38 -0
- web/src/pages/add-knowledge/components/knowledge-setting/tag-table/rename-dialog/rename-form.tsx +83 -0
- web/src/pages/add-knowledge/constant.ts +2 -0
- web/src/services/knowledge-service.ts +13 -1
- web/src/utils/api.ts +6 -0
web/src/components/auto-keywords-item.tsx
CHANGED
@@ -16,11 +16,7 @@ export const AutoKeywordsItem = () => {
|
|
16 |
<Slider max={30} style={{ width: '100%' }} />
|
17 |
</Form.Item>
|
18 |
</Flex>
|
19 |
-
<Form.Item
|
20 |
-
name={['parser_config', 'auto_keywords']}
|
21 |
-
noStyle
|
22 |
-
initialValue={0}
|
23 |
-
>
|
24 |
<InputNumber max={30} min={0} />
|
25 |
</Form.Item>
|
26 |
</Flex>
|
@@ -43,11 +39,7 @@ export const AutoQuestionsItem = () => {
|
|
43 |
<Slider max={10} style={{ width: '100%' }} />
|
44 |
</Form.Item>
|
45 |
</Flex>
|
46 |
-
<Form.Item
|
47 |
-
name={['parser_config', 'auto_questions']}
|
48 |
-
noStyle
|
49 |
-
initialValue={0}
|
50 |
-
>
|
51 |
<InputNumber max={10} min={0} />
|
52 |
</Form.Item>
|
53 |
</Flex>
|
|
|
16 |
<Slider max={30} style={{ width: '100%' }} />
|
17 |
</Form.Item>
|
18 |
</Flex>
|
19 |
+
<Form.Item name={['parser_config', 'auto_keywords']} noStyle>
|
|
|
|
|
|
|
|
|
20 |
<InputNumber max={30} min={0} />
|
21 |
</Form.Item>
|
22 |
</Flex>
|
|
|
39 |
<Slider max={10} style={{ width: '100%' }} />
|
40 |
</Form.Item>
|
41 |
</Flex>
|
42 |
+
<Form.Item name={['parser_config', 'auto_questions']} noStyle>
|
|
|
|
|
|
|
|
|
43 |
<InputNumber max={10} min={0} />
|
44 |
</Form.Item>
|
45 |
</Flex>
|
web/src/components/chunk-method-modal/hooks.ts
CHANGED
@@ -119,7 +119,7 @@ export const useFetchParserListOnMount = (
|
|
119 |
return { parserList: nextParserList, handleChange, selectedTag };
|
120 |
};
|
121 |
|
122 |
-
const hideAutoKeywords = ['qa', 'table', 'resume', 'knowledge_graph'];
|
123 |
|
124 |
export const useShowAutoKeywords = () => {
|
125 |
const showAutoKeywords = useCallback((selectedTag: string) => {
|
|
|
119 |
return { parserList: nextParserList, handleChange, selectedTag };
|
120 |
};
|
121 |
|
122 |
+
const hideAutoKeywords = ['qa', 'table', 'resume', 'knowledge_graph', 'tag'];
|
123 |
|
124 |
export const useShowAutoKeywords = () => {
|
125 |
const showAutoKeywords = useCallback((selectedTag: string) => {
|
web/src/components/confirm-delete-dialog.tsx
CHANGED
@@ -21,6 +21,7 @@ interface IProps {
|
|
21 |
export function ConfirmDeleteDialog({
|
22 |
children,
|
23 |
title,
|
|
|
24 |
}: IProps & PropsWithChildren) {
|
25 |
const { t } = useTranslation();
|
26 |
|
@@ -39,7 +40,10 @@ export function ConfirmDeleteDialog({
|
|
39 |
</AlertDialogHeader>
|
40 |
<AlertDialogFooter>
|
41 |
<AlertDialogCancel>{t('common.cancel')}</AlertDialogCancel>
|
42 |
-
<AlertDialogAction
|
|
|
|
|
|
|
43 |
<Trash2 />
|
44 |
{t('common.ok')}
|
45 |
</AlertDialogAction>
|
|
|
21 |
export function ConfirmDeleteDialog({
|
22 |
children,
|
23 |
title,
|
24 |
+
onOk,
|
25 |
}: IProps & PropsWithChildren) {
|
26 |
const { t } = useTranslation();
|
27 |
|
|
|
40 |
</AlertDialogHeader>
|
41 |
<AlertDialogFooter>
|
42 |
<AlertDialogCancel>{t('common.cancel')}</AlertDialogCancel>
|
43 |
+
<AlertDialogAction
|
44 |
+
className="bg-colors-background-functional-solid-danger text--colors-text-neutral-strong"
|
45 |
+
onClick={onOk}
|
46 |
+
>
|
47 |
<Trash2 />
|
48 |
{t('common.ok')}
|
49 |
</AlertDialogAction>
|
web/src/components/max-token-number.tsx
CHANGED
@@ -25,7 +25,6 @@ const MaxTokenNumber = ({ initialValue = 128, max = 2048 }: IProps) => {
|
|
25 |
<Form.Item
|
26 |
name={['parser_config', 'chunk_token_num']}
|
27 |
noStyle
|
28 |
-
initialValue={initialValue}
|
29 |
rules={[{ required: true, message: t('chunkTokenNumberMessage') }]}
|
30 |
>
|
31 |
<InputNumber max={max} min={0} />
|
|
|
25 |
<Form.Item
|
26 |
name={['parser_config', 'chunk_token_num']}
|
27 |
noStyle
|
|
|
28 |
rules={[{ required: true, message: t('chunkTokenNumberMessage') }]}
|
29 |
>
|
30 |
<InputNumber max={max} min={0} />
|
web/src/components/page-rank.tsx
CHANGED
@@ -17,12 +17,7 @@ const PageRank = () => {
|
|
17 |
<Slider max={100} style={{ width: '100%' }} />
|
18 |
</Form.Item>
|
19 |
</Flex>
|
20 |
-
<Form.Item
|
21 |
-
name={['pagerank']}
|
22 |
-
noStyle
|
23 |
-
initialValue={0}
|
24 |
-
rules={[{ required: true }]}
|
25 |
-
>
|
26 |
<InputNumber max={100} min={0} />
|
27 |
</Form.Item>
|
28 |
</Flex>
|
|
|
17 |
<Slider max={100} style={{ width: '100%' }} />
|
18 |
</Form.Item>
|
19 |
</Flex>
|
20 |
+
<Form.Item name={['pagerank']} noStyle rules={[{ required: true }]}>
|
|
|
|
|
|
|
|
|
|
|
21 |
<InputNumber max={100} min={0} />
|
22 |
</Form.Item>
|
23 |
</Flex>
|
web/src/components/parse-configuration/index.tsx
CHANGED
@@ -18,6 +18,8 @@ export const excludedParseMethods = [
|
|
18 |
'one',
|
19 |
'picture',
|
20 |
'knowledge_graph',
|
|
|
|
|
21 |
];
|
22 |
|
23 |
export const showRaptorParseConfiguration = (parserId: string) => {
|
|
|
18 |
'one',
|
19 |
'picture',
|
20 |
'knowledge_graph',
|
21 |
+
'qa',
|
22 |
+
'tag',
|
23 |
];
|
24 |
|
25 |
export const showRaptorParseConfiguration = (parserId: string) => {
|
web/src/hooks/knowledge-hooks.ts
CHANGED
@@ -1,7 +1,15 @@
|
|
1 |
import { ResponsePostType } from '@/interfaces/database/base';
|
2 |
-
import {
|
|
|
|
|
|
|
|
|
3 |
import i18n from '@/locales/config';
|
4 |
-
import kbService
|
|
|
|
|
|
|
|
|
5 |
import {
|
6 |
useInfiniteQuery,
|
7 |
useIsMutating,
|
@@ -259,3 +267,74 @@ export const useSelectIsTestingSuccess = () => {
|
|
259 |
return status.at(-1) === 'success';
|
260 |
};
|
261 |
//#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import { ResponsePostType } from '@/interfaces/database/base';
|
2 |
+
import {
|
3 |
+
IKnowledge,
|
4 |
+
IRenameTag,
|
5 |
+
ITestingResult,
|
6 |
+
} from '@/interfaces/database/knowledge';
|
7 |
import i18n from '@/locales/config';
|
8 |
+
import kbService, {
|
9 |
+
listTag,
|
10 |
+
removeTag,
|
11 |
+
renameTag,
|
12 |
+
} from '@/services/knowledge-service';
|
13 |
import {
|
14 |
useInfiniteQuery,
|
15 |
useIsMutating,
|
|
|
267 |
return status.at(-1) === 'success';
|
268 |
};
|
269 |
//#endregion
|
270 |
+
|
271 |
+
//#region tags
|
272 |
+
|
273 |
+
export const useFetchTagList = () => {
|
274 |
+
const knowledgeBaseId = useKnowledgeBaseId();
|
275 |
+
|
276 |
+
const { data, isFetching: loading } = useQuery<Array<[string, number]>>({
|
277 |
+
queryKey: ['fetchTagList'],
|
278 |
+
initialData: [],
|
279 |
+
gcTime: 0, // https://tanstack.com/query/latest/docs/framework/react/guides/caching?from=reactQueryV3
|
280 |
+
queryFn: async () => {
|
281 |
+
const { data } = await listTag(knowledgeBaseId);
|
282 |
+
const list = data?.data || [];
|
283 |
+
return list;
|
284 |
+
},
|
285 |
+
});
|
286 |
+
|
287 |
+
return { list: data, loading };
|
288 |
+
};
|
289 |
+
|
290 |
+
export const useDeleteTag = () => {
|
291 |
+
const knowledgeBaseId = useKnowledgeBaseId();
|
292 |
+
|
293 |
+
const queryClient = useQueryClient();
|
294 |
+
const {
|
295 |
+
data,
|
296 |
+
isPending: loading,
|
297 |
+
mutateAsync,
|
298 |
+
} = useMutation({
|
299 |
+
mutationKey: ['deleteTag'],
|
300 |
+
mutationFn: async (tags: string[]) => {
|
301 |
+
const { data } = await removeTag(knowledgeBaseId, tags);
|
302 |
+
if (data.code === 0) {
|
303 |
+
message.success(i18n.t(`message.deleted`));
|
304 |
+
queryClient.invalidateQueries({
|
305 |
+
queryKey: ['fetchTagList'],
|
306 |
+
});
|
307 |
+
}
|
308 |
+
return data?.data ?? [];
|
309 |
+
},
|
310 |
+
});
|
311 |
+
|
312 |
+
return { data, loading, deleteTag: mutateAsync };
|
313 |
+
};
|
314 |
+
|
315 |
+
export const useRenameTag = () => {
|
316 |
+
const knowledgeBaseId = useKnowledgeBaseId();
|
317 |
+
|
318 |
+
const queryClient = useQueryClient();
|
319 |
+
const {
|
320 |
+
data,
|
321 |
+
isPending: loading,
|
322 |
+
mutateAsync,
|
323 |
+
} = useMutation({
|
324 |
+
mutationKey: ['deleteTag'],
|
325 |
+
mutationFn: async (params: IRenameTag) => {
|
326 |
+
const { data } = await renameTag(knowledgeBaseId, params);
|
327 |
+
if (data.code === 0) {
|
328 |
+
message.success(i18n.t(`message.modified`));
|
329 |
+
queryClient.invalidateQueries({
|
330 |
+
queryKey: ['fetchTagList'],
|
331 |
+
});
|
332 |
+
}
|
333 |
+
return data?.data ?? [];
|
334 |
+
},
|
335 |
+
});
|
336 |
+
|
337 |
+
return { data, loading, renameTag: mutateAsync };
|
338 |
+
};
|
339 |
+
|
340 |
+
//#endregion
|
web/src/interfaces/database/knowledge.ts
CHANGED
@@ -117,3 +117,5 @@ export interface ITestingResult {
|
|
117 |
documents: ITestingDocument[];
|
118 |
total: number;
|
119 |
}
|
|
|
|
|
|
117 |
documents: ITestingDocument[];
|
118 |
total: number;
|
119 |
}
|
120 |
+
|
121 |
+
export type IRenameTag = { fromTag: string; toTag: string };
|
web/src/locales/en.ts
CHANGED
@@ -34,6 +34,8 @@ export default {
|
|
34 |
pleaseInput: 'Please input',
|
35 |
submit: 'Submit',
|
36 |
embedIntoSite: 'Embed into webpage',
|
|
|
|
|
37 |
},
|
38 |
login: {
|
39 |
login: 'Sign in',
|
@@ -308,6 +310,9 @@ The above is the content you need to summarize.`,
|
|
308 |
vietnamese: 'Vietnamese',
|
309 |
pageRank: 'Page rank',
|
310 |
pageRankTip: `This increases the relevance score of the knowledge base. Its value will be added to the relevance score of all retrieved chunks from this knowledge base. Useful when you are searching within multiple knowledge bases and wanting to assign a higher pagerank score to a specific one.`,
|
|
|
|
|
|
|
311 |
},
|
312 |
chunk: {
|
313 |
chunk: 'Chunk',
|
|
|
34 |
pleaseInput: 'Please input',
|
35 |
submit: 'Submit',
|
36 |
embedIntoSite: 'Embed into webpage',
|
37 |
+
previousPage: 'Previous',
|
38 |
+
nextPage: 'Next',
|
39 |
},
|
40 |
login: {
|
41 |
login: 'Sign in',
|
|
|
310 |
vietnamese: 'Vietnamese',
|
311 |
pageRank: 'Page rank',
|
312 |
pageRankTip: `This increases the relevance score of the knowledge base. Its value will be added to the relevance score of all retrieved chunks from this knowledge base. Useful when you are searching within multiple knowledge bases and wanting to assign a higher pagerank score to a specific one.`,
|
313 |
+
tag: 'Tag',
|
314 |
+
frequency: 'Frequency',
|
315 |
+
searchTags: 'Search tags',
|
316 |
},
|
317 |
chunk: {
|
318 |
chunk: 'Chunk',
|
web/src/locales/zh-traditional.ts
CHANGED
@@ -34,6 +34,8 @@ export default {
|
|
34 |
pleaseInput: '請輸入',
|
35 |
submit: '提交',
|
36 |
embedIntoSite: '嵌入網站',
|
|
|
|
|
37 |
},
|
38 |
login: {
|
39 |
login: '登入',
|
@@ -292,6 +294,9 @@ export default {
|
|
292 |
pageRank: '頁面排名',
|
293 |
pageRankTip: `這用來提高相關性分數。所有檢索到的區塊的相關性得分將加上該數字。
|
294 |
當您想要先搜尋給定的知識庫時,請設定比其他人更高的 pagerank 分數。`,
|
|
|
|
|
|
|
295 |
},
|
296 |
chunk: {
|
297 |
chunk: '解析塊',
|
|
|
34 |
pleaseInput: '請輸入',
|
35 |
submit: '提交',
|
36 |
embedIntoSite: '嵌入網站',
|
37 |
+
previousPage: '上一頁',
|
38 |
+
nextPage: '下一頁',
|
39 |
},
|
40 |
login: {
|
41 |
login: '登入',
|
|
|
294 |
pageRank: '頁面排名',
|
295 |
pageRankTip: `這用來提高相關性分數。所有檢索到的區塊的相關性得分將加上該數字。
|
296 |
當您想要先搜尋給定的知識庫時,請設定比其他人更高的 pagerank 分數。`,
|
297 |
+
tag: '標籤',
|
298 |
+
frequency: '頻次',
|
299 |
+
searchTags: '搜尋標籤',
|
300 |
},
|
301 |
chunk: {
|
302 |
chunk: '解析塊',
|
web/src/locales/zh.ts
CHANGED
@@ -34,6 +34,8 @@ export default {
|
|
34 |
pleaseInput: '请输入',
|
35 |
submit: '提交',
|
36 |
embedIntoSite: '嵌入网站',
|
|
|
|
|
37 |
},
|
38 |
login: {
|
39 |
login: '登录',
|
@@ -309,6 +311,9 @@ export default {
|
|
309 |
pageRank: '页面排名',
|
310 |
pageRankTip: `这用于提高相关性得分。所有检索到的块的相关性得分将加上此数字。
|
311 |
当您想首先搜索给定的知识库时,请设置比其他知识库更高的 pagerank 得分。`,
|
|
|
|
|
|
|
312 |
},
|
313 |
chunk: {
|
314 |
chunk: '解析块',
|
|
|
34 |
pleaseInput: '请输入',
|
35 |
submit: '提交',
|
36 |
embedIntoSite: '嵌入网站',
|
37 |
+
previousPage: '上一页',
|
38 |
+
nextPage: '下一页',
|
39 |
},
|
40 |
login: {
|
41 |
login: '登录',
|
|
|
311 |
pageRank: '页面排名',
|
312 |
pageRankTip: `这用于提高相关性得分。所有检索到的块的相关性得分将加上此数字。
|
313 |
当您想首先搜索给定的知识库时,请设置比其他知识库更高的 pagerank 得分。`,
|
314 |
+
tag: '标签',
|
315 |
+
frequency: '频次',
|
316 |
+
searchTags: '搜索标签',
|
317 |
},
|
318 |
chunk: {
|
319 |
chunk: '解析块',
|
web/src/pages/add-knowledge/components/knowledge-setting/category-panel.tsx
CHANGED
@@ -6,6 +6,7 @@ import DOMPurify from 'dompurify';
|
|
6 |
import camelCase from 'lodash/camelCase';
|
7 |
import { useMemo } from 'react';
|
8 |
import styles from './index.less';
|
|
|
9 |
import { ImageMap } from './utils';
|
10 |
|
11 |
const { Title, Text } = Typography;
|
@@ -68,6 +69,7 @@ const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
|
|
68 |
<SvgIcon name={'chunk-method/chunk-empty'} width={'100%'}></SvgIcon>
|
69 |
</Empty>
|
70 |
)}
|
|
|
71 |
</section>
|
72 |
);
|
73 |
};
|
|
|
6 |
import camelCase from 'lodash/camelCase';
|
7 |
import { useMemo } from 'react';
|
8 |
import styles from './index.less';
|
9 |
+
import { TagTable } from './tag-table';
|
10 |
import { ImageMap } from './utils';
|
11 |
|
12 |
const { Title, Text } = Typography;
|
|
|
69 |
<SvgIcon name={'chunk-method/chunk-empty'} width={'100%'}></SvgIcon>
|
70 |
</Empty>
|
71 |
)}
|
72 |
+
{chunkMethod === 'tag' && <TagTable></TagTable>}
|
73 |
</section>
|
74 |
);
|
75 |
};
|
web/src/pages/add-knowledge/components/knowledge-setting/hooks.ts
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
import { LlmModelType } from '@/constants/knowledge';
|
|
|
2 |
import {
|
3 |
useFetchKnowledgeBaseConfiguration,
|
|
|
4 |
useUpdateKnowledge,
|
5 |
} from '@/hooks/knowledge-hooks';
|
6 |
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
@@ -14,7 +16,7 @@ import { useIsFetching } from '@tanstack/react-query';
|
|
14 |
import { Form, UploadFile } from 'antd';
|
15 |
import { FormInstance } from 'antd/lib';
|
16 |
import pick from 'lodash/pick';
|
17 |
-
import { useCallback, useEffect } from 'react';
|
18 |
|
19 |
export const useSubmitKnowledgeConfiguration = (form: FormInstance) => {
|
20 |
const { saveKnowledgeConfiguration, loading } = useUpdateKnowledge();
|
@@ -87,3 +89,44 @@ export const useHandleChunkMethodChange = () => {
|
|
87 |
|
88 |
return { form, chunkMethod };
|
89 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import { LlmModelType } from '@/constants/knowledge';
|
2 |
+
import { useSetModalState } from '@/hooks/common-hooks';
|
3 |
import {
|
4 |
useFetchKnowledgeBaseConfiguration,
|
5 |
+
useRenameTag,
|
6 |
useUpdateKnowledge,
|
7 |
} from '@/hooks/knowledge-hooks';
|
8 |
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
|
|
16 |
import { Form, UploadFile } from 'antd';
|
17 |
import { FormInstance } from 'antd/lib';
|
18 |
import pick from 'lodash/pick';
|
19 |
+
import { useCallback, useEffect, useState } from 'react';
|
20 |
|
21 |
export const useSubmitKnowledgeConfiguration = (form: FormInstance) => {
|
22 |
const { saveKnowledgeConfiguration, loading } = useUpdateKnowledge();
|
|
|
89 |
|
90 |
return { form, chunkMethod };
|
91 |
};
|
92 |
+
|
93 |
+
export const useRenameKnowledgeTag = () => {
|
94 |
+
const [tag, setTag] = useState<string>('');
|
95 |
+
const {
|
96 |
+
visible: tagRenameVisible,
|
97 |
+
hideModal: hideTagRenameModal,
|
98 |
+
showModal: showFileRenameModal,
|
99 |
+
} = useSetModalState();
|
100 |
+
const { renameTag, loading } = useRenameTag();
|
101 |
+
|
102 |
+
const onTagRenameOk = useCallback(
|
103 |
+
async (name: string) => {
|
104 |
+
const ret = await renameTag({
|
105 |
+
fromTag: tag,
|
106 |
+
toTag: name,
|
107 |
+
});
|
108 |
+
|
109 |
+
if (ret === 0) {
|
110 |
+
hideTagRenameModal();
|
111 |
+
}
|
112 |
+
},
|
113 |
+
[renameTag, tag, hideTagRenameModal],
|
114 |
+
);
|
115 |
+
|
116 |
+
const handleShowTagRenameModal = useCallback(
|
117 |
+
(record: string) => {
|
118 |
+
setTag(record);
|
119 |
+
showFileRenameModal();
|
120 |
+
},
|
121 |
+
[showFileRenameModal],
|
122 |
+
);
|
123 |
+
|
124 |
+
return {
|
125 |
+
renameLoading: loading,
|
126 |
+
initialName: tag,
|
127 |
+
onTagRenameOk,
|
128 |
+
tagRenameVisible,
|
129 |
+
hideTagRenameModal,
|
130 |
+
showTagRenameModal: handleShowTagRenameModal,
|
131 |
+
};
|
132 |
+
};
|
web/src/pages/add-knowledge/components/knowledge-setting/tag-table/index.tsx
ADDED
@@ -0,0 +1,309 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
'use client';
|
2 |
+
|
3 |
+
import {
|
4 |
+
ColumnDef,
|
5 |
+
ColumnFiltersState,
|
6 |
+
SortingState,
|
7 |
+
VisibilityState,
|
8 |
+
flexRender,
|
9 |
+
getCoreRowModel,
|
10 |
+
getFilteredRowModel,
|
11 |
+
getPaginationRowModel,
|
12 |
+
getSortedRowModel,
|
13 |
+
useReactTable,
|
14 |
+
} from '@tanstack/react-table';
|
15 |
+
import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react';
|
16 |
+
import * as React from 'react';
|
17 |
+
|
18 |
+
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
19 |
+
import { Button } from '@/components/ui/button';
|
20 |
+
import { Checkbox } from '@/components/ui/checkbox';
|
21 |
+
import { Input } from '@/components/ui/input';
|
22 |
+
import {
|
23 |
+
Table,
|
24 |
+
TableBody,
|
25 |
+
TableCell,
|
26 |
+
TableHead,
|
27 |
+
TableHeader,
|
28 |
+
TableRow,
|
29 |
+
} from '@/components/ui/table';
|
30 |
+
import {
|
31 |
+
Tooltip,
|
32 |
+
TooltipContent,
|
33 |
+
TooltipProvider,
|
34 |
+
TooltipTrigger,
|
35 |
+
} from '@/components/ui/tooltip';
|
36 |
+
import { useDeleteTag, useFetchTagList } from '@/hooks/knowledge-hooks';
|
37 |
+
import { useCallback, useEffect, useState } from 'react';
|
38 |
+
import { useTranslation } from 'react-i18next';
|
39 |
+
import { useRenameKnowledgeTag } from '../hooks';
|
40 |
+
import { RenameDialog } from './rename-dialog';
|
41 |
+
|
42 |
+
export type ITag = {
|
43 |
+
tag: string;
|
44 |
+
frequency: number;
|
45 |
+
};
|
46 |
+
|
47 |
+
export function TagTable() {
|
48 |
+
const { t } = useTranslation();
|
49 |
+
const { list } = useFetchTagList();
|
50 |
+
const [tagList, setTagList] = useState<ITag[]>([]);
|
51 |
+
|
52 |
+
const [sorting, setSorting] = React.useState<SortingState>([]);
|
53 |
+
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
54 |
+
[],
|
55 |
+
);
|
56 |
+
const [columnVisibility, setColumnVisibility] =
|
57 |
+
React.useState<VisibilityState>({});
|
58 |
+
const [rowSelection, setRowSelection] = useState({});
|
59 |
+
|
60 |
+
const { deleteTag } = useDeleteTag();
|
61 |
+
|
62 |
+
useEffect(() => {
|
63 |
+
setTagList(list.map((x) => ({ tag: x[0], frequency: x[1] })));
|
64 |
+
}, [list]);
|
65 |
+
|
66 |
+
const handleDeleteTag = useCallback(
|
67 |
+
(tags: string[]) => () => {
|
68 |
+
deleteTag(tags);
|
69 |
+
},
|
70 |
+
[deleteTag],
|
71 |
+
);
|
72 |
+
|
73 |
+
const {
|
74 |
+
showTagRenameModal,
|
75 |
+
hideTagRenameModal,
|
76 |
+
tagRenameVisible,
|
77 |
+
onTagRenameOk,
|
78 |
+
initialName,
|
79 |
+
} = useRenameKnowledgeTag();
|
80 |
+
|
81 |
+
const columns: ColumnDef<ITag>[] = [
|
82 |
+
{
|
83 |
+
id: 'select',
|
84 |
+
header: ({ table }) => (
|
85 |
+
<Checkbox
|
86 |
+
checked={
|
87 |
+
table.getIsAllPageRowsSelected() ||
|
88 |
+
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
89 |
+
}
|
90 |
+
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
91 |
+
aria-label="Select all"
|
92 |
+
/>
|
93 |
+
),
|
94 |
+
cell: ({ row }) => (
|
95 |
+
<Checkbox
|
96 |
+
checked={row.getIsSelected()}
|
97 |
+
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
98 |
+
aria-label="Select row"
|
99 |
+
/>
|
100 |
+
),
|
101 |
+
enableSorting: false,
|
102 |
+
enableHiding: false,
|
103 |
+
},
|
104 |
+
{
|
105 |
+
accessorKey: 'tag',
|
106 |
+
header: ({ column }) => {
|
107 |
+
return (
|
108 |
+
<Button
|
109 |
+
variant="ghost"
|
110 |
+
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
111 |
+
>
|
112 |
+
{t('knowledgeConfiguration.tag')}
|
113 |
+
<ArrowUpDown />
|
114 |
+
</Button>
|
115 |
+
);
|
116 |
+
},
|
117 |
+
cell: ({ row }) => {
|
118 |
+
const value: string = row.getValue('tag');
|
119 |
+
return <div>{value}</div>;
|
120 |
+
},
|
121 |
+
},
|
122 |
+
{
|
123 |
+
accessorKey: 'frequency',
|
124 |
+
header: ({ column }) => {
|
125 |
+
return (
|
126 |
+
<Button
|
127 |
+
variant="ghost"
|
128 |
+
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
129 |
+
>
|
130 |
+
{t('knowledgeConfiguration.frequency')}
|
131 |
+
<ArrowUpDown />
|
132 |
+
</Button>
|
133 |
+
);
|
134 |
+
},
|
135 |
+
cell: ({ row }) => (
|
136 |
+
<div className="capitalize ">{row.getValue('frequency')}</div>
|
137 |
+
),
|
138 |
+
},
|
139 |
+
{
|
140 |
+
id: 'actions',
|
141 |
+
enableHiding: false,
|
142 |
+
header: t('common.action'),
|
143 |
+
cell: ({ row }) => {
|
144 |
+
return (
|
145 |
+
<div className="flex gap-1">
|
146 |
+
<Tooltip>
|
147 |
+
<ConfirmDeleteDialog onOk={handleDeleteTag([row.original.tag])}>
|
148 |
+
<TooltipTrigger asChild>
|
149 |
+
<Button variant="ghost" size="icon">
|
150 |
+
<Trash2 />
|
151 |
+
</Button>
|
152 |
+
</TooltipTrigger>
|
153 |
+
</ConfirmDeleteDialog>
|
154 |
+
<TooltipContent>
|
155 |
+
<p>{t('common.delete')}</p>
|
156 |
+
</TooltipContent>
|
157 |
+
</Tooltip>
|
158 |
+
<Tooltip>
|
159 |
+
<TooltipTrigger asChild>
|
160 |
+
<Button
|
161 |
+
variant="ghost"
|
162 |
+
size="icon"
|
163 |
+
onClick={() => showTagRenameModal(row.original.tag)}
|
164 |
+
>
|
165 |
+
<Pencil />
|
166 |
+
</Button>
|
167 |
+
</TooltipTrigger>
|
168 |
+
<TooltipContent>
|
169 |
+
<p>{t('common.rename')}</p>
|
170 |
+
</TooltipContent>
|
171 |
+
</Tooltip>
|
172 |
+
</div>
|
173 |
+
);
|
174 |
+
},
|
175 |
+
},
|
176 |
+
];
|
177 |
+
|
178 |
+
const table = useReactTable({
|
179 |
+
data: tagList,
|
180 |
+
columns,
|
181 |
+
onSortingChange: setSorting,
|
182 |
+
onColumnFiltersChange: setColumnFilters,
|
183 |
+
getCoreRowModel: getCoreRowModel(),
|
184 |
+
getPaginationRowModel: getPaginationRowModel(),
|
185 |
+
getSortedRowModel: getSortedRowModel(),
|
186 |
+
getFilteredRowModel: getFilteredRowModel(),
|
187 |
+
onColumnVisibilityChange: setColumnVisibility,
|
188 |
+
onRowSelectionChange: setRowSelection,
|
189 |
+
state: {
|
190 |
+
sorting,
|
191 |
+
columnFilters,
|
192 |
+
columnVisibility,
|
193 |
+
rowSelection,
|
194 |
+
},
|
195 |
+
});
|
196 |
+
|
197 |
+
const selectedRowLength = table.getFilteredSelectedRowModel().rows.length;
|
198 |
+
|
199 |
+
return (
|
200 |
+
<TooltipProvider>
|
201 |
+
<div className="w-full">
|
202 |
+
<div className="flex items-center justify-between py-4 ">
|
203 |
+
<Input
|
204 |
+
placeholder={t('knowledgeConfiguration.searchTags')}
|
205 |
+
value={(table.getColumn('tag')?.getFilterValue() as string) ?? ''}
|
206 |
+
onChange={(event) =>
|
207 |
+
table.getColumn('tag')?.setFilterValue(event.target.value)
|
208 |
+
}
|
209 |
+
className="w-1/2"
|
210 |
+
/>
|
211 |
+
{selectedRowLength > 0 && (
|
212 |
+
<ConfirmDeleteDialog
|
213 |
+
onOk={handleDeleteTag(
|
214 |
+
table
|
215 |
+
.getFilteredSelectedRowModel()
|
216 |
+
.rows.map((x) => x.original.tag),
|
217 |
+
)}
|
218 |
+
>
|
219 |
+
<Button variant="outline" size="icon">
|
220 |
+
<Trash2 />
|
221 |
+
</Button>
|
222 |
+
</ConfirmDeleteDialog>
|
223 |
+
)}
|
224 |
+
</div>
|
225 |
+
<div className="rounded-md border">
|
226 |
+
<Table>
|
227 |
+
<TableHeader>
|
228 |
+
{table.getHeaderGroups().map((headerGroup) => (
|
229 |
+
<TableRow key={headerGroup.id}>
|
230 |
+
{headerGroup.headers.map((header) => {
|
231 |
+
return (
|
232 |
+
<TableHead key={header.id}>
|
233 |
+
{header.isPlaceholder
|
234 |
+
? null
|
235 |
+
: flexRender(
|
236 |
+
header.column.columnDef.header,
|
237 |
+
header.getContext(),
|
238 |
+
)}
|
239 |
+
</TableHead>
|
240 |
+
);
|
241 |
+
})}
|
242 |
+
</TableRow>
|
243 |
+
))}
|
244 |
+
</TableHeader>
|
245 |
+
<TableBody>
|
246 |
+
{table.getRowModel().rows?.length ? (
|
247 |
+
table.getRowModel().rows.map((row) => (
|
248 |
+
<TableRow
|
249 |
+
key={row.id}
|
250 |
+
data-state={row.getIsSelected() && 'selected'}
|
251 |
+
>
|
252 |
+
{row.getVisibleCells().map((cell) => (
|
253 |
+
<TableCell key={cell.id}>
|
254 |
+
{flexRender(
|
255 |
+
cell.column.columnDef.cell,
|
256 |
+
cell.getContext(),
|
257 |
+
)}
|
258 |
+
</TableCell>
|
259 |
+
))}
|
260 |
+
</TableRow>
|
261 |
+
))
|
262 |
+
) : (
|
263 |
+
<TableRow>
|
264 |
+
<TableCell
|
265 |
+
colSpan={columns.length}
|
266 |
+
className="h-24 text-center"
|
267 |
+
>
|
268 |
+
No results.
|
269 |
+
</TableCell>
|
270 |
+
</TableRow>
|
271 |
+
)}
|
272 |
+
</TableBody>
|
273 |
+
</Table>
|
274 |
+
</div>
|
275 |
+
<div className="flex items-center justify-end space-x-2 py-4">
|
276 |
+
<div className="flex-1 text-sm text-muted-foreground">
|
277 |
+
{selectedRowLength} of {table.getFilteredRowModel().rows.length}{' '}
|
278 |
+
row(s) selected.
|
279 |
+
</div>
|
280 |
+
<div className="space-x-2">
|
281 |
+
<Button
|
282 |
+
variant="outline"
|
283 |
+
size="sm"
|
284 |
+
onClick={() => table.previousPage()}
|
285 |
+
disabled={!table.getCanPreviousPage()}
|
286 |
+
>
|
287 |
+
{t('common.previousPage')}
|
288 |
+
</Button>
|
289 |
+
<Button
|
290 |
+
variant="outline"
|
291 |
+
size="sm"
|
292 |
+
onClick={() => table.nextPage()}
|
293 |
+
disabled={!table.getCanNextPage()}
|
294 |
+
>
|
295 |
+
{t('common.nextPage')}
|
296 |
+
</Button>
|
297 |
+
</div>
|
298 |
+
</div>
|
299 |
+
</div>
|
300 |
+
{tagRenameVisible && (
|
301 |
+
<RenameDialog
|
302 |
+
hideModal={hideTagRenameModal}
|
303 |
+
onOk={onTagRenameOk}
|
304 |
+
initialName={initialName}
|
305 |
+
></RenameDialog>
|
306 |
+
)}
|
307 |
+
</TooltipProvider>
|
308 |
+
);
|
309 |
+
}
|
web/src/pages/add-knowledge/components/knowledge-setting/tag-table/rename-dialog/index.tsx
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Button } from '@/components/ui/button';
|
2 |
+
import {
|
3 |
+
Dialog,
|
4 |
+
DialogContent,
|
5 |
+
DialogFooter,
|
6 |
+
DialogHeader,
|
7 |
+
DialogTitle,
|
8 |
+
} from '@/components/ui/dialog';
|
9 |
+
import { IModalProps } from '@/interfaces/common';
|
10 |
+
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
11 |
+
import { useTranslation } from 'react-i18next';
|
12 |
+
import { RenameForm } from './rename-form';
|
13 |
+
|
14 |
+
export function RenameDialog({
|
15 |
+
hideModal,
|
16 |
+
initialName,
|
17 |
+
}: IModalProps<any> & { initialName: string }) {
|
18 |
+
const { t } = useTranslation();
|
19 |
+
|
20 |
+
return (
|
21 |
+
<Dialog open onOpenChange={hideModal}>
|
22 |
+
<DialogContent className="sm:max-w-[425px]">
|
23 |
+
<DialogHeader>
|
24 |
+
<DialogTitle>{t('common.rename')}</DialogTitle>
|
25 |
+
</DialogHeader>
|
26 |
+
<RenameForm
|
27 |
+
initialName={initialName}
|
28 |
+
hideModal={hideModal}
|
29 |
+
></RenameForm>
|
30 |
+
<DialogFooter>
|
31 |
+
<Button type="submit" form={TagRenameId}>
|
32 |
+
{t('common.save')}
|
33 |
+
</Button>
|
34 |
+
</DialogFooter>
|
35 |
+
</DialogContent>
|
36 |
+
</Dialog>
|
37 |
+
);
|
38 |
+
}
|
web/src/pages/add-knowledge/components/knowledge-setting/tag-table/rename-dialog/rename-form.tsx
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
'use client';
|
2 |
+
|
3 |
+
import { zodResolver } from '@hookform/resolvers/zod';
|
4 |
+
import { useForm } from 'react-hook-form';
|
5 |
+
import { z } from 'zod';
|
6 |
+
|
7 |
+
import {
|
8 |
+
Form,
|
9 |
+
FormControl,
|
10 |
+
FormField,
|
11 |
+
FormItem,
|
12 |
+
FormLabel,
|
13 |
+
FormMessage,
|
14 |
+
} from '@/components/ui/form';
|
15 |
+
import { Input } from '@/components/ui/input';
|
16 |
+
import { useRenameTag } from '@/hooks/knowledge-hooks';
|
17 |
+
import { IModalProps } from '@/interfaces/common';
|
18 |
+
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
19 |
+
import { useEffect } from 'react';
|
20 |
+
import { useTranslation } from 'react-i18next';
|
21 |
+
|
22 |
+
export function RenameForm({
|
23 |
+
initialName,
|
24 |
+
hideModal,
|
25 |
+
}: IModalProps<any> & { initialName: string }) {
|
26 |
+
const { t } = useTranslation();
|
27 |
+
const FormSchema = z.object({
|
28 |
+
name: z
|
29 |
+
.string()
|
30 |
+
.min(1, {
|
31 |
+
message: t('common.namePlaceholder'),
|
32 |
+
})
|
33 |
+
.trim(),
|
34 |
+
});
|
35 |
+
|
36 |
+
const form = useForm<z.infer<typeof FormSchema>>({
|
37 |
+
resolver: zodResolver(FormSchema),
|
38 |
+
defaultValues: {
|
39 |
+
name: '',
|
40 |
+
},
|
41 |
+
});
|
42 |
+
|
43 |
+
const { renameTag } = useRenameTag();
|
44 |
+
|
45 |
+
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
46 |
+
const ret = await renameTag({ fromTag: initialName, toTag: data.name });
|
47 |
+
if (ret) {
|
48 |
+
hideModal?.();
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
useEffect(() => {
|
53 |
+
form.setValue('name', initialName);
|
54 |
+
}, [form, initialName]);
|
55 |
+
|
56 |
+
return (
|
57 |
+
<Form {...form}>
|
58 |
+
<form
|
59 |
+
onSubmit={form.handleSubmit(onSubmit)}
|
60 |
+
className="space-y-6"
|
61 |
+
id={TagRenameId}
|
62 |
+
>
|
63 |
+
<FormField
|
64 |
+
control={form.control}
|
65 |
+
name="name"
|
66 |
+
render={({ field }) => (
|
67 |
+
<FormItem>
|
68 |
+
<FormLabel>{t('common.name')}</FormLabel>
|
69 |
+
<FormControl>
|
70 |
+
<Input
|
71 |
+
placeholder={t('common.namePlaceholder')}
|
72 |
+
{...field}
|
73 |
+
autoComplete="off"
|
74 |
+
/>
|
75 |
+
</FormControl>
|
76 |
+
<FormMessage />
|
77 |
+
</FormItem>
|
78 |
+
)}
|
79 |
+
/>
|
80 |
+
</form>
|
81 |
+
</Form>
|
82 |
+
);
|
83 |
+
}
|
web/src/pages/add-knowledge/constant.ts
CHANGED
@@ -17,3 +17,5 @@ export const datasetRouteMap = {
|
|
17 |
};
|
18 |
|
19 |
export * from '@/constants/knowledge';
|
|
|
|
|
|
17 |
};
|
18 |
|
19 |
export * from '@/constants/knowledge';
|
20 |
+
|
21 |
+
export const TagRenameId = 'tagRename';
|
web/src/services/knowledge-service.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
|
|
1 |
import api from '@/utils/api';
|
2 |
import registerServer from '@/utils/register-server';
|
3 |
-
import request from '@/utils/request';
|
4 |
|
5 |
const {
|
6 |
create_kb,
|
@@ -143,4 +144,15 @@ const methods = {
|
|
143 |
|
144 |
const kbService = registerServer<keyof typeof methods>(methods, request);
|
145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
export default kbService;
|
|
|
1 |
+
import { IRenameTag } from '@/interfaces/database/knowledge';
|
2 |
import api from '@/utils/api';
|
3 |
import registerServer from '@/utils/register-server';
|
4 |
+
import request, { post } from '@/utils/request';
|
5 |
|
6 |
const {
|
7 |
create_kb,
|
|
|
144 |
|
145 |
const kbService = registerServer<keyof typeof methods>(methods, request);
|
146 |
|
147 |
+
export const listTag = (knowledgeId: string) =>
|
148 |
+
request.get(api.listTag(knowledgeId));
|
149 |
+
|
150 |
+
export const removeTag = (knowledgeId: string, tags: string[]) =>
|
151 |
+
post(api.removeTag(knowledgeId), { tags });
|
152 |
+
|
153 |
+
export const renameTag = (
|
154 |
+
knowledgeId: string,
|
155 |
+
{ fromTag, toTag }: IRenameTag,
|
156 |
+
) => post(api.renameTag(knowledgeId), { fromTag, toTag });
|
157 |
+
|
158 |
export default kbService;
|
web/src/utils/api.ts
CHANGED
@@ -37,6 +37,12 @@ export default {
|
|
37 |
rm_kb: `${api_host}/kb/rm`,
|
38 |
get_kb_detail: `${api_host}/kb/detail`,
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
// chunk
|
41 |
chunk_list: `${api_host}/chunk/list`,
|
42 |
create_chunk: `${api_host}/chunk/create`,
|
|
|
37 |
rm_kb: `${api_host}/kb/rm`,
|
38 |
get_kb_detail: `${api_host}/kb/detail`,
|
39 |
|
40 |
+
// tags
|
41 |
+
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,
|
42 |
+
removeTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/rm_tags`,
|
43 |
+
renameTag: (knowledgeId: string) =>
|
44 |
+
`${api_host}/kb/${knowledgeId}/rename_tag`,
|
45 |
+
|
46 |
// chunk
|
47 |
chunk_list: `${api_host}/chunk/list`,
|
48 |
create_chunk: `${api_host}/chunk/create`,
|