mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-01 08:05:07 +08:00
Refactor: Replace antd with shadcn (#12718)
### What problem does this PR solve? Refactor: Replace antd with shadcn ### Type of change - [x] Refactoring
This commit is contained in:
@ -11,8 +11,8 @@ import {
|
|||||||
} from '@/hooks/use-user-setting-request';
|
} from '@/hooks/use-user-setting-request';
|
||||||
import { IStats } from '@/interfaces/database/chat';
|
import { IStats } from '@/interfaces/database/chat';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { message } from 'antd';
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import message from '../ui/message';
|
||||||
|
|
||||||
export const useOperateApiKey = (idKey: string, dialogId?: string) => {
|
export const useOperateApiKey = (idKey: string, dialogId?: string) => {
|
||||||
const { removeToken } = useRemoveSystemToken();
|
const { removeToken } = useRemoveSystemToken();
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
||||||
import { Tooltip } from 'antd';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CopyToClipboard as Clipboard, Props } from 'react-copy-to-clipboard';
|
import { CopyToClipboard as Clipboard, Props } from 'react-copy-to-clipboard';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||||
|
|
||||||
const CopyToClipboard = ({ text }: Props) => {
|
const CopyToClipboard = ({ text }: Props) => {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
@ -16,10 +16,13 @@ const CopyToClipboard = ({ text }: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={copied ? t('copied') : t('copy')}>
|
<Tooltip>
|
||||||
<Clipboard text={text} onCopy={handleCopy}>
|
<TooltipTrigger>
|
||||||
{copied ? <CheckOutlined /> : <CopyOutlined />}
|
<Clipboard text={text} onCopy={handleCopy}>
|
||||||
</Clipboard>
|
{copied ? <CheckOutlined /> : <CopyOutlined />}
|
||||||
|
</Clipboard>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>{copied ? t('copied') : t('copy')}</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,98 +0,0 @@
|
|||||||
import { Form, FormInstance, Input, InputRef, Typography } from 'antd';
|
|
||||||
import { omit } from 'lodash';
|
|
||||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
const EditableContext = React.createContext<FormInstance<any> | null>(null);
|
|
||||||
const { Text } = Typography;
|
|
||||||
|
|
||||||
interface EditableRowProps {
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Item {
|
|
||||||
key: string;
|
|
||||||
name: string;
|
|
||||||
age: string;
|
|
||||||
address: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EditableRow: React.FC<EditableRowProps> = ({ ...props }) => {
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
return (
|
|
||||||
<Form form={form} component={false}>
|
|
||||||
<EditableContext.Provider value={form}>
|
|
||||||
<tr {...omit(props, 'index')} />
|
|
||||||
</EditableContext.Provider>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface EditableCellProps {
|
|
||||||
title: React.ReactNode;
|
|
||||||
editable: boolean;
|
|
||||||
children: React.ReactNode;
|
|
||||||
dataIndex: keyof Item;
|
|
||||||
record: Item;
|
|
||||||
handleSave: (record: Item) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EditableCell: React.FC<EditableCellProps> = ({
|
|
||||||
title,
|
|
||||||
editable,
|
|
||||||
children,
|
|
||||||
dataIndex,
|
|
||||||
record,
|
|
||||||
handleSave,
|
|
||||||
...restProps
|
|
||||||
}) => {
|
|
||||||
const [editing, setEditing] = useState(false);
|
|
||||||
const inputRef = useRef<InputRef>(null);
|
|
||||||
const form = useContext(EditableContext)!;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (editing) {
|
|
||||||
inputRef.current!.focus();
|
|
||||||
}
|
|
||||||
}, [editing]);
|
|
||||||
|
|
||||||
const toggleEdit = () => {
|
|
||||||
setEditing(!editing);
|
|
||||||
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
|
|
||||||
};
|
|
||||||
|
|
||||||
const save = async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
|
|
||||||
toggleEdit();
|
|
||||||
handleSave({ ...record, ...values });
|
|
||||||
} catch (errInfo) {
|
|
||||||
console.log('Save failed:', errInfo);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let childNode = children;
|
|
||||||
|
|
||||||
if (editable) {
|
|
||||||
childNode = editing ? (
|
|
||||||
<Form.Item
|
|
||||||
style={{ margin: 0, minWidth: 70 }}
|
|
||||||
name={dataIndex}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: `${title} is required.`,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
|
|
||||||
</Form.Item>
|
|
||||||
) : (
|
|
||||||
<div onClick={toggleEdit} className="editable-cell-value-wrap">
|
|
||||||
<Text>{children}</Text>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <td {...restProps}>{childNode}</td>;
|
|
||||||
};
|
|
||||||
@ -15,12 +15,12 @@ import {
|
|||||||
} from '@/utils/chat';
|
} from '@/utils/chat';
|
||||||
import { getExtension } from '@/utils/document-util';
|
import { getExtension } from '@/utils/document-util';
|
||||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
import { Button, Flex, Popover, Tooltip } from 'antd';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import 'katex/dist/katex.min.css';
|
import 'katex/dist/katex.min.css';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { pipe } from 'lodash/fp';
|
import { pipe } from 'lodash/fp';
|
||||||
|
import { Info } from 'lucide-react';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Markdown from 'react-markdown';
|
import Markdown from 'react-markdown';
|
||||||
@ -37,6 +37,9 @@ import remarkMath from 'remark-math';
|
|||||||
import { visitParents } from 'unist-util-visit-parents';
|
import { visitParents } from 'unist-util-visit-parents';
|
||||||
import styles from './floating-chat-widget-markdown.module.less';
|
import styles from './floating-chat-widget-markdown.module.less';
|
||||||
import { useIsDarkTheme } from './theme-provider';
|
import { useIsDarkTheme } from './theme-provider';
|
||||||
|
import { Button } from './ui/button';
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from './ui/popover';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||||
|
|
||||||
const getChunkIndex = (match: string) => Number(match.replace(/\[|\]/g, ''));
|
const getChunkIndex = (match: string) => Number(match.replace(/\[|\]/g, ''));
|
||||||
|
|
||||||
@ -161,19 +164,19 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
className="flex gap-2 widget-citation-content"
|
className="flex gap-2 widget-citation-content"
|
||||||
>
|
>
|
||||||
{imageId && (
|
{imageId && (
|
||||||
<Popover
|
<Popover>
|
||||||
placement="left"
|
<TooltipTrigger asChild>
|
||||||
content={
|
<Image
|
||||||
|
id={imageId}
|
||||||
|
className="w-24 h-24 object-contain rounded m-1 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="left">
|
||||||
<Image
|
<Image
|
||||||
id={imageId}
|
id={imageId}
|
||||||
className="max-w-[80vw] max-h-[60vh] rounded"
|
className="max-w-[80vw] max-h-[60vh] rounded"
|
||||||
/>
|
/>
|
||||||
}
|
</TooltipContent>
|
||||||
>
|
|
||||||
<Image
|
|
||||||
id={imageId}
|
|
||||||
className="w-24 h-24 object-contain rounded m-1 cursor-pointer"
|
|
||||||
/>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
)}
|
)}
|
||||||
<div className="space-y-2 flex-1 min-w-0">
|
<div className="space-y-2 flex-1 min-w-0">
|
||||||
@ -184,7 +187,7 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
className="max-h-[250px] overflow-y-auto text-xs leading-relaxed p-2 bg-gray-50 dark:bg-gray-800 rounded prose-sm"
|
className="max-h-[250px] overflow-y-auto text-xs leading-relaxed p-2 bg-gray-50 dark:bg-gray-800 rounded prose-sm"
|
||||||
></div>
|
></div>
|
||||||
{documentId && (
|
{documentId && (
|
||||||
<Flex gap={'small'} align="center">
|
<section className="flex gap-1 justify-center">
|
||||||
{fileThumbnail ? (
|
{fileThumbnail ? (
|
||||||
<img
|
<img
|
||||||
src={fileThumbnail}
|
src={fileThumbnail}
|
||||||
@ -194,32 +197,33 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
) : (
|
) : (
|
||||||
<SvgIcon name={`file-icon/${fileExtension}`} width={20} />
|
<SvgIcon name={`file-icon/${fileExtension}`} width={20} />
|
||||||
)}
|
)}
|
||||||
<Tooltip
|
<Tooltip>
|
||||||
title={
|
<TooltipTrigger asChild>
|
||||||
!documentUrl && fileExtension !== 'pdf'
|
<Button
|
||||||
|
size={'sm'}
|
||||||
|
variant={'link'}
|
||||||
|
className="p-0 text-xs break-words h-auto text-left flex-1"
|
||||||
|
onClick={handleDocumentButtonClick(
|
||||||
|
documentId,
|
||||||
|
chunkItem,
|
||||||
|
fileExtension === 'pdf',
|
||||||
|
documentUrl,
|
||||||
|
)}
|
||||||
|
disabled={!documentUrl && fileExtension !== 'pdf'}
|
||||||
|
style={{ whiteSpace: 'normal' }}
|
||||||
|
>
|
||||||
|
<span className="truncate">
|
||||||
|
{document?.doc_name ?? 'Unnamed Document'}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{!documentUrl && fileExtension !== 'pdf'
|
||||||
? 'Document link unavailable'
|
? 'Document link unavailable'
|
||||||
: document.doc_name
|
: document.doc_name}
|
||||||
}
|
</TooltipContent>
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
className="p-0 text-xs break-words h-auto text-left flex-1"
|
|
||||||
onClick={handleDocumentButtonClick(
|
|
||||||
documentId,
|
|
||||||
chunkItem,
|
|
||||||
fileExtension === 'pdf',
|
|
||||||
documentUrl,
|
|
||||||
)}
|
|
||||||
disabled={!documentUrl && fileExtension !== 'pdf'}
|
|
||||||
style={{ whiteSpace: 'normal' }}
|
|
||||||
>
|
|
||||||
<span className="truncate">
|
|
||||||
{document?.doc_name ?? 'Unnamed Document'}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Flex>
|
</section>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -236,8 +240,11 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return (
|
return (
|
||||||
<Tooltip key={`err-tooltip-${i}`} title="Reference unavailable">
|
<Tooltip key={`err-tooltip-${i}`}>
|
||||||
<InfoCircleOutlined className={styles.referenceIcon} />
|
<TooltipTrigger asChild>
|
||||||
|
<Info className={styles.referenceIcon} />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Reference unavailable</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -262,8 +269,11 @@ const FloatingChatWidgetMarkdown = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover content={getPopoverContent(chunkIndex)} key={`popover-${i}`}>
|
<Popover key={`popover-${i}`}>
|
||||||
<InfoCircleOutlined className={styles.referenceIcon} />
|
<PopoverTrigger asChild>
|
||||||
|
<InfoCircleOutlined className={styles.referenceIcon} />
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>{getPopoverContent(chunkIndex)}</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import IndentedTree from './indented-tree';
|
|
||||||
|
|
||||||
import { useFetchKnowledgeGraph } from '@/hooks/use-knowledge-request';
|
|
||||||
import { IModalProps } from '@/interfaces/common';
|
|
||||||
import { Modal } from 'antd';
|
|
||||||
|
|
||||||
const IndentedTreeModal = ({
|
|
||||||
visible,
|
|
||||||
hideModal,
|
|
||||||
}: IModalProps<any> & { documentId: string }) => {
|
|
||||||
const { data } = useFetchKnowledgeGraph();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={t('chunk.mind')}
|
|
||||||
open={visible}
|
|
||||||
onCancel={hideModal}
|
|
||||||
width={'90vw'}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<section>
|
|
||||||
<IndentedTree data={data?.mind_map} show></IndentedTree>
|
|
||||||
</section>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IndentedTreeModal;
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
import { LlmModelType } from '@/constants/knowledge';
|
|
||||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
|
||||||
import { Popover as AntPopover, Select as AntSelect } from 'antd';
|
|
||||||
import LlmSettingItems from '../llm-setting-items';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
id?: string;
|
|
||||||
value?: string;
|
|
||||||
onInitialValue?: (value: string, option: any) => void;
|
|
||||||
onChange?: (value: string, option: any) => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const LLMSelect = ({
|
|
||||||
id,
|
|
||||||
value,
|
|
||||||
onInitialValue,
|
|
||||||
onChange,
|
|
||||||
disabled,
|
|
||||||
}: IProps) => {
|
|
||||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
|
||||||
LlmModelType.Chat,
|
|
||||||
LlmModelType.Image2text,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (onInitialValue && value) {
|
|
||||||
for (const modelOption of modelOptions) {
|
|
||||||
for (const option of modelOption.options) {
|
|
||||||
if (option.value === value) {
|
|
||||||
onInitialValue(value, option);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = (
|
|
||||||
<div style={{ width: 400 }}>
|
|
||||||
<LlmSettingItems
|
|
||||||
onChange={onChange}
|
|
||||||
formItemLayout={{ labelCol: { span: 10 }, wrapperCol: { span: 14 } }}
|
|
||||||
></LlmSettingItems>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AntPopover
|
|
||||||
content={content}
|
|
||||||
trigger="click"
|
|
||||||
placement="left"
|
|
||||||
arrow={false}
|
|
||||||
destroyTooltipOnHide
|
|
||||||
>
|
|
||||||
<AntSelect
|
|
||||||
options={modelOptions}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
dropdownStyle={{ display: 'none' }}
|
|
||||||
id={id}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</AntPopover>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LLMSelect;
|
|
||||||
@ -1,350 +0,0 @@
|
|||||||
import {
|
|
||||||
LlmModelType,
|
|
||||||
ModelVariableType,
|
|
||||||
settledModelVariableMap,
|
|
||||||
} from '@/constants/knowledge';
|
|
||||||
import { Flex, Form, InputNumber, Select, Slider, Switch, Tooltip } from 'antd';
|
|
||||||
import camelCase from 'lodash/camelCase';
|
|
||||||
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
|
||||||
import { setChatVariableEnabledFieldValuePage } from '@/utils/chat';
|
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import styles from './index.module.less';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
prefix?: string;
|
|
||||||
formItemLayout?: any;
|
|
||||||
handleParametersChange?(value: ModelVariableType): void;
|
|
||||||
onChange?(value: string, option: any): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const LlmSettingItems = ({ prefix, formItemLayout = {}, onChange }: IProps) => {
|
|
||||||
const form = Form.useFormInstance();
|
|
||||||
const { t } = useTranslate('chat');
|
|
||||||
const parameterOptions = Object.values(ModelVariableType).map((x) => ({
|
|
||||||
label: t(camelCase(x)),
|
|
||||||
value: x,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const handleParametersChange = useCallback(
|
|
||||||
(value: ModelVariableType) => {
|
|
||||||
const variable = settledModelVariableMap[value];
|
|
||||||
let nextVariable: Record<string, any> = variable;
|
|
||||||
if (prefix) {
|
|
||||||
nextVariable = { [prefix]: variable };
|
|
||||||
}
|
|
||||||
const variableCheckBoxFieldMap = setChatVariableEnabledFieldValuePage();
|
|
||||||
form.setFieldsValue({ ...nextVariable, ...variableCheckBoxFieldMap });
|
|
||||||
},
|
|
||||||
[form, prefix],
|
|
||||||
);
|
|
||||||
|
|
||||||
const memorizedPrefix = useMemo(() => (prefix ? [prefix] : []), [prefix]);
|
|
||||||
|
|
||||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
|
||||||
LlmModelType.Chat,
|
|
||||||
LlmModelType.Image2text,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item
|
|
||||||
label={t('model')}
|
|
||||||
name="llm_id"
|
|
||||||
tooltip={t('modelTip')}
|
|
||||||
{...formItemLayout}
|
|
||||||
rules={[{ required: true, message: t('modelMessage') }]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
options={modelOptions}
|
|
||||||
showSearch
|
|
||||||
popupMatchSelectWidth={false}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<div className="border rounded-md">
|
|
||||||
<div className="flex justify-between bg-slate-100 p-2 mb-2">
|
|
||||||
<div className="space-x-1 items-center">
|
|
||||||
<span className="text-lg font-semibold">{t('freedom')}</span>
|
|
||||||
<Tooltip title={t('freedomTip')}>
|
|
||||||
<QuestionCircleOutlined></QuestionCircleOutlined>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div className="w-1/4 min-w-32">
|
|
||||||
<Form.Item
|
|
||||||
label={t('freedom')}
|
|
||||||
name="parameter"
|
|
||||||
tooltip={t('freedomTip')}
|
|
||||||
initialValue={ModelVariableType.Precise}
|
|
||||||
labelCol={{ span: 0 }}
|
|
||||||
wrapperCol={{ span: 24 }}
|
|
||||||
className="m-0"
|
|
||||||
>
|
|
||||||
<Select<ModelVariableType>
|
|
||||||
options={parameterOptions}
|
|
||||||
onChange={handleParametersChange}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="pr-2">
|
|
||||||
<Form.Item
|
|
||||||
label={t('temperature')}
|
|
||||||
tooltip={t('temperatureTip')}
|
|
||||||
{...formItemLayout}
|
|
||||||
>
|
|
||||||
<Flex gap={20} align="center">
|
|
||||||
<Form.Item
|
|
||||||
name={'temperatureEnabled'}
|
|
||||||
valuePropName="checked"
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Switch size="small" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
noStyle
|
|
||||||
dependencies={['temperatureEnabled']}
|
|
||||||
shouldUpdate
|
|
||||||
>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const disabled = !getFieldValue('temperatureEnabled');
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex flex={1}>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'temperature']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Slider
|
|
||||||
className={styles.variableSlider}
|
|
||||||
max={1}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'temperature']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<InputNumber
|
|
||||||
className={styles.sliderInputNumber}
|
|
||||||
max={1}
|
|
||||||
min={0}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('topP')}
|
|
||||||
tooltip={t('topPTip')}
|
|
||||||
{...formItemLayout}
|
|
||||||
>
|
|
||||||
<Flex gap={20} align="center">
|
|
||||||
<Form.Item name={'topPEnabled'} valuePropName="checked" noStyle>
|
|
||||||
<Switch size="small" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item noStyle dependencies={['topPEnabled']} shouldUpdate>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const disabled = !getFieldValue('topPEnabled');
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex flex={1}>
|
|
||||||
<Form.Item name={[...memorizedPrefix, 'top_p']} noStyle>
|
|
||||||
<Slider
|
|
||||||
className={styles.variableSlider}
|
|
||||||
max={1}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
<Form.Item name={[...memorizedPrefix, 'top_p']} noStyle>
|
|
||||||
<InputNumber
|
|
||||||
className={styles.sliderInputNumber}
|
|
||||||
max={1}
|
|
||||||
min={0}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('presencePenalty')}
|
|
||||||
tooltip={t('presencePenaltyTip')}
|
|
||||||
{...formItemLayout}
|
|
||||||
>
|
|
||||||
<Flex gap={20} align="center">
|
|
||||||
<Form.Item
|
|
||||||
name={'presencePenaltyEnabled'}
|
|
||||||
valuePropName="checked"
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Switch size="small" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
noStyle
|
|
||||||
dependencies={['presencePenaltyEnabled']}
|
|
||||||
shouldUpdate
|
|
||||||
>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const disabled = !getFieldValue('presencePenaltyEnabled');
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex flex={1}>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'presence_penalty']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Slider
|
|
||||||
className={styles.variableSlider}
|
|
||||||
max={1}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'presence_penalty']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<InputNumber
|
|
||||||
className={styles.sliderInputNumber}
|
|
||||||
max={1}
|
|
||||||
min={0}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('frequencyPenalty')}
|
|
||||||
tooltip={t('frequencyPenaltyTip')}
|
|
||||||
{...formItemLayout}
|
|
||||||
>
|
|
||||||
<Flex gap={20} align="center">
|
|
||||||
<Form.Item
|
|
||||||
name={'frequencyPenaltyEnabled'}
|
|
||||||
valuePropName="checked"
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Switch size="small" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
noStyle
|
|
||||||
dependencies={['frequencyPenaltyEnabled']}
|
|
||||||
shouldUpdate
|
|
||||||
>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const disabled = !getFieldValue('frequencyPenaltyEnabled');
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex flex={1}>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'frequency_penalty']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Slider
|
|
||||||
className={styles.variableSlider}
|
|
||||||
max={1}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'frequency_penalty']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<InputNumber
|
|
||||||
className={styles.sliderInputNumber}
|
|
||||||
max={1}
|
|
||||||
min={0}
|
|
||||||
step={0.01}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('maxTokens')}
|
|
||||||
tooltip={t('maxTokensTip')}
|
|
||||||
{...formItemLayout}
|
|
||||||
>
|
|
||||||
<Flex gap={20} align="center">
|
|
||||||
<Form.Item
|
|
||||||
name={'maxTokensEnabled'}
|
|
||||||
valuePropName="checked"
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Switch size="small" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
noStyle
|
|
||||||
dependencies={['maxTokensEnabled']}
|
|
||||||
shouldUpdate
|
|
||||||
>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const disabled = !getFieldValue('maxTokensEnabled');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex flex={1}>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'max_tokens']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<Slider
|
|
||||||
className={styles.variableSlider}
|
|
||||||
max={128000}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
<Form.Item
|
|
||||||
name={[...memorizedPrefix, 'max_tokens']}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<InputNumber
|
|
||||||
disabled={disabled}
|
|
||||||
className={styles.sliderInputNumber}
|
|
||||||
max={128000}
|
|
||||||
min={0}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LlmSettingItems;
|
|
||||||
@ -1,11 +1,10 @@
|
|||||||
import { IconMap, LLMFactory } from '@/constants/llm';
|
import { IconMap, LLMFactory } from '@/constants/llm';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import Icon, { UserOutlined } from '@ant-design/icons';
|
import Icon from '@ant-design/icons';
|
||||||
import { IconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
import { IconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
||||||
import { Avatar } from 'antd';
|
|
||||||
import { AvatarSize } from 'antd/es/avatar/AvatarContext';
|
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { IconFontFill } from './icon-font';
|
import { IconFontFill } from './icon-font';
|
||||||
|
import { RAGFlowAvatar } from './ragflow-avatar';
|
||||||
import { useIsDarkTheme } from './theme-provider';
|
import { useIsDarkTheme } from './theme-provider';
|
||||||
|
|
||||||
// const importAll = (requireContext: __WebpackModuleApi.RequireContext) => {
|
// const importAll = (requireContext: __WebpackModuleApi.RequireContext) => {
|
||||||
@ -91,13 +90,11 @@ export const LlmIcon = ({
|
|||||||
name,
|
name,
|
||||||
height = 48,
|
height = 48,
|
||||||
width = 48,
|
width = 48,
|
||||||
size = 'large',
|
|
||||||
imgClass,
|
imgClass,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
height?: number;
|
height?: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
size?: AvatarSize;
|
|
||||||
imgClass?: string;
|
imgClass?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const isDark = useIsDarkTheme();
|
const isDark = useIsDarkTheme();
|
||||||
@ -134,7 +131,6 @@ export const LlmIcon = ({
|
|||||||
name={'moxing-default'}
|
name={'moxing-default'}
|
||||||
className={cn('size-8 flex items-center justify-center', imgClass)}
|
className={cn('size-8 flex items-center justify-center', imgClass)}
|
||||||
/>
|
/>
|
||||||
// <Avatar shape="square" size={size} icon={<UserOutlined />} />
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -142,13 +138,11 @@ export const HomeIcon = ({
|
|||||||
name,
|
name,
|
||||||
height = '32',
|
height = '32',
|
||||||
width = '32',
|
width = '32',
|
||||||
size = 'large',
|
|
||||||
imgClass,
|
imgClass,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
height?: string;
|
height?: string;
|
||||||
width?: string;
|
width?: string;
|
||||||
size?: AvatarSize;
|
|
||||||
imgClass?: string;
|
imgClass?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const isDark = useIsDarkTheme();
|
const isDark = useIsDarkTheme();
|
||||||
@ -162,7 +156,7 @@ export const HomeIcon = ({
|
|||||||
imgClass={imgClass}
|
imgClass={imgClass}
|
||||||
></SvgIcon>
|
></SvgIcon>
|
||||||
) : (
|
) : (
|
||||||
<Avatar shape="square" size={size} icon={<UserOutlined />} />
|
<RAGFlowAvatar avatar={'user'}></RAGFlowAvatar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
import { Divider, Layout, theme } from 'antd';
|
|
||||||
import React from 'react';
|
|
||||||
import { Outlet } from 'react-router';
|
|
||||||
import '../locales/config';
|
|
||||||
import Header from './components/header';
|
|
||||||
|
|
||||||
import styles from './index.module.less';
|
|
||||||
|
|
||||||
const { Content } = Layout;
|
|
||||||
|
|
||||||
const App: React.FC = () => {
|
|
||||||
const {
|
|
||||||
token: { colorBgContainer, borderRadiusLG },
|
|
||||||
} = theme.useToken();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout className={styles.layout}>
|
|
||||||
<Layout>
|
|
||||||
<Header></Header>
|
|
||||||
<Divider orientationMargin={0} className={styles.divider} />
|
|
||||||
<Content
|
|
||||||
style={{
|
|
||||||
minHeight: 280,
|
|
||||||
background: colorBgContainer,
|
|
||||||
borderRadius: borderRadiusLG,
|
|
||||||
overflow: 'auto',
|
|
||||||
display: 'flex',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Outlet />
|
|
||||||
</Content>
|
|
||||||
</Layout>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@ -1,223 +0,0 @@
|
|||||||
import FilterIcon from '@/assets/filter.svg';
|
|
||||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import {
|
|
||||||
IChunkListResult,
|
|
||||||
useSelectChunkList,
|
|
||||||
} from '@/hooks/use-chunk-request';
|
|
||||||
import { useKnowledgeBaseId } from '@/hooks/use-knowledge-request';
|
|
||||||
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 'react-router';
|
|
||||||
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,74 +0,0 @@
|
|||||||
import {
|
|
||||||
useNavigateWithFromState,
|
|
||||||
useSecondPathName,
|
|
||||||
useThirdPathName,
|
|
||||||
} from '@/hooks/route-hook';
|
|
||||||
import { useKnowledgeBaseId } from '@/hooks/use-knowledge-request';
|
|
||||||
import { Breadcrumb } from 'antd';
|
|
||||||
import { ItemType } from 'antd/es/breadcrumb/Breadcrumb';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { Link, Outlet } from 'react-router';
|
|
||||||
import Siderbar from './components/knowledge-sidebar';
|
|
||||||
import { KnowledgeDatasetRouteKey, KnowledgeRouteKey } from './constant';
|
|
||||||
import styles from './index.module.less';
|
|
||||||
|
|
||||||
const KnowledgeAdding = () => {
|
|
||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const activeKey: KnowledgeRouteKey =
|
|
||||||
(useSecondPathName() as KnowledgeRouteKey) || KnowledgeRouteKey.Dataset;
|
|
||||||
|
|
||||||
const datasetActiveKey: KnowledgeDatasetRouteKey =
|
|
||||||
useThirdPathName() as KnowledgeDatasetRouteKey;
|
|
||||||
|
|
||||||
const gotoList = useNavigateWithFromState();
|
|
||||||
|
|
||||||
const breadcrumbItems: ItemType[] = useMemo(() => {
|
|
||||||
const items: ItemType[] = [
|
|
||||||
{
|
|
||||||
title: (
|
|
||||||
<a onClick={() => gotoList('/knowledge')}>
|
|
||||||
{t('header.knowledgeBase')}
|
|
||||||
</a>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: datasetActiveKey ? (
|
|
||||||
<Link
|
|
||||||
to={`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`}
|
|
||||||
>
|
|
||||||
{t(`knowledgeDetails.${activeKey}`)}
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
t(`knowledgeDetails.${activeKey}`)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (datasetActiveKey) {
|
|
||||||
items.push({
|
|
||||||
title: t(`knowledgeDetails.${datasetActiveKey}`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return items;
|
|
||||||
}, [activeKey, datasetActiveKey, gotoList, knowledgeBaseId, t]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.container}>
|
|
||||||
<Siderbar></Siderbar>
|
|
||||||
<div className={styles.contentWrapper}>
|
|
||||||
<Breadcrumb items={breadcrumbItems} />
|
|
||||||
<div className={styles.content}>
|
|
||||||
<Outlet></Outlet>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default KnowledgeAdding;
|
|
||||||
@ -1,223 +0,0 @@
|
|||||||
import FilterIcon from '@/assets/filter.svg';
|
|
||||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import {
|
|
||||||
IChunkListResult,
|
|
||||||
useSelectChunkList,
|
|
||||||
} from '@/hooks/use-chunk-request';
|
|
||||||
import { useKnowledgeBaseId } from '@/hooks/use-knowledge-request';
|
|
||||||
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 'react-router';
|
|
||||||
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;
|
|
||||||
Reference in New Issue
Block a user