feat: modify routing to nested mode and rename document (#52)

* feat: modify routing to nested mode

* feat: rename document
This commit is contained in:
balibabu 2024-02-02 18:49:54 +08:00 committed by GitHub
parent 503735cd1d
commit 7b71fb2db6
33 changed files with 681 additions and 175 deletions

View File

@ -0,0 +1,28 @@
import { ExclamationCircleFilled } from '@ant-design/icons';
import { Modal } from 'antd';
const { confirm } = Modal;
interface IProps {
onOk?: (...args: any[]) => any;
onCancel?: (...args: any[]) => any;
}
export const showDeleteConfirm = ({ onOk, onCancel }: IProps) => {
confirm({
title: 'Are you sure delete this item?',
icon: <ExclamationCircleFilled />,
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
onOk() {
onOk?.();
},
onCancel() {
onCancel?.();
},
});
};
export default showDeleteConfirm;

View File

@ -0,0 +1,24 @@
import { useState } from 'react';
interface IProps {
children: (props: {
showModal(): void;
hideModal(): void;
visible: boolean;
}) => React.ReactNode;
}
const ModalManager = ({ children }: IProps) => {
const [visible, setVisible] = useState(false);
const showModal = () => {
setVisible(true);
};
const hideModal = () => {
setVisible(false);
};
return children({ visible, showModal, hideModal });
};
export default ModalManager;

View File

@ -1,5 +1,13 @@
export enum KnowledgeRouteKey {
Dataset = 'dataset',
Testing = 'testing',
Configration = 'configration',
Configuration = 'configuration',
}
export enum RunningStatus {
UNSTART = '0',
RUNNING = '1',
CANCEL = '2',
DONE = '3',
FAIL = '4',
}

View File

@ -0,0 +1,8 @@
import { useSearchParams } from 'umi';
export const useKnowledgeBaseId = (): string => {
const [searchParams] = useSearchParams();
const knowledgeBaseId = searchParams.get('id');
return knowledgeBaseId || '';
};

View File

@ -0,0 +1,21 @@
import { useLocation } from 'umi';
export enum SegmentIndex {
Second = '2',
Third = '3',
}
export const useSegmentedPathName = (index: SegmentIndex) => {
const { pathname } = useLocation();
const pathArray = pathname.split('/');
return pathArray[index] || '';
};
export const useSecondPathName = () => {
return useSegmentedPathName(SegmentIndex.Second);
};
export const useThirdPathName = () => {
return useSegmentedPathName(SegmentIndex.Third);
};

View File

@ -0,0 +1,26 @@
import { RunningStatus } from '@/constants/knowledge';
export interface IKnowledgeFile {
chunk_num: number;
create_date: string;
create_time: number;
created_by: string;
id: string;
kb_id: string;
location: string;
name: string;
parser_id: string;
process_begin_at?: any;
process_duation: number;
progress: number; // parsing process
progress_msg: string; // parsing log
run: RunningStatus; // parsing status
size: number;
source_type: string;
status: string; // enabled
thumbnail?: any; // base64
token_num: number;
type: string;
update_date: string;
update_time: number;
}

View File

@ -14,11 +14,11 @@ import {
Spin,
Switch,
} from 'antd';
import { debounce } from 'lodash';
import React, { useCallback, useEffect, useState } from 'react';
import { useDispatch, useNavigate, useSelector } from 'umi';
import { useDispatch, useSearchParams, useSelector } from 'umi';
import CreateModal from './components/createModal';
import { debounce } from 'lodash';
import styles from './index.less';
interface PayloadType {
@ -27,18 +27,13 @@ interface PayloadType {
available_int?: number;
}
interface IProps {
doc_id: string;
}
const Chunk = ({ doc_id }: IProps) => {
const Chunk = () => {
const dispatch = useDispatch();
const chunkModel = useSelector((state: any) => state.chunkModel);
const [keywords, SetKeywords] = useState('');
const [available_int, setAvailableInt] = useState(-1);
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [pagination, setPagination] = useState({ page: 1, size: 30 });
// const [datas, setDatas] = useState(data)
const { data = [], total, chunk_id, isShowCreateModal } = chunkModel;
const effects = useSelector((state: any) => state.loading.effects);
const loading = getOneNamespaceEffectsLoading('chunkModel', effects, [
@ -46,10 +41,11 @@ const Chunk = ({ doc_id }: IProps) => {
'chunk_list',
'switch_chunk',
]);
const documentId: string = searchParams.get('doc_id') || '';
const getChunkList = (value?: string) => {
const payload: PayloadType = {
doc_id,
doc_id: documentId,
keywords: value || keywords,
available_int,
};
@ -81,7 +77,7 @@ const Chunk = ({ doc_id }: IProps) => {
payload: {
isShowCreateModal: true,
chunk_id,
doc_id,
doc_id: documentId,
},
});
getChunkList();
@ -100,7 +96,7 @@ const Chunk = ({ doc_id }: IProps) => {
payload: {
chunk_ids: [id],
available_int: Number(available_int),
doc_id,
doc_id: documentId,
},
});
@ -109,7 +105,7 @@ const Chunk = ({ doc_id }: IProps) => {
useEffect(() => {
getChunkList();
}, [doc_id, available_int, pagination]);
}, [documentId, available_int, pagination]);
const debounceChange = debounce(getChunkList, 300);
const debounceCallback = useCallback(
@ -270,7 +266,7 @@ const Chunk = ({ doc_id }: IProps) => {
</div>
</div>
<CreateModal
doc_id={doc_id}
doc_id={documentId}
isShowCreateModal={isShowCreateModal}
chunk_id={chunk_id}
getChunkList={getChunkList}

View File

@ -0,0 +1,7 @@
import { Outlet } from 'umi';
export const KnowledgeDataset = () => {
return <Outlet></Outlet>;
};
export default KnowledgeDataset;

View File

@ -0,0 +1,5 @@
const KnowledgeUploadFile = () => {
return <div>KnowledgeUploadFile</div>;
};
export default KnowledgeUploadFile;

View File

@ -0,0 +1,17 @@
import { RunningStatus } from '@/constants/knowledge';
export const RunningStatusMap = {
[RunningStatus.UNSTART]: {
label: 'UNSTART',
color: 'cyan',
},
[RunningStatus.RUNNING]: {
label: 'Parsing',
color: 'blue',
},
[RunningStatus.CANCEL]: { label: 'CANCEL', color: 'orange' },
[RunningStatus.DONE]: { label: 'SUCCESS', color: 'geekblue' },
[RunningStatus.FAIL]: { label: 'FAIL', color: 'red' },
};
export * from '@/constants/knowledge';

View File

@ -13,9 +13,9 @@ interface kFProps {
const FileCreatingModal: React.FC<kFProps> = ({ getKfList, kb_id }) => {
const dispatch = useDispatch();
const [form] = Form.useForm();
const kFModel = useSelector((state: any) => state.kFModel);
const { isShowCEFwModal } = kFModel;
const [form] = Form.useForm();
const { t } = useTranslation();
const handleCancel = () => {
@ -26,7 +26,8 @@ const FileCreatingModal: React.FC<kFProps> = ({ getKfList, kb_id }) => {
},
});
};
const handleOk = async () => {
const createDocument = async () => {
try {
const values = await form.validateFields();
const retcode = await dispatch<any>({
@ -44,9 +45,13 @@ const FileCreatingModal: React.FC<kFProps> = ({ getKfList, kb_id }) => {
}
};
const handleOk = async () => {
createDocument();
};
return (
<Modal
title="Basic Modal"
title="File Name"
open={isShowCEFwModal}
onOk={handleOk}
onCancel={handleCancel}
@ -54,15 +59,15 @@ const FileCreatingModal: React.FC<kFProps> = ({ getKfList, kb_id }) => {
<Form
form={form}
name="validateOnly"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
style={{ maxWidth: 600 }}
autoComplete="off"
>
<Form.Item<FieldType>
label="文件名"
label="File Name"
name="name"
rules={[{ required: true, message: 'Please input value!' }]}
rules={[{ required: true, message: 'Please input name!' }]}
>
<Input />
</Form.Item>

View File

@ -1,28 +1,34 @@
.datasetWrapper {
padding: 30px;
flex: 1;
}
.filter {
height: 32px;
display: flex;
margin: 10px 0;
justify-content: space-between;
height: 32px;
display: flex;
margin: 10px 0;
justify-content: space-between;
padding: 24px 20px;
.search {
flex: 1;
}
// .search {
// flex: 1;
// }
.operate {
width: 200px;
}
// .operate {
// width: 200px;
// }
}
.img {
height: 16px;
width: 16px;
margin-right: 6px;
height: 16px;
width: 16px;
margin-right: 6px;
}
.column {
min-width: 200px
min-width: 200px;
}
.tochunks {
cursor: pointer;
}
cursor: pointer;
}

View File

@ -1,37 +1,38 @@
import { KnowledgeRouteKey } from '@/constants/knowledge';
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil';
import { DownOutlined } from '@ant-design/icons';
import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Button, Dropdown, Input, Space, Switch, Table } from 'antd';
import {
Button,
Divider,
Dropdown,
Input,
Space,
Switch,
Table,
Tag,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { debounce } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useNavigate, useSelector } from 'umi';
import CreateEPModal from './createEFileModal';
import styles from './index.less';
import ParsingActionCell from './parsing-action-cell';
import ParsingStatusCell from './parsing-status-cell';
import RenameModal from './rename-modal';
import SegmentSetModal from './segmentSetModal';
import UploadFile from './upload';
interface DataType {
name: string;
chunk_num: string;
token_num: number;
update_date: string;
size: string;
status: string;
id: string;
parser_id: string;
}
interface KFProps {
kb_id: string;
}
const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
const KnowledgeFile = () => {
const dispatch = useDispatch();
const kFModel = useSelector((state: any) => state.kFModel);
const effects = useSelector((state: any) => state.loading.effects);
const { data } = kFModel;
const knowledgeBaseId = useKnowledgeBaseId();
const loading = getOneNamespaceEffectsLoading('kFModel', effects, [
'getKfList',
'updateDocumentStatus',
@ -43,7 +44,7 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
const getKfList = (keywords?: string) => {
const payload = {
kb_id,
kb_id: knowledgeBaseId,
keywords,
};
if (!keywords) {
@ -56,10 +57,10 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
};
useEffect(() => {
if (kb_id) {
if (knowledgeBaseId) {
getKfList();
}
}, [kb_id]);
}, [knowledgeBaseId]);
const debounceChange = debounce(getKfList, 300);
const debounceCallback = useCallback(
@ -79,7 +80,7 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
payload: {
doc_id,
status: Number(e),
kb_id,
kb_id: knowledgeBaseId,
},
});
};
@ -88,7 +89,7 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
type: 'kFModel/document_rm',
payload: {
doc_id,
kb_id,
kb_id: knowledgeBaseId,
},
});
};
@ -109,13 +110,14 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
},
});
};
const actionItems: MenuProps['items'] = useMemo(() => {
return [
{
key: '1',
label: (
<div>
<UploadFile kb_id={kb_id} getKfList={getKfList} />
<UploadFile kb_id={knowledgeBaseId} getKfList={getKfList} />
</div>
),
},
@ -132,7 +134,7 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
// disabled: true,
},
];
}, [kb_id]);
}, [knowledgeBaseId]);
const chunkItems: MenuProps['items'] = [
{
key: '1',
@ -158,14 +160,21 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
// disabled: true,
},
];
const toChunk = (id: string) => {
navigate(
`/knowledge/${KnowledgeRouteKey.Dataset}?id=${kb_id}&doc_id=${id}`,
`/knowledge/${KnowledgeRouteKey.Dataset}/chunk?id=${knowledgeBaseId}&doc_id=${id}`,
);
};
const columns: ColumnsType<DataType> = [
const setDocumentAndParserId = (record: IKnowledgeFile) => () => {
setDocId(record.id);
setParserId(record.parser_id);
};
const columns: ColumnsType<IKnowledgeFile> = [
{
title: '名称',
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text: any, { id }) => (
@ -178,32 +187,30 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
{text}
</div>
),
className: `${styles.column}`,
},
{
title: '数据总量',
title: 'Chunk Number',
dataIndex: 'chunk_num',
key: 'chunk_num',
className: `${styles.column}`,
},
{
title: 'Tokens',
dataIndex: 'token_num',
key: 'token_num',
className: `${styles.column}`,
title: 'Upload Date',
dataIndex: 'create_date',
key: 'create_date',
},
{
title: '文件大小',
dataIndex: 'size',
key: 'size',
className: `${styles.column}`,
title: 'Parsing Status',
dataIndex: 'run',
key: 'run',
render: (text, record) => {
return <ParsingStatusCell record={record}></ParsingStatusCell>;
},
},
{
title: '状态',
title: 'Enabled',
key: 'status',
dataIndex: 'status',
className: `${styles.column}`,
render: (_, { status: string, id }) => (
render: (_, { status, id }) => (
<>
<Switch
defaultChecked={status === '1'}
@ -217,58 +224,65 @@ const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
{
title: 'Action',
key: 'action',
className: `${styles.column}`,
render: (_, record) => (
<Space size="middle">
<Dropdown menu={{ items: chunkItems }} trigger={['click']}>
<a
onClick={() => {
setDocId(record.id);
setParserId(record.parser_id);
}}
>
<DownOutlined />
</a>
</Dropdown>
</Space>
<ParsingActionCell
documentId={doc_id}
knowledgeBaseId={knowledgeBaseId}
setDocumentAndParserId={setDocumentAndParserId(record)}
record={record}
></ParsingActionCell>
),
},
];
const finalColumns = columns.map((x) => ({
...x,
className: `${styles.column}`,
}));
return (
<>
<div className={styles.datasetWrapper}>
<h3>Dataset</h3>
<p>Hey, don't forget to adjust the chunk after adding the dataset! 😉</p>
<Divider></Divider>
<div className={styles.filter}>
<div className="search">
<Space>
<h3>Total</h3>
<Tag color="purple">100 files</Tag>
</Space>
<Space>
<Input
placeholder="搜索"
placeholder="Seach your files"
value={inputValue}
style={{ width: 220 }}
allowClear
onChange={handleInputChange}
prefix={<SearchOutlined />}
/>
</div>
<div className="operate">
<Dropdown menu={{ items: actionItems }} trigger={['click']}>
<a>
<DownOutlined />
</a>
<Button type="primary" icon={<PlusOutlined />}>
Add file
</Button>
</Dropdown>
</div>
</Space>
</div>
<Table
rowKey="id"
columns={columns}
columns={finalColumns}
dataSource={data}
loading={loading}
pagination={false}
scroll={{ scrollToFirstRowOnChange: true, x: true }}
scroll={{ scrollToFirstRowOnChange: true, x: true, y: 'fill' }}
/>
<CreateEPModal getKfList={getKfList} kb_id={kb_id} />
<CreateEPModal getKfList={getKfList} kb_id={knowledgeBaseId} />
<SegmentSetModal
getKfList={getKfList}
parser_id={parser_id}
doc_id={doc_id}
/>
</>
<RenameModal></RenameModal>
</div>
);
};

View File

@ -1,14 +1,19 @@
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import kbService from '@/services/kbService';
import { message } from 'antd';
import omit from 'lodash/omit';
import pick from 'lodash/pick';
import { Nullable } from 'typings';
import { DvaModel } from 'umi';
export interface KFModelState {
isShowCEFwModal: boolean;
isShowTntModal: boolean;
isShowSegmentSetModal: boolean;
isShowRenameModal: boolean;
tenantIfo: any;
data: any[];
data: IKnowledgeFile[];
currentRecord: Nullable<IKnowledgeFile>;
}
const model: DvaModel<KFModelState> = {
@ -17,8 +22,10 @@ const model: DvaModel<KFModelState> = {
isShowCEFwModal: false,
isShowTntModal: false,
isShowSegmentSetModal: false,
isShowRenameModal: false,
tenantIfo: {},
data: [],
currentRecord: null,
},
reducers: {
updateState(state, { payload }) {
@ -27,6 +34,12 @@ const model: DvaModel<KFModelState> = {
...payload,
};
},
setIsShowRenameModal(state, { payload }) {
return { ...state, isShowRenameModal: payload };
},
setCurrentRecord(state, { payload }) {
return { ...state, currentRecord: payload };
},
},
subscriptions: {
setup({ dispatch, history }) {
@ -99,6 +112,26 @@ const model: DvaModel<KFModelState> = {
});
}
},
*document_rename({ payload = {} }, { call, put }) {
const { data } = yield call(
kbService.document_rename,
omit(payload, ['kb_id']),
);
const { retcode, data: res, retmsg } = data;
if (retcode === 0) {
message.success('rename success');
yield put({
type: 'setIsShowRenameModal',
payload: false,
});
yield put({
type: 'getKfList',
payload: { kb_id: payload.kb_id },
});
}
return retcode;
},
*document_create({ payload = {} }, { call, put }) {
const { data, response } = yield call(kbService.document_create, payload);
const { retcode, data: res, retmsg } = data;

View File

@ -0,0 +1,88 @@
import showDeleteConfirm from '@/components/deleting-confirm';
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import { DeleteOutlined, EditOutlined, ToolOutlined } from '@ant-design/icons';
import { Button, Dropdown, MenuProps, Space, Tooltip } from 'antd';
import { useDispatch } from 'umi';
interface IProps {
documentId: string;
knowledgeBaseId: string;
record: IKnowledgeFile;
setDocumentAndParserId: () => void;
}
const ParsingActionCell = ({
documentId,
knowledgeBaseId,
record,
setDocumentAndParserId,
}: IProps) => {
const dispatch = useDispatch();
const removeDocument = () => {
dispatch({
type: 'kFModel/document_rm',
payload: {
doc_id: documentId,
kb_id: knowledgeBaseId,
},
});
};
const onRmDocument = () => {
showDeleteConfirm({ onOk: removeDocument });
};
const setCurrentRecord = () => {
dispatch({
type: 'kFModel/setCurrentRecord',
payload: record,
});
};
const showSegmentSetModal = () => {
dispatch({
type: 'kFModel/updateState',
payload: {
isShowSegmentSetModal: true,
},
});
};
const showRenameModal = () => {
setCurrentRecord();
dispatch({
type: 'kFModel/setIsShowRenameModal',
payload: true,
});
};
const onRename = () => {};
const chunkItems: MenuProps['items'] = [
{
key: '1',
label: (
<div>
<Button type="link" onClick={showSegmentSetModal}>
</Button>
</div>
),
},
];
return (
<Space size={'middle'}>
<Dropdown menu={{ items: chunkItems }} trigger={['click']}>
<ToolOutlined size={20} onClick={setDocumentAndParserId} />
</Dropdown>
<Tooltip title="Rename">
<EditOutlined size={20} onClick={showRenameModal} />
</Tooltip>
<DeleteOutlined size={20} onClick={onRmDocument} />
</Space>
);
};
export default ParsingActionCell;

View File

@ -0,0 +1,3 @@
.popover-content {
width: 300px;
}

View File

@ -0,0 +1,68 @@
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
import { Badge, DescriptionsProps, Flex, Popover, Space, Tag } from 'antd';
import { RunningStatus, RunningStatusMap } from '../constant';
import styles from './index.less';
interface IProps {
record: IKnowledgeFile;
}
const PopoverContent = ({ record }: IProps) => {
const items: DescriptionsProps['items'] = [
{
key: 'process_begin_at',
label: 'Process Begin At',
children: record.process_begin_at,
},
{
key: 'process_duation',
label: 'Process Duration',
children: record.process_duation,
},
{
key: 'progress_msg',
label: 'Progress Msg',
children: record.progress_msg,
},
];
return (
<Flex vertical className={styles['popover-content']}>
{items.map((x) => {
return (
<div>
<b>{x.label}:</b>
<p>{x.children}</p>
</div>
);
})}
</Flex>
);
};
export const ParsingStatusCell = ({ record }: IProps) => {
const text = record.run;
const runningStatus = RunningStatusMap[text];
const isRunning = text === RunningStatus.RUNNING;
return (
<Popover
content={isRunning && <PopoverContent record={record}></PopoverContent>}
>
<Tag color={runningStatus.color}>
{isRunning ? (
<Space>
<Badge color={runningStatus.color} />
`${runningStatus.label}${record.progress * 100}%`
</Space>
) : (
runningStatus.label
)}
</Tag>
</Popover>
);
};
export default ParsingStatusCell;

View File

@ -0,0 +1,88 @@
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
import { Form, Input, Modal } from 'antd';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'umi';
const RenameModal = () => {
const [form] = Form.useForm();
const dispatch = useDispatch();
const kFModel = useSelector((state: any) => state.kFModel);
const loading = useSelector(
(state: any) => state.loading.effects['kFModel/document_rename'],
);
const knowledgeBaseId = useKnowledgeBaseId();
const isModalOpen = kFModel.isShowRenameModal;
const initialName = kFModel.currentRecord?.name;
const documentId = kFModel.currentRecord?.id;
type FieldType = {
name?: string;
};
const closeModal = () => {
dispatch({
type: 'kFModel/setIsShowRenameModal',
payload: false,
});
};
const handleOk = async () => {
const ret = await form.validateFields();
dispatch({
type: 'kFModel/document_rename',
payload: {
doc_id: documentId,
name: ret.name,
kb_id: knowledgeBaseId,
},
});
};
const handleCancel = () => {
closeModal();
};
const onFinish = (values: any) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo);
};
useEffect(() => {
form.setFieldValue('name', initialName);
}, [initialName, documentId]);
return (
<Modal
title="Rename"
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
okButtonProps={{ loading }}
>
<Form
name="basic"
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
style={{ maxWidth: 600 }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
form={form}
>
<Form.Item<FieldType>
label="Name"
name="name"
rules={[{ required: true, message: 'Please input name!' }]}
>
<Input />
</Form.Item>
</Form>
</Modal>
);
};
export default RenameModal;

View File

@ -41,7 +41,6 @@ const SegmentSetModal: React.FC<kFProps> = ({
};
const handleOk = async () => {
console.log(1111, selectedTag);
const retcode = await dispatch<any>({
type: 'kFModel/document_change_parser',
payload: {

View File

@ -1,3 +1,5 @@
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { api_host } from '@/utils/api';
import { DeleteOutlined, MinusSquareOutlined } from '@ant-design/icons';
import type { PaginationProps } from 'antd';
@ -12,18 +14,14 @@ import {
Spin,
Switch,
} from 'antd';
import { debounce } from 'lodash';
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'umi';
import CreateModal from '../knowledge-chunk/components/createModal';
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { debounce } from 'lodash';
import styles from './index.less';
interface chunkProps {
kb_id: string;
}
const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => {
const KnowledgeSearching = () => {
const dispatch = useDispatch();
const kSearchModel = useSelector((state: any) => state.kSearchModel);
const chunkModel = useSelector((state: any) => state.chunkModel);
@ -31,6 +29,7 @@ const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => {
'chunk_list',
'switch_chunk',
]);
const knowledgeBaseId = useKnowledgeBaseId();
const {
data = [],
@ -46,7 +45,7 @@ const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => {
dispatch({
type: 'kSearchModel/chunk_list',
payload: {
kb_id,
kb_id: knowledgeBaseId,
},
});
};
@ -55,7 +54,7 @@ const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => {
type: 'kSearchModel/rm_chunk',
payload: {
chunk_ids: [id],
kb_id,
kb_id: knowledgeBaseId,
},
});
};
@ -93,7 +92,7 @@ const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => {
dispatch({
type: 'kSearchModel/getKfList',
payload: {
kb_id,
kb_id: knowledgeBaseId,
},
});
}, []);
@ -106,7 +105,7 @@ const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => {
chunk_ids: [chunk_id],
doc_id,
available_int,
kb_id,
kb_id: knowledgeBaseId,
},
});
};

View File

@ -1,6 +1,7 @@
import { KnowledgeRouteKey } from '@/constants/knowledge';
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
import { Button, Form, Input, Radio, Select, Space, Tag } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useDispatch, useNavigate, useSelector } from 'umi';
import styles from './index.less';
@ -13,10 +14,7 @@ const layout = {
const { Option } = Select;
/* eslint-disable no-template-curly-in-string */
interface kSProps {
kb_id: string;
}
const KnowledgeSetting: React.FC<kSProps> = ({ kb_id }) => {
const KnowledgeSetting = () => {
const dispatch = useDispatch();
const settingModel = useSelector((state: any) => state.settingModel);
let navigate = useNavigate();
@ -25,17 +23,18 @@ const KnowledgeSetting: React.FC<kSProps> = ({ kb_id }) => {
const [form] = Form.useForm();
const [selectedTag, setSelectedTag] = useState('');
const values = Form.useWatch([], form);
const knowledgeBaseId = useKnowledgeBaseId();
const getTenantInfo = useCallback(async () => {
dispatch({
type: 'settingModel/getTenantInfo',
payload: {},
});
if (kb_id) {
if (knowledgeBaseId) {
const data = await dispatch<any>({
type: 'kSModel/getKbDetail',
payload: {
kb_id,
kb_id: knowledgeBaseId,
},
});
if (data.retcode === 0) {
@ -44,19 +43,19 @@ const KnowledgeSetting: React.FC<kSProps> = ({ kb_id }) => {
setSelectedTag(data.data.parser_id);
}
}
}, [kb_id]);
}, [knowledgeBaseId]);
const onFinish = async () => {
try {
await form.validateFields();
if (kb_id) {
if (knowledgeBaseId) {
dispatch({
type: 'kSModel/updateKb',
payload: {
...values,
parser_id: selectedTag,
kb_id,
kb_id: knowledgeBaseId,
embd_id: undefined,
},
});
@ -69,7 +68,9 @@ const KnowledgeSetting: React.FC<kSProps> = ({ kb_id }) => {
},
});
retcode === 0 &&
navigate(`/knowledge/${KnowledgeRouteKey.Dataset}?id=${kb_id}`);
navigate(
`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`,
);
}
} catch (error) {
console.warn(error);

View File

@ -1,12 +1,13 @@
import { ReactComponent as ConfigrationIcon } from '@/assets/svg/knowledge-configration.svg';
import { ReactComponent as DatasetIcon } from '@/assets/svg/knowledge-dataset.svg';
import { ReactComponent as TestingIcon } from '@/assets/svg/knowledge-testing.svg';
import { useSecondPathName } from '@/hooks/routeHook';
import { getWidth } from '@/utils';
import { AntDesignOutlined } from '@ant-design/icons';
import { Avatar, Menu, MenuProps, Space } from 'antd';
import classNames from 'classnames';
import { useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams, useSelector } from 'umi';
import { useNavigate, useSelector } from 'umi';
import { KnowledgeRouteKey, routeMap } from '../../constant';
import styles from './index.less';
@ -14,8 +15,7 @@ const KnowledgeSidebar = () => {
const kAModel = useSelector((state: any) => state.kAModel);
const { id } = kAModel;
let navigate = useNavigate();
const params = useParams();
const activeKey = params.module || KnowledgeRouteKey.Dataset;
const activeKey = useSecondPathName();
const [windowWidth, setWindowWidth] = useState(getWidth());
const [collapsed, setCollapsed] = useState(false);
@ -56,8 +56,8 @@ const KnowledgeSidebar = () => {
<TestingIcon />,
),
getItem(
routeMap[KnowledgeRouteKey.Configration],
KnowledgeRouteKey.Configration,
routeMap[KnowledgeRouteKey.Configuration],
KnowledgeRouteKey.Configuration,
<ConfigrationIcon />,
),
];

View File

@ -3,7 +3,17 @@ import { KnowledgeRouteKey } from '@/constants/knowledge';
export const routeMap = {
[KnowledgeRouteKey.Dataset]: 'Dataset',
[KnowledgeRouteKey.Testing]: 'Retrieval testing',
[KnowledgeRouteKey.Configration]: 'Configuration',
[KnowledgeRouteKey.Configuration]: 'Configuration',
};
export enum KnowledgeDatasetRouteKey {
Chunk = 'chunk',
File = 'file',
}
export const datasetRouteMap = {
[KnowledgeDatasetRouteKey.Chunk]: 'Chunk',
[KnowledgeDatasetRouteKey.File]: 'File Upload',
};
export * from '@/constants/knowledge';

View File

@ -7,9 +7,12 @@
height: 100%;
background-color: rgba(247, 248, 250, 1);
padding: 16px 20px 28px 40px;
display: flex;
flex-direction: column;
}
.content {
background-color: white;
margin-top: 16px;
// flex: 1;
}
}

View File

@ -1,45 +1,60 @@
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
import { useSecondPathName, useThirdPathName } from '@/hooks/routeHook';
import { Breadcrumb } from 'antd';
import { ItemType } from 'antd/es/breadcrumb/Breadcrumb';
import { useEffect, useMemo } from 'react';
import {
useDispatch,
useLocation,
useNavigate,
useParams,
useSelector,
} from 'umi';
import Chunk from './components/knowledge-chunk';
import File from './components/knowledge-file';
import Search from './components/knowledge-search';
import Setting from './components/knowledge-setting';
import { Link, Outlet, useDispatch, useLocation, useNavigate } from 'umi';
import Siderbar from './components/knowledge-sidebar';
import { KnowledgeRouteKey, routeMap } from './constant';
import {
KnowledgeDatasetRouteKey,
KnowledgeRouteKey,
datasetRouteMap,
routeMap,
} from './constant';
import styles from './index.less';
const KnowledgeAdding = () => {
const dispatch = useDispatch();
const kAModel = useSelector((state: any) => state.kAModel);
const navigate = useNavigate();
const { id, doc_id } = kAModel;
const knowledgeBaseId = useKnowledgeBaseId();
const location = useLocation();
const params = useParams();
const activeKey: KnowledgeRouteKey =
(params.module as KnowledgeRouteKey) || KnowledgeRouteKey.Dataset;
(useSecondPathName() as KnowledgeRouteKey) || KnowledgeRouteKey.Dataset;
const datasetActiveKey: KnowledgeDatasetRouteKey =
useThirdPathName() as KnowledgeDatasetRouteKey;
const gotoList = () => {
navigate('/knowledge');
};
const breadcrumbItems = useMemo(() => {
return [
const breadcrumbItems: ItemType[] = useMemo(() => {
const items: ItemType[] = [
{
title: <a onClick={gotoList}>Knowledge Base</a>,
},
{
title: routeMap[activeKey],
title: datasetActiveKey ? (
<Link
to={`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`}
>
{routeMap[activeKey]}
</Link>
) : (
routeMap[activeKey]
),
},
];
}, [activeKey]);
if (datasetActiveKey) {
items.push({
title: datasetRouteMap[datasetActiveKey],
});
}
return items;
}, [activeKey, datasetActiveKey]);
useEffect(() => {
const search: string = location.search.slice(1);
@ -65,16 +80,7 @@ const KnowledgeAdding = () => {
<div className={styles.contentWrapper}>
<Breadcrumb items={breadcrumbItems} />
<div className={styles.content}>
{activeKey === KnowledgeRouteKey.Dataset && !doc_id && (
<File kb_id={id} />
)}
{activeKey === KnowledgeRouteKey.Configration && (
<Setting kb_id={id} />
)}
{activeKey === KnowledgeRouteKey.Testing && <Search kb_id={id} />}
{activeKey === KnowledgeRouteKey.Dataset && !!doc_id && (
<Chunk doc_id={doc_id} />
)}
<Outlet></Outlet>
</div>
</div>
</div>

View File

@ -1,6 +1,7 @@
import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
import { KnowledgeRouteKey } from '@/constants/knowledge';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Col, Row, Space } from 'antd';
import { Button, Flex, Space } from 'antd';
import { useCallback, useEffect } from 'react';
import { useDispatch, useNavigate, useSelector } from 'umi';
import styles from './index.less';
@ -20,7 +21,7 @@ const Knowledge = () => {
}, []);
const handleAddKnowledge = () => {
navigate(`add/setting`);
navigate(`/knowledge/${KnowledgeRouteKey.Configuration}`);
};
useEffect(() => {
@ -50,7 +51,7 @@ const Knowledge = () => {
</Button>
</Space>
</div>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
{/* <Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
{data.map((item: any) => {
return (
<Col
@ -58,14 +59,19 @@ const Knowledge = () => {
key={item.name}
xs={24}
sm={12}
md={8}
lg={6}
md={10}
lg={8}
>
<KnowledgeCard item={item}></KnowledgeCard>
</Col>
);
})}
</Row>
</Row> */}
<Flex gap="large" wrap="wrap">
{data.map((item: any) => {
return <KnowledgeCard item={item} key={item.name}></KnowledgeCard>;
})}
</Flex>
</div>
);
};

View File

@ -26,7 +26,7 @@
border: 1px solid rgba(234, 236, 240, 1);
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
padding: 24px;
min-width: 300px;
max-width: 300px;
cursor: pointer;
.titleWrapper {

View File

@ -16,8 +16,37 @@ const routes = [
component: '@/pages/knowledge',
},
{
path: '/knowledge/:module',
path: '/knowledge',
component: '@/pages/add-knowledge',
routes: [
{
path: '/knowledge/dataset',
component: '@/pages/add-knowledge/components/knowledge-dataset',
routes: [
{
path: '/knowledge/dataset',
component: '@/pages/add-knowledge/components/knowledge-file',
},
{
path: '/knowledge/dataset/upload',
component:
'@/pages/add-knowledge/components/knowledge-dataset/knowledge-upload-file',
},
{
path: '/knowledge/dataset/chunk',
component: '@/pages/add-knowledge/components/knowledge-chunk',
},
],
},
{
path: '/knowledge/configuration',
component: '@/pages/add-knowledge/components/knowledge-setting',
},
{
path: '/knowledge/testing',
component: '@/pages/add-knowledge/components/knowledge-search',
},
],
},
{
path: '/chat',

View File

@ -20,6 +20,7 @@ const {
switch_chunk,
rm_chunk,
retrieval_test,
document_rename,
} = api;
const methods = {
@ -57,6 +58,10 @@ const methods = {
url: document_rm,
method: 'post',
},
document_rename: {
url: document_rename,
method: 'post',
},
document_create: {
url: document_create,
method: 'post',

View File

@ -33,11 +33,12 @@ export default {
rm_chunk: `${api_host}/chunk/rm`,
retrieval_test: `${api_host}/chunk/retrieval_test`,
// 上传
// 文件管理
upload: `${api_host}/document/upload`,
get_document_list: `${api_host}/document/list`,
document_change_status: `${api_host}/document/change_status`,
document_rm: `${api_host}/document/rm`,
document_rename: `${api_host}/document/rename`,
document_create: `${api_host}/document/create`,
document_change_parser: `${api_host}/document/change_parser`,
};

4
web/typings.d.ts vendored
View File

@ -1,2 +1,4 @@
import 'umi/typings';
declare module 'lodash'
declare module 'lodash';
export type Nullable<T> = T | null;