mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Add TagFeatureItem #4368 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -1,15 +1,23 @@
|
||||
import EditTag from '@/components/edit-tag';
|
||||
import { useFetchChunk } from '@/hooks/chunk-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { DeleteOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { Divider, Form, Input, Modal, Space, Switch, Tooltip } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { IChunk } from '@/interfaces/database/knowledge';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { Divider, Form, Input, Modal, Space, Switch } from 'antd';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDeleteChunkByIds } from '../../hooks';
|
||||
import {
|
||||
transformTagFeaturesArrayToObject,
|
||||
transformTagFeaturesObjectToArray,
|
||||
} from '../../utils';
|
||||
import { TagFeatureItem } from './tag-feature-item';
|
||||
|
||||
type FieldType = Pick<
|
||||
IChunk,
|
||||
'content_with_weight' | 'tag_kwd' | 'question_kwd' | 'important_kwd'
|
||||
>;
|
||||
|
||||
type FieldType = {
|
||||
content?: string;
|
||||
};
|
||||
interface kFProps {
|
||||
doc_id: string;
|
||||
chunkId: string | undefined;
|
||||
@ -26,62 +34,48 @@ const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [keywords, setKeywords] = useState<string[]>([]);
|
||||
const [question, setQuestion] = useState<string[]>([]);
|
||||
const [tagKeyWords, setTagKeyWords] = useState<string[]>([]);
|
||||
const { removeChunk } = useDeleteChunkByIds();
|
||||
const { data } = useFetchChunk(chunkId);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isTagParser = parserId === 'tag';
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.code === 0) {
|
||||
const {
|
||||
content_with_weight,
|
||||
important_kwd = [],
|
||||
available_int,
|
||||
question_kwd = [],
|
||||
tag_kwd = [],
|
||||
} = data.data;
|
||||
form.setFieldsValue({ content: content_with_weight });
|
||||
setKeywords(important_kwd);
|
||||
setQuestion(question_kwd);
|
||||
setTagKeyWords(tag_kwd);
|
||||
setChecked(available_int !== 0);
|
||||
}
|
||||
|
||||
if (!chunkId) {
|
||||
setKeywords([]);
|
||||
setQuestion([]);
|
||||
setTagKeyWords([]);
|
||||
form.setFieldsValue({ content: undefined });
|
||||
}
|
||||
}, [data, form, chunkId]);
|
||||
|
||||
const handleOk = async () => {
|
||||
const handleOk = useCallback(async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
console.log('🚀 ~ handleOk ~ values:', values);
|
||||
|
||||
onOk?.({
|
||||
content: values.content,
|
||||
keywords, // keywords
|
||||
question_kwd: question,
|
||||
tag_kwd: tagKeyWords,
|
||||
...values,
|
||||
tag_feas: transformTagFeaturesArrayToObject(values.tag_feas),
|
||||
available_int: checked ? 1 : 0, // available_int
|
||||
});
|
||||
} catch (errorInfo) {
|
||||
console.log('Failed:', errorInfo);
|
||||
}
|
||||
};
|
||||
}, [checked, form, onOk]);
|
||||
|
||||
const handleRemove = () => {
|
||||
const handleRemove = useCallback(() => {
|
||||
if (chunkId) {
|
||||
return removeChunk([chunkId], doc_id);
|
||||
}
|
||||
};
|
||||
const handleCheck = () => {
|
||||
}, [chunkId, doc_id, removeChunk]);
|
||||
|
||||
const handleCheck = useCallback(() => {
|
||||
setChecked(!checked);
|
||||
};
|
||||
}, [checked]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.code === 0) {
|
||||
const { available_int, tag_feas } = data.data;
|
||||
form.setFieldsValue({
|
||||
...(data.data || {}),
|
||||
tag_feas: transformTagFeaturesObjectToArray(tag_feas),
|
||||
});
|
||||
|
||||
setChecked(available_int !== 0);
|
||||
}
|
||||
}, [data, form, chunkId]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -95,31 +89,34 @@ const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
|
||||
<Form form={form} autoComplete="off" layout={'vertical'}>
|
||||
<Form.Item<FieldType>
|
||||
label={t('chunk.chunk')}
|
||||
name="content"
|
||||
name="content_with_weight"
|
||||
rules={[{ required: true, message: t('chunk.chunkMessage') }]}
|
||||
>
|
||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 10 }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<FieldType> label={t('chunk.keyword')} name="important_kwd">
|
||||
<EditTag></EditTag>
|
||||
</Form.Item>
|
||||
<Form.Item<FieldType>
|
||||
label={t('chunk.question')}
|
||||
name="question_kwd"
|
||||
tooltip={t('chunk.questionTip')}
|
||||
>
|
||||
<EditTag></EditTag>
|
||||
</Form.Item>
|
||||
{isTagParser && (
|
||||
<Form.Item<FieldType>
|
||||
label={t('knowledgeConfiguration.tagName')}
|
||||
name="tag_kwd"
|
||||
>
|
||||
<EditTag></EditTag>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{!isTagParser && <TagFeatureItem></TagFeatureItem>}
|
||||
</Form>
|
||||
<section>
|
||||
<p className="mb-2">{t('chunk.keyword')} </p>
|
||||
<EditTag tags={keywords} setTags={setKeywords} />
|
||||
</section>
|
||||
<section className="mt-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span>{t('chunk.question')}</span>
|
||||
<Tooltip title={t('chunk.questionTip')}>
|
||||
<QuestionCircleOutlined className="text-xs" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<EditTag tags={question} setTags={setQuestion} />
|
||||
</section>
|
||||
{isTagParser && (
|
||||
<section className="mt-4">
|
||||
<p className="mb-2">{t('knowledgeConfiguration.tagName')} </p>
|
||||
<EditTag tags={tagKeyWords} setTags={setTagKeyWords} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{chunkId && (
|
||||
<section>
|
||||
<Divider></Divider>
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
import {
|
||||
useFetchKnowledgeBaseConfiguration,
|
||||
useFetchTagListByKnowledgeIds,
|
||||
} from '@/hooks/knowledge-hooks';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Form, InputNumber, Select } from 'antd';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FormListItem } from '../../utils';
|
||||
|
||||
const FieldKey = 'tag_feas';
|
||||
|
||||
export const TagFeatureItem = () => {
|
||||
const form = Form.useFormInstance();
|
||||
const { t } = useTranslation();
|
||||
const { data: knowledgeConfiguration } = useFetchKnowledgeBaseConfiguration();
|
||||
|
||||
const { setKnowledgeIds, list } = useFetchTagListByKnowledgeIds();
|
||||
|
||||
const tagKnowledgeIds = useMemo(() => {
|
||||
return knowledgeConfiguration?.parser_config?.tag_kb_ids ?? [];
|
||||
}, [knowledgeConfiguration?.parser_config?.tag_kb_ids]);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return list.map((x) => ({
|
||||
value: x[0],
|
||||
label: x[0],
|
||||
}));
|
||||
}, [list]);
|
||||
|
||||
const filterOptions = useCallback(
|
||||
(index: number) => {
|
||||
const tags: FormListItem[] = form.getFieldValue(FieldKey) ?? [];
|
||||
|
||||
// Exclude it's own current data
|
||||
const list = tags
|
||||
.filter((x, idx) => x && index !== idx)
|
||||
.map((x) => x.tag);
|
||||
|
||||
// Exclude the selected data from other options from one's own options.
|
||||
return options.filter((x) => !list.some((y) => x.value === y));
|
||||
},
|
||||
[form, options],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setKnowledgeIds(tagKnowledgeIds);
|
||||
}, [setKnowledgeIds, tagKnowledgeIds]);
|
||||
|
||||
return (
|
||||
<Form.Item label={t('knowledgeConfiguration.tags')}>
|
||||
<Form.List name={FieldKey} initialValue={[]}>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<div key={key} className="flex gap-3 items-center">
|
||||
<div className="flex flex-1 gap-8">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'tag']}
|
||||
rules={[
|
||||
{ required: true, message: t('common.pleaseSelect') },
|
||||
]}
|
||||
className="w-2/3"
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
placeholder={t('knowledgeConfiguration.tagName')}
|
||||
options={filterOptions(name)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'frequency']}
|
||||
rules={[
|
||||
{ required: true, message: t('common.pleaseInput') },
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
placeholder={t('knowledgeConfiguration.frequency')}
|
||||
max={10}
|
||||
min={0}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<MinusCircleOutlined
|
||||
onClick={() => remove(name)}
|
||||
className="mb-6"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
{t('knowledgeConfiguration.addTag')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
@ -95,27 +95,11 @@ export const useUpdateChunk = () => {
|
||||
const { documentId } = useGetKnowledgeSearchParams();
|
||||
|
||||
const onChunkUpdatingOk = useCallback(
|
||||
async ({
|
||||
content,
|
||||
keywords,
|
||||
available_int,
|
||||
question_kwd,
|
||||
tag_kwd,
|
||||
}: {
|
||||
content: string;
|
||||
keywords: string;
|
||||
available_int: number;
|
||||
question_kwd: string;
|
||||
tag_kwd: string;
|
||||
}) => {
|
||||
async (params: IChunk) => {
|
||||
const code = await createChunk({
|
||||
content_with_weight: content,
|
||||
...params,
|
||||
doc_id: documentId,
|
||||
chunk_id: chunkId,
|
||||
important_kwd: keywords, // keywords
|
||||
available_int,
|
||||
question_kwd,
|
||||
tag_kwd,
|
||||
});
|
||||
|
||||
if (code === 0) {
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
export type FormListItem = {
|
||||
frequency: number;
|
||||
tag: string;
|
||||
};
|
||||
|
||||
export function transformTagFeaturesArrayToObject(
|
||||
list: Array<FormListItem> = [],
|
||||
) {
|
||||
return list.reduce<Record<string, number>>((pre, cur) => {
|
||||
pre[cur.tag] = cur.frequency;
|
||||
|
||||
return pre;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function transformTagFeaturesObjectToArray(
|
||||
object: Record<string, number> = {},
|
||||
) {
|
||||
return Object.keys(object).reduce<Array<FormListItem>>((pre, key) => {
|
||||
pre.push({ frequency: object[key], tag: key });
|
||||
|
||||
return pre;
|
||||
}, []);
|
||||
}
|
||||
Reference in New Issue
Block a user