mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Fix (dataset setting): Remove the introduction and use of TagItems in the configuration. #9869 (#10595)
### What problem does this PR solve? Fix (dataset setting): Remove the introduction and use of TagItems in the configuration. #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -19,7 +19,6 @@ import { Switch } from '@/components/ui/switch';
|
|||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { useFetchChunk } from '@/hooks/chunk-hooks';
|
import { useFetchChunk } from '@/hooks/chunk-hooks';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { Trash2 } from 'lucide-react';
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { FieldValues, FormProvider, useForm } from 'react-hook-form';
|
import { FieldValues, FormProvider, useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -194,9 +193,9 @@ const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
|
|||||||
{t('chunk.enabled')}
|
{t('chunk.enabled')}
|
||||||
<Switch checked={checked} onCheckedChange={handleCheck} />
|
<Switch checked={checked} onCheckedChange={handleCheck} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1" onClick={handleRemove}>
|
{/* <div className="flex items-center gap-1" onClick={handleRemove}>
|
||||||
<Trash2 size={16} /> {t('common.delete')}
|
<Trash2 size={16} /> {t('common.delete')}
|
||||||
</div>
|
</div> */}
|
||||||
</Space>
|
</Space>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ import {
|
|||||||
useNavigatePage,
|
useNavigatePage,
|
||||||
} from '@/hooks/logic-hooks/navigate-hooks';
|
} from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||||
import { useGetDocumentUrl } from '../../../knowledge-chunk/components/document-preview/hooks';
|
import { useGetDocumentUrl } from './components/document-preview/hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const Chunk = () => {
|
const Chunk = () => {
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
.image {
|
|
||||||
width: 100px !important;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imagePreview {
|
|
||||||
max-width: 50vw;
|
|
||||||
max-height: 50vh;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
.chunkText;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contentEllipsis {
|
|
||||||
.multipleLineEllipsis(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.contentText {
|
|
||||||
word-break: break-all !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chunkCard {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cardSelected {
|
|
||||||
background-color: @selectedBackgroundColor;
|
|
||||||
}
|
|
||||||
.cardSelectedDark {
|
|
||||||
background-color: #ffffff2f;
|
|
||||||
}
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
import Image from '@/components/image';
|
|
||||||
import { IChunk } from '@/interfaces/database/knowledge';
|
|
||||||
import { Card, Checkbox, CheckboxProps, Flex, Popover, Switch } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import DOMPurify from 'dompurify';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { useTheme } from '@/components/theme-provider';
|
|
||||||
import { ChunkTextMode } from '../../constant';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
item: IChunk;
|
|
||||||
checked: boolean;
|
|
||||||
switchChunk: (available?: number, chunkIds?: string[]) => void;
|
|
||||||
editChunk: (chunkId: string) => void;
|
|
||||||
handleCheckboxClick: (chunkId: string, checked: boolean) => void;
|
|
||||||
selected: boolean;
|
|
||||||
clickChunkCard: (chunkId: string) => void;
|
|
||||||
textMode: ChunkTextMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChunkCard = ({
|
|
||||||
item,
|
|
||||||
checked,
|
|
||||||
handleCheckboxClick,
|
|
||||||
editChunk,
|
|
||||||
switchChunk,
|
|
||||||
selected,
|
|
||||||
clickChunkCard,
|
|
||||||
textMode,
|
|
||||||
}: IProps) => {
|
|
||||||
const available = Number(item.available_int);
|
|
||||||
const [enabled, setEnabled] = useState(false);
|
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
const onChange = (checked: boolean) => {
|
|
||||||
setEnabled(checked);
|
|
||||||
switchChunk(available === 0 ? 1 : 0, [item.chunk_id]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCheck: CheckboxProps['onChange'] = (e) => {
|
|
||||||
handleCheckboxClick(item.chunk_id, e.target.checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleContentDoubleClick = () => {
|
|
||||||
editChunk(item.chunk_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleContentClick = () => {
|
|
||||||
clickChunkCard(item.chunk_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setEnabled(available === 1);
|
|
||||||
}, [available]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
className={classNames(styles.chunkCard, {
|
|
||||||
[`${theme === 'dark' ? styles.cardSelectedDark : styles.cardSelected}`]:
|
|
||||||
selected,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Flex gap={'middle'} justify={'space-between'}>
|
|
||||||
<Checkbox onChange={handleCheck} checked={checked}></Checkbox>
|
|
||||||
{item.image_id && (
|
|
||||||
<Popover
|
|
||||||
placement="right"
|
|
||||||
content={
|
|
||||||
<Image id={item.image_id} className={styles.imagePreview}></Image>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Image id={item.image_id} className={styles.image}></Image>
|
|
||||||
</Popover>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<section
|
|
||||||
onDoubleClick={handleContentDoubleClick}
|
|
||||||
onClick={handleContentClick}
|
|
||||||
className={styles.content}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: DOMPurify.sanitize(item.content_with_weight),
|
|
||||||
}}
|
|
||||||
className={classNames(styles.contentText, {
|
|
||||||
[styles.contentEllipsis]: textMode === ChunkTextMode.Ellipse,
|
|
||||||
})}
|
|
||||||
></div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Switch checked={enabled} onChange={onChange} />
|
|
||||||
</div>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChunkCard;
|
|
||||||
@ -1,140 +0,0 @@
|
|||||||
import EditTag from '@/components/edit-tag';
|
|
||||||
import { useFetchChunk } from '@/hooks/chunk-hooks';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
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'
|
|
||||||
>;
|
|
||||||
|
|
||||||
interface kFProps {
|
|
||||||
doc_id: string;
|
|
||||||
chunkId: string | undefined;
|
|
||||||
parserId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
|
|
||||||
doc_id,
|
|
||||||
chunkId,
|
|
||||||
hideModal,
|
|
||||||
onOk,
|
|
||||||
loading,
|
|
||||||
parserId,
|
|
||||||
}) => {
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [checked, setChecked] = useState(false);
|
|
||||||
const { removeChunk } = useDeleteChunkByIds();
|
|
||||||
const { data } = useFetchChunk(chunkId);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const isTagParser = parserId === 'tag';
|
|
||||||
|
|
||||||
const handleOk = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
console.log('🚀 ~ handleOk ~ values:', values);
|
|
||||||
|
|
||||||
onOk?.({
|
|
||||||
...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 = useCallback(() => {
|
|
||||||
if (chunkId) {
|
|
||||||
return removeChunk([chunkId], doc_id);
|
|
||||||
}
|
|
||||||
}, [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
|
|
||||||
title={`${chunkId ? t('common.edit') : t('common.create')} ${t('chunk.chunk')}`}
|
|
||||||
open={true}
|
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={hideModal}
|
|
||||||
okButtonProps={{ loading }}
|
|
||||||
destroyOnClose
|
|
||||||
>
|
|
||||||
<Form form={form} autoComplete="off" layout={'vertical'}>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label={t('chunk.chunk')}
|
|
||||||
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>
|
|
||||||
|
|
||||||
{chunkId && (
|
|
||||||
<section>
|
|
||||||
<Divider></Divider>
|
|
||||||
<Space size={'large'}>
|
|
||||||
<Switch
|
|
||||||
checkedChildren={t('chunk.enabled')}
|
|
||||||
unCheckedChildren={t('chunk.disabled')}
|
|
||||||
onChange={handleCheck}
|
|
||||||
checked={checked}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span onClick={handleRemove}>
|
|
||||||
<DeleteOutlined /> {t('common.delete')}
|
|
||||||
</span>
|
|
||||||
</Space>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default ChunkCreatingModal;
|
|
||||||
@ -1,107 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,221 +0,0 @@
|
|||||||
import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
|
|
||||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
|
||||||
import { IChunkListResult, useSelectChunkList } from '@/hooks/chunk-hooks';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { useKnowledgeBaseId } from '@/hooks/knowledge-hooks';
|
|
||||||
import {
|
|
||||||
ArrowLeftOutlined,
|
|
||||||
CheckCircleOutlined,
|
|
||||||
CloseCircleOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
DownOutlined,
|
|
||||||
FilePdfOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
SearchOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Flex,
|
|
||||||
Input,
|
|
||||||
Menu,
|
|
||||||
MenuProps,
|
|
||||||
Popover,
|
|
||||||
Radio,
|
|
||||||
RadioChangeEvent,
|
|
||||||
Segmented,
|
|
||||||
SegmentedProps,
|
|
||||||
Space,
|
|
||||||
Typography,
|
|
||||||
} from 'antd';
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
|
||||||
import { Link } from 'umi';
|
|
||||||
import { ChunkTextMode } from '../../constant';
|
|
||||||
|
|
||||||
const { Text } = Typography;
|
|
||||||
|
|
||||||
interface IProps
|
|
||||||
extends Pick<
|
|
||||||
IChunkListResult,
|
|
||||||
'searchString' | 'handleInputChange' | 'available' | 'handleSetAvailable'
|
|
||||||
> {
|
|
||||||
checked: boolean;
|
|
||||||
selectAllChunk: (checked: boolean) => void;
|
|
||||||
createChunk: () => void;
|
|
||||||
removeChunk: () => void;
|
|
||||||
switchChunk: (available: number) => void;
|
|
||||||
changeChunkTextMode(mode: ChunkTextMode): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChunkToolBar = ({
|
|
||||||
selectAllChunk,
|
|
||||||
checked,
|
|
||||||
createChunk,
|
|
||||||
removeChunk,
|
|
||||||
switchChunk,
|
|
||||||
changeChunkTextMode,
|
|
||||||
available,
|
|
||||||
handleSetAvailable,
|
|
||||||
searchString,
|
|
||||||
handleInputChange,
|
|
||||||
}: IProps) => {
|
|
||||||
const data = useSelectChunkList();
|
|
||||||
const documentInfo = data?.documentInfo;
|
|
||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
|
||||||
const [isShowSearchBox, setIsShowSearchBox] = useState(false);
|
|
||||||
const { t } = useTranslate('chunk');
|
|
||||||
|
|
||||||
const handleSelectAllCheck = useCallback(
|
|
||||||
(e: any) => {
|
|
||||||
selectAllChunk(e.target.checked);
|
|
||||||
},
|
|
||||||
[selectAllChunk],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSearchIconClick = () => {
|
|
||||||
setIsShowSearchBox(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearchBlur = () => {
|
|
||||||
if (!searchString?.trim()) {
|
|
||||||
setIsShowSearchBox(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = useCallback(() => {
|
|
||||||
removeChunk();
|
|
||||||
}, [removeChunk]);
|
|
||||||
|
|
||||||
const handleEnabledClick = useCallback(() => {
|
|
||||||
switchChunk(1);
|
|
||||||
}, [switchChunk]);
|
|
||||||
|
|
||||||
const handleDisabledClick = useCallback(() => {
|
|
||||||
switchChunk(0);
|
|
||||||
}, [switchChunk]);
|
|
||||||
|
|
||||||
const items: MenuProps['items'] = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<Checkbox onChange={handleSelectAllCheck} checked={checked}>
|
|
||||||
<b>{t('selectAll')}</b>
|
|
||||||
</Checkbox>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
label: (
|
|
||||||
<Space onClick={handleEnabledClick}>
|
|
||||||
<CheckCircleOutlined />
|
|
||||||
<b>{t('enabledSelected')}</b>
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3',
|
|
||||||
label: (
|
|
||||||
<Space onClick={handleDisabledClick}>
|
|
||||||
<CloseCircleOutlined />
|
|
||||||
<b>{t('disabledSelected')}</b>
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
key: '4',
|
|
||||||
label: (
|
|
||||||
<Space onClick={handleDelete}>
|
|
||||||
<DeleteOutlined />
|
|
||||||
<b>{t('deleteSelected')}</b>
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [
|
|
||||||
checked,
|
|
||||||
handleSelectAllCheck,
|
|
||||||
handleDelete,
|
|
||||||
handleEnabledClick,
|
|
||||||
handleDisabledClick,
|
|
||||||
t,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const content = (
|
|
||||||
<Menu style={{ width: 200 }} items={items} selectable={false} />
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFilterChange = (e: RadioChangeEvent) => {
|
|
||||||
selectAllChunk(false);
|
|
||||||
handleSetAvailable(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterContent = (
|
|
||||||
<Radio.Group onChange={handleFilterChange} value={available}>
|
|
||||||
<Space direction="vertical">
|
|
||||||
<Radio value={undefined}>{t('all')}</Radio>
|
|
||||||
<Radio value={1}>{t('enabled')}</Radio>
|
|
||||||
<Radio value={0}>{t('disabled')}</Radio>
|
|
||||||
</Space>
|
|
||||||
</Radio.Group>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex justify="space-between" align="center">
|
|
||||||
<Space size={'middle'}>
|
|
||||||
<Link
|
|
||||||
to={`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`}
|
|
||||||
>
|
|
||||||
<ArrowLeftOutlined />
|
|
||||||
</Link>
|
|
||||||
<FilePdfOutlined />
|
|
||||||
<Text ellipsis={{ tooltip: documentInfo?.name }} style={{ width: 150 }}>
|
|
||||||
{documentInfo?.name}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
<Space>
|
|
||||||
<Segmented
|
|
||||||
options={[
|
|
||||||
{ label: t(ChunkTextMode.Full), value: ChunkTextMode.Full },
|
|
||||||
{ label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse },
|
|
||||||
]}
|
|
||||||
onChange={changeChunkTextMode as SegmentedProps['onChange']}
|
|
||||||
/>
|
|
||||||
<Popover content={content} placement="bottom" arrow={false}>
|
|
||||||
<Button>
|
|
||||||
{t('bulk')}
|
|
||||||
<DownOutlined />
|
|
||||||
</Button>
|
|
||||||
</Popover>
|
|
||||||
{isShowSearchBox ? (
|
|
||||||
<Input
|
|
||||||
size="middle"
|
|
||||||
placeholder={t('search')}
|
|
||||||
prefix={<SearchOutlined />}
|
|
||||||
allowClear
|
|
||||||
onChange={handleInputChange}
|
|
||||||
onBlur={handleSearchBlur}
|
|
||||||
value={searchString}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Button icon={<SearchOutlined />} onClick={handleSearchIconClick} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Popover content={filterContent} placement="bottom" arrow={false}>
|
|
||||||
<Button icon={<FilterIcon />} />
|
|
||||||
</Popover>
|
|
||||||
<Button
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
type="primary"
|
|
||||||
onClick={() => createChunk()}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChunkToolBar;
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
|
||||||
import { api_host } from '@/utils/api';
|
|
||||||
import { useSize } from 'ahooks';
|
|
||||||
import { CustomTextRenderer } from 'node_modules/react-pdf/dist/esm/shared/types';
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
export const useDocumentResizeObserver = () => {
|
|
||||||
const [containerWidth, setContainerWidth] = useState<number>();
|
|
||||||
const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);
|
|
||||||
const size = useSize(containerRef);
|
|
||||||
|
|
||||||
const onResize = useCallback((width?: number) => {
|
|
||||||
if (width) {
|
|
||||||
setContainerWidth(width);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onResize(size?.width);
|
|
||||||
}, [size?.width, onResize]);
|
|
||||||
|
|
||||||
return { containerWidth, setContainerRef };
|
|
||||||
};
|
|
||||||
|
|
||||||
function highlightPattern(text: string, pattern: string, pageNumber: number) {
|
|
||||||
if (pageNumber === 2) {
|
|
||||||
return `<mark>${text}</mark>`;
|
|
||||||
}
|
|
||||||
if (text.trim() !== '' && pattern.match(text)) {
|
|
||||||
// return pattern.replace(text, (value) => `<mark>${value}</mark>`);
|
|
||||||
return `<mark>${text}</mark>`;
|
|
||||||
}
|
|
||||||
return text.replace(pattern, (value) => `<mark>${value}</mark>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useHighlightText = (searchText: string = '') => {
|
|
||||||
const textRenderer: CustomTextRenderer = useCallback(
|
|
||||||
(textItem) => {
|
|
||||||
return highlightPattern(textItem.str, searchText, textItem.pageNumber);
|
|
||||||
},
|
|
||||||
[searchText],
|
|
||||||
);
|
|
||||||
|
|
||||||
return textRenderer;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetDocumentUrl = () => {
|
|
||||||
const { documentId } = useGetKnowledgeSearchParams();
|
|
||||||
|
|
||||||
const url = useMemo(() => {
|
|
||||||
return `${api_host}/document/get/${documentId}`;
|
|
||||||
}, [documentId]);
|
|
||||||
|
|
||||||
return url;
|
|
||||||
};
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
.documentContainer {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100vh - 284px);
|
|
||||||
position: relative;
|
|
||||||
:global(.PdfHighlighter) {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
:global(.Highlight--scrolledTo .Highlight__part) {
|
|
||||||
overflow-x: hidden;
|
|
||||||
background-color: rgba(255, 226, 143, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
import { Skeleton } from 'antd';
|
|
||||||
import { memo, useEffect, useRef } from 'react';
|
|
||||||
import {
|
|
||||||
AreaHighlight,
|
|
||||||
Highlight,
|
|
||||||
IHighlight,
|
|
||||||
PdfHighlighter,
|
|
||||||
PdfLoader,
|
|
||||||
Popup,
|
|
||||||
} from 'react-pdf-highlighter';
|
|
||||||
import { useGetDocumentUrl } from './hooks';
|
|
||||||
|
|
||||||
import { useCatchDocumentError } from '@/components/pdf-previewer/hooks';
|
|
||||||
import FileError from '@/pages/document-viewer/file-error';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
highlights: IHighlight[];
|
|
||||||
setWidthAndHeight: (width: number, height: number) => void;
|
|
||||||
}
|
|
||||||
const HighlightPopup = ({
|
|
||||||
comment,
|
|
||||||
}: {
|
|
||||||
comment: { text: string; emoji: string };
|
|
||||||
}) =>
|
|
||||||
comment.text ? (
|
|
||||||
<div className="Highlight__popup">
|
|
||||||
{comment.emoji} {comment.text}
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
// TODO: merge with DocumentPreviewer
|
|
||||||
const Preview = ({ highlights: state, setWidthAndHeight }: IProps) => {
|
|
||||||
const url = useGetDocumentUrl();
|
|
||||||
|
|
||||||
const ref = useRef<(highlight: IHighlight) => void>(() => {});
|
|
||||||
const error = useCatchDocumentError(url);
|
|
||||||
|
|
||||||
const resetHash = () => {};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (state.length > 0) {
|
|
||||||
ref?.current(state[0]);
|
|
||||||
}
|
|
||||||
}, [state]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.documentContainer}>
|
|
||||||
<PdfLoader
|
|
||||||
url={url}
|
|
||||||
beforeLoad={<Skeleton active />}
|
|
||||||
workerSrc="/pdfjs-dist/pdf.worker.min.js"
|
|
||||||
errorMessage={<FileError>{error}</FileError>}
|
|
||||||
>
|
|
||||||
{(pdfDocument) => {
|
|
||||||
pdfDocument.getPage(1).then((page) => {
|
|
||||||
const viewport = page.getViewport({ scale: 1 });
|
|
||||||
const width = viewport.width;
|
|
||||||
const height = viewport.height;
|
|
||||||
setWidthAndHeight(width, height);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PdfHighlighter
|
|
||||||
pdfDocument={pdfDocument}
|
|
||||||
enableAreaSelection={(event) => event.altKey}
|
|
||||||
onScrollChange={resetHash}
|
|
||||||
scrollRef={(scrollTo) => {
|
|
||||||
ref.current = scrollTo;
|
|
||||||
}}
|
|
||||||
onSelectionFinished={() => null}
|
|
||||||
highlightTransform={(
|
|
||||||
highlight,
|
|
||||||
index,
|
|
||||||
setTip,
|
|
||||||
hideTip,
|
|
||||||
viewportToScaled,
|
|
||||||
screenshot,
|
|
||||||
isScrolledTo,
|
|
||||||
) => {
|
|
||||||
const isTextHighlight = !Boolean(
|
|
||||||
highlight.content && highlight.content.image,
|
|
||||||
);
|
|
||||||
|
|
||||||
const component = isTextHighlight ? (
|
|
||||||
<Highlight
|
|
||||||
isScrolledTo={isScrolledTo}
|
|
||||||
position={highlight.position}
|
|
||||||
comment={highlight.comment}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<AreaHighlight
|
|
||||||
isScrolledTo={isScrolledTo}
|
|
||||||
highlight={highlight}
|
|
||||||
onChange={() => {}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popup
|
|
||||||
popupContent={<HighlightPopup {...highlight} />}
|
|
||||||
onMouseOver={(popupContent) =>
|
|
||||||
setTip(highlight, () => popupContent)
|
|
||||||
}
|
|
||||||
onMouseOut={hideTip}
|
|
||||||
key={index}
|
|
||||||
>
|
|
||||||
{component}
|
|
||||||
</Popup>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
highlights={state}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</PdfLoader>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(Preview);
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
export enum ChunkTextMode {
|
|
||||||
Full = 'full',
|
|
||||||
Ellipse = 'ellipse',
|
|
||||||
}
|
|
||||||
@ -1,129 +0,0 @@
|
|||||||
import {
|
|
||||||
useCreateChunk,
|
|
||||||
useDeleteChunk,
|
|
||||||
useSelectChunkList,
|
|
||||||
} from '@/hooks/chunk-hooks';
|
|
||||||
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
|
|
||||||
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
|
||||||
import { IChunk } from '@/interfaces/database/knowledge';
|
|
||||||
import { buildChunkHighlights } from '@/utils/document-util';
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
|
||||||
import { IHighlight } from 'react-pdf-highlighter';
|
|
||||||
import { ChunkTextMode } from './constant';
|
|
||||||
|
|
||||||
export const useHandleChunkCardClick = () => {
|
|
||||||
const [selectedChunkId, setSelectedChunkId] = useState<string>('');
|
|
||||||
|
|
||||||
const handleChunkCardClick = useCallback((chunkId: string) => {
|
|
||||||
setSelectedChunkId(chunkId);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { handleChunkCardClick, selectedChunkId };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetSelectedChunk = (selectedChunkId: string) => {
|
|
||||||
const data = useSelectChunkList();
|
|
||||||
return (
|
|
||||||
data?.data?.find((x) => x.chunk_id === selectedChunkId) ?? ({} as IChunk)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetChunkHighlights = (selectedChunkId: string) => {
|
|
||||||
const [size, setSize] = useState({ width: 849, height: 1200 });
|
|
||||||
const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId);
|
|
||||||
|
|
||||||
const highlights: IHighlight[] = useMemo(() => {
|
|
||||||
return buildChunkHighlights(selectedChunk, size);
|
|
||||||
}, [selectedChunk, size]);
|
|
||||||
|
|
||||||
const setWidthAndHeight = useCallback((width: number, height: number) => {
|
|
||||||
setSize((pre) => {
|
|
||||||
if (pre.height !== height || pre.width !== width) {
|
|
||||||
return { height, width };
|
|
||||||
}
|
|
||||||
return pre;
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { highlights, setWidthAndHeight };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Switch chunk text to be fully displayed or ellipse
|
|
||||||
export const useChangeChunkTextMode = () => {
|
|
||||||
const [textMode, setTextMode] = useState<ChunkTextMode>(ChunkTextMode.Full);
|
|
||||||
|
|
||||||
const changeChunkTextMode = useCallback((mode: ChunkTextMode) => {
|
|
||||||
setTextMode(mode);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { textMode, changeChunkTextMode };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDeleteChunkByIds = (): {
|
|
||||||
removeChunk: (chunkIds: string[], documentId: string) => Promise<number>;
|
|
||||||
} => {
|
|
||||||
const { deleteChunk } = useDeleteChunk();
|
|
||||||
const showDeleteConfirm = useShowDeleteConfirm();
|
|
||||||
|
|
||||||
const removeChunk = useCallback(
|
|
||||||
(chunkIds: string[], documentId: string) => () => {
|
|
||||||
return deleteChunk({ chunkIds, doc_id: documentId });
|
|
||||||
},
|
|
||||||
[deleteChunk],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onRemoveChunk = useCallback(
|
|
||||||
(chunkIds: string[], documentId: string): Promise<number> => {
|
|
||||||
return showDeleteConfirm({ onOk: removeChunk(chunkIds, documentId) });
|
|
||||||
},
|
|
||||||
[removeChunk, showDeleteConfirm],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
removeChunk: onRemoveChunk,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useUpdateChunk = () => {
|
|
||||||
const [chunkId, setChunkId] = useState<string | undefined>('');
|
|
||||||
const {
|
|
||||||
visible: chunkUpdatingVisible,
|
|
||||||
hideModal: hideChunkUpdatingModal,
|
|
||||||
showModal,
|
|
||||||
} = useSetModalState();
|
|
||||||
const { createChunk, loading } = useCreateChunk();
|
|
||||||
const { documentId } = useGetKnowledgeSearchParams();
|
|
||||||
|
|
||||||
const onChunkUpdatingOk = useCallback(
|
|
||||||
async (params: IChunk) => {
|
|
||||||
const code = await createChunk({
|
|
||||||
...params,
|
|
||||||
doc_id: documentId,
|
|
||||||
chunk_id: chunkId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (code === 0) {
|
|
||||||
hideChunkUpdatingModal();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[createChunk, hideChunkUpdatingModal, chunkId, documentId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleShowChunkUpdatingModal = useCallback(
|
|
||||||
async (id?: string) => {
|
|
||||||
setChunkId(id);
|
|
||||||
showModal();
|
|
||||||
},
|
|
||||||
[showModal],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
chunkUpdatingLoading: loading,
|
|
||||||
onChunkUpdatingOk,
|
|
||||||
chunkUpdatingVisible,
|
|
||||||
hideChunkUpdatingModal,
|
|
||||||
showChunkUpdatingModal: handleShowChunkUpdatingModal,
|
|
||||||
chunkId,
|
|
||||||
documentId,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
.chunkPage {
|
|
||||||
padding: 24px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
// height: calc(100vh - 112px);
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.filter {
|
|
||||||
margin: 10px 0;
|
|
||||||
display: flex;
|
|
||||||
height: 32px;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagePdfWrapper {
|
|
||||||
width: 60%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pageWrapper {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pageContent {
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 12px;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.spin {
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.documentPreview {
|
|
||||||
width: 40%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chunkContainer {
|
|
||||||
display: flex;
|
|
||||||
height: calc(100vh - 332px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chunkOtherContainer {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pageFooter {
|
|
||||||
padding-top: 10px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
height: 100px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.context {
|
|
||||||
flex: 1;
|
|
||||||
// width: 207px;
|
|
||||||
height: 88px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
height: 20px;
|
|
||||||
|
|
||||||
.text {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
:global {
|
|
||||||
.ant-card-body {
|
|
||||||
padding: 10px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
import { useFetchNextChunkList, useSwitchChunk } from '@/hooks/chunk-hooks';
|
|
||||||
import type { PaginationProps } from 'antd';
|
|
||||||
import { Divider, Flex, Pagination, Space, Spin, message } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useCallback, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import ChunkCard from './components/chunk-card';
|
|
||||||
import CreatingModal from './components/chunk-creating-modal';
|
|
||||||
import ChunkToolBar from './components/chunk-toolbar';
|
|
||||||
import DocumentPreview from './components/document-preview/preview';
|
|
||||||
import {
|
|
||||||
useChangeChunkTextMode,
|
|
||||||
useDeleteChunkByIds,
|
|
||||||
useGetChunkHighlights,
|
|
||||||
useHandleChunkCardClick,
|
|
||||||
useUpdateChunk,
|
|
||||||
} from './hooks';
|
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const Chunk = () => {
|
|
||||||
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
|
|
||||||
const { removeChunk } = useDeleteChunkByIds();
|
|
||||||
const {
|
|
||||||
data: { documentInfo, data = [], total },
|
|
||||||
pagination,
|
|
||||||
loading,
|
|
||||||
searchString,
|
|
||||||
handleInputChange,
|
|
||||||
available,
|
|
||||||
handleSetAvailable,
|
|
||||||
} = useFetchNextChunkList();
|
|
||||||
const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick();
|
|
||||||
const isPdf = documentInfo?.type === 'pdf';
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { changeChunkTextMode, textMode } = useChangeChunkTextMode();
|
|
||||||
const { switchChunk } = useSwitchChunk();
|
|
||||||
const {
|
|
||||||
chunkUpdatingLoading,
|
|
||||||
onChunkUpdatingOk,
|
|
||||||
showChunkUpdatingModal,
|
|
||||||
hideChunkUpdatingModal,
|
|
||||||
chunkId,
|
|
||||||
chunkUpdatingVisible,
|
|
||||||
documentId,
|
|
||||||
} = useUpdateChunk();
|
|
||||||
|
|
||||||
const onPaginationChange: PaginationProps['onShowSizeChange'] = (
|
|
||||||
page,
|
|
||||||
size,
|
|
||||||
) => {
|
|
||||||
setSelectedChunkIds([]);
|
|
||||||
pagination.onChange?.(page, size);
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectAllChunk = useCallback(
|
|
||||||
(checked: boolean) => {
|
|
||||||
setSelectedChunkIds(checked ? data.map((x) => x.chunk_id) : []);
|
|
||||||
},
|
|
||||||
[data],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSingleCheckboxClick = useCallback(
|
|
||||||
(chunkId: string, checked: boolean) => {
|
|
||||||
setSelectedChunkIds((previousIds) => {
|
|
||||||
const idx = previousIds.findIndex((x) => x === chunkId);
|
|
||||||
const nextIds = [...previousIds];
|
|
||||||
if (checked && idx === -1) {
|
|
||||||
nextIds.push(chunkId);
|
|
||||||
} else if (!checked && idx !== -1) {
|
|
||||||
nextIds.splice(idx, 1);
|
|
||||||
}
|
|
||||||
return nextIds;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const showSelectedChunkWarning = useCallback(() => {
|
|
||||||
message.warning(t('message.pleaseSelectChunk'));
|
|
||||||
}, [t]);
|
|
||||||
|
|
||||||
const handleRemoveChunk = useCallback(async () => {
|
|
||||||
if (selectedChunkIds.length > 0) {
|
|
||||||
const resCode: number = await removeChunk(selectedChunkIds, documentId);
|
|
||||||
if (resCode === 0) {
|
|
||||||
setSelectedChunkIds([]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showSelectedChunkWarning();
|
|
||||||
}
|
|
||||||
}, [selectedChunkIds, documentId, removeChunk, showSelectedChunkWarning]);
|
|
||||||
|
|
||||||
const handleSwitchChunk = useCallback(
|
|
||||||
async (available?: number, chunkIds?: string[]) => {
|
|
||||||
let ids = chunkIds;
|
|
||||||
if (!chunkIds) {
|
|
||||||
ids = selectedChunkIds;
|
|
||||||
if (selectedChunkIds.length === 0) {
|
|
||||||
showSelectedChunkWarning();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resCode: number = await switchChunk({
|
|
||||||
chunk_ids: ids,
|
|
||||||
available_int: available,
|
|
||||||
doc_id: documentId,
|
|
||||||
});
|
|
||||||
if (!chunkIds && resCode === 0) {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[switchChunk, documentId, selectedChunkIds, showSelectedChunkWarning],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { highlights, setWidthAndHeight } =
|
|
||||||
useGetChunkHighlights(selectedChunkId);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.chunkPage}>
|
|
||||||
<ChunkToolBar
|
|
||||||
selectAllChunk={selectAllChunk}
|
|
||||||
createChunk={showChunkUpdatingModal}
|
|
||||||
removeChunk={handleRemoveChunk}
|
|
||||||
checked={selectedChunkIds.length === data.length}
|
|
||||||
switchChunk={handleSwitchChunk}
|
|
||||||
changeChunkTextMode={changeChunkTextMode}
|
|
||||||
searchString={searchString}
|
|
||||||
handleInputChange={handleInputChange}
|
|
||||||
available={available}
|
|
||||||
handleSetAvailable={handleSetAvailable}
|
|
||||||
></ChunkToolBar>
|
|
||||||
<Divider></Divider>
|
|
||||||
<Flex flex={1} gap={'middle'}>
|
|
||||||
<Flex
|
|
||||||
vertical
|
|
||||||
className={isPdf ? styles.pagePdfWrapper : styles.pageWrapper}
|
|
||||||
>
|
|
||||||
<Spin spinning={loading} className={styles.spin} size="large">
|
|
||||||
<div className={styles.pageContent}>
|
|
||||||
<Space
|
|
||||||
direction="vertical"
|
|
||||||
size={'middle'}
|
|
||||||
className={classNames(styles.chunkContainer, {
|
|
||||||
[styles.chunkOtherContainer]: !isPdf,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{data.map((item) => (
|
|
||||||
<ChunkCard
|
|
||||||
item={item}
|
|
||||||
key={item.chunk_id}
|
|
||||||
editChunk={showChunkUpdatingModal}
|
|
||||||
checked={selectedChunkIds.some(
|
|
||||||
(x) => x === item.chunk_id,
|
|
||||||
)}
|
|
||||||
handleCheckboxClick={handleSingleCheckboxClick}
|
|
||||||
switchChunk={handleSwitchChunk}
|
|
||||||
clickChunkCard={handleChunkCardClick}
|
|
||||||
selected={item.chunk_id === selectedChunkId}
|
|
||||||
textMode={textMode}
|
|
||||||
></ChunkCard>
|
|
||||||
))}
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
<div className={styles.pageFooter}>
|
|
||||||
<Pagination
|
|
||||||
{...pagination}
|
|
||||||
total={total}
|
|
||||||
size={'small'}
|
|
||||||
onChange={onPaginationChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Flex>
|
|
||||||
{isPdf && (
|
|
||||||
<section className={styles.documentPreview}>
|
|
||||||
<DocumentPreview
|
|
||||||
highlights={highlights}
|
|
||||||
setWidthAndHeight={setWidthAndHeight}
|
|
||||||
></DocumentPreview>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</div>
|
|
||||||
{chunkUpdatingVisible && (
|
|
||||||
<CreatingModal
|
|
||||||
doc_id={documentId}
|
|
||||||
chunkId={chunkId}
|
|
||||||
hideModal={hideChunkUpdatingModal}
|
|
||||||
visible={chunkUpdatingVisible}
|
|
||||||
loading={chunkUpdatingLoading}
|
|
||||||
onOk={onChunkUpdatingOk}
|
|
||||||
parserId={documentInfo.parser_id}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Chunk;
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
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;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
@ -4,8 +4,6 @@ import {
|
|||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { ConfigurationFormContainer } from '../configuration-form-container';
|
import { ConfigurationFormContainer } from '../configuration-form-container';
|
||||||
|
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
|
|
||||||
export function AudioConfiguration() {
|
export function AudioConfiguration() {
|
||||||
return (
|
return (
|
||||||
<ConfigurationFormContainer>
|
<ConfigurationFormContainer>
|
||||||
@ -14,7 +12,7 @@ export function AudioConfiguration() {
|
|||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
<TagItems></TagItems>
|
{/* <TagItems></TagItems> */}
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import {
|
import {
|
||||||
ConfigurationFormContainer,
|
ConfigurationFormContainer,
|
||||||
MainContainer,
|
MainContainer,
|
||||||
@ -20,9 +19,9 @@ export function BookConfiguration() {
|
|||||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
<ConfigurationFormContainer>
|
{/* <ConfigurationFormContainer>
|
||||||
<TagItems></TagItems>
|
<TagItems></TagItems>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer> */}
|
||||||
</MainContainer>
|
</MainContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import {
|
|||||||
AutoKeywordsFormField,
|
AutoKeywordsFormField,
|
||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import { ConfigurationFormContainer } from '../configuration-form-container';
|
import { ConfigurationFormContainer } from '../configuration-form-container';
|
||||||
|
|
||||||
export function EmailConfiguration() {
|
export function EmailConfiguration() {
|
||||||
@ -12,7 +11,7 @@ export function EmailConfiguration() {
|
|||||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</>
|
</>
|
||||||
<TagItems></TagItems>
|
{/* <TagItems></TagItems> */}
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import {
|
import {
|
||||||
ConfigurationFormContainer,
|
ConfigurationFormContainer,
|
||||||
MainContainer,
|
MainContainer,
|
||||||
@ -21,9 +20,9 @@ export function LawsConfiguration() {
|
|||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
|
|
||||||
<ConfigurationFormContainer>
|
{/* <ConfigurationFormContainer>
|
||||||
<TagItems></TagItems>
|
<TagItems></TagItems>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer> */}
|
||||||
</MainContainer>
|
</MainContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import {
|
import {
|
||||||
ConfigurationFormContainer,
|
ConfigurationFormContainer,
|
||||||
MainContainer,
|
MainContainer,
|
||||||
@ -21,7 +20,7 @@ export function ManualConfiguration() {
|
|||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
|
|
||||||
<TagItems></TagItems>
|
{/* <TagItems></TagItems> */}
|
||||||
</MainContainer>
|
</MainContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import { ConfigurationFormContainer } from '../configuration-form-container';
|
import { ConfigurationFormContainer } from '../configuration-form-container';
|
||||||
|
|
||||||
export function OneConfiguration() {
|
export function OneConfiguration() {
|
||||||
@ -15,7 +14,7 @@ export function OneConfiguration() {
|
|||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
<TagItems></TagItems>
|
{/* <TagItems></TagItems> */}
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import {
|
import {
|
||||||
ConfigurationFormContainer,
|
ConfigurationFormContainer,
|
||||||
MainContainer,
|
MainContainer,
|
||||||
@ -20,9 +19,9 @@ export function PaperConfiguration() {
|
|||||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
<ConfigurationFormContainer>
|
{/* <ConfigurationFormContainer>
|
||||||
<TagItems></TagItems>
|
<TagItems></TagItems>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer> */}
|
||||||
</MainContainer>
|
</MainContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import {
|
|||||||
AutoKeywordsFormField,
|
AutoKeywordsFormField,
|
||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import { ConfigurationFormContainer } from '../configuration-form-container';
|
import { ConfigurationFormContainer } from '../configuration-form-container';
|
||||||
|
|
||||||
export function PictureConfiguration() {
|
export function PictureConfiguration() {
|
||||||
@ -12,7 +11,7 @@ export function PictureConfiguration() {
|
|||||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</>
|
</>
|
||||||
<TagItems></TagItems>
|
{/* <TagItems></TagItems> */}
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
AutoQuestionsFormField,
|
AutoQuestionsFormField,
|
||||||
} from '@/components/auto-keywords-form-field';
|
} from '@/components/auto-keywords-form-field';
|
||||||
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import {
|
import {
|
||||||
ConfigurationFormContainer,
|
ConfigurationFormContainer,
|
||||||
MainContainer,
|
MainContainer,
|
||||||
@ -21,9 +20,9 @@ export function PresentationConfiguration() {
|
|||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
<AutoQuestionsFormField></AutoQuestionsFormField>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
|
|
||||||
<ConfigurationFormContainer>
|
{/* <ConfigurationFormContainer>
|
||||||
<TagItems></TagItems>
|
<TagItems></TagItems>
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer> */}
|
||||||
</MainContainer>
|
</MainContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import { ConfigurationFormContainer } from '../configuration-form-container';
|
|
||||||
|
|
||||||
export function QAConfiguration() {
|
export function QAConfiguration() {
|
||||||
return (
|
return (
|
||||||
<ConfigurationFormContainer>
|
<></>
|
||||||
<TagItems></TagItems>
|
// <ConfigurationFormContainer>
|
||||||
</ConfigurationFormContainer>
|
// <TagItems></TagItems>
|
||||||
|
// </ConfigurationFormContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import { TagItems } from '../components/tag-item';
|
|
||||||
import { ConfigurationFormContainer } from '../configuration-form-container';
|
|
||||||
|
|
||||||
export function ResumeConfiguration() {
|
export function ResumeConfiguration() {
|
||||||
return (
|
return (
|
||||||
<ConfigurationFormContainer>
|
<></>
|
||||||
<TagItems></TagItems>
|
// <ConfigurationFormContainer>
|
||||||
</ConfigurationFormContainer>
|
// <TagItems></TagItems>
|
||||||
|
// </ConfigurationFormContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user